From f0965a8d8bd600a88592e08bb7ec06017ae887c0 Mon Sep 17 00:00:00 2001
From: Matthias Urhahn <matthias.urhahn@sap.com>
Date: Mon, 21 Jun 2021 18:05:26 +0200
Subject: [PATCH] Implement algorithm to find highest priority certificate
 (EXPOSUREAPP-7948) (#3510)

* Implement algorithm to find highest priority certificate.

* Fix conflicts and adjust tests.

Co-authored-by: Mohamed Metwalli <mohamed.metwalli@sap.com>
---
 .../ui/TestCertificateDetailsFragmentTest.kt  |   7 +-
 .../common/certificate/CwaCovidCertificate.kt |   2 +
 .../person/core/PersonCertificates.kt         |   2 +-
 .../core/PersonCertificatesExtensions.kt      | 229 ++++++++++++++++++
 .../person/core/PersonCertificatesProvider.kt |   7 +-
 .../recovery/core/RecoveryCertificate.kt      |   3 +
 .../storage/RecoveryCertificateContainer.kt   |   3 +
 .../test/core/TestCertificate.kt              |   3 +
 .../core/storage/TestCertificateContainer.kt  |   4 +
 .../core/VaccinationCertificate.kt            |   3 +
 .../storage/VaccinationContainer.kt           |   3 +
 .../core/PersonCertificatesProviderTest.kt    |   4 +
 .../ui/overview/PersonCertificatesData.kt     |   5 +
 .../overview/PersonOverviewViewModelTest.kt   |  34 ++-
 14 files changed, 297 insertions(+), 12 deletions(-)
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesExtensions.kt

diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateDetailsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateDetailsFragmentTest.kt
index 8f545721a..f283390d3 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateDetailsFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateDetailsFragmentTest.kt
@@ -14,15 +14,17 @@ import dagger.Module
 import dagger.android.ContributesAndroidInjector
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier
+import de.rki.coronawarnapp.covidcertificate.common.certificate.TestDccV1
 import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
 import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId
 import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
-import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsViewModel
 import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragment
 import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragmentArgs
+import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsViewModel
 import io.mockk.MockKAnnotations
 import io.mockk.every
 import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
 import org.joda.time.DateTime
 import org.joda.time.Instant
 import org.joda.time.LocalDate
@@ -88,6 +90,9 @@ class TestCertificateDetailsFragmentTest : BaseUITest() {
         val testDate = DateTime.parse("12.05.2021 19:00", formatter).toInstant()
         return MutableLiveData(
             object : TestCertificate {
+                override val rawCertificate: TestDccV1
+                    get() = mockk()
+
                 override val containerId: TestCertificateContainerId
                     get() = TestCertificateContainerId("identifier")
                 override val targetName: String
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CwaCovidCertificate.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CwaCovidCertificate.kt
index 8f0481ca1..bf337960d 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CwaCovidCertificate.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CwaCovidCertificate.kt
@@ -27,4 +27,6 @@ interface CwaCovidCertificate {
      * The ID of the container holding this certificate in the CWA.
      */
     val containerId: CertificateContainerId
+
+    val rawCertificate: DccV1.MetaData
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificates.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificates.kt
index 0c085098a..2567379ee 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificates.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificates.kt
@@ -11,5 +11,5 @@ data class PersonCertificates(
         get() = certificates.first().personIdentifier
 
     val highestPriorityCertificate: CwaCovidCertificate
-        get() = certificates.first()
+        get() = certificates.findHighestPriorityCertificate()
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesExtensions.kt
new file mode 100644
index 000000000..18fcc6afd
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesExtensions.kt
@@ -0,0 +1,229 @@
+package de.rki.coronawarnapp.covidcertificate.person.core
+
+import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate
+import de.rki.coronawarnapp.covidcertificate.recovery.core.RecoveryCertificate
+import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
+import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
+import org.joda.time.Days
+import org.joda.time.Duration
+import org.joda.time.Instant
+import timber.log.Timber
+
+fun Collection<CwaCovidCertificate>.toCertificateSortOrder(): List<CwaCovidCertificate> {
+    return this.sortedBy { it.issuedAt }
+}
+
+/**
+ * 1
+ * PCR Test Certificate <= 48 hours
+ * Find Test Certificates (i.e. DGC with t[0]) where t[0].tt is set to LP6464-4 and the time difference between the
+ * time represented by t[0].sc and the current device time is <= 48 hours, sorted descending by t[0].sc
+ * (i.e. latest first).
+ * If there is one or more certificates matching these requirements,
+ * the first one is returned as a result of the operation.
+ */
+private fun Collection<CwaCovidCertificate>.rule1FindRecentPcrCertificate(
+    nowUtc: Instant
+): CwaCovidCertificate? = this
+    .filterIsInstance<TestCertificate>()
+    .filter { it.rawCertificate.test.testType == "LP6464-4" }
+    .filter { Duration(it.rawCertificate.test.sampleCollectedAt, nowUtc) <= Duration.standardHours(48) }
+    .maxByOrNull { it.rawCertificate.test.sampleCollectedAt }
+
+/**
+ * 2
+ * RAT Test Certificate <= 24 hours
+ * Find Test Certificates (i.e. DGC with t[0]) where t[0].tt is set to LP217198-3 and the time difference between
+ * the time represented by t[0].sc and the current device time is <= 24 hours, sorted descending by t[0].sc
+ * (i.e. latest first).
+ * If there is one or more certificates matching these requirements,
+ * the first one is returned as a result of the operation.
+ */
+private fun Collection<CwaCovidCertificate>.rule2FindRecentRaCertificate(
+    nowUtc: Instant
+): CwaCovidCertificate? = this
+    .filterIsInstance<TestCertificate>()
+    .filter { it.rawCertificate.test.testType == "LP217198-3" }
+    .filter { Duration(it.rawCertificate.test.sampleCollectedAt, nowUtc) <= Duration.standardHours(24) }
+    .maxByOrNull { it.rawCertificate.test.sampleCollectedAt }
+
+/**
+ * 3
+ * Series-completing Vaccination Certificate > 14 days:
+ * Find Vaccination Certificates (i.e. DGC with v[0]) where v[0].dn equal to v[0].sd and the time difference
+ * between the time represented by v[0].dt and the current device time is > 14 days, sorted descending by v[0].dt
+ * (i.e. latest first).
+ * If there is one or more certificates matching these requirements,
+ * the first one is returned as a result of the operation.
+ */
+private fun Collection<CwaCovidCertificate>.rule3FindRecentLastShot(
+    nowUtc: Instant
+): CwaCovidCertificate? = this
+    .filterIsInstance<VaccinationCertificate>()
+    .filter {
+        with(it.rawCertificate.vaccination) { doseNumber == totalSeriesOfDoses }
+    }
+    .filter {
+        Days.daysBetween(it.rawCertificate.vaccination.vaccinatedAt, nowUtc.toLocalDateUtc()).days > 14
+    }
+    .maxByOrNull { it.rawCertificate.vaccination.vaccinatedAt }
+
+/**
+ * 4
+ * Recovery Certificate <= 180 days
+ * Find Recovery Certificates (i.e. DGC with r[0]) where the time difference between the time
+ * represented by r[0].df and the current device time is <= 180 days, sorted descending by r[0].df
+ * i.e. latest first).
+ * If there is one or more certificates matching these requirements,
+ * the first one is returned as a result of the operation.
+ */
+private fun Collection<CwaCovidCertificate>.rule4findRecentRecovery(
+    nowUtc: Instant
+): CwaCovidCertificate? = this
+    .filterIsInstance<RecoveryCertificate>()
+    .filter {
+        Days.daysBetween(it.rawCertificate.recovery.validFrom, nowUtc.toLocalDateUtc()).days <= 180
+    }.maxByOrNull { it.rawCertificate.recovery.validFrom }
+
+/**
+ * 5
+ * Series-completing Vaccination Certificate <= 14 days
+ * Find Vaccination Certificates (i.e. DGC with v[0]) where v[0].dn equal to v[0].sd and the time difference
+ * between the time represented by v[0].dt and the current device time is <= 14 days,
+ * sorted descending by v[0].dt (i.e. latest first).
+ * If there is one or more certificates matching these requirements,
+ * the first one is returned as a result of the operation.
+ */
+private fun Collection<CwaCovidCertificate>.rule5findTooRecentFinalShot(
+    nowUtc: Instant
+): CwaCovidCertificate? = this
+    .filterIsInstance<VaccinationCertificate>()
+    .filter {
+        with(it.rawCertificate.vaccination) { doseNumber == totalSeriesOfDoses }
+    }
+    .filter {
+        Days.daysBetween(it.rawCertificate.vaccination.vaccinatedAt, nowUtc.toLocalDateUtc()).days <= 14
+    }
+    .maxByOrNull { it.rawCertificate.vaccination.vaccinatedAt }
+
+/**
+ * 6
+ * Other Vaccination Certificate
+ * Find Vaccination Certificates (i.e. DGC with v[0])sorted descending by v[0].dt (i.e. latest first).
+ * If there is one or more certificates matching these requirements,
+ * the first one is returned as a result of the operation.
+ */
+private fun Collection<CwaCovidCertificate>.rule6findOtherVaccinations(): CwaCovidCertificate? = this
+    .filterIsInstance<VaccinationCertificate>()
+    .maxByOrNull { it.rawCertificate.vaccination.vaccinatedAt }
+
+/**
+ * 7
+ * Recovery Certificate > 180 days
+ * Find Recovery Certificates (i.e. DGC with r[0]) where the time difference between the time represented by r[0].df
+ * and the current device time is > 180 days, sorted descending by r[0].df (i.e. latest first).
+ * If there is one or more certificates matching these requirements,
+ * the first one is returned as a result of the operation.
+ */
+private fun Collection<CwaCovidCertificate>.rule7FindOldRecovery(
+    nowUtc: Instant
+): CwaCovidCertificate? = this
+    .filterIsInstance<RecoveryCertificate>()
+    .filter {
+        Days.daysBetween(it.rawCertificate.recovery.validFrom, nowUtc.toLocalDateUtc()).days > 180
+    }
+    .maxByOrNull { it.rawCertificate.recovery.validFrom }
+
+/**
+ * 8
+ * PCR Test Certificate > 48 hours
+ * Find Test Certificates (i.e. DGC with t[0]) where t[0].tt is set to LP6464-4 and the time difference between
+ * the time represented by t[0].sc and the current device time is > 48 hours,
+ * sorted descending by t[0].sc (i.e. latest first).
+ * If there is one or more certificates matching these requirements,
+ * the first one is returned as a result of the operation.
+ */
+private fun Collection<CwaCovidCertificate>.rule8FindOldPcrTest(
+    nowUtc: Instant
+): CwaCovidCertificate? = this
+    .filterIsInstance<TestCertificate>()
+    .filter { it.rawCertificate.test.testType == "LP6464-4" }
+    .filter { Duration(it.rawCertificate.test.sampleCollectedAt, nowUtc) > Duration.standardHours(48) }
+    .maxByOrNull { it.rawCertificate.test.sampleCollectedAt }
+
+/**
+ * 9
+ * RAT Test Certificate > 24 hours
+ * Find Test Certificates (i.e. DGC with t[0]) where t[0].tt is set to LP217198-3 and the time difference between
+ * the time represented by t[0].sc and the current device time is > 24 hours,
+ * sorted descending by t[0].sc (i.e. latest first).
+ * If there is one or more certificates matching these requirements,
+ * the first one is returned as a result of the operation.
+ */
+private fun Collection<CwaCovidCertificate>.rule9FindOldRaTest(
+    nowUtc: Instant
+): CwaCovidCertificate? = this
+    .filterIsInstance<TestCertificate>()
+    .filter { it.rawCertificate.test.testType == "LP217198-3" }
+    .filter { Duration(it.rawCertificate.test.sampleCollectedAt, nowUtc) > Duration.standardHours(24) }
+    .maxByOrNull { it.rawCertificate.test.sampleCollectedAt }
+
+@Suppress("ReturnCount")
+fun Collection<CwaCovidCertificate>.findHighestPriorityCertificate(
+    nowUtc: Instant = Instant.now()
+): CwaCovidCertificate {
+    Timber.d("findHighestPriorityCertificate(nowUtc=%s): %s", nowUtc, this)
+
+    rule1FindRecentPcrCertificate(nowUtc)?.let {
+        Timber.d("Rule 1 match (PCR Test Certificate <= 48 hours): %s", it)
+        return it
+    }
+
+    rule2FindRecentRaCertificate(nowUtc)?.let {
+        Timber.d("Rule 2 match (RA Test Certificate <= 24 hours): %s", it)
+        return it
+    }
+
+    rule3FindRecentLastShot(nowUtc)?.let {
+        Timber.d("Rule 3 match (Series-completing Vaccination Certificate > 14 days): %s", it)
+        return it
+    }
+
+    rule4findRecentRecovery(nowUtc)?.let {
+        Timber.d("Rule 4 match (Recovery Certificate <= 180 days): %s", it)
+        return it
+    }
+
+    rule5findTooRecentFinalShot(nowUtc)?.let {
+        Timber.d("Rule 5 match (Series-completing Vaccination Certificate <= 14 days): %s", it)
+        return it
+    }
+
+    rule6findOtherVaccinations()?.let {
+        Timber.d("Rule 6 match (Other Vaccination Certificate): %s", it)
+        return it
+    }
+
+    rule7FindOldRecovery(nowUtc)?.let {
+        Timber.d("Rule 7 match (Recovery Certificate > 180 days): %s", it)
+        return it
+    }
+
+    rule8FindOldPcrTest(nowUtc)?.let {
+        Timber.d("Rule 8 match (PCR Test Certificate > 48 hours): %s", it)
+        return it
+    }
+
+    rule9FindOldRaTest(nowUtc)?.let {
+        Timber.d("Rule 9 match (RAT Test Certificate > 24 hours): %s", it)
+        return it
+    }
+
+    /**
+     * Fallback: return the first DGC from the set.
+     * Note that this fallback should never apply in a real scenario.
+     */
+    Timber.e("No priority match, this should not happen: %s", this)
+    return first()
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesProvider.kt
index 9621c268c..6487c5303 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesProvider.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesProvider.kt
@@ -45,17 +45,12 @@ class PersonCertificatesProvider @Inject constructor(
         mapping.entries.map { (personIdentifier, certs) ->
             Timber.tag(TAG).v("PersonCertificates for %s with %d certs.", personIdentifier, certs.size)
             PersonCertificates(
-                certificates = certs.toPrioritySortOrder(),
+                certificates = certs.toCertificateSortOrder(),
                 isCwaUser = personIdentifier == cwaUser,
             )
         }.toSet()
     }
 
-    fun Collection<CwaCovidCertificate>.toPrioritySortOrder(): List<CwaCovidCertificate> {
-        // TODO
-        return this.toList()
-    }
-
     /**
      * Set the current cwa user with regards to listed persons in the certificates tab.
      * After calling this [personCertificates] will emit new values.
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/RecoveryCertificate.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/RecoveryCertificate.kt
index a7c79343f..e47e5d6df 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/RecoveryCertificate.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/RecoveryCertificate.kt
@@ -1,6 +1,7 @@
 package de.rki.coronawarnapp.covidcertificate.recovery.core
 
 import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate
+import de.rki.coronawarnapp.covidcertificate.common.certificate.RecoveryDccV1
 import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId
 import org.joda.time.LocalDate
 
@@ -9,4 +10,6 @@ interface RecoveryCertificate : CwaCovidCertificate {
     val testedPositiveOn: LocalDate
     val validFrom: LocalDate
     val validUntil: LocalDate
+
+    override val rawCertificate: RecoveryDccV1
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/storage/RecoveryCertificateContainer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/storage/RecoveryCertificateContainer.kt
index 02e1191bf..580d5a548 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/storage/RecoveryCertificateContainer.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/storage/RecoveryCertificateContainer.kt
@@ -51,6 +51,9 @@ data class RecoveryCertificateContainer(
             override val containerId: RecoveryCertificateContainerId
                 get() = this@RecoveryCertificateContainer.containerId
 
+            override val rawCertificate: RecoveryDccV1
+                get() = certificate
+
             override val personIdentifier: CertificatePersonIdentifier
                 get() = certificate.personIdentifier
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificate.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificate.kt
index a26e13150..91aa04767 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificate.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificate.kt
@@ -1,6 +1,7 @@
 package de.rki.coronawarnapp.covidcertificate.test.core
 
 import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate
+import de.rki.coronawarnapp.covidcertificate.common.certificate.TestDccV1
 import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId
 import org.joda.time.Instant
 
@@ -28,4 +29,6 @@ interface TestCertificate : CwaCovidCertificate {
     val registeredAt: Instant
     val isUpdatingData: Boolean
     val isCertificateRetrievalPending: Boolean
+
+    override val rawCertificate: TestDccV1
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateContainer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateContainer.kt
index 198a497ed..c58017f61 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateContainer.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateContainer.kt
@@ -3,6 +3,7 @@ package de.rki.coronawarnapp.covidcertificate.test.core.storage
 import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier
 import de.rki.coronawarnapp.covidcertificate.common.certificate.DccQrCodeExtractor
 import de.rki.coronawarnapp.covidcertificate.common.certificate.DccV1Parser
+import de.rki.coronawarnapp.covidcertificate.common.certificate.TestDccV1
 import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
 import de.rki.coronawarnapp.covidcertificate.common.repository.CertificateRepoContainer
 import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId
@@ -73,6 +74,9 @@ data class TestCertificateContainer(
             override val containerId: TestCertificateContainerId
                 get() = this@TestCertificateContainer.containerId
 
+            override val rawCertificate: TestDccV1
+                get() = certificate
+
             override val personIdentifier: CertificatePersonIdentifier
                 get() = certificate.personIdentifier
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationCertificate.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationCertificate.kt
index 4cafa9cd7..39ae0f2a9 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationCertificate.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationCertificate.kt
@@ -1,6 +1,7 @@
 package de.rki.coronawarnapp.covidcertificate.vaccination.core
 
 import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate
+import de.rki.coronawarnapp.covidcertificate.common.certificate.VaccinationDccV1
 import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId
 import org.joda.time.LocalDate
 
@@ -15,5 +16,7 @@ interface VaccinationCertificate : CwaCovidCertificate {
     val doseNumber: Int
     val totalSeriesOfDoses: Int
 
+    override val rawCertificate: VaccinationDccV1
+
     val isFinalShot get() = doseNumber == totalSeriesOfDoses
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainer.kt
index 374607f40..d2b5ba3b3 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainer.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainer.kt
@@ -69,6 +69,9 @@ data class VaccinationContainer internal constructor(
         override val containerId: VaccinationCertificateContainerId
             get() = this@VaccinationContainer.containerId
 
+        override val rawCertificate: VaccinationDccV1
+            get() = certificate
+
         override val personIdentifier: CertificatePersonIdentifier
             get() = certificate.personIdentifier
 
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesProviderTest.kt
index 33fe14aed..ba2bd4596 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesProviderTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificatesProviderTest.kt
@@ -19,6 +19,7 @@ import io.mockk.verify
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.test.runBlockingTest
+import org.joda.time.Instant
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import testhelpers.BaseTest
@@ -34,18 +35,21 @@ class PersonCertificatesProviderTest : BaseTest() {
 
     private val vaccinatedPersonACertificate1 = mockk<VaccinationCertificate>().apply {
         every { personIdentifier } returns identifierA
+        every { issuedAt } returns Instant.EPOCH
     }
     private val vaccinatedPersonA = mockk<VaccinatedPerson>().apply {
         every { vaccinationCertificates } returns setOf(vaccinatedPersonACertificate1)
     }
     private val testWrapperACertificate = mockk<TestCertificate>().apply {
         every { personIdentifier } returns identifierA
+        every { issuedAt } returns Instant.EPOCH
     }
     private val testWrapperA = mockk<TestCertificateWrapper>().apply {
         every { testCertificate } returns testWrapperACertificate
     }
     private val recoveryWrapperACertificate = mockk<RecoveryCertificate>().apply {
         every { personIdentifier } returns identifierA
+        every { issuedAt } returns Instant.EPOCH
     }
     private val recoveryWrapperA = mockk<RecoveryCertificateWrapper>().apply {
         every { testCertificate } returns recoveryWrapperACertificate
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonCertificatesData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonCertificatesData.kt
index 2c950ba42..7725b08c8 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonCertificatesData.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonCertificatesData.kt
@@ -1,10 +1,12 @@
 package de.rki.coronawarnapp.covidcertificate.person.ui.overview
 
 import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier
+import de.rki.coronawarnapp.covidcertificate.common.certificate.TestDccV1
 import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
 import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId
 import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificates
 import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
+import io.mockk.mockk
 import org.joda.time.Instant
 import org.joda.time.LocalDate
 import java.util.UUID
@@ -72,4 +74,7 @@ fun testCertificate(
     override val certificateIssuer: String = "certificateIssuer"
     override val certificateCountry: String = "certificateCountry"
     override val certificateId: String = "certificateId"
+
+    override val rawCertificate: TestDccV1
+        get() = mockk()
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModelTest.kt
index c7b9d15b8..0a0a69a9d 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModelTest.kt
@@ -20,6 +20,7 @@ import io.mockk.impl.annotations.MockK
 import io.mockk.just
 import io.mockk.mockk
 import io.mockk.mockkStatic
+import io.mockk.spyk
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flowOf
 import org.junit.jupiter.api.BeforeEach
@@ -46,6 +47,7 @@ class PersonOverviewViewModelTest : BaseTest() {
     fun setup() {
         MockKAnnotations.init(this, true)
         mockkStatic("de.rki.coronawarnapp.contactdiary.util.ContactDiaryExtensionsKt")
+
         coEvery { testCertificateRepository.refresh(any()) } returns setOf(refreshResult)
         coEvery { qrCodeGenerator.createQrCode(any(), any(), any(), any(), any()) } returns mockk()
         every { personCertificatesProvider.personCertificates } returns emptyFlow()
@@ -94,7 +96,13 @@ class PersonOverviewViewModelTest : BaseTest() {
     @Test
     fun `Sorting - List has pending certificate`() {
         every { personCertificatesProvider.personCertificates } returns
-            flowOf(PersonCertificatesData.certificatesWithPending)
+            PersonCertificatesData.certificatesWithPending
+                .map {
+                    spyk(it).apply {
+                        every { highestPriorityCertificate } returns certificates.first()
+                    }
+                }.run { flowOf(this.toSet()) }
+
         instance.personCertificates.getOrAwaitValue().apply {
             (get(0) as CovidTestCertificatePendingCard.Item).apply { certificate.fullName shouldBe "Max Mustermann" }
             (get(1) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Zeebee" }
@@ -105,7 +113,13 @@ class PersonOverviewViewModelTest : BaseTest() {
     @Test
     fun `Sorting - List has pending & updating certificate`() {
         every { personCertificatesProvider.personCertificates } returns
-            flowOf(PersonCertificatesData.certificatesWithUpdating)
+            PersonCertificatesData.certificatesWithUpdating
+                .map {
+                    spyk(it).apply {
+                        every { highestPriorityCertificate } returns certificates.first()
+                    }
+                }.run { flowOf(this.toSet()) }
+
         instance.personCertificates.getOrAwaitValue().apply {
             (get(0) as CovidTestCertificatePendingCard.Item).apply { certificate.fullName shouldBe "Max Mustermann" }
             (get(1) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Zeebee" }
@@ -116,7 +130,13 @@ class PersonOverviewViewModelTest : BaseTest() {
     @Test
     fun `Sorting - List has no CWA user`() {
         every { personCertificatesProvider.personCertificates } returns
-            flowOf(PersonCertificatesData.certificatesWithoutCwaUser)
+            PersonCertificatesData.certificatesWithoutCwaUser
+                .map {
+                    spyk(it).apply {
+                        every { highestPriorityCertificate } returns certificates.first()
+                    }
+                }.run { flowOf(this.toSet()) }
+
         instance.personCertificates.getOrAwaitValue().apply {
             (get(0) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Andrea Schneider" }
             (get(1) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Erika Musterfrau" }
@@ -127,7 +147,13 @@ class PersonOverviewViewModelTest : BaseTest() {
     @Test
     fun `Sorting - List has CWA user`() {
         every { personCertificatesProvider.personCertificates } returns
-            flowOf(PersonCertificatesData.certificatesWithCwaUser)
+            PersonCertificatesData.certificatesWithCwaUser
+                .map {
+                    spyk(it).apply {
+                        every { highestPriorityCertificate } returns certificates.first()
+                    }
+                }.run { flowOf(this.toSet()) }
+
         instance.personCertificates.getOrAwaitValue().apply {
             (get(0) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Zeebee" } // CWA user
             (get(1) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Andrea Schneider" }
-- 
GitLab