diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/Calculations.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/Calculations.kt
new file mode 100644
index 0000000000000000000000000000000000000000..25531761c63d7553637cce94d1418271e1f927d3
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/Calculations.kt
@@ -0,0 +1,17 @@
+package de.rki.coronawarnapp.datadonation.analytics.common
+
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDate
+import org.joda.time.Days
+import org.joda.time.Instant
+
+fun calculateDaysSinceMostRecentDateAtRiskLevelAtTestRegistration(
+    lastChangeCheckedRiskLevelTimestamp: Instant?,
+    testRegisteredAt: Instant?
+): Int {
+    val lastChangeCheckedRiskLevelDate = lastChangeCheckedRiskLevelTimestamp?.toLocalDate() ?: return 0
+    val testRegisteredAtDate = testRegisteredAt?.toLocalDate() ?: return 0
+    return Days.daysBetween(
+        lastChangeCheckedRiskLevelDate,
+        testRegisteredAtDate
+    ).days
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposurewindows/AnalyticsExposureWindowDonor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposurewindows/AnalyticsExposureWindowDonor.kt
index 20483943d92062ecf5fafdf68654d18f69fcb955..aba744f242fd913e2fc50cbc059f528b5b6b2a28 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposurewindows/AnalyticsExposureWindowDonor.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposurewindows/AnalyticsExposureWindowDonor.kt
@@ -76,7 +76,7 @@ internal fun List<AnalyticsExposureWindowEntityWrapper>.asPpaData() = map {
     }
 
     val exposureWindow = PpaData.PPAExposureWindow.newBuilder()
-        .setDate(it.exposureWindowEntity.dateMillis)
+        .setDate(it.exposureWindowEntity.dateMillis / 1000)
         .setCalibrationConfidence(it.exposureWindowEntity.calibrationConfidence)
         .setInfectiousnessValue(it.exposureWindowEntity.infectiousness)
         .setReportTypeValue(it.exposureWindowEntity.reportType)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionDonor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionDonor.kt
index 43be380985e0258331d73625b7e037fe7dcc700b..f92c04cfc8b4a207d35b7f9c3f46bc0403096a36 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionDonor.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionDonor.kt
@@ -11,7 +11,7 @@ import javax.inject.Singleton
 
 @Singleton
 class AnalyticsKeySubmissionDonor @Inject constructor(
-    val repository: AnalyticsKeySubmissionRepository,
+    private val repository: AnalyticsKeySubmissionRepository,
     private val timeStamper: TimeStamper
 ) : DonorModule {
 
@@ -54,7 +54,8 @@ class AnalyticsKeySubmissionDonor @Inject constructor(
             .setSubmittedInBackground(repository.submittedInBackground)
             .setSubmittedWithTeleTAN(repository.submittedWithTeleTAN)
 
-    private fun shouldSubmitData(timeSinceTestResultToSubmit: Duration): Boolean {
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    internal fun shouldSubmitData(timeSinceTestResultToSubmit: Duration): Boolean {
         return positiveTestResultReceived && (
             keysSubmitted || enoughTimeHasPassedSinceResult(timeSinceTestResultToSubmit)
             )
@@ -66,7 +67,8 @@ class AnalyticsKeySubmissionDonor @Inject constructor(
     private val keysSubmitted: Boolean
         get() = repository.submitted
 
-    private fun enoughTimeHasPassedSinceResult(timeSinceTestResultToSubmit: Duration): Boolean =
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    internal fun enoughTimeHasPassedSinceResult(timeSinceTestResultToSubmit: Duration): Boolean =
         timeStamper.nowUTC.minus(timeSinceTestResultToSubmit) > Instant.ofEpochMilli(repository.testResultReceivedAt)
 
     override suspend fun deleteData() {
@@ -74,7 +76,7 @@ class AnalyticsKeySubmissionDonor @Inject constructor(
     }
 }
 
-@VisibleForTesting
+@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
 object AnalyticsKeySubmissionNoContribution : DonorModule.Contribution {
     override suspend fun injectData(protobufContainer: PpaData.PPADataAndroid.Builder) = Unit
     override suspend fun finishDonation(successful: Boolean) = Unit
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionRepository.kt
index 8982f68c86e54aa6a91873ed41244e1446ac447c..b2d639381ce9edda8d010567061a65ffbf3a8d16 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionRepository.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionRepository.kt
@@ -1,5 +1,6 @@
 package de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission
 
+import de.rki.coronawarnapp.datadonation.analytics.common.calculateDaysSinceMostRecentDateAtRiskLevelAtTestRegistration
 import de.rki.coronawarnapp.risk.RiskLevelSettings
 import org.joda.time.Duration
 import org.joda.time.Instant
@@ -47,10 +48,10 @@ class AnalyticsKeySubmissionRepository @Inject constructor(
         get() = Duration.millis(max(submittedAt - testRegisteredAt, 0L)).toStandardHours().hours
 
     val daysSinceMostRecentDateAtRiskLevelAtTestRegistration: Int
-        get() = Duration(
+        get() = calculateDaysSinceMostRecentDateAtRiskLevelAtTestRegistration(
             riskLevelSettings.lastChangeCheckedRiskLevelTimestamp,
             Instant.ofEpochMilli(testRegisteredAt)
-        ).standardDays.toInt()
+        )
 
     val hoursSinceHighRiskWarningAtTestRegistration: Int
         get() = storage.hoursSinceHighRiskWarningAtTestRegistration.value
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 914bd7cc0e81776c4b5700926e03cec87b980452..52b8be6d4a4c6d7012d4306dba6a3f0f759cf252 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/datadonation/analytics/modules/registeredtest/TestResultDonor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDonor.kt
index c2e83444227257deed67e55f255ece8db2dbe47b..4a2053feca5bf156a479f8fb6085c4f70e80faf2 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDonor.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDonor.kt
@@ -1,5 +1,6 @@
 package de.rki.coronawarnapp.datadonation.analytics.modules.registeredtest
 
+import de.rki.coronawarnapp.datadonation.analytics.common.calculateDaysSinceMostRecentDateAtRiskLevelAtTestRegistration
 import de.rki.coronawarnapp.datadonation.analytics.modules.DonorModule
 import de.rki.coronawarnapp.datadonation.analytics.storage.TestResultDonorSettings
 import de.rki.coronawarnapp.risk.RiskLevelSettings
@@ -50,10 +51,10 @@ class TestResultDonor @Inject constructor(
             testResultDonorSettings.testResultAtRegistration.value ?: return TestResultMetadataNoContribution
 
         val daysSinceMostRecentDateAtRiskLevelAtTestRegistration =
-            Duration(
+            calculateDaysSinceMostRecentDateAtRiskLevelAtTestRegistration(
                 riskLevelSettings.lastChangeCheckedRiskLevelTimestamp,
                 registrationTime
-            ).standardDays.toInt()
+            )
 
         val riskLevelAtRegistration = testResultDonorSettings.riskLevelAtTestRegistration.value
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/tekhistory/DefaultTEKHistoryProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/tekhistory/DefaultTEKHistoryProvider.kt
index 2452ac44b62c3ab3b127e9e083ff96a803bcd373..756128817bbf0c7af6c7b91ca01f7ce88e7cf1f8 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/tekhistory/DefaultTEKHistoryProvider.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/tekhistory/DefaultTEKHistoryProvider.kt
@@ -130,29 +130,16 @@ class DefaultTEKHistoryProvider @Inject constructor(
     }
 
     private suspend fun getPreAuthTEKHistoryOnV18(): List<TemporaryExposureKey> {
-        val isPreAuthorized = try {
-            preAuthorizeExposureKeyHistory()
+        return try {
+            Timber.d("Pre-Auth retrieving TEK.")
+            getPreAuthorizedExposureKeys().also {
+                Timber.d("Pre-Auth TEK: %s", it.joinToString("\n"))
+            }
         } catch (exception: Exception) {
-            Timber.d(exception, "Requesting Pre-Auth history failed")
+            Timber.d(exception, "Pre-Auth retrieving TEK failed")
             if (exception.isResolvable) throw exception
-            false
-        }
-
-        return if (isPreAuthorized) {
-            try {
-                Timber.d("Pre-Auth retrieving TEK.")
-                getPreAuthorizedExposureKeys().also {
-                    Timber.d("Pre-Auth TEK: %s", it.joinToString("\n"))
-                }
-            } catch (exception: Exception) {
-                Timber.d(exception, "Pre-Auth retrieving TEK failed")
-                if (exception.isResolvable) throw exception
 
-                Timber.d("Fallback: Retrieving TEK on pre v1.8")
-                getTEKHistoryOnPreV18()
-            }
-        } else {
-            Timber.d("Retrieving TEK on pre v1.8")
+            Timber.d("Fallback: Retrieving TEK on pre v1.8")
             getTEKHistoryOnPreV18()
         }
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/SubmissionRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/SubmissionRepository.kt
index fc70b8d81502d4dba3bb15c703443778ce207dd2..8f496e37159faa663aadcb93e23adf3709b988b2 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/SubmissionRepository.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/SubmissionRepository.kt
@@ -37,7 +37,8 @@ class SubmissionRepository @Inject constructor(
     private val deadmanNotificationScheduler: DeadmanNotificationScheduler,
     private val analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector
 ) {
-    private val testResultReceivedDateFlowInternal = MutableStateFlow(Date())
+    private val testResultReceivedDateFlowInternal =
+        MutableStateFlow(Date(LocalData.initialTestResultReceivedTimestamp() ?: System.currentTimeMillis()))
     val testResultReceivedDateFlow: Flow<Date> = testResultReceivedDateFlowInternal
 
     private val deviceUIStateFlowInternal =
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 e4087abac71bccf897054b11663c304de677f51d..250fa3998c34879d1bf4895a14ca99087bc46008 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/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt
index 83566280d37b0b27506f1007a781499d7c4936df..3e5d0a7cbaf823d3ec6cdf162fa0a64992ffbc39 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt
@@ -42,43 +42,41 @@ class SubmissionTestResultAvailableViewModel @AssistedInject constructor(
             override fun onTEKAvailable(teks: List<TemporaryExposureKey>) {
                 Timber.d("onTEKAvailable(teks.size=%d)", teks.size)
                 autoSubmission.updateMode(AutoSubmission.Mode.MONITOR)
-
+                showKeysRetrievalProgress.postValue(false)
                 routeToScreen.postValue(
                     SubmissionTestResultAvailableFragmentDirections
                         .actionSubmissionTestResultAvailableFragmentToSubmissionTestResultConsentGivenFragment()
                 )
-
-                showKeysRetrievalProgress.postValue(false)
             }
 
             override fun onTEKPermissionDeclined() {
                 Timber.d("onTEKPermissionDeclined")
+                showKeysRetrievalProgress.postValue(false)
                 routeToScreen.postValue(
                     SubmissionTestResultAvailableFragmentDirections
                         .actionSubmissionTestResultAvailableFragmentToSubmissionTestResultNoConsentFragment()
                 )
-                showKeysRetrievalProgress.postValue(false)
             }
 
             override fun onTracingConsentRequired(onConsentResult: (given: Boolean) -> Unit) {
                 Timber.d("onTracingConsentRequired")
-                showTracingConsentDialog.postValue(onConsentResult)
                 showKeysRetrievalProgress.postValue(false)
+                showTracingConsentDialog.postValue(onConsentResult)
             }
 
             override fun onPermissionRequired(permissionRequest: (Activity) -> Unit) {
                 Timber.d("onPermissionRequired")
-                showPermissionRequest.postValue(permissionRequest)
                 showKeysRetrievalProgress.postValue(false)
+                showPermissionRequest.postValue(permissionRequest)
             }
 
             override fun onError(error: Throwable) {
                 Timber.e(error, "Failed to update TEKs.")
+                showKeysRetrievalProgress.postValue(false)
                 error.report(
                     exceptionCategory = ExceptionCategory.EXPOSURENOTIFICATION,
                     prefix = "SubmissionTestResultAvailableViewModel"
                 )
-                showKeysRetrievalProgress.postValue(false)
             }
         }
     )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultready/SubmissionResultReadyViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultready/SubmissionResultReadyViewModel.kt
index a59d4a698ed5115aa0b9b6cb19749983ef196860..df6c44bed844e135cef802f811cbd844a041f51a 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultready/SubmissionResultReadyViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultready/SubmissionResultReadyViewModel.kt
@@ -1,5 +1,7 @@
 package de.rki.coronawarnapp.ui.submission.resultready
 
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
 import androidx.lifecycle.asLiveData
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -16,8 +18,18 @@ class SubmissionResultReadyViewModel @AssistedInject constructor(
     dispatcherProvider: DispatcherProvider
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
 
-    val showUploadDialog = autoSubmission.isSubmissionRunning
-        .asLiveData(context = dispatcherProvider.Default)
+    private val mediatorShowUploadDialog = MediatorLiveData<Boolean>()
+
+    init {
+        mediatorShowUploadDialog.addSource(
+            autoSubmission.isSubmissionRunning.asLiveData(context = dispatcherProvider.Default)
+        ) { show ->
+            mediatorShowUploadDialog.postValue(show)
+        }
+    }
+
+    val showUploadDialog: LiveData<Boolean> = mediatorShowUploadDialog
+
     val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent()
 
     fun onContinueWithSymptomRecordingPressed() {
@@ -32,6 +44,8 @@ class SubmissionResultReadyViewModel @AssistedInject constructor(
             } catch (e: Exception) {
                 Timber.e(e, "greenlightSubmission() failed.")
             } finally {
+                Timber.i("Hide uploading progress and navigate to MainActivity")
+                mediatorShowUploadDialog.postValue(false)
                 routeToScreen.postValue(SubmissionNavigationEvents.NavigateToMainActivity)
             }
         }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt
index 5d422d14af934617dc1bc59bb78338e361f0678d..6628ddc879fa20a4f736fb7d9b74f4c8bd683273 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt
@@ -1,5 +1,7 @@
 package de.rki.coronawarnapp.ui.submission.symptoms.introduction
 
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
 import androidx.lifecycle.asLiveData
 import androidx.navigation.NavDirections
 import dagger.assisted.AssistedFactory
@@ -29,8 +31,17 @@ class SubmissionSymptomIntroductionViewModel @AssistedInject constructor(
     val navigation = SingleLiveEvent<NavDirections>()
 
     val showCancelDialog = SingleLiveEvent<Unit>()
-    val showUploadDialog = autoSubmission.isSubmissionRunning
-        .asLiveData(context = dispatcherProvider.Default)
+    private val mediatorShowUploadDialog = MediatorLiveData<Boolean>()
+
+    init {
+        mediatorShowUploadDialog.addSource(
+            autoSubmission.isSubmissionRunning.asLiveData(context = dispatcherProvider.Default)
+        ) { show ->
+            mediatorShowUploadDialog.postValue(show)
+        }
+    }
+
+    val showUploadDialog: LiveData<Boolean> = mediatorShowUploadDialog
 
     fun onNextClicked() {
         launch {
@@ -98,6 +109,8 @@ class SubmissionSymptomIntroductionViewModel @AssistedInject constructor(
             } catch (e: Exception) {
                 Timber.e(e, "doSubmit() failed.")
             } finally {
+                Timber.i("Hide uploading progress and navigate to HomeFragment")
+                mediatorShowUploadDialog.postValue(false)
                 navigation.postValue(
                     SubmissionSymptomIntroductionFragmentDirections
                         .actionSubmissionSymptomIntroductionFragmentToMainFragment()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt
index 5eb733701ba3164646c710325bb8e2d944819fc2..da6c4e5148d24e6535c49def2d4de866d86047c9 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt
@@ -100,8 +100,8 @@ class SubmissionResultPositiveOtherWarningNoConsentViewModel @AssistedInject con
                 tekHistoryUpdater.updateTEKHistoryOrRequestPermission()
             } else {
                 Timber.d("showEnableTracingEvent:Unit")
-                showEnableTracingEvent.postValue(Unit)
                 showKeysRetrievalProgress.postValue(false)
+                showEnableTracingEvent.postValue(Unit)
             }
         }
     }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposurewindows/AnalyticsExposureWindowDonorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposurewindows/AnalyticsExposureWindowDonorTest.kt
index 307fade38797106d34a72ae99ce097727abd1b60..1014934eee8cf81209338bd073b688b115b896d5 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposurewindows/AnalyticsExposureWindowDonorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposurewindows/AnalyticsExposureWindowDonorTest.kt
@@ -2,6 +2,7 @@ package de.rki.coronawarnapp.datadonation.analytics.modules.exposurewindows
 
 import de.rki.coronawarnapp.appconfig.ConfigData
 import de.rki.coronawarnapp.datadonation.analytics.modules.DonorModule
+import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData
 import io.kotest.matchers.shouldBe
 import io.mockk.MockKAnnotations
 import io.mockk.Runs
@@ -25,18 +26,18 @@ class AnalyticsExposureWindowDonorTest : BaseTest() {
         override val currentConfig: ConfigData
             get() = configData
     }
-    private val window = AnalyticsExposureWindowEntity(
-        "hash",
-        1,
-        1L,
-        1,
-        1,
-        1.0,
-        1
+    private val testWindow = AnalyticsExposureWindowEntity(
+        sha256Hash = "hash",
+        calibrationConfidence = 1,
+        dateMillis = 1234567890L,
+        infectiousness = 2,
+        reportType = 3,
+        normalizedTime = 4.0,
+        transmissionRiskLevel = 5
     )
     private val scanInstance = AnalyticsScanInstanceEntity(1, "hash", 1, 1, 1)
     private val wrapper = AnalyticsExposureWindowEntityWrapper(
-        window,
+        testWindow,
         listOf(scanInstance)
     )
 
@@ -49,6 +50,20 @@ class AnalyticsExposureWindowDonorTest : BaseTest() {
         every { Random.nextDouble() } returns .5
     }
 
+    @Test
+    fun `protobuf conversion uses correct formats`() {
+        val newWindow = listOf(wrapper).asPpaData().single().apply {
+            normalizedTime shouldBe 4.0
+            transmissionRiskLevel shouldBe 5
+        }
+        newWindow.exposureWindow.apply {
+            date shouldBe 1234567L
+            calibrationConfidence shouldBe 1
+            infectiousness shouldBe PpaData.PPAExposureWindowInfectiousness.INFECTIOUSNESS_HIGH
+            reportType shouldBe PpaData.PPAExposureWindowReportType.REPORT_TYPE_SELF_REPORT
+        }
+    }
+
     @Test
     fun `skip submission when random number greater than probability`() {
         val donor = newInstance()
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollectorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollectorTest.kt
index ac0144363e0d88499e81d547406dfd63295e2f59..fe9666112e81d20e74876399cd76ef22b47b0ada 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollectorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollectorTest.kt
@@ -1,3 +1,210 @@
 package de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission
 
-class AnalyticsKeySubmissionCollectorTest
+import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings
+import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.RiskLevelSettings
+import de.rki.coronawarnapp.risk.RiskState
+import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
+import de.rki.coronawarnapp.util.TimeStamper
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.verify
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runBlockingTest
+import org.joda.time.Hours
+import org.joda.time.Instant
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import testhelpers.BaseTest
+import testhelpers.preferences.mockFlowPreference
+
+class AnalyticsKeySubmissionCollectorTest : BaseTest() {
+
+    @MockK lateinit var timeStamper: TimeStamper
+    @MockK lateinit var analyticsSettings: AnalyticsSettings
+    @MockK lateinit var analyticsKeySubmissionStorage: AnalyticsKeySubmissionStorage
+    @MockK lateinit var riskLevelStorage: RiskLevelStorage
+    @MockK lateinit var riskLevelSettings: RiskLevelSettings
+    @MockK lateinit var riskLevelResult: RiskLevelResult
+
+    private val now = Instant.now()
+
+    @BeforeEach
+    fun setup() {
+        MockKAnnotations.init(this)
+        every { timeStamper.nowUTC } returns now
+    }
+
+    @Test
+    fun `save test registered`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        every { riskLevelResult.riskState } returns RiskState.INCREASED_RISK
+        coEvery {
+            riskLevelStorage.latestAndLastSuccessful
+        } returns flowOf(listOf(riskLevelResult))
+        every { riskLevelSettings.lastChangeToHighRiskLevelTimestamp } returns now.minus(
+            Hours.hours(2).toStandardDuration()
+        )
+        val testRegisteredAt = mockFlowPreference(now.millis)
+        coEvery { analyticsKeySubmissionStorage.testRegisteredAt } returns testRegisteredAt
+        every { riskLevelResult.wasSuccessfullyCalculated } returns true
+        val riskLevelAtTestRegistration = mockFlowPreference(-1)
+        every { analyticsKeySubmissionStorage.riskLevelAtTestRegistration } returns riskLevelAtTestRegistration
+        val hoursSinceHighRiskWarningAtTestRegistration = mockFlowPreference(-1)
+        every { analyticsKeySubmissionStorage.hoursSinceHighRiskWarningAtTestRegistration } returns hoursSinceHighRiskWarningAtTestRegistration
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportTestRegistered()
+            verify { testRegisteredAt.update(any()) }
+            verify { riskLevelAtTestRegistration.update(any()) }
+            verify { hoursSinceHighRiskWarningAtTestRegistration.update(any()) }
+        }
+    }
+
+    @Test
+    fun `save keys submitted`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        val submittedFlow = mockFlowPreference(false)
+        every { analyticsKeySubmissionStorage.submitted } returns submittedFlow
+        val submittedAtFlow = mockFlowPreference(now.millis)
+        every { analyticsKeySubmissionStorage.submittedAt } returns submittedAtFlow
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportSubmitted()
+            verify { submittedFlow.update(any()) }
+            verify { submittedAtFlow.update(any()) }
+        }
+    }
+
+    @Test
+    fun `save keys submitted after cancel`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        val flow = mockFlowPreference(false)
+        every { analyticsKeySubmissionStorage.submittedAfterCancel } returns flow
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportSubmittedAfterCancel()
+            verify { flow.update(any()) }
+        }
+    }
+
+    @Test
+    fun `save keys submitted in background`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        val flow = mockFlowPreference(false)
+        every { analyticsKeySubmissionStorage.submittedInBackground } returns flow
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportSubmittedInBackground()
+            verify { flow.update(any()) }
+        }
+    }
+
+    @Test
+    fun `save keys submitted after symptom flow`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        val flow = mockFlowPreference(false)
+        every { analyticsKeySubmissionStorage.submittedAfterSymptomFlow } returns flow
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportSubmittedAfterSymptomFlow()
+            verify { flow.update(any()) }
+        }
+    }
+
+    @Test
+    fun `save positive test result received`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        val flow = mockFlowPreference(now.millis)
+        every { analyticsKeySubmissionStorage.testResultReceivedAt } returns flow
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportPositiveTestResultReceived()
+            verify { flow.update(any()) }
+        }
+    }
+
+    @Test
+    fun `save advanced consent given`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        val flow = mockFlowPreference(false)
+        every { analyticsKeySubmissionStorage.advancedConsentGiven } returns flow
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportAdvancedConsentGiven()
+            verify { flow.update(any()) }
+        }
+    }
+
+    @Test
+    fun `save consent withdrawn`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        val flow = mockFlowPreference(false)
+        every { analyticsKeySubmissionStorage.advancedConsentGiven } returns flow
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportConsentWithdrawn()
+            verify { flow.update(any()) }
+        }
+    }
+
+    @Test
+    fun `save registered with tele tan`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        val flow = mockFlowPreference(false)
+        every { analyticsKeySubmissionStorage.registeredWithTeleTAN } returns flow
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportRegisteredWithTeleTAN()
+            verify { flow.update(any()) }
+        }
+    }
+
+    @Test
+    fun `save last submission flow screen`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns true
+        val flow = mockFlowPreference(0)
+        every { analyticsKeySubmissionStorage.lastSubmissionFlowScreen } returns flow
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportLastSubmissionFlowScreen(Screen.WARN_OTHERS)
+            verify { flow.update(any()) }
+        }
+    }
+
+    @Test
+    fun `no data collection if disabled`() {
+        coEvery { analyticsSettings.analyticsEnabled.value } returns false
+        runBlockingTest {
+            val collector = createInstance()
+            collector.reportTestRegistered()
+            verify(exactly = 0) { analyticsKeySubmissionStorage.testRegisteredAt }
+            verify(exactly = 0) { analyticsKeySubmissionStorage.riskLevelAtTestRegistration }
+            verify(exactly = 0) { analyticsKeySubmissionStorage.hoursSinceHighRiskWarningAtTestRegistration }
+            collector.reportSubmitted()
+            verify(exactly = 0) { analyticsKeySubmissionStorage.submitted }
+            collector.reportSubmittedInBackground()
+            verify(exactly = 0) { analyticsKeySubmissionStorage.submittedInBackground }
+            collector.reportAdvancedConsentGiven()
+            verify(exactly = 0) { analyticsKeySubmissionStorage.advancedConsentGiven }
+            collector.reportConsentWithdrawn()
+            verify(exactly = 0) { analyticsKeySubmissionStorage.advancedConsentGiven }
+            collector.reportLastSubmissionFlowScreen(Screen.UNKNOWN)
+            verify(exactly = 0) { analyticsKeySubmissionStorage.lastSubmissionFlowScreen }
+            collector.reportPositiveTestResultReceived()
+            verify(exactly = 0) { analyticsKeySubmissionStorage.testResultReceivedAt }
+            collector.reportSubmittedAfterCancel()
+            verify(exactly = 0) { analyticsKeySubmissionStorage.submittedAfterCancel }
+        }
+    }
+
+    fun createInstance() = AnalyticsKeySubmissionCollector(
+        timeStamper,
+        analyticsSettings,
+        analyticsKeySubmissionStorage,
+        riskLevelStorage,
+        riskLevelSettings
+    )
+}
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionDonorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionDonorTest.kt
index 252fa5d3b015fc6d749f9c1188bfe66fce6d568b..990fb04b761c9b46f61f41e8df2f594dc97b233b 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionDonorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionDonorTest.kt
@@ -1,3 +1,102 @@
 package de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission
 
-class AnalyticsKeySubmissionDonorTest
+import de.rki.coronawarnapp.appconfig.ConfigData
+import de.rki.coronawarnapp.datadonation.analytics.modules.DonorModule
+import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData
+import de.rki.coronawarnapp.util.TimeStamper
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.just
+import io.mockk.verify
+import kotlinx.coroutines.test.runBlockingTest
+import org.joda.time.Duration
+import org.joda.time.Instant
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import testhelpers.BaseTest
+
+class AnalyticsKeySubmissionDonorTest : BaseTest() {
+    @MockK lateinit var repository: AnalyticsKeySubmissionRepository
+    @MockK lateinit var timeStamper: TimeStamper
+    @MockK lateinit var configData: ConfigData
+    private val request = object : DonorModule.Request {
+        override val currentConfig: ConfigData
+            get() = configData
+    }
+    private val now = Instant.now()
+
+    @MockK lateinit var ppaData: PpaData.PPADataAndroid.Builder
+
+    @BeforeEach
+    fun setup() {
+        MockKAnnotations.init(this)
+        every { timeStamper.nowUTC } returns now
+        every { configData.analytics.hoursSinceTestResultToSubmitKeySubmissionMetadata } returns 6
+    }
+
+    @Test
+    fun `no contribution without test result`() {
+        every { repository.testResultReceivedAt } returns -1
+        every { repository.submitted } returns false
+        runBlockingTest {
+            val donor = createInstance()
+            donor.beginDonation(request) shouldBe AnalyticsKeySubmissionNoContribution
+        }
+    }
+
+    @Test
+    fun `no contribution when neither submitted nor enough time passed`() {
+        every { repository.testResultReceivedAt } returns now.minus(Duration.standardHours(4)).millis
+        every { repository.submitted } returns false
+        runBlockingTest {
+            val donor = createInstance()
+            donor.beginDonation(request) shouldBe AnalyticsKeySubmissionNoContribution
+        }
+    }
+
+    @Test
+    fun `regular contribution when keys submitted`() {
+        every { repository.testResultReceivedAt } returns now.minus(Duration.standardHours(4)).millis
+        every { repository.advancedConsentGiven } returns true
+        every { repository.daysSinceMostRecentDateAtRiskLevelAtTestRegistration } returns 1
+        every { repository.hoursSinceHighRiskWarningAtTestRegistration } returns 1
+        every { repository.hoursSinceTestResult } returns 1
+        every { repository.hoursSinceTestRegistration } returns 1
+        every { repository.lastSubmissionFlowScreen } returns 1
+        every { repository.submittedAfterCancel } returns true
+        every { repository.submittedAfterSymptomFlow } returns true
+        every { repository.submittedInBackground } returns true
+        every { repository.submittedWithTeleTAN } returns false
+        every { repository.submitted } returns true
+        every { ppaData.addKeySubmissionMetadataSet(any<PpaData.PPAKeySubmissionMetadata.Builder>()) } returns ppaData
+        every { repository.reset() } just Runs
+        runBlockingTest {
+            val donor = createInstance()
+            val contribution = donor.beginDonation(request)
+            contribution.injectData(ppaData)
+            coVerify { ppaData.addKeySubmissionMetadataSet(any<PpaData.PPAKeySubmissionMetadata.Builder>()) }
+            contribution.finishDonation(false)
+            verify(exactly = 0) { repository.reset() }
+            contribution.finishDonation(true)
+            verify(exactly = 1) { repository.reset() }
+        }
+    }
+
+    @Test
+    fun `submit contribution after enough time has passed`() {
+        every { repository.testResultReceivedAt } returns now.minus(Duration.standardHours(4)).millis
+        every { repository.submitted } returns true
+        val minTimePassedToSubmit = Duration.standardHours(3)
+        runBlockingTest {
+            val donor = createInstance()
+            donor.enoughTimeHasPassedSinceResult(Duration.standardHours(3)) shouldBe true
+            donor.shouldSubmitData(minTimePassedToSubmit) shouldBe true
+        }
+    }
+
+    fun createInstance() = AnalyticsKeySubmissionDonor(repository, timeStamper)
+}
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionRepositoryTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..136eaae38cf3cd1b3a7f1955e16bc2d6964e11f4
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionRepositoryTest.kt
@@ -0,0 +1,116 @@
+package de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission
+
+import de.rki.coronawarnapp.risk.RiskLevelSettings
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.impl.annotations.MockK
+import org.joda.time.Days
+import org.joda.time.Hours
+import org.joda.time.Instant
+import org.joda.time.LocalTime
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import testhelpers.BaseTest
+
+class AnalyticsKeySubmissionRepositoryTest : BaseTest() {
+
+    @MockK lateinit var storage: AnalyticsKeySubmissionStorage
+    @MockK lateinit var riskLevelSettings: RiskLevelSettings
+
+    private val now = Instant.now()
+
+    @BeforeEach
+    fun setup() {
+        MockKAnnotations.init(this)
+    }
+
+    private fun createInstance() = AnalyticsKeySubmissionRepository(
+        storage, riskLevelSettings
+    )
+
+    @Test
+    fun `hours since test result are calculated correctly`() {
+        coEvery { storage.submittedAt.value } returns now.millis
+        coEvery { storage.testResultReceivedAt.value } returns now.minus(Hours.hours(5).toStandardDuration()).millis
+        val repository = createInstance()
+        repository.hoursSinceTestResult shouldBe 5
+    }
+
+    @Test
+    fun `hours since test result when not submitted should be 0`() {
+        coEvery { storage.submittedAt.value } returns -1
+        coEvery { storage.testResultReceivedAt.value } returns now.minus(Hours.hours(5).toStandardDuration()).millis
+        val repository = createInstance()
+        repository.hoursSinceTestResult shouldBe 0
+    }
+
+    @Test
+    fun `hours since test result when not received or submitted should be 0`() {
+        coEvery { storage.submittedAt.value } returns -1
+        coEvery { storage.testResultReceivedAt.value } returns -1
+        val repository = createInstance()
+        repository.hoursSinceTestResult shouldBe 0
+    }
+
+    @Test
+    fun `hours since test result should be 0 when dates have been manipulated`() {
+        coEvery { storage.submittedAt.value } returns now.minus(Hours.hours(5).toStandardDuration()).millis
+        coEvery { storage.testResultReceivedAt.value } returns now.millis
+        val repository = createInstance()
+        repository.hoursSinceTestResult shouldBe 0
+    }
+
+    @Test
+    fun `hours since test registration are calculated correctly`() {
+        coEvery { storage.submittedAt.value } returns now.millis
+        coEvery { storage.testRegisteredAt.value } returns now.minus(Hours.hours(5).toStandardDuration()).millis
+        val repository = createInstance()
+        repository.hoursSinceTestRegistration shouldBe 5
+    }
+
+    @Test
+    fun `hours since test registration should be 0 if not submitted`() {
+        coEvery { storage.submittedAt.value } returns -1
+        coEvery { storage.testRegisteredAt.value } returns now.minus(Hours.hours(5).toStandardDuration()).millis
+        val repository = createInstance()
+        repository.hoursSinceTestRegistration shouldBe 0
+    }
+
+    @Test
+    fun `days since most recent date at risk level at test registration are calculated correctly`() {
+        coEvery {
+            riskLevelSettings.lastChangeCheckedRiskLevelTimestamp
+        } returns now
+            .minus(Days.days(2).toStandardDuration()).toDateTime().toLocalDate()
+            .toDateTime(LocalTime(22, 0)).toInstant()
+        coEvery { storage.testRegisteredAt.value } returns
+            now.toDateTime().toLocalDate().toDateTime(LocalTime(5, 0)).millis
+        val repository = createInstance()
+        repository.daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 2
+    }
+
+    @Test
+    fun `days between most recent risk level change and test registration should be 0 if on same day`() {
+        coEvery {
+            riskLevelSettings.lastChangeCheckedRiskLevelTimestamp
+        } returns now
+            .toDateTime().toLocalDate()
+            .toDateTime(LocalTime(13, 0)).toInstant()
+        coEvery { storage.testRegisteredAt.value } returns
+            now.toDateTime().toLocalDate().toDateTime(LocalTime(14, 0)).millis
+        val repository = createInstance()
+        repository.daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 0
+    }
+
+    @Test
+    fun `days should be 0 if lastChangeCheckedRiskLevelTimestamp is null`() {
+        coEvery {
+            riskLevelSettings.lastChangeCheckedRiskLevelTimestamp
+        } returns null
+        coEvery { storage.testRegisteredAt.value } returns
+            now.toDateTime().toLocalDate().toDateTime(LocalTime(14, 0)).millis
+        val repository = createInstance()
+        repository.daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 0
+    }
+}
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 104a2d06deaafc25023c3edac0582567f65e10c4..a0a8ba370dda50e7c7e3a73c55339af02f6af696 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
+        }
+    }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/tekhistory/DefaultTEKHistoryProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/tekhistory/DefaultTEKHistoryProviderTest.kt
index 3202a9622b856686646335c54ddb2552484385d2..129e488001b32d78090f9dd151e81879b9a9aa07 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/tekhistory/DefaultTEKHistoryProviderTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/tekhistory/DefaultTEKHistoryProviderTest.kt
@@ -185,7 +185,7 @@ class DefaultTEKHistoryProviderTest : BaseTest() {
             every { status.hasResolution() } returns true
             every { printStackTrace(any<PrintWriter>()) } just Runs
         }
-        every { client.requestPreAuthorizedTemporaryExposureKeyHistory() } answers { MockGMSTask.forError(error) }
+        every { client.requestPreAuthorizedTemporaryExposureKeyRelease() } answers { MockGMSTask.forError(error) }
 
         shouldThrow<ApiException> {
             createInstance().getTEKHistory()
@@ -195,13 +195,25 @@ class DefaultTEKHistoryProviderTest : BaseTest() {
     @Test
     fun `ENFV1_8 getTEKHistory request keys from new Api fallback to old Api on error`() = runBlockingTest {
         coEvery { enfVersion.isAtLeast(ENFVersion.V1_8) } returns true
-        every { client.requestPreAuthorizedTemporaryExposureKeyHistory() } answers { MockGMSTask.forError(Exception()) }
+        every { client.requestPreAuthorizedTemporaryExposureKeyRelease() } answers { MockGMSTask.forError(Exception()) }
         val mockTEK = mockk<TemporaryExposureKey>()
         every { client.temporaryExposureKeyHistory } answers { MockGMSTask.forValue(listOf(mockTEK)) }
 
         createInstance().getTEKHistory() shouldBe listOf(mockTEK)
 
         verify(exactly = 1) { client.temporaryExposureKeyHistory }
+        verify { client.requestPreAuthorizedTemporaryExposureKeyHistory() wasNot Called }
+    }
+
+    @Test
+    fun `ENFV1_8 getTEKHistory request keys from new Api does not check for preAuth`() = runBlockingTest {
+        coEvery { enfVersion.isAtLeast(ENFVersion.V1_8) } returns true
+        every { client.requestPreAuthorizedTemporaryExposureKeyRelease() } answers { MockGMSTask.forValue(null) }
+
+        createInstance().getTEKHistory()
+
+        verify(exactly = 1) { client.requestPreAuthorizedTemporaryExposureKeyRelease() }
+        verify { client.requestPreAuthorizedTemporaryExposureKeyHistory() wasNot Called }
     }
 
     @Test
@@ -265,7 +277,7 @@ class DefaultTEKHistoryProviderTest : BaseTest() {
     @Test
     fun `ENFV1_8 getTEKHistoryOrRequestPermission - if not preauthorized and no resolution then use the old API`() {
         coEvery { enfVersion.isAtLeast(ENFVersion.V1_8) } returns true
-        every { client.requestPreAuthorizedTemporaryExposureKeyHistory() } returns MockGMSTask.forError(Exception())
+        every { client.requestPreAuthorizedTemporaryExposureKeyRelease() } returns MockGMSTask.forError(Exception())
 
         val onTEKHistoryAvailable = mockk<(List<TemporaryExposureKey>) -> Unit>(relaxed = true)
         val onPermissionRequired = mockk<(Status) -> Unit>(relaxed = true)
@@ -320,7 +332,6 @@ class DefaultTEKHistoryProviderTest : BaseTest() {
             onPermissionRequired wasNot Called
         }
         verifyOrder {
-            client.requestPreAuthorizedTemporaryExposureKeyHistory()
             client.requestPreAuthorizedTemporaryExposureKeyRelease()
             client.temporaryExposureKeyHistory
         }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/storage/SubmissionRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/storage/SubmissionRepositoryTest.kt
index e78e650721b97d777d8d9409d12eca30d09271ea..31d2aaa7c6d0775833f853d5d30f53f923de3dfe 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/storage/SubmissionRepositoryTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/storage/SubmissionRepositoryTest.kt
@@ -133,7 +133,7 @@ class SubmissionRepositoryTest : BaseTest() {
 
         submissionRepository.asyncRegisterDeviceViaGUID(guid)
 
-        verify(exactly = 1) {
+        verify {
             LocalData.devicePairingSuccessfulTimestamp(any())
             LocalData.registrationToken(registrationToken)
             backgroundNoise.scheduleDummyPattern()
@@ -151,7 +151,7 @@ class SubmissionRepositoryTest : BaseTest() {
 
         submissionRepository.asyncRegisterDeviceViaTAN(tan)
 
-        coVerify(exactly = 1) {
+        coVerify {
             LocalData.devicePairingSuccessfulTimestamp(any())
             LocalData.registrationToken(registrationToken)
             backgroundNoise.scheduleDummyPattern()