From 6049f4d883027d0fe1ee9122bc799817ff3348db Mon Sep 17 00:00:00 2001
From: Mohamed Metwalli <mohamed.metwalli@sap.com>
Date: Thu, 17 Jun 2021 17:03:57 +0200
Subject: [PATCH] Sort persons by name (EXPOSUREAPP-7952) (#3476)

* Sort persons by full name

* Setup tests

* Add tests

* Fix test

* Update PersonCertificatesData.kt

* Fix test

Co-authored-by: BMItter <Berndus@gmx.de>
---
 .../ui/overview/PersonOverviewViewModel.kt    |   3 +-
 .../ui/overview/PersonCertificatesData.kt     |  71 +++++++++
 .../overview/PersonOverviewViewModelTest.kt   | 149 ++++++++++++++++++
 3 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonCertificatesData.kt
 create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModelTest.kt

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt
index b6d64f628..7ed8a797b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt
@@ -139,6 +139,7 @@ class PersonOverviewViewModel @AssistedInject constructor(
 
     private fun Set<PersonCertificates>.filterNotPending() = this
         .filter { !it.hasPendingTestCertificate() }
+        .sortedBy { it.highestPriorityCertificate.fullName }
         .sortedByDescending { it.isCwaUser }
 
     private suspend fun generateQrCode(qrCode: QrCodeString): Bitmap? = try {
@@ -148,7 +149,7 @@ class PersonOverviewViewModel @AssistedInject constructor(
         null
     }
 
-    private fun refreshCertificate(identifier: TestCertificateIdentifier) =
+    fun refreshCertificate(identifier: TestCertificateIdentifier) =
         launch {
             val error = testCertificateRepository.refresh(identifier).mapNotNull { it.error }.singleOrNull()
             error?.let { events.postValue(ShowRefreshErrorDialog(error)) }
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
new file mode 100644
index 000000000..49097d8ba
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonCertificatesData.kt
@@ -0,0 +1,71 @@
+package de.rki.coronawarnapp.covidcertificate.person.ui.overview
+
+import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier
+import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
+import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificates
+import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
+import org.joda.time.Instant
+import org.joda.time.LocalDate
+
+object PersonCertificatesData {
+    val certificatesWithPending = mutableSetOf<PersonCertificates>()
+        .apply {
+            add(PersonCertificates(listOf(testCertificate(fullName = "Andrea Schneider"))))
+            add(PersonCertificates(listOf(testCertificate(fullName = "Max Mustermann", isPending = true))))
+            add(PersonCertificates(listOf(testCertificate(fullName = "Zeebee")), isCwaUser = true))
+        }
+    val certificatesWithUpdating = mutableSetOf<PersonCertificates>().apply {
+        add(PersonCertificates(listOf(testCertificate(fullName = "Andrea Schneider"))))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Zeebee")), isCwaUser = true))
+        add(
+            PersonCertificates(
+                listOf(testCertificate(fullName = "Max Mustermann", isPending = true, isUpdating = true))
+            )
+        )
+    }
+    val certificatesWithCwaUser = mutableSetOf<PersonCertificates>().apply {
+        add(PersonCertificates(listOf(testCertificate(fullName = "Max Mustermann"))))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Erika Musterfrau"))))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Andrea Schneider"))))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Zeebee")), isCwaUser = true))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Zeebee A"))))
+    }
+    val certificatesWithoutCwaUser = mutableSetOf<PersonCertificates>().apply {
+        add(PersonCertificates(listOf(testCertificate("Max Mustermann"))))
+        add(PersonCertificates(listOf(testCertificate("Erika Musterfrau"))))
+        add(PersonCertificates(listOf(testCertificate("Andrea Schneider"))))
+    }
+}
+
+fun testCertificate(
+    fullName: String,
+    isPending: Boolean = false,
+    isUpdating: Boolean = false
+) = object : TestCertificate {
+    override val targetName: String = "targetName"
+    override val testType: String = "testType"
+    override val testResult: String = "testResult"
+    override val testName: String = "testName"
+    override val testNameAndManufacturer: String = "testNameAndManufacturer"
+    override val sampleCollectedAt: Instant = Instant.EPOCH
+    override val testCenter: String = ""
+    override val registeredAt: Instant = Instant.EPOCH
+    override val isUpdatingData: Boolean = isUpdating
+    override val isCertificateRetrievalPending: Boolean = isPending
+    override val issuer: String = "issuer"
+    override val issuedAt: Instant = Instant.EPOCH
+    override val expiresAt: Instant = Instant.EPOCH
+    override val qrCode: QrCodeString = "qrCode"
+    override val firstName: String = "firstName"
+    override val lastName: String = "lastName"
+    override val fullName: String = fullName
+    override val dateOfBirth: LocalDate = LocalDate.now()
+    override val personIdentifier = CertificatePersonIdentifier(
+        dateOfBirth = LocalDate(System.currentTimeMillis()),
+        lastNameStandardized = "lastNameStandardized",
+        firstNameStandardized = "firstNameStandardized"
+    )
+    override val certificateIssuer: String = "certificateIssuer"
+    override val certificateCountry: String = "certificateCountry"
+    override val certificateId: String = "certificateId"
+}
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
new file mode 100644
index 000000000..5aea17846
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModelTest.kt
@@ -0,0 +1,149 @@
+package de.rki.coronawarnapp.covidcertificate.person.ui.overview
+
+import android.content.Context
+import de.rki.coronawarnapp.contactdiary.util.getLocale
+import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificatesProvider
+import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CovidTestCertificatePendingCard
+import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.PersonCertificateCard
+import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository
+import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository
+import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator
+import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.permission.CameraPermissionProvider
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.just
+import io.mockk.mockk
+import io.mockk.mockkStatic
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flowOf
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import testhelpers.BaseTest
+import testhelpers.TestDispatcherProvider
+import testhelpers.extensions.InstantExecutorExtension
+import testhelpers.extensions.getOrAwaitValue
+import java.util.Locale
+
+@ExtendWith(InstantExecutorExtension::class)
+class PersonOverviewViewModelTest : BaseTest() {
+
+    @MockK lateinit var qrCodeGenerator: QrCodeGenerator
+    @MockK lateinit var personCertificatesProvider: PersonCertificatesProvider
+    @MockK lateinit var testCertificateRepository: TestCertificateRepository
+    @MockK lateinit var refreshResult: TestCertificateRepository.RefreshResult
+    @MockK lateinit var valueSetsRepository: ValueSetsRepository
+    @MockK lateinit var context: Context
+    @MockK lateinit var cameraPermissionProvider: CameraPermissionProvider
+
+    @BeforeEach
+    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()
+        every { refreshResult.error } returns null
+        every { testCertificateRepository.certificates } returns emptyFlow()
+        every { context.getLocale() } returns Locale.GERMAN
+        every { valueSetsRepository.triggerUpdateValueSet(any()) } just Runs
+        every { cameraPermissionProvider.deniedPermanently } returns flowOf(false)
+    }
+
+    @Test
+    fun `refreshCertificate causes an error dialog event`() {
+        val error = mockk<Exception>()
+        every { refreshResult.error } returns error
+
+        instance.apply {
+            refreshCertificate("Identifier")
+            events.getOrAwaitValue() shouldBe ShowRefreshErrorDialog(error)
+        }
+    }
+
+    @Test
+    fun `refreshCertificate triggers refresh operation in repo`() {
+        instance.refreshCertificate("Identifier")
+        coVerify { testCertificateRepository.refresh(any()) }
+    }
+
+    @Test
+    fun `deleteTestCertificate deletes certificates from repo`() {
+        coEvery { testCertificateRepository.deleteCertificate(any()) } just Runs
+        instance.apply {
+            deleteTestCertificate("Identifier")
+        }
+
+        coEvery { testCertificateRepository.deleteCertificate(any()) }
+    }
+
+    @Test
+    fun onScanQrCode() {
+        instance.apply {
+            onScanQrCode()
+            events.getOrAwaitValue() shouldBe ScanQrCode
+        }
+    }
+
+    @Test
+    fun `Sorting - List has pending certificate`() {
+        every { personCertificatesProvider.personCertificates } returns
+            flowOf(PersonCertificatesData.certificatesWithPending)
+        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" }
+            (get(2) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Andrea Schneider" }
+        }
+    }
+
+    @Test
+    fun `Sorting - List has pending & updating certificate`() {
+        every { personCertificatesProvider.personCertificates } returns
+            flowOf(PersonCertificatesData.certificatesWithUpdating)
+        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" }
+            (get(2) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Andrea Schneider" }
+        }
+    }
+
+    @Test
+    fun `Sorting - List has no CWA user`() {
+        every { personCertificatesProvider.personCertificates } returns
+            flowOf(PersonCertificatesData.certificatesWithoutCwaUser)
+        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" }
+            (get(2) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Max Mustermann" }
+        }
+    }
+
+    @Test
+    fun `Sorting - List has CWA user`() {
+        every { personCertificatesProvider.personCertificates } returns
+            flowOf(PersonCertificatesData.certificatesWithCwaUser)
+        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" }
+            (get(2) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Erika Musterfrau" }
+            (get(3) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Max Mustermann" }
+            (get(4) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Zeebee A" }
+        }
+    }
+
+    private val instance
+        get() = PersonOverviewViewModel(
+            dispatcherProvider = TestDispatcherProvider(),
+            testCertificateRepository = testCertificateRepository,
+            certificatesProvider = personCertificatesProvider,
+            qrCodeGenerator = qrCodeGenerator,
+            valueSetsRepository = valueSetsRepository,
+            context = context,
+            cameraPermissionProvider = cameraPermissionProvider
+        )
+}
-- 
GitLab