From 397d21fbfbc82a753925279b4887c72c65ffdac4 Mon Sep 17 00:00:00 2001 From: Chilja Gossow <49635654+chiljamgossow@users.noreply.github.com> Date: Tue, 1 Jun 2021 16:52:10 +0200 Subject: [PATCH] Unit tests PPA (EXPOSUREAPP-6723) (#3313) * add storage * adjust donor * refactor detector * tests * fix tests * fix tests * klint * fixes * fix crash * fix shared prefs * fix shared prefs * detekt * refactoring * fix tests * fix type klint * remove unnecessary storage * use last calculated * remove change detector * use last calculated * fix test * merge 2.4 * fix naming * fix getLastChangeToHighRiskPt/Ew * calculations unit tests * refactoring * refactoring * merge 2.4 * more tests * rename * detekt * merge 2.4 * fix order Co-authored-by: Mohamed Metwalli <mohamed.metwalli@sap.com> --- .../coronatest/type/pcr/PCRProcessor.kt | 19 +- .../type/rapidantigen/RAProcessor.kt | 4 +- .../analytics/common/PpaDataExtensions.kt | 3 - .../ExposureRiskMetadataDonor.kt | 2 +- .../AnalyticsKeySubmissionStorage.kt | 12 +- .../AnalyticsTestResultCollector.kt | 27 +- .../testresult/AnalyticsTestResultDonor.kt | 6 - .../testresult/AnalyticsTestResultSettings.kt | 20 -- .../scan/SubmissionQRCodeScanViewModel.kt | 2 +- .../coronatest/type/pcr/PCRProcessorTest.kt | 4 +- .../rapidantigen/RapidAntigenProcessorTest.kt | 4 +- .../analytics/common/CalculationsTest.kt | 73 +++++ .../AnalyticsKeySubmissionStorageTest.kt | 176 ++++++++++++ ....kt => AnalyticsPCRTestResultDonorTest.kt} | 23 +- .../AnalyticsRATestResultDonorTest.kt | 218 ++++++++++++++ .../AnalyticsTestResultCollectorTest.kt | 266 ++++++++++++++++++ .../AnalyticsTestResultDataCollectorTest.kt | 187 ------------ .../AnalyticsTestResultSettingsTest.kt | 91 ++++++ 18 files changed, 876 insertions(+), 261 deletions(-) create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionStorageTest.kt rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/{AnalyticsPCRTestResultTest.kt => AnalyticsPCRTestResultDonorTest.kt} (88%) create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsRATestResultDonorTest.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultCollectorTest.kt delete mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDataCollectorTest.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultSettingsTest.kt diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt index 75f83e9dd..a4e0346e2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt @@ -55,6 +55,9 @@ class PCRProcessor @Inject constructor( private suspend fun createQR(request: CoronaTestQRCode.PCR): PCRCoronaTest { Timber.tag(TAG).d("createQR(data=%s)", request) + analyticsKeySubmissionCollector.reset(type) + analyticsTestResultCollector.clear(type) + val dateOfBirthKey = if (request.isDccConsentGiven && request.dateOfBirth != null) { DateOfBirthKey(request.qrCodeGUID, request.dateOfBirth) } else null @@ -69,8 +72,10 @@ class PCRProcessor @Inject constructor( Timber.tag(TAG).d("Request %s gave us %s", request, it) } - // This saves received at - analyticsTestResultCollector.saveTestResult(registrationData.testResultResponse.coronaTestResult, type) + analyticsTestResultCollector.reportTestResultAtRegistration( + registrationData.testResultResponse.coronaTestResult, + type + ) return createCoronaTest(request, registrationData) } @@ -78,6 +83,9 @@ class PCRProcessor @Inject constructor( private suspend fun createTAN(request: CoronaTestTAN.PCR): CoronaTest { Timber.tag(TAG).d("createTAN(data=%s)", request) + analyticsKeySubmissionCollector.reset(type) + analyticsTestResultCollector.clear(type) + val serverRequest = RegistrationRequest( key = request.tan, dateOfBirthKey = null, @@ -98,12 +106,9 @@ class PCRProcessor @Inject constructor( response: RegistrationData ): PCRCoronaTest { - analyticsKeySubmissionCollector.reset(type) - analyticsTestResultCollector.clear(type) - val testResult = response.testResultResponse.coronaTestResult.let { Timber.tag(TAG).v("Raw test result $it") - analyticsTestResultCollector.updatePendingTestResultReceivedTime(it, type) + analyticsTestResultCollector.reportTestResultReceived(it, type) it.toValidatedResult() } @@ -148,7 +153,7 @@ class PCRProcessor @Inject constructor( val newTestResult = try { submissionService.checkTestResult(test.registrationToken).coronaTestResult.let { Timber.tag(TAG).d("Raw test result was %s", it) - analyticsTestResultCollector.updatePendingTestResultReceivedTime(it, type) + analyticsTestResultCollector.reportTestResultReceived(it, type) it.toValidatedResult() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt index 3dcd82592..463d38167 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt @@ -73,8 +73,8 @@ class RAProcessor @Inject constructor( val testResult = registrationData.testResultResponse.coronaTestResult.let { Timber.tag(TAG).v("Raw test result was %s", it) // This saves received at - analyticsTestResultCollector.saveTestResult(it, type) - analyticsTestResultCollector.updatePendingTestResultReceivedTime(it, type) + analyticsTestResultCollector.reportTestResultAtRegistration(it, type) + analyticsTestResultCollector.reportTestResultReceived(it, type) it.toValidatedResult() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/PpaDataExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/PpaDataExtensions.kt index b50296009..c763ed1d7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/PpaDataExtensions.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/PpaDataExtensions.kt @@ -3,7 +3,6 @@ package de.rki.coronawarnapp.datadonation.analytics.common import androidx.annotation.StringRes import de.rki.coronawarnapp.R import de.rki.coronawarnapp.coronatest.server.CoronaTestResult -import de.rki.coronawarnapp.risk.EwRiskLevelResult import de.rki.coronawarnapp.risk.RiskState import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData @@ -68,8 +67,6 @@ val PpaData.PPAFederalState.federalStateShortName: String ) } -fun EwRiskLevelResult.toMetadataRiskLevel(): PpaData.PPARiskLevel = riskState.toMetadataRiskLevel() - fun RiskState.toMetadataRiskLevel(): PpaData.PPARiskLevel = when (this) { RiskState.INCREASED_RISK -> PpaData.PPARiskLevel.RISK_LEVEL_HIGH diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposureriskmetadata/ExposureRiskMetadataDonor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposureriskmetadata/ExposureRiskMetadataDonor.kt index ac420c74e..21758898d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposureriskmetadata/ExposureRiskMetadataDonor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposureriskmetadata/ExposureRiskMetadataDonor.kt @@ -54,7 +54,7 @@ class ExposureRiskMetadataDonor @Inject constructor( .lastCalculated .ewRiskLevelResult - val riskLevelEWForMetadata = lastEWRiskResult.toMetadataRiskLevel() + val riskLevelEWForMetadata = lastEWRiskResult.riskState.toMetadataRiskLevel() val mostRecentDateAtEWRiskLevel = lastEWRiskResult.mostRecentDateAtRiskState?.seconds ?: -1 builder diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionStorage.kt index 6ef4cfb5f..c530c7af1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionStorage.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionStorage.kt @@ -8,11 +8,19 @@ import javax.inject.Inject class AnalyticsPCRKeySubmissionStorage @Inject constructor( @AppContext context: Context -) : AnalyticsKeySubmissionStorage(context, "") // the original +) : AnalyticsKeySubmissionStorage(context, sharedPrefKeySuffix) { + companion object { + const val sharedPrefKeySuffix = "" // the original + } +} class AnalyticsRAKeySubmissionStorage @Inject constructor( @AppContext context: Context -) : AnalyticsKeySubmissionStorage(context, "_RAT") +) : AnalyticsKeySubmissionStorage(context, sharedPrefKeySuffix) { + companion object { + const val sharedPrefKeySuffix = "_RAT" + } +} open class AnalyticsKeySubmissionStorage( val context: Context, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultCollector.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultCollector.kt index e991a5135..8e40456a3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultCollector.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultCollector.kt @@ -8,7 +8,7 @@ import de.rki.coronawarnapp.datadonation.analytics.common.calculateDaysSinceMost import de.rki.coronawarnapp.datadonation.analytics.common.getLastChangeToHighEwRiskBefore import de.rki.coronawarnapp.datadonation.analytics.common.getLastChangeToHighPtRiskBefore import de.rki.coronawarnapp.datadonation.analytics.common.isFinal -import de.rki.coronawarnapp.datadonation.analytics.common.isPending +import de.rki.coronawarnapp.datadonation.analytics.common.toMetadataRiskLevel import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings import de.rki.coronawarnapp.risk.RiskState import de.rki.coronawarnapp.risk.storage.RiskLevelStorage @@ -81,7 +81,7 @@ class AnalyticsTestResultCollector @Inject constructor( } } - suspend fun saveTestResult(testResult: CoronaTestResult, type: CoronaTest.Type) { + suspend fun reportTestResultAtRegistration(testResult: CoronaTestResult, type: CoronaTest.Type) { if (analyticsDisabled) return val validTestResults = when (type) { @@ -99,24 +99,31 @@ class AnalyticsTestResultCollector @Inject constructor( if (testResult !in validTestResults) return // Not interested in other values + type.settings.testResultAtRegistration.update { testResult } + + if (testResult.isFinal) { + type.settings.finalTestResultReceivedAt.update { timeStamper.nowUTC } + } + val lastRiskLevel = riskLevelStorage .latestAndLastSuccessfulCombinedEwPtRiskLevelResult .first() .lastSuccessfullyCalculated - type.settings.saveTestResultDonorDataAtRegistration(testResult, lastRiskLevel) + type.settings.ewRiskLevelAtTestRegistration.update { + lastRiskLevel.ewRiskLevelResult.riskState.toMetadataRiskLevel() + } + type.settings.ptRiskLevelAtTestRegistration.update { + lastRiskLevel.ptRiskLevelResult.riskState.toMetadataRiskLevel() + } } - fun updatePendingTestResultReceivedTime(testResult: CoronaTestResult, type: CoronaTest.Type) { + fun reportTestResultReceived(testResult: CoronaTestResult, type: CoronaTest.Type) { if (analyticsDisabled) return - val shouldUpdate = type.settings.testScannedAfterConsent.value && - type.settings.testResultAtRegistration.value.isPending && - testResult.isFinal - if (shouldUpdate) { + if (testResult.isFinal) { val receivedAt = timeStamper.nowUTC - Timber.d("updatePendingTestResultReceivedTime($testResult, $receivedAt") + Timber.d("finalTestResultReceivedAt($testResult, $receivedAt") type.settings.finalTestResultReceivedAt.update { receivedAt } - type.settings.testResultAtRegistration.update { testResult } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDonor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDonor.kt index 5978dcc6b..8ec58e107 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDonor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDonor.kt @@ -30,12 +30,6 @@ abstract class AnalyticsTestResultDonor( ) : DonorModule { override suspend fun beginDonation(request: DonorModule.Request): DonorModule.Contribution { - val scannedAfterConsent = testResultSettings.testScannedAfterConsent.value - if (!scannedAfterConsent) { - Timber.d("Skipping TestResultMetadata donation (scannedAfterConsent=%s)", scannedAfterConsent) - return TestResultMetadataNoContribution - } - val timestampAtRegistration = testResultSettings.testRegisteredAt.value if (timestampAtRegistration == null) { Timber.d("Skipping TestResultMetadata donation (timestampAtRegistration is missing)") diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultSettings.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultSettings.kt index 303514cce..c0e24dd75 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultSettings.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultSettings.kt @@ -2,9 +2,6 @@ package de.rki.coronawarnapp.datadonation.analytics.modules.testresult import android.content.Context import de.rki.coronawarnapp.coronatest.server.CoronaTestResult -import de.rki.coronawarnapp.datadonation.analytics.common.isFinal -import de.rki.coronawarnapp.datadonation.analytics.common.toMetadataRiskLevel -import de.rki.coronawarnapp.risk.CombinedEwPtRiskLevelResult import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.di.AppContext @@ -49,11 +46,6 @@ open class AnalyticsTestResultSettings( } ) - val testScannedAfterConsent = prefs.createFlowPreference( - key = PREFS_KEY_TEST_SCANNED_AFTER_CONSENT + sharedPrefKeySuffix, - defaultValue = false - ) - val ewRiskLevelAtTestRegistration = prefs.createFlowPreference( key = PREFS_KEY_RISK_LEVEL_AT_REGISTRATION_EW + sharedPrefKeySuffix, reader = { key -> @@ -124,21 +116,9 @@ open class AnalyticsTestResultSettings( defaultValue = -1 ) - fun saveTestResultDonorDataAtRegistration(testResult: CoronaTestResult, lastResult: CombinedEwPtRiskLevelResult) { - testScannedAfterConsent.update { true } - testResultAtRegistration.update { testResult } - if (testResult.isFinal) { - finalTestResultReceivedAt.update { timeStamper.nowUTC } - } - - ewRiskLevelAtTestRegistration.update { lastResult.ewRiskLevelResult.toMetadataRiskLevel() } - ptRiskLevelAtTestRegistration.update { lastResult.ptRiskLevelResult.riskState.toMetadataRiskLevel() } - } - fun clear() = prefs.clearAndNotify() companion object { - private const val PREFS_KEY_TEST_SCANNED_AFTER_CONSENT = "testResultDonor.testScannedAfterConsent" private const val PREFS_KEY_TEST_RESULT_AT_REGISTRATION = "testResultDonor.testResultAtRegistration" private const val PREFS_KEY_RISK_LEVEL_AT_REGISTRATION_EW = "testResultDonor.riskLevelAtRegistration" 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 0f9d04592..cc2f2087c 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 @@ -52,10 +52,10 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor( ) ) } else { + qrCodeRegistrationStateProcessor.startQrCodeRegistration(coronaTestQRCode, isConsentGiven) if (isConsentGiven) { analyticsKeySubmissionCollector.reportAdvancedConsentGiven(coronaTestQRCode.type) } - qrCodeRegistrationStateProcessor.startQrCodeRegistration(coronaTestQRCode, isConsentGiven) } } catch (err: InvalidQRCodeException) { qrCodeValidationState.postValue(QrCodeRegistrationStateProcessor.ValidationState.INVALID) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt index f7d32ec8e..3c89c21c4 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt @@ -84,8 +84,8 @@ class PCRProcessorTest : BaseTest() { coEvery { reportTestRegistered(PCR) } just Runs } analyticsTestResultCollector.apply { - coEvery { updatePendingTestResultReceivedTime(any(), any()) } just Runs - coEvery { saveTestResult(any(), PCR) } just Runs + coEvery { reportTestResultReceived(any(), any()) } just Runs + coEvery { reportTestResultAtRegistration(any(), PCR) } just Runs coEvery { reportTestRegistered(PCR) } just Runs coEvery { clear(PCR) } just Runs } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt index 5222c1be5..8f5ba33f3 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt @@ -87,8 +87,8 @@ class RapidAntigenProcessorTest : BaseTest() { } analyticsTestResultCollector.apply { - coEvery { saveTestResult(any(), RAPID_ANTIGEN) } just Runs - coEvery { updatePendingTestResultReceivedTime(any(), RAPID_ANTIGEN) } just Runs + coEvery { reportTestResultAtRegistration(any(), RAPID_ANTIGEN) } just Runs + coEvery { reportTestResultReceived(any(), RAPID_ANTIGEN) } just Runs coEvery { reportTestRegistered(RAPID_ANTIGEN) } just Runs coEvery { clear(RAPID_ANTIGEN) } just Runs } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/common/CalculationsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/common/CalculationsTest.kt index 21eeafb49..8e59f3a82 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/common/CalculationsTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/common/CalculationsTest.kt @@ -1,8 +1,12 @@ package de.rki.coronawarnapp.datadonation.analytics.common +import com.google.android.gms.nearby.exposurenotification.ExposureWindow import de.rki.coronawarnapp.presencetracing.risk.PtRiskLevelResult +import de.rki.coronawarnapp.risk.EwRiskLevelResult import de.rki.coronawarnapp.risk.RiskState +import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult import io.kotest.matchers.shouldBe +import org.joda.time.Instant import org.joda.time.LocalDate import org.junit.jupiter.api.Test import testhelpers.BaseTest @@ -189,4 +193,73 @@ class CalculationsTest : BaseTest() { listOf(ptRisk0).getLastChangeToHighPtRiskBefore(registeredAt) shouldBe LocalDate(2021, 3, 19).toDateTimeAtStartOfDay().toInstant() } + + @Test + fun `getLastChangeToHighEwRiskBefore works`() { + val ewRisk0 = TestEwRiskLevelResult( + calculatedAt = LocalDate(2021, 3, 19).toDateTimeAtStartOfDay().toInstant(), + riskState = RiskState.INCREASED_RISK + ) + val registeredAt = LocalDate(2021, 3, 23).toDateTimeAtStartOfDay().toInstant() + listOf(ewRisk0).getLastChangeToHighEwRiskBefore(registeredAt) shouldBe + LocalDate(2021, 3, 19).toDateTimeAtStartOfDay().toInstant() + } + + @Test + fun `getLastChangeToHighEwRiskBefore works 2`() { + val risk0 = TestEwRiskLevelResult( + calculatedAt = LocalDate(2021, 3, 19).toDateTimeAtStartOfDay().toInstant(), + riskState = RiskState.LOW_RISK + ) + val risk1 = TestEwRiskLevelResult( + calculatedAt = LocalDate(2021, 3, 20).toDateTimeAtStartOfDay().toInstant(), + riskState = RiskState.INCREASED_RISK + ) + val risk2 = TestEwRiskLevelResult( + calculatedAt = LocalDate(2021, 3, 21).toDateTimeAtStartOfDay().toInstant(), + riskState = RiskState.LOW_RISK + ) + val risk3 = TestEwRiskLevelResult( + calculatedAt = LocalDate(2021, 3, 22).toDateTimeAtStartOfDay().toInstant(), + riskState = RiskState.INCREASED_RISK + ) + val risk4 = TestEwRiskLevelResult( + calculatedAt = LocalDate(2021, 3, 23).toDateTimeAtStartOfDay().toInstant(), + riskState = RiskState.CALCULATION_FAILED + ) + val risk5 = TestEwRiskLevelResult( + calculatedAt = LocalDate(2021, 3, 24).toDateTimeAtStartOfDay().toInstant(), + riskState = RiskState.INCREASED_RISK + ) + val registeredAt = LocalDate(2021, 3, 24).toDateTimeAtStartOfDay().toInstant() + listOf( + risk0, + risk1, + risk2, + risk3, + risk4, + risk5 + ).getLastChangeToHighEwRiskBefore(registeredAt) shouldBe + LocalDate(2021, 3, 22).toDateTimeAtStartOfDay().toInstant() + } + + @Test + fun `getLastChangeToHighEwRiskBefore works 3`() { + val registeredAt = LocalDate(2021, 3, 23).toDateTimeAtStartOfDay().toInstant() + listOf<EwRiskLevelResult>().getLastChangeToHighEwRiskBefore(registeredAt) shouldBe + null + } +} + +private data class TestEwRiskLevelResult( + override val calculatedAt: Instant, + override val riskState: RiskState +) : EwRiskLevelResult { + override val wasSuccessfullyCalculated = true + override val failureReason: EwRiskLevelResult.FailureReason? + get() = null + override val ewAggregatedRiskResult: EwAggregatedRiskResult? + get() = null + override val exposureWindows: List<ExposureWindow>? + get() = null } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionStorageTest.kt new file mode 100644 index 000000000..0365dcbad --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionStorageTest.kt @@ -0,0 +1,176 @@ +package de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission + +import android.content.Context +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.preferences.MockSharedPreferences + +class AnalyticsKeySubmissionStorageTest : BaseTest() { + @MockK lateinit var context: Context + lateinit var preferences: MockSharedPreferences + lateinit var pcrStorage: AnalyticsPCRKeySubmissionStorage + lateinit var raStorage: AnalyticsRAKeySubmissionStorage + private val sharedPrefKey = "analytics_key_submission_localdata" + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + preferences = MockSharedPreferences() + every { + context.getSharedPreferences( + "$sharedPrefKey${AnalyticsPCRKeySubmissionStorage.sharedPrefKeySuffix}", + Context.MODE_PRIVATE + ) + } returns preferences + every { + context.getSharedPreferences( + "$sharedPrefKey${AnalyticsRAKeySubmissionStorage.sharedPrefKeySuffix}", + Context.MODE_PRIVATE + ) + } returns preferences + pcrStorage = AnalyticsPCRKeySubmissionStorage( + context = context + ) + raStorage = AnalyticsRAKeySubmissionStorage( + context = context + ) + } + + @AfterEach + fun tearDown() { + pcrStorage.clear() + raStorage.clear() + } + + @Test + fun dataIsNotMixedPcr() { + pcrStorage.submitted.update { true } + pcrStorage.submitted.value shouldBe true + raStorage.submitted.value shouldBe false + + pcrStorage.submittedWithCheckIns.update { true } + pcrStorage.submittedWithCheckIns.value shouldBe true + raStorage.submittedWithCheckIns.value shouldBe false + + pcrStorage.submittedAfterCancel.update { true } + pcrStorage.submittedAfterCancel.value shouldBe true + raStorage.submittedAfterCancel.value shouldBe false + + pcrStorage.submittedAfterSymptomFlow.update { true } + pcrStorage.submittedAfterSymptomFlow.value shouldBe true + raStorage.submittedAfterSymptomFlow.value shouldBe false + + pcrStorage.submittedInBackground.update { true } + pcrStorage.submittedInBackground.value shouldBe true + raStorage.submittedInBackground.value shouldBe false + + pcrStorage.submittedAt.update { 2000 } + pcrStorage.submittedAt.value shouldBe 2000L + raStorage.submittedAt.value shouldBe -1L + + pcrStorage.registeredWithTeleTAN.update { true } + pcrStorage.registeredWithTeleTAN.value shouldBe true + raStorage.registeredWithTeleTAN.value shouldBe false + + pcrStorage.advancedConsentGiven.update { true } + pcrStorage.advancedConsentGiven.value shouldBe true + raStorage.advancedConsentGiven.value shouldBe false + + pcrStorage.lastSubmissionFlowScreen.update { 3 } + pcrStorage.lastSubmissionFlowScreen.value shouldBe 3 + raStorage.lastSubmissionFlowScreen.value shouldBe 0 + + pcrStorage.testRegisteredAt.update { 1000 } + pcrStorage.testRegisteredAt.value shouldBe 1000L + raStorage.testRegisteredAt.value shouldBe -1L + + pcrStorage.testResultReceivedAt.update { 3000 } + pcrStorage.testResultReceivedAt.value shouldBe 3000L + raStorage.testResultReceivedAt.value shouldBe -1L + + pcrStorage.ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.update { 3 } + pcrStorage.ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe 3 + raStorage.ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe -1 + + pcrStorage.ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.update { 2 } + pcrStorage.ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe 2 + raStorage.ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe -1 + + pcrStorage.ewHoursSinceHighRiskWarningAtTestRegistration.update { 10 } + pcrStorage.ewHoursSinceHighRiskWarningAtTestRegistration.value shouldBe 10 + raStorage.ewHoursSinceHighRiskWarningAtTestRegistration.value shouldBe -1 + + pcrStorage.ptHoursSinceHighRiskWarningAtTestRegistration.update { 10 } + pcrStorage.ptHoursSinceHighRiskWarningAtTestRegistration.value shouldBe 10 + raStorage.ptHoursSinceHighRiskWarningAtTestRegistration.value shouldBe -1 + } + + @Test + fun dataIsNotMixedRa() { + raStorage.submitted.update { true } + raStorage.submitted.value shouldBe true + pcrStorage.submitted.value shouldBe false + + raStorage.submittedWithCheckIns.update { true } + raStorage.submittedWithCheckIns.value shouldBe true + pcrStorage.submittedWithCheckIns.value shouldBe false + + raStorage.submittedAfterCancel.update { true } + raStorage.submittedAfterCancel.value shouldBe true + pcrStorage.submittedAfterCancel.value shouldBe false + + raStorage.submittedAfterSymptomFlow.update { true } + raStorage.submittedAfterSymptomFlow.value shouldBe true + pcrStorage.submittedAfterSymptomFlow.value shouldBe false + + raStorage.submittedInBackground.update { true } + raStorage.submittedInBackground.value shouldBe true + pcrStorage.submittedInBackground.value shouldBe false + + raStorage.submittedAt.update { 2000 } + raStorage.submittedAt.value shouldBe 2000L + pcrStorage.submittedAt.value shouldBe -1L + + raStorage.registeredWithTeleTAN.update { true } + raStorage.registeredWithTeleTAN.value shouldBe true + pcrStorage.registeredWithTeleTAN.value shouldBe false + + raStorage.advancedConsentGiven.update { true } + raStorage.advancedConsentGiven.value shouldBe true + pcrStorage.advancedConsentGiven.value shouldBe false + + raStorage.testRegisteredAt.update { 1000 } + raStorage.testRegisteredAt.value shouldBe 1000L + pcrStorage.testRegisteredAt.value shouldBe -1L + + raStorage.testResultReceivedAt.update { 3000 } + raStorage.testResultReceivedAt.value shouldBe 3000L + pcrStorage.testResultReceivedAt.value shouldBe -1L + + raStorage.lastSubmissionFlowScreen.update { 3 } + raStorage.lastSubmissionFlowScreen.value shouldBe 3 + pcrStorage.lastSubmissionFlowScreen.value shouldBe 0 + + raStorage.ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.update { 3 } + raStorage.ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe 3 + pcrStorage.ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe -1 + + raStorage.ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.update { 2 } + raStorage.ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe 2 + pcrStorage.ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe -1 + + raStorage.ewHoursSinceHighRiskWarningAtTestRegistration.update { 10 } + raStorage.ewHoursSinceHighRiskWarningAtTestRegistration.value shouldBe 10 + pcrStorage.ewHoursSinceHighRiskWarningAtTestRegistration.value shouldBe -1 + + raStorage.ptHoursSinceHighRiskWarningAtTestRegistration.update { 10 } + raStorage.ptHoursSinceHighRiskWarningAtTestRegistration.value shouldBe 10 + pcrStorage.ptHoursSinceHighRiskWarningAtTestRegistration.value shouldBe -1 + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsPCRTestResultTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsPCRTestResultDonorTest.kt similarity index 88% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsPCRTestResultTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsPCRTestResultDonorTest.kt index cb7200c36..c23987af9 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsPCRTestResultTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsPCRTestResultDonorTest.kt @@ -24,7 +24,7 @@ import org.junit.jupiter.api.Test import testhelpers.BaseTest import testhelpers.preferences.mockFlowPreference -class AnalyticsPCRTestResultTest : BaseTest() { +class AnalyticsPCRTestResultDonorTest : BaseTest() { @MockK lateinit var testResultSettings: AnalyticsPCRTestResultSettings @MockK lateinit var timeStamper: TimeStamper @@ -57,29 +57,20 @@ class AnalyticsPCRTestResultTest : BaseTest() { unmockkAll() } - @Test - fun `No donation when user did not allow consent`() = runBlockingTest { - every { testResultSettings.testScannedAfterConsent } returns mockFlowPreference(false) - testResultDonor.beginDonation(TestRequest) shouldBe AnalyticsTestResultDonor.TestResultMetadataNoContribution - } - @Test fun `No donation when timestamp at registration is missing`() = runBlockingTest { every { testResultSettings.testRegisteredAt } returns mockFlowPreference(null) - every { testResultSettings.testScannedAfterConsent } returns mockFlowPreference(true) testResultDonor.beginDonation(TestRequest) shouldBe AnalyticsTestResultDonor.TestResultMetadataNoContribution } @Test fun `No donation when test result is INVALID`() = runBlockingTest { - every { testResultSettings.testScannedAfterConsent } returns mockFlowPreference(true) every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_INVALID) testResultDonor.beginDonation(TestRequest) shouldBe AnalyticsTestResultDonor.TestResultMetadataNoContribution } @Test fun `No donation when test result is REDEEMED`() = runBlockingTest { - every { testResultSettings.testScannedAfterConsent } returns mockFlowPreference(true) every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_REDEEMED) testResultDonor.beginDonation(TestRequest) shouldBe AnalyticsTestResultDonor.TestResultMetadataNoContribution } @@ -87,7 +78,6 @@ class AnalyticsPCRTestResultTest : BaseTest() { @Test fun `No donation when test result is PENDING and hours isn't greater or equal to config hours`() { runBlockingTest { - every { testResultSettings.testScannedAfterConsent } returns mockFlowPreference(true) every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_OR_RAT_PENDING) @@ -99,7 +89,6 @@ class AnalyticsPCRTestResultTest : BaseTest() { @Test fun `Donation is collected when test result is PENDING and hours is greater or equal to config hours`() { runBlockingTest { - every { testResultSettings.testScannedAfterConsent } returns mockFlowPreference(true) every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_OR_RAT_PENDING) val timeDayBefore = baseTime.minus(Duration.standardDays(1)) @@ -120,10 +109,10 @@ class AnalyticsPCRTestResultTest : BaseTest() { @Test fun `Donation is collected when test result is POSITIVE`() { runBlockingTest { - every { testResultSettings.testScannedAfterConsent } returns mockFlowPreference(true) every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_POSITIVE) - every { testResultSettings.finalTestResultReceivedAt } returns mockFlowPreference(baseTime) + every { testResultSettings.finalTestResultReceivedAt } returns + mockFlowPreference(baseTime) val donation = testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution @@ -140,10 +129,10 @@ class AnalyticsPCRTestResultTest : BaseTest() { @Test fun `Donation is collected when test result is NEGATIVE`() { runBlockingTest { - every { testResultSettings.testScannedAfterConsent } returns mockFlowPreference(true) every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_NEGATIVE) - every { testResultSettings.finalTestResultReceivedAt } returns mockFlowPreference(baseTime) + every { testResultSettings.finalTestResultReceivedAt } returns + mockFlowPreference(baseTime) val donation = testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution @@ -160,7 +149,6 @@ class AnalyticsPCRTestResultTest : BaseTest() { @Test fun `Scenario 1 LowRisk`() = runBlockingTest { with(testResultSettings) { - every { testScannedAfterConsent } returns mockFlowPreference(true) every { testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_NEGATIVE) every { finalTestResultReceivedAt } returns mockFlowPreference( Instant.parse("2021-03-20T20:00:00Z") @@ -186,7 +174,6 @@ class AnalyticsPCRTestResultTest : BaseTest() { @Test fun `Scenario 2 HighRisk`() = runBlockingTest { with(testResultSettings) { - every { testScannedAfterConsent } returns mockFlowPreference(true) every { testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_POSITIVE) every { finalTestResultReceivedAt } returns mockFlowPreference( Instant.parse("2021-03-20T20:00:00Z") diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsRATestResultDonorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsRATestResultDonorTest.kt new file mode 100644 index 000000000..c2cd808c3 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsRATestResultDonorTest.kt @@ -0,0 +1,218 @@ +package de.rki.coronawarnapp.datadonation.analytics.modules.testresult + +import de.rki.coronawarnapp.appconfig.AnalyticsConfig +import de.rki.coronawarnapp.appconfig.ConfigData +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult +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.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.mockk +import io.mockk.unmockkAll +import io.mockk.verify +import kotlinx.coroutines.test.runBlockingTest +import org.joda.time.Duration +import org.joda.time.Instant +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.preferences.mockFlowPreference + +class AnalyticsRATestResultDonorTest : BaseTest() { + @MockK lateinit var testResultSettings: AnalyticsRATestResultSettings + @MockK lateinit var timeStamper: TimeStamper + + private lateinit var testResultDonor: AnalyticsTestResultDonor + + private val baseTime = Instant.ofEpochMilli(101010101) + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this, true) + with(testResultSettings) { + every { testRegisteredAt } returns mockFlowPreference(baseTime) + every { ewRiskLevelAtTestRegistration } returns mockFlowPreference(PpaData.PPARiskLevel.RISK_LEVEL_LOW) + every { ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration } returns mockFlowPreference(1) + every { ewHoursSinceHighRiskWarningAtTestRegistration } returns mockFlowPreference(1) + every { ptRiskLevelAtTestRegistration } returns mockFlowPreference(PpaData.PPARiskLevel.RISK_LEVEL_LOW) + every { ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration } returns mockFlowPreference(1) + every { ptHoursSinceHighRiskWarningAtTestRegistration } returns mockFlowPreference(1) + } + every { timeStamper.nowUTC } returns baseTime + + testResultDonor = AnalyticsRATestResultDonor( + testResultSettings, + timeStamper + ) + } + + @AfterEach + fun tearDown() { + unmockkAll() + } + + @Test + fun `No donation when timestamp at registration is missing`() = runBlockingTest { + every { testResultSettings.testRegisteredAt } returns mockFlowPreference(null) + testResultDonor.beginDonation(TestRequest) shouldBe AnalyticsTestResultDonor.TestResultMetadataNoContribution + } + + @Test + fun `No donation when test result is INVALID`() = runBlockingTest { + every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_INVALID) + testResultDonor.beginDonation(TestRequest) shouldBe AnalyticsTestResultDonor.TestResultMetadataNoContribution + } + + @Test + fun `No donation when test result is REDEEMED`() = runBlockingTest { + every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_REDEEMED) + testResultDonor.beginDonation(TestRequest) shouldBe AnalyticsTestResultDonor.TestResultMetadataNoContribution + } + + @Test + fun `No donation when test result is PENDING and hours isn't greater or equal to config hours`() { + runBlockingTest { + every { testResultSettings.testResultAtRegistration } returns + mockFlowPreference(CoronaTestResult.PCR_OR_RAT_PENDING) + + testResultDonor.beginDonation(TestRequest) shouldBe + AnalyticsTestResultDonor.TestResultMetadataNoContribution + } + } + + @Test + fun `Donation is collected when test result is PENDING and hours is greater or equal to config hours`() { + runBlockingTest { + every { testResultSettings.testResultAtRegistration } returns + mockFlowPreference(CoronaTestResult.PCR_OR_RAT_PENDING) + val timeDayBefore = baseTime.minus(Duration.standardDays(1)) + every { testResultSettings.testRegisteredAt } returns mockFlowPreference(timeDayBefore) + + val donation = + testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution + with(donation.testResultMetadata) { + riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_PENDING + hoursSinceTestRegistration shouldBe 24 + hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 + daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 1 + } + } + } + + @Test + fun `Donation is collected when test result is POSITIVE`() { + runBlockingTest { + every { testResultSettings.testResultAtRegistration } returns + mockFlowPreference(CoronaTestResult.PCR_POSITIVE) + every { testResultSettings.finalTestResultReceivedAt } returns mockFlowPreference(baseTime) + + val donation = + testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution + with(donation.testResultMetadata) { + riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_POSITIVE + hoursSinceTestRegistration shouldBe 0 + hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 + daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 1 + } + } + } + + @Test + fun `Donation is collected when test result is NEGATIVE`() { + runBlockingTest { + every { testResultSettings.testResultAtRegistration } returns + mockFlowPreference(CoronaTestResult.PCR_NEGATIVE) + every { testResultSettings.finalTestResultReceivedAt } returns mockFlowPreference(baseTime) + + val donation = + testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution + with(donation.testResultMetadata) { + riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_NEGATIVE + hoursSinceTestRegistration shouldBe 0 + hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 + daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 1 + } + } + } + + @Test + fun `Scenario 1 LowRisk`() = runBlockingTest { + with(testResultSettings) { + every { testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_NEGATIVE) + every { finalTestResultReceivedAt } returns mockFlowPreference( + Instant.parse("2021-03-20T20:00:00Z") + ) + every { ewRiskLevelAtTestRegistration } returns mockFlowPreference(PpaData.PPARiskLevel.RISK_LEVEL_LOW) + every { testRegisteredAt } returns mockFlowPreference( + Instant.parse("2021-03-20T00:00:00Z") + ) + } + every { timeStamper.nowUTC } returns Instant.parse("2021-03-20T00:00:00Z") + + val donation = + testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution + with(donation.testResultMetadata) { + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_NEGATIVE + hoursSinceTestRegistration shouldBe 20 + riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW + hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 + daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 1 + } + } + + @Test + fun `Scenario 2 HighRisk`() = runBlockingTest { + with(testResultSettings) { + every { testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_POSITIVE) + every { finalTestResultReceivedAt } returns mockFlowPreference( + Instant.parse("2021-03-20T20:00:00Z") + ) + every { testRegisteredAt } returns mockFlowPreference( + Instant.parse("2021-03-20T00:00:00Z") + ) + every { ewRiskLevelAtTestRegistration } returns mockFlowPreference(PpaData.PPARiskLevel.RISK_LEVEL_HIGH) + } + + every { timeStamper.nowUTC } returns Instant.parse("2021-03-20T00:00:00Z") + + val donation = + testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution + with(donation.testResultMetadata) { + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_POSITIVE + hoursSinceTestRegistration shouldBe 20 // hours + riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_HIGH + hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 + daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 1 + } + } + + @Test + fun deleteData() = runBlockingTest { + every { testResultSettings.clear() } just Runs + + testResultDonor.deleteData() + + verify { + testResultSettings.clear() + } + } + + object TestRequest : DonorModule.Request { + override val currentConfig: ConfigData + get() = mockk<ConfigData>().apply { + every { analytics } returns + mockk<AnalyticsConfig>().apply { + every { hoursSinceTestRegistrationToSubmitTestResultMetadata } returns 20 + } + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultCollectorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultCollectorTest.kt new file mode 100644 index 000000000..1b1a3ce4e --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultCollectorTest.kt @@ -0,0 +1,266 @@ +package de.rki.coronawarnapp.datadonation.analytics.modules.testresult + +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_INVALID +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_NEGATIVE +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_OR_RAT_PENDING +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_POSITIVE +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_REDEEMED +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_INVALID +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_NEGATIVE +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_POSITIVE +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_REDEEMED +import de.rki.coronawarnapp.coronatest.type.CoronaTest.Type.PCR +import de.rki.coronawarnapp.coronatest.type.CoronaTest.Type.RAPID_ANTIGEN +import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings +import de.rki.coronawarnapp.presencetracing.risk.PtRiskLevelResult +import de.rki.coronawarnapp.risk.CombinedEwPtRiskLevelResult +import de.rki.coronawarnapp.risk.EwRiskLevelResult +import de.rki.coronawarnapp.risk.LastCombinedRiskResults +import de.rki.coronawarnapp.risk.RiskState +import de.rki.coronawarnapp.risk.storage.RiskLevelStorage +import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData +import de.rki.coronawarnapp.util.TimeStamper +import io.mockk.Called +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.verify +import kotlinx.coroutines.flow.flowOf +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 +import testhelpers.preferences.mockFlowPreference + +class AnalyticsTestResultCollectorTest : BaseTest() { + + @MockK lateinit var analyticsSettings: AnalyticsSettings + @MockK lateinit var pcrTestResultSettings: AnalyticsPCRTestResultSettings + @MockK lateinit var raTestResultSettings: AnalyticsRATestResultSettings + @MockK lateinit var riskLevelStorage: RiskLevelStorage + @MockK lateinit var timeStamper: TimeStamper + @MockK lateinit var combinedResult: CombinedEwPtRiskLevelResult + @MockK lateinit var ewRiskLevelResult: EwRiskLevelResult + @MockK lateinit var ptRiskLevelResult: PtRiskLevelResult + + private lateinit var analyticsTestResultCollector: AnalyticsTestResultCollector + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + every { timeStamper.nowUTC } returns Instant.parse("2021-03-02T09:57:11+01:00") + every { pcrTestResultSettings.clear() } just Runs + every { raTestResultSettings.clear() } just Runs + + val lastCombinedResults = LastCombinedRiskResults(combinedResult, combinedResult) + every { combinedResult.ewRiskLevelResult } returns ewRiskLevelResult + every { combinedResult.ptRiskLevelResult } returns ptRiskLevelResult + every { ewRiskLevelResult.riskState } returns RiskState.LOW_RISK + every { ptRiskLevelResult.riskState } returns RiskState.LOW_RISK + every { riskLevelStorage.latestAndLastSuccessfulCombinedEwPtRiskLevelResult } returns + flowOf(lastCombinedResults) + + analyticsTestResultCollector = AnalyticsTestResultCollector( + analyticsSettings, + pcrTestResultSettings, + raTestResultSettings, + riskLevelStorage, + timeStamper, + ) + } + + @Test + fun `saveTestResultAnalyticsSettings does not save anything when no user consent`() = + runBlockingTest { + every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(false) + analyticsTestResultCollector.reportTestResultAtRegistration(PCR_POSITIVE, PCR) + + verify(exactly = 0) { + pcrTestResultSettings.testResultAtRegistration + raTestResultSettings.testResultAtRegistration + } + + analyticsTestResultCollector.reportTestResultAtRegistration(RAT_POSITIVE, RAPID_ANTIGEN) + + verify(exactly = 0) { + pcrTestResultSettings.testResultAtRegistration + raTestResultSettings.testResultAtRegistration + } + } + + @Test + fun `saveTestResultAtRegistration saves data when user gave consent`() = + runBlockingTest { + every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true) + + // PCR + every { pcrTestResultSettings.testResultAtRegistration } returns + mockFlowPreference(null) + every { pcrTestResultSettings.finalTestResultReceivedAt } returns + mockFlowPreference(Instant.EPOCH) + every { pcrTestResultSettings.ewRiskLevelAtTestRegistration } returns + mockFlowPreference(PpaData.PPARiskLevel.RISK_LEVEL_LOW) + every { pcrTestResultSettings.ptRiskLevelAtTestRegistration } returns + mockFlowPreference(PpaData.PPARiskLevel.RISK_LEVEL_LOW) + + analyticsTestResultCollector.reportTestResultAtRegistration(PCR_POSITIVE, PCR) + + verify { + analyticsSettings.analyticsEnabled + pcrTestResultSettings.testResultAtRegistration + pcrTestResultSettings.finalTestResultReceivedAt + pcrTestResultSettings.ewRiskLevelAtTestRegistration + pcrTestResultSettings.ptRiskLevelAtTestRegistration + } + + // RAT + every { raTestResultSettings.testResultAtRegistration } returns + mockFlowPreference(null) + every { raTestResultSettings.finalTestResultReceivedAt } returns + mockFlowPreference(Instant.EPOCH) + every { raTestResultSettings.ewRiskLevelAtTestRegistration } returns + mockFlowPreference(PpaData.PPARiskLevel.RISK_LEVEL_LOW) + every { raTestResultSettings.ptRiskLevelAtTestRegistration } returns + mockFlowPreference(PpaData.PPARiskLevel.RISK_LEVEL_LOW) + + analyticsTestResultCollector.reportTestResultAtRegistration(RAT_POSITIVE, RAPID_ANTIGEN) + + verify { + analyticsSettings.analyticsEnabled + raTestResultSettings.testResultAtRegistration + raTestResultSettings.finalTestResultReceivedAt + raTestResultSettings.ptRiskLevelAtTestRegistration + } + } + + @Test + fun `saveTestResultAnalyticsSettings does not save data when TestResult is INVALID`() = + runBlockingTest { + every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(false) + analyticsTestResultCollector.reportTestResultAtRegistration(PCR_INVALID, PCR) + analyticsTestResultCollector.reportTestResultAtRegistration(RAT_INVALID, RAPID_ANTIGEN) + + verify { + analyticsSettings.analyticsEnabled wasNot Called + } + } + + @Test + fun `saveTestResultAnalyticsSettings does not save data when TestResult is REDEEMED`() = + runBlockingTest { + every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(false) + analyticsTestResultCollector.reportTestResultAtRegistration(PCR_REDEEMED, PCR) + analyticsTestResultCollector.reportTestResultAtRegistration(RAT_REDEEMED, RAPID_ANTIGEN) + verify { + analyticsSettings.analyticsEnabled wasNot Called + } + } + + @Test + fun `reportTestResultReceived doesn't update when TestResult isn't POS or NEG`() = + runBlockingTest { + every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true) + every { pcrTestResultSettings.testResultAtRegistration } returns mockFlowPreference( + PCR_OR_RAT_PENDING + ) + for (testResult in listOf(PCR_REDEEMED, PCR_INVALID, PCR_OR_RAT_PENDING)) { + analyticsTestResultCollector.reportTestResultReceived(testResult, PCR) + + verify { + analyticsSettings.analyticsEnabled + pcrTestResultSettings.finalTestResultReceivedAt wasNot Called + } + } + + every { raTestResultSettings.testResultAtRegistration } returns mockFlowPreference( + PCR_OR_RAT_PENDING + ) + for (testResult in listOf(RAT_REDEEMED, RAT_INVALID, PCR_OR_RAT_PENDING)) { + analyticsTestResultCollector.reportTestResultReceived(testResult, RAPID_ANTIGEN) + + verify { + analyticsSettings.analyticsEnabled + raTestResultSettings.finalTestResultReceivedAt wasNot Called + } + } + } + + @Test + fun `updatePendingTestResultReceivedTime doesn't update when Test is not scanned after consent`() = + runBlockingTest { + every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true) + every { pcrTestResultSettings.testResultAtRegistration } returns mockFlowPreference(PCR_OR_RAT_PENDING) + every { pcrTestResultSettings.finalTestResultReceivedAt } returns + mockFlowPreference(Instant.parse("2021-03-02T09:57:11+01:00")) + analyticsTestResultCollector.reportTestResultReceived(PCR_NEGATIVE, PCR) + + verify { + analyticsSettings.analyticsEnabled + pcrTestResultSettings.testResultAtRegistration wasNot Called + pcrTestResultSettings.finalTestResultReceivedAt wasNot Called + pcrTestResultSettings.testResultAtRegistration wasNot Called + } + + every { raTestResultSettings.testResultAtRegistration } returns mockFlowPreference(PCR_OR_RAT_PENDING) + every { raTestResultSettings.finalTestResultReceivedAt } returns + mockFlowPreference(Instant.parse("2021-03-02T09:57:11+01:00")) + analyticsTestResultCollector.reportTestResultReceived(RAT_NEGATIVE, RAPID_ANTIGEN) + + verify { + analyticsSettings.analyticsEnabled + raTestResultSettings.testResultAtRegistration wasNot Called + raTestResultSettings.finalTestResultReceivedAt wasNot Called + raTestResultSettings.testResultAtRegistration wasNot Called + } + } + + @Test + fun `updatePendingTestResultReceivedTime update when TestResult is POS or NEG`() = + runBlockingTest { + for (testResult in listOf(PCR_NEGATIVE, PCR_POSITIVE)) { + every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true) + every { pcrTestResultSettings.testResultAtRegistration } returns mockFlowPreference( + PCR_OR_RAT_PENDING + ) + every { pcrTestResultSettings.finalTestResultReceivedAt } returns + mockFlowPreference(Instant.EPOCH) + + analyticsTestResultCollector.reportTestResultReceived(testResult, PCR) + + verify { + analyticsSettings.analyticsEnabled + pcrTestResultSettings.finalTestResultReceivedAt + } + } + + for (testResult in listOf(RAT_NEGATIVE, RAT_POSITIVE)) { + every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true) + every { raTestResultSettings.testResultAtRegistration } returns mockFlowPreference( + PCR_OR_RAT_PENDING + ) + every { raTestResultSettings.finalTestResultReceivedAt } returns mockFlowPreference(Instant.EPOCH) + analyticsTestResultCollector.reportTestResultReceived(testResult, RAPID_ANTIGEN) + + verify { + analyticsSettings.analyticsEnabled + pcrTestResultSettings.finalTestResultReceivedAt + } + } + } + + @Test + fun `clear is clearing saved data`() { + analyticsTestResultCollector.clear(PCR) + verify { + pcrTestResultSettings.clear() + } + analyticsTestResultCollector.clear(RAPID_ANTIGEN) + verify { + raTestResultSettings.clear() + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDataCollectorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDataCollectorTest.kt deleted file mode 100644 index b2318310f..000000000 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDataCollectorTest.kt +++ /dev/null @@ -1,187 +0,0 @@ -package de.rki.coronawarnapp.datadonation.analytics.modules.testresult - -import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_INVALID -import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_NEGATIVE -import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_OR_RAT_PENDING -import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_POSITIVE -import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_REDEEMED -import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_POSITIVE -import de.rki.coronawarnapp.coronatest.type.CoronaTest.Type.PCR -import de.rki.coronawarnapp.coronatest.type.CoronaTest.Type.RAPID_ANTIGEN -import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings -import de.rki.coronawarnapp.risk.CombinedEwPtRiskLevelResult -import de.rki.coronawarnapp.risk.LastCombinedRiskResults -import de.rki.coronawarnapp.risk.storage.RiskLevelStorage -import de.rki.coronawarnapp.util.TimeStamper -import io.mockk.Called -import io.mockk.MockKAnnotations -import io.mockk.Runs -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.just -import io.mockk.verify -import kotlinx.coroutines.flow.flowOf -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 -import testhelpers.preferences.mockFlowPreference - -class AnalyticsTestResultDataCollectorTest : BaseTest() { - - @MockK lateinit var analyticsSettings: AnalyticsSettings - @MockK lateinit var pcrTestResultDonorSettings: AnalyticsPCRTestResultSettings - @MockK lateinit var raTestResultDonorSettings: AnalyticsRATestResultSettings - @MockK lateinit var riskLevelStorage: RiskLevelStorage - @MockK lateinit var timeStamper: TimeStamper - @MockK lateinit var combinedResult: CombinedEwPtRiskLevelResult - - private lateinit var analyticsTestResultCollector: AnalyticsTestResultCollector - - @BeforeEach - fun setup() { - MockKAnnotations.init(this) - - every { timeStamper.nowUTC } returns Instant.parse("2021-03-02T09:57:11+01:00") - every { pcrTestResultDonorSettings.clear() } just Runs - every { raTestResultDonorSettings.clear() } just Runs - - val lastCombinedResults = LastCombinedRiskResults(combinedResult, combinedResult) - every { riskLevelStorage.latestAndLastSuccessfulCombinedEwPtRiskLevelResult } returns - flowOf(lastCombinedResults) - - analyticsTestResultCollector = AnalyticsTestResultCollector( - analyticsSettings, - pcrTestResultDonorSettings, - raTestResultDonorSettings, - riskLevelStorage, - timeStamper, - ) - } - - @Test - fun `saveTestResultAnalyticsSettings does not save anything when no user consent`() = - runBlockingTest { - every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(false) - analyticsTestResultCollector.saveTestResult(PCR_POSITIVE, PCR) - - verify(exactly = 0) { - pcrTestResultDonorSettings.saveTestResultDonorDataAtRegistration(any(), any()) - raTestResultDonorSettings.saveTestResultDonorDataAtRegistration(any(), any()) - } - } - - @Test - fun `saveTestResultAnalyticsSettings saves data when user gave consent`() = - runBlockingTest { - every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true) - every { pcrTestResultDonorSettings.saveTestResultDonorDataAtRegistration(any(), any()) } just Runs - every { raTestResultDonorSettings.saveTestResultDonorDataAtRegistration(any(), any()) } just Runs - analyticsTestResultCollector.saveTestResult(PCR_POSITIVE, PCR) - - verify(exactly = 1) { - pcrTestResultDonorSettings.saveTestResultDonorDataAtRegistration(any(), any()) - } - - analyticsTestResultCollector.saveTestResult(RAT_POSITIVE, RAPID_ANTIGEN) - - verify(exactly = 1) { - raTestResultDonorSettings.saveTestResultDonorDataAtRegistration(any(), any()) - } - } - - @Test - fun `saveTestResultAnalyticsSettings does not save data when TestResult is INVALID`() = - runBlockingTest { - every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(false) - analyticsTestResultCollector.saveTestResult(PCR_INVALID, PCR) - - verify { - analyticsSettings.analyticsEnabled wasNot Called - } - } - - @Test - fun `saveTestResultAnalyticsSettings does not save data when TestResult is REDEEMED`() = - runBlockingTest { - every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(false) - analyticsTestResultCollector.saveTestResult(PCR_REDEEMED, PCR) - - verify { - analyticsSettings.analyticsEnabled wasNot Called - } - } - - @Test - fun `updatePendingTestResultReceivedTime doesn't update when TestResult isn't POS or NEG`() = - runBlockingTest { - for (testResult in listOf(PCR_REDEEMED, PCR_INVALID, PCR_OR_RAT_PENDING)) { - every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true) - every { pcrTestResultDonorSettings.testScannedAfterConsent } returns mockFlowPreference(true) - every { pcrTestResultDonorSettings.testResultAtRegistration } returns mockFlowPreference( - PCR_OR_RAT_PENDING - ) - analyticsTestResultCollector.updatePendingTestResultReceivedTime(testResult, PCR) - - verify { - analyticsSettings.analyticsEnabled - pcrTestResultDonorSettings.testScannedAfterConsent - pcrTestResultDonorSettings.testResultAtRegistration - pcrTestResultDonorSettings.finalTestResultReceivedAt wasNot Called - pcrTestResultDonorSettings.testResultAtRegistration wasNot Called - } - } - } - - @Test - fun `updatePendingTestResultReceivedTime doesn't update when Test is not scanned after consent`() = - runBlockingTest { - every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true) - every { pcrTestResultDonorSettings.testScannedAfterConsent } returns mockFlowPreference(false) - every { pcrTestResultDonorSettings.testResultAtRegistration } returns mockFlowPreference(PCR_OR_RAT_PENDING) - analyticsTestResultCollector.updatePendingTestResultReceivedTime(PCR_NEGATIVE, PCR) - - verify { - analyticsSettings.analyticsEnabled - pcrTestResultDonorSettings.testScannedAfterConsent - pcrTestResultDonorSettings.testResultAtRegistration wasNot Called - pcrTestResultDonorSettings.finalTestResultReceivedAt wasNot Called - pcrTestResultDonorSettings.testResultAtRegistration wasNot Called - } - } - - @Test - fun `updatePendingTestResultReceivedTime update when TestResult is POS or NEG`() = - runBlockingTest { - for (testResult in listOf(PCR_NEGATIVE, PCR_POSITIVE)) { - every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true) - every { pcrTestResultDonorSettings.testScannedAfterConsent } returns mockFlowPreference(true) - every { pcrTestResultDonorSettings.testResultAtRegistration } returns mockFlowPreference( - PCR_OR_RAT_PENDING - ) - every { pcrTestResultDonorSettings.finalTestResultReceivedAt } returns mockFlowPreference(Instant.EPOCH) - analyticsTestResultCollector.updatePendingTestResultReceivedTime(testResult, PCR) - - verify { - analyticsSettings.analyticsEnabled - pcrTestResultDonorSettings.testScannedAfterConsent - pcrTestResultDonorSettings.testResultAtRegistration - pcrTestResultDonorSettings.finalTestResultReceivedAt - pcrTestResultDonorSettings.testResultAtRegistration - } - } - } - - @Test - fun `clear is clearing saved data`() { - analyticsTestResultCollector.clear(PCR) - verify { - pcrTestResultDonorSettings.clear() - } - analyticsTestResultCollector.clear(RAPID_ANTIGEN) - verify { - raTestResultDonorSettings.clear() - } - } -} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultSettingsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultSettingsTest.kt new file mode 100644 index 000000000..3408ac25f --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultSettingsTest.kt @@ -0,0 +1,91 @@ +package de.rki.coronawarnapp.datadonation.analytics.modules.testresult + +import android.content.Context +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.every +import io.mockk.impl.annotations.MockK +import org.joda.time.Instant +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.preferences.MockSharedPreferences + +class AnalyticsTestResultSettingsTest : BaseTest() { + @MockK lateinit var context: Context + @MockK lateinit var timeStamper: TimeStamper + lateinit var preferences: MockSharedPreferences + lateinit var pcrStorage: AnalyticsPCRTestResultSettings + lateinit var raStorage: AnalyticsRATestResultSettings + private val sharedPrefKey = "analytics_testResultDonor" + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + preferences = MockSharedPreferences() + every { + context.getSharedPreferences( + sharedPrefKey, + Context.MODE_PRIVATE + ) + } returns preferences + every { + context.getSharedPreferences( + sharedPrefKey + "_RAT", + Context.MODE_PRIVATE + ) + } returns preferences + pcrStorage = AnalyticsPCRTestResultSettings( + context = context, + timeStamper = timeStamper + ) + raStorage = AnalyticsRATestResultSettings( + context = context, + timeStamper = timeStamper + ) + } + + @AfterEach + fun tearDown() { + pcrStorage.clear() + raStorage.clear() + } + + @Test + fun dataIsNotMixedPcr() { + pcrStorage.testRegisteredAt.update { Instant.ofEpochMilli(1000) } + pcrStorage.testRegisteredAt.value shouldBe Instant.ofEpochMilli(1000) + raStorage.testRegisteredAt.value shouldBe null + + pcrStorage.finalTestResultReceivedAt.update { Instant.ofEpochMilli(3000) } + pcrStorage.finalTestResultReceivedAt.value shouldBe Instant.ofEpochMilli(3000) + raStorage.finalTestResultReceivedAt.value shouldBe null + + pcrStorage.ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.update { 3 } + pcrStorage.ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe 3 + raStorage.ewDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe -1 + + pcrStorage.ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.update { 2 } + pcrStorage.ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe 2 + raStorage.ptDaysSinceMostRecentDateAtRiskLevelAtTestRegistration.value shouldBe -1 + + pcrStorage.ewHoursSinceHighRiskWarningAtTestRegistration.update { 10 } + pcrStorage.ewHoursSinceHighRiskWarningAtTestRegistration.value shouldBe 10 + raStorage.ewHoursSinceHighRiskWarningAtTestRegistration.value shouldBe -1 + + pcrStorage.ptHoursSinceHighRiskWarningAtTestRegistration.update { 10 } + pcrStorage.ptHoursSinceHighRiskWarningAtTestRegistration.value shouldBe 10 + raStorage.ptHoursSinceHighRiskWarningAtTestRegistration.value shouldBe -1 + + pcrStorage.ewRiskLevelAtTestRegistration.update { PpaData.PPARiskLevel.RISK_LEVEL_HIGH } + pcrStorage.ewRiskLevelAtTestRegistration.value shouldBe PpaData.PPARiskLevel.RISK_LEVEL_HIGH + raStorage.ewRiskLevelAtTestRegistration.value shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW + + pcrStorage.ptRiskLevelAtTestRegistration.update { PpaData.PPARiskLevel.RISK_LEVEL_HIGH } + pcrStorage.ptRiskLevelAtTestRegistration.value shouldBe PpaData.PPARiskLevel.RISK_LEVEL_HIGH + raStorage.ptRiskLevelAtTestRegistration.value shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW + } +} -- GitLab