From cd278162ec9991e3c35f59585acd6a784f8c247d Mon Sep 17 00:00:00 2001
From: Mohamed <mohamed.metwalli@sap.com>
Date: Tue, 2 Mar 2021 08:58:39 +0100
Subject: [PATCH] Fix race condition in test result donation (DEV) (#2489)

* Fix race condition in test result donation

* Make Klint happy

* Update docs

Co-authored-by: Ralf Gehrer <ralfgehrer@users.noreply.github.com>
---
 .../registeredtest/TestResultDataCollector.kt |  8 +++++++
 .../scan/SubmissionQRCodeScanViewModel.kt     |  6 ++++--
 .../TestResultDataCollectorTest.kt            | 21 +++++++++++++++++++
 3 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollector.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollector.kt
index 914bd7cc0..52b8be6d4 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollector.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollector.kt
@@ -19,6 +19,14 @@ class TestResultDataCollector @Inject constructor(
      *  exclude any registered test result before giving a consent
      */
     suspend fun saveTestResultAnalyticsSettings(testResult: TestResult) {
+        val validTestResults = listOf(
+            TestResult.POSITIVE,
+            TestResult.PENDING,
+            TestResult.NEGATIVE
+        )
+
+        if (testResult !in validTestResults) return // Not interested in other values
+
         if (analyticsSettings.analyticsEnabled.value) {
             val lastRiskResult = riskLevelStorage
                 .latestAndLastSuccessful
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt
index e4087abac..250fa3998 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt
@@ -56,10 +56,12 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor(
         try {
             registrationState.postValue(RegistrationState(ApiRequestState.STARTED))
             val testResult = submissionRepository.asyncRegisterDeviceViaGUID(scanResult.guid!!)
+            // Order here is important. When `registrationState.postValue` is called before
+            // `saveTestResultAnalyticsSettings`, this coroutine will get canceled due to the navigation
+            // to the next screen.
+            testResultDataCollector.saveTestResultAnalyticsSettings(testResult)
             checkTestResult(testResult)
             registrationState.postValue(RegistrationState(ApiRequestState.SUCCESS, testResult))
-            // Order here is important. Save Analytics after SUCCESS
-            testResultDataCollector.saveTestResultAnalyticsSettings(testResult)
         } catch (err: CwaWebException) {
             registrationState.postValue(RegistrationState(ApiRequestState.FAILED))
             registrationError.postValue(err)
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollectorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollectorTest.kt
index 104a2d06d..a0a8ba370 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollectorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollectorTest.kt
@@ -5,6 +5,7 @@ import de.rki.coronawarnapp.datadonation.analytics.storage.TestResultDonorSettin
 import de.rki.coronawarnapp.risk.RiskLevelResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.util.formatter.TestResult
+import io.mockk.Called
 import io.mockk.MockKAnnotations
 import io.mockk.Runs
 import io.mockk.every
@@ -66,4 +67,24 @@ class TestResultDataCollectorTest : BaseTest() {
             testResultDonorSettings.saveTestResultDonorDataAtRegistration(any(), any())
         }
     }
+
+    @Test
+    fun `saveTestResultAnalyticsSettings does not save data when TestResult is INVALID`() = runBlockingTest {
+        every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(false)
+        testResultDataCollector.saveTestResultAnalyticsSettings(TestResult.INVALID)
+
+        verify {
+            analyticsSettings.analyticsEnabled wasNot Called
+        }
+    }
+
+    @Test
+    fun `saveTestResultAnalyticsSettings does not save data when TestResult is REDEEMED`() = runBlockingTest {
+        every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(false)
+        testResultDataCollector.saveTestResultAnalyticsSettings(TestResult.REDEEMED)
+
+        verify {
+            analyticsSettings.analyticsEnabled wasNot Called
+        }
+    }
 }
-- 
GitLab