diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestModule.kt index 3fc92f6ac584cc36b0ca7079a53d52afeeba7254..b4295fc63485450d62cca4ca0ddcdd2552c533fe 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestModule.kt @@ -6,7 +6,7 @@ import dagger.multibindings.IntoSet import de.rki.coronawarnapp.coronatest.server.VerificationModule import de.rki.coronawarnapp.coronatest.type.CoronaTestProcessor import de.rki.coronawarnapp.coronatest.type.pcr.PCRProcessor -import de.rki.coronawarnapp.coronatest.type.rapidantigen.RapidAntigenProcessor +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RAProcessor @Module( includes = [VerificationModule::class] @@ -22,6 +22,6 @@ abstract class CoronaTestModule { @Binds @IntoSet abstract fun ratProcessor( - processor: RapidAntigenProcessor + processor: RAProcessor ): CoronaTestProcessor } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt index 8bf90d76a4c739712f64ae769d64f1412d473fc0..c0815c4f3ca43e16e2bab4cc6146b0e488909d99 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt @@ -4,12 +4,9 @@ import de.rki.coronawarnapp.bugreporting.reportProblem import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.coronatest.errors.CoronaTestNotFoundException import de.rki.coronawarnapp.coronatest.errors.DuplicateCoronaTestException -import de.rki.coronawarnapp.coronatest.errors.UnknownTestTypeException import de.rki.coronawarnapp.coronatest.migration.PCRTestMigration import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestGUID -import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode import de.rki.coronawarnapp.coronatest.storage.CoronaTestStorage -import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.coronatest.type.CoronaTestProcessor import de.rki.coronawarnapp.coronatest.type.TestIdentifier @@ -86,14 +83,10 @@ class CoronaTestRepository @Inject constructor( throw DuplicateCoronaTestException("There is already a test of this type: ${request.type}.") } - val test = when (request) { - is CoronaTestQRCode -> processor.create(request) - is CoronaTestTAN -> processor.create(request) - else -> throw UnknownTestTypeException("Unknown test request: $request") + val test = processor.create(request).also { + Timber.tag(TAG).i("New test created: %s", it) } - Timber.tag(TAG).i("Adding new test: %s", test) - toMutableMap().apply { this[test.identifier] = test } } @@ -198,11 +191,11 @@ class CoronaTestRepository @Inject constructor( } } - suspend fun updateConsent(identifier: TestIdentifier, consented: Boolean) { - Timber.tag(TAG).i("updateConsent(identifier=%s, consented=%b)", identifier, consented) + suspend fun updateSubmissionConsent(identifier: TestIdentifier, consented: Boolean) { + Timber.tag(TAG).i("updateSubmissionConsent(identifier=%s, consented=%b)", identifier, consented) modifyTest(identifier) { processor, before -> - processor.updateConsent(before, consented) + processor.updateSubmissionConsent(before, consented) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/TestRegistrationRequest.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/TestRegistrationRequest.kt index 27542f0fd03527fd95ec339e5b4b68d18538efcd..766ca2e57a4280959da1d71db51f0617a1056013 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/TestRegistrationRequest.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/TestRegistrationRequest.kt @@ -1,8 +1,12 @@ package de.rki.coronawarnapp.coronatest import de.rki.coronawarnapp.coronatest.type.CoronaTest +import org.joda.time.LocalDate interface TestRegistrationRequest { val type: CoronaTest.Type val identifier: String + val isDccSupportedbyPoc: Boolean + val isDccConsentGiven: Boolean + val dateOfBirth: LocalDate? } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/CoronaTestQRCode.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/CoronaTestQRCode.kt index 9a5c1801af361fe74a511694f49ccfa804c94e82..2e9417b64e2e845f4985a4f5afcedb5a5b7daf71 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/CoronaTestQRCode.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/CoronaTestQRCode.kt @@ -16,8 +16,13 @@ sealed class CoronaTestQRCode : Parcelable, TestRegistrationRequest { @Parcelize data class PCR( val qrCodeGUID: CoronaTestGUID, + override val isDccConsentGiven: Boolean = false, + override val dateOfBirth: LocalDate? = null, ) : CoronaTestQRCode() { + @IgnoredOnParcel + override val isDccSupportedbyPoc: Boolean = true + @IgnoredOnParcel override val type: CoronaTest.Type = CoronaTest.Type.PCR @@ -36,9 +41,11 @@ sealed class CoronaTestQRCode : Parcelable, TestRegistrationRequest { val createdAt: Instant, val firstName: String? = null, val lastName: String? = null, - val dateOfBirth: LocalDate? = null, + override val dateOfBirth: LocalDate? = null, val testid: String? = null, - val salt: String? = null + val salt: String? = null, + override val isDccConsentGiven: Boolean = false, + override val isDccSupportedbyPoc: Boolean = false, ) : CoronaTestQRCode() { @IgnoredOnParcel diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt index c5881f97364a97bd6c5f8c795b3b1df7c1c72df0..f0bffc6132e18aa10c61c7ca3a06f1209a55ad09 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt @@ -40,7 +40,8 @@ class RapidAntigenQrCodeExtractor @Inject constructor() : QrCodeExtractor<Corona lastName = payload.lastName, dateOfBirth = payload.dateOfBirth, testid = payload.testId, - salt = payload.salt + salt = payload.salt, + isDccSupportedbyPoc = false, // TODO ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/tan/CoronaTestTAN.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/tan/CoronaTestTAN.kt index 62200ac43ba6e549b36fe4246c201c209ef88451..348576e799c27bedc7ff3d32d07ee1bf2818ee64 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/tan/CoronaTestTAN.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/tan/CoronaTestTAN.kt @@ -5,6 +5,7 @@ import de.rki.coronawarnapp.coronatest.TestRegistrationRequest import de.rki.coronawarnapp.coronatest.type.CoronaTest import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize +import org.joda.time.LocalDate sealed class CoronaTestTAN : Parcelable, TestRegistrationRequest { @@ -20,15 +21,17 @@ sealed class CoronaTestTAN : Parcelable, TestRegistrationRequest { override val tan: TestTAN, ) : CoronaTestTAN() { - @IgnoredOnParcel override val type: CoronaTest.Type = CoronaTest.Type.PCR - } + @IgnoredOnParcel + override val type: CoronaTest.Type = CoronaTest.Type.PCR - @Parcelize - data class RapidAntigen( - override val tan: TestTAN, - ) : CoronaTestTAN() { + @IgnoredOnParcel + override val isDccSupportedbyPoc: Boolean = false + + @IgnoredOnParcel + override val isDccConsentGiven: Boolean = false - @IgnoredOnParcel override val type: CoronaTest.Type = CoronaTest.Type.RAPID_ANTIGEN + @IgnoredOnParcel + override val dateOfBirth: LocalDate? = null } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt index dea5510d6bc1daa0a79bf5a93d6aeb8f4ccbae80..464bb89b5ddb7f93da019e5d3f3c6bc44300a483 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt @@ -38,6 +38,15 @@ interface CoronaTest { val isResultAvailableNotificationSent: Boolean + // Is the digital green certificate supported by the point of care that issued the test + val isDccSupportedByPoc: Boolean + + // Has the user given consent to us obtaining the DGC + val isDccConsentGiven: Boolean + + // Has the corresponding entry been created in the test certificate storage + val isDccDataSetCreated: Boolean + enum class Type(val raw: String) { @SerializedName("PCR") PCR("PCR"), diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestProcessor.kt index 0605ddfc98541a337d7d2ebfb4c47a55f04a3495..68b69445ce7604d310952cb757277f46d3ff4883 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestProcessor.kt @@ -1,15 +1,12 @@ package de.rki.coronawarnapp.coronatest.type -import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode -import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN +import de.rki.coronawarnapp.coronatest.TestRegistrationRequest interface CoronaTestProcessor { val type: CoronaTest.Type - suspend fun create(request: CoronaTestQRCode): CoronaTest - - suspend fun create(request: CoronaTestTAN): CoronaTest + suspend fun create(request: TestRegistrationRequest): CoronaTest suspend fun pollServer(test: CoronaTest): CoronaTest @@ -24,7 +21,7 @@ interface CoronaTestProcessor { suspend fun markViewed(test: CoronaTest): CoronaTest - suspend fun updateConsent(test: CoronaTest, consented: Boolean): CoronaTest + suspend fun updateSubmissionConsent(test: CoronaTest, consented: Boolean): CoronaTest suspend fun updateResultNotification(test: CoronaTest, sent: Boolean): CoronaTest } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTest.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTest.kt index f9ef2816debd4d9462a5dd5dcd2d152ab129f8fd..fc85fb4eb2c8bde2604ea0e7004f41776216d099 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTest.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTest.kt @@ -40,6 +40,12 @@ data class PCRCoronaTest( @Transient override val isProcessing: Boolean = false, @Transient override val lastError: Throwable? = null, + + @SerializedName("isDccConsentGiven") + override val isDccConsentGiven: Boolean = false, + + @SerializedName("isDccDataSetCreated") + override val isDccDataSetCreated: Boolean = false, ) : CoronaTest { override val type: CoronaTest.Type @@ -70,6 +76,9 @@ data class PCRCoronaTest( else -> throw IllegalArgumentException("Invalid PCR test state $testResult") } + override val isDccSupportedByPoc: Boolean + get() = true + enum class State { PENDING, INVALID, 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 f733872c34c95c8761c89e6406666369104bdee0..8ee04a4240d5f6c91c116e63e31a51e5671c66c0 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 @@ -42,9 +42,14 @@ class PCRProcessor @Inject constructor( override val type: CoronaTest.Type = CoronaTest.Type.PCR - override suspend fun create(request: CoronaTestQRCode): PCRCoronaTest { - Timber.tag(TAG).d("create(data=%s)", request) - request as CoronaTestQRCode.PCR + override suspend fun create(request: TestRegistrationRequest): CoronaTest = when (request) { + is CoronaTestQRCode.PCR -> createQR(request) + is CoronaTestTAN.PCR -> createTAN(request) + else -> throw IllegalArgumentException("PCRProcessor: Unknown test request: $request") + } + + private suspend fun createQR(request: CoronaTestQRCode.PCR): PCRCoronaTest { + Timber.tag(TAG).d("createQR(data=%s)", request) val registrationData = submissionService.asyncRegisterDeviceViaGUID(request.qrCodeGUID).also { Timber.tag(TAG).d("Request %s gave us %s", request, it) @@ -56,9 +61,8 @@ class PCRProcessor @Inject constructor( return createCoronaTest(request, registrationData) } - override suspend fun create(request: CoronaTestTAN): CoronaTest { - Timber.tag(TAG).d("create(data=%s)", request) - request as CoronaTestTAN.PCR + private suspend fun createTAN(request: CoronaTestTAN.PCR): CoronaTest { + Timber.tag(TAG).d("createTAN(data=%s)", request) val registrationData = submissionService.asyncRegisterDeviceViaTAN(request.tan) @@ -202,8 +206,8 @@ class PCRProcessor @Inject constructor( return test.copy(isViewed = true) } - override suspend fun updateConsent(test: CoronaTest, consented: Boolean): CoronaTest { - Timber.tag(TAG).v("updateConsent(test=%s, consented=%b)", test, consented) + override suspend fun updateSubmissionConsent(test: CoronaTest, consented: Boolean): CoronaTest { + Timber.tag(TAG).v("updateSubmissionConsent(test=%s, consented=%b)", test, consented) test as PCRCoronaTest return test.copy(isAdvancedConsentGiven = consented) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTest.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTest.kt index 101457efd572ef8c9c8c261448618473ad8d8d5e..44689e8dc054a5755525af7bb407c8b5d17f57a6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTest.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTest.kt @@ -63,6 +63,13 @@ data class RACoronaTest( @Transient override val isProcessing: Boolean = false, @Transient override val lastError: Throwable? = null, + + @SerializedName("isDccSupportedByPoc") + override val isDccSupportedByPoc: Boolean = false, + @SerializedName("isDccConsentGiven") + override val isDccConsentGiven: Boolean = false, + @SerializedName("isDccDataSetCreated") + override val isDccDataSetCreated: Boolean = false, ) : CoronaTest { override val type: CoronaTest.Type diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt similarity index 90% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt index e86a84a5491ab412a847c3b463ca129b30f4b982..c28253d5fe4d2e99f518a5f75bdf5571b62e7a76 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.coronatest.type.rapidantigen import dagger.Reusable +import de.rki.coronawarnapp.coronatest.TestRegistrationRequest import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode import de.rki.coronawarnapp.coronatest.server.CoronaTestResult import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_INVALID @@ -15,7 +16,6 @@ import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_POSITIVE import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_REDEEMED import de.rki.coronawarnapp.coronatest.server.CoronaTestResultResponse import de.rki.coronawarnapp.coronatest.server.VerificationServer -import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.coronatest.type.CoronaTestProcessor import de.rki.coronawarnapp.coronatest.type.CoronaTestService @@ -33,7 +33,7 @@ import timber.log.Timber import javax.inject.Inject @Reusable -class RapidAntigenProcessor @Inject constructor( +class RAProcessor @Inject constructor( private val timeStamper: TimeStamper, private val submissionService: CoronaTestService, private val analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector, @@ -42,9 +42,13 @@ class RapidAntigenProcessor @Inject constructor( override val type: CoronaTest.Type = CoronaTest.Type.RAPID_ANTIGEN - override suspend fun create(request: CoronaTestQRCode): RACoronaTest { - Timber.tag(TAG).d("create(data=%s)", request) - request as CoronaTestQRCode.RapidAntigen + override suspend fun create(request: TestRegistrationRequest): CoronaTest = when (request) { + is CoronaTestQRCode.RapidAntigen -> createQR(request) + else -> throw IllegalArgumentException("RAProcessor: Unknown test request: $request") + } + + private suspend fun createQR(request: CoronaTestQRCode.RapidAntigen): RACoronaTest { + Timber.tag(TAG).d("createQR(data=%s)", request) analyticsKeySubmissionCollector.reset(type) analyticsTestResultCollector.clear(type) @@ -83,16 +87,11 @@ class RapidAntigenProcessor @Inject constructor( firstName = request.firstName, lastName = request.lastName, dateOfBirth = request.dateOfBirth, - sampleCollectedAt = sampleCollectedAt + sampleCollectedAt = sampleCollectedAt, + isDccSupportedByPoc = request.isDccSupportedbyPoc, ) } - override suspend fun create(request: CoronaTestTAN): CoronaTest { - Timber.tag(TAG).d("create(data=%s)", request) - request as CoronaTestTAN.RapidAntigen - throw UnsupportedOperationException("There are no TAN based RATs") - } - private fun determineReceivedDate(oldTest: RACoronaTest?, newTestResult: CoronaTestResult): Instant? = when { oldTest != null && FINAL_STATES.contains(oldTest.testResult) -> oldTest.testResultReceivedAt FINAL_STATES.contains(newTestResult) -> timeStamper.nowUTC @@ -198,8 +197,8 @@ class RapidAntigenProcessor @Inject constructor( return test.copy(isViewed = true) } - override suspend fun updateConsent(test: CoronaTest, consented: Boolean): CoronaTest { - Timber.tag(TAG).v("updateConsent(test=%s, consented=%b)", test, consented) + override suspend fun updateSubmissionConsent(test: CoronaTest, consented: Boolean): CoronaTest { + Timber.tag(TAG).v("updateSubmissionConsent(test=%s, consented=%b)", test, consented) test as RACoronaTest return test.copy(isAdvancedConsentGiven = consented) @@ -236,7 +235,7 @@ private fun CoronaTestResult.toValidatedResult(): CoronaTestResult { return if (isValid) { this } else { - Timber.tag(RapidAntigenProcessor.TAG).e("Server returned invalid RapidAntigen testresult $this") + Timber.tag(RAProcessor.TAG).e("Server returned invalid RapidAntigen testresult $this") RAT_INVALID } } 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 02092bb927dd58f9f18a50efabd426d89e152d1b..9215b9f36c135c5fa7d96017bd02f82c05932c8c 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 @@ -51,7 +51,7 @@ class SubmissionRepository @Inject constructor( val test = coronaTestRepository.coronaTests.first().singleOrNull { it.type == type } ?: throw IllegalStateException("No test of type $type available") Timber.tag(TAG).v("giveConsentToSubmission(type=$type): %s", test) - coronaTestRepository.updateConsent(identifier = test.identifier, consented = true) + coronaTestRepository.updateSubmissionConsent(identifier = test.identifier, consented = true) } } @@ -62,7 +62,7 @@ class SubmissionRepository @Inject constructor( val test = coronaTestRepository.coronaTests.first().singleOrNull { it.type == type } ?: throw IllegalStateException("No test of type $type available") - coronaTestRepository.updateConsent(identifier = test.identifier, consented = false) + coronaTestRepository.updateSubmissionConsent(identifier = test.identifier, consented = false) } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestRepositoryTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..267613b4ada2052a2b2e0e5086468ed12c2ba75e --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestRepositoryTest.kt @@ -0,0 +1,103 @@ +package de.rki.coronawarnapp.coronatest + +import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository +import de.rki.coronawarnapp.coronatest.migration.PCRTestMigration +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult +import de.rki.coronawarnapp.coronatest.storage.CoronaTestStorage +import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest +import de.rki.coronawarnapp.coronatest.type.pcr.PCRProcessor +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RAProcessor +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import kotlinx.coroutines.CoroutineScope +import org.joda.time.Instant +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.TestDispatcherProvider +import testhelpers.coroutines.runBlockingTest2 + +class CoronaTestRepositoryTest : BaseTest() { + @MockK lateinit var storage: CoronaTestStorage + @MockK lateinit var legacyMigration: PCRTestMigration + @MockK lateinit var contactDiaryRepository: ContactDiaryRepository + + private var coronaTestsInStorage = mutableSetOf<CoronaTest>() + + private val pcrTest = PCRCoronaTest( + identifier = "pcr-identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = Instant.EPOCH, + registrationToken = "token", + testResult = CoronaTestResult.PCR_REDEEMED, + ) + @MockK lateinit var pcrProcessor: PCRProcessor + + @MockK lateinit var raProcessor: RAProcessor + private val raTest = RACoronaTest( + identifier = "ra-identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = Instant.EPOCH, + registrationToken = "token", + testResult = CoronaTestResult.RAT_REDEEMED, + testedAt = Instant.EPOCH, + ) + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + coronaTestsInStorage.add(pcrTest) + coronaTestsInStorage.add(raTest) + + legacyMigration.apply { + coEvery { startMigration() } returns emptySet() + coEvery { finishMigration() } just Runs + } + + storage.apply { + every { coronaTests = any() } answers { + coronaTestsInStorage.clear() + coronaTestsInStorage.addAll(arg(0)) + } + every { coronaTests } answers { coronaTestsInStorage } + } + + contactDiaryRepository.apply { + coEvery { updateTests(any()) } just Runs + } + + pcrProcessor.apply { + coEvery { updateSubmissionConsent(any(), any()) } answers { arg<PCRCoronaTest>(0) } + every { type } returns CoronaTest.Type.PCR + } + + raProcessor.apply { + coEvery { updateSubmissionConsent(any(), any()) } answers { arg<RACoronaTest>(0) } + every { type } returns CoronaTest.Type.RAPID_ANTIGEN + } + } + + private fun createInstance(scope: CoroutineScope) = CoronaTestRepository( + appScope = scope, + dispatcherProvider = TestDispatcherProvider(), + storage = storage, + processors = setOf(pcrProcessor, raProcessor), + legacyMigration = legacyMigration, + contactDiaryRepository = contactDiaryRepository, + ) + + @Test + fun `give submission consent`() = runBlockingTest2(ignoreActive = true) { + createInstance(this).updateSubmissionConsent(pcrTest.identifier, true) + + coVerify { pcrProcessor.updateSubmissionConsent(pcrTest, true) } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/qrcode/CoronaTestQRCodeTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/qrcode/CoronaTestQRCodeTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..a1d45959dae7474558f2444cbed2d3e11e25a09a --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/qrcode/CoronaTestQRCodeTest.kt @@ -0,0 +1,30 @@ +package de.rki.coronawarnapp.coronatest.qrcode + +import io.kotest.matchers.shouldBe +import org.joda.time.Instant +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class CoronaTestQRCodeTest : BaseTest() { + + private val instancePCR = CoronaTestQRCode.PCR("pcr") + private val instanceRA = CoronaTestQRCode.RapidAntigen("ra", createdAt = Instant.EPOCH) + + @Test + fun `PCR defaults`() { + instancePCR.apply { + isDccSupportedbyPoc shouldBe true + isDccConsentGiven shouldBe false + dateOfBirth shouldBe null + } + } + + @Test + fun `RA defaults`() { + instanceRA.apply { + isDccSupportedbyPoc shouldBe false + isDccConsentGiven shouldBe false + dateOfBirth shouldBe null + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/storage/CoronaTestStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/storage/CoronaTestStorageTest.kt index 100c26a50ea788a0a2776bc5afdbaee42534a5d3..331c6f5334ead76ca072a55512f55a9008740e96 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/storage/CoronaTestStorageTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/storage/CoronaTestStorageTest.kt @@ -51,6 +51,8 @@ class CoronaTestStorageTest : BaseTest() { testResult = CoronaTestResult.PCR_POSITIVE, testResultReceivedAt = Instant.ofEpochMilli(2000), lastUpdatedAt = Instant.ofEpochMilli(2001), + isDccConsentGiven = true, + isDccDataSetCreated = true, ) private val raTest = RACoronaTest( identifier = "identifier-ra", @@ -67,6 +69,9 @@ class CoronaTestStorageTest : BaseTest() { dateOfBirth = LocalDate.parse("2021-12-24"), testedAt = Instant.ofEpochMilli(3000), lastUpdatedAt = Instant.ofEpochMilli(2001), + isDccSupportedByPoc = true, + isDccConsentGiven = true, + isDccDataSetCreated = true, ) @Test @@ -110,7 +115,9 @@ class CoronaTestStorageTest : BaseTest() { "isResultAvailableNotificationSent": false, "testResultReceivedAt": 2000, "testResult": 2, - "lastUpdatedAt": 2001 + "lastUpdatedAt": 2001, + "isDccConsentGiven": true, + "isDccDataSetCreated": true } ] """.toComparableJsonPretty() @@ -152,7 +159,10 @@ class CoronaTestStorageTest : BaseTest() { "testedAt": 3000, "firstName": "firstname", "lastName": "lastname", - "dateOfBirth": "2021-12-24" + "dateOfBirth": "2021-12-24", + "isDccSupportedByPoc": true, + "isDccConsentGiven": true, + "isDccDataSetCreated": true } ] """.toComparableJsonPretty() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/tan/CoronaTestTANTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/tan/CoronaTestTANTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..20b1559d6d3744faf374bd667660ca10442a6f22 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/tan/CoronaTestTANTest.kt @@ -0,0 +1,19 @@ +package de.rki.coronawarnapp.coronatest.tan + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class CoronaTestTANTest : BaseTest() { + + private val instancePCR = CoronaTestTAN.PCR("tan") + + @Test + fun `dcc is not supported by tans`() { + instancePCR.apply { + isDccConsentGiven shouldBe false + isDccSupportedbyPoc shouldBe false + dateOfBirth shouldBe null + } + } +} 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 fb402a8914b098bdb7fe371bae92b6c9f14dc662..f67ba4c3d29ce4ecccca5855f553a82d1b259319 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 @@ -43,6 +43,13 @@ class PCRProcessorTest : BaseTest() { @MockK lateinit var analyticsTestResultCollector: AnalyticsTestResultCollector private val nowUTC = Instant.parse("2021-03-15T05:45:00.000Z") + private val defaultTest = PCRCoronaTest( + identifier = "identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = nowUTC, + registrationToken = "regtoken", + testResult = PCR_OR_RAT_PENDING + ) @BeforeEach fun setup() { @@ -96,12 +103,8 @@ class PCRProcessorTest : BaseTest() { fun `if we receive a pending result 60 days after registration, we map to REDEEMED`() = runBlockingTest { val instance = createInstance() - val pcrTest = PCRCoronaTest( - identifier = "identifier", - lastUpdatedAt = Instant.EPOCH, - registeredAt = nowUTC, - registrationToken = "regtoken", - testResult = PCR_POSITIVE + val pcrTest = defaultTest.copy( + testResult = PCR_POSITIVE, ) instance.pollServer(pcrTest).testResult shouldBe PCR_OR_RAT_PENDING @@ -164,12 +167,8 @@ class PCRProcessorTest : BaseTest() { val instance = createInstance() - val pcrTest = PCRCoronaTest( - identifier = "identifier", - lastUpdatedAt = Instant.EPOCH, - registeredAt = nowUTC, - registrationToken = "regtoken", - testResult = PCR_POSITIVE + val pcrTest = defaultTest.copy( + testResult = PCR_POSITIVE, ) values().forEach { @@ -220,12 +219,9 @@ class PCRProcessorTest : BaseTest() { val instance = createInstance() - val pcrTest = PCRCoronaTest( - identifier = "identifier", - lastUpdatedAt = Instant.EPOCH, + val pcrTest = defaultTest.copy( registeredAt = nowUTC.minus(Duration.standardDays(22)), - registrationToken = "regtoken", - testResult = PCR_REDEEMED + testResult = PCR_REDEEMED, ) // Older than 21 days and already redeemed @@ -244,12 +240,8 @@ class PCRProcessorTest : BaseTest() { val instance = createInstance() - val pcrTest = PCRCoronaTest( - identifier = "identifier", - lastUpdatedAt = Instant.EPOCH, - registeredAt = nowUTC, - registrationToken = "regtoken", - testResult = PCR_POSITIVE + val pcrTest = defaultTest.copy( + testResult = PCR_POSITIVE, ) // Test is not older than 21 days, we want the error! @@ -264,4 +256,16 @@ class PCRProcessorTest : BaseTest() { lastError shouldBe null } } + + @Test + fun `giving submission consent`() = runBlockingTest { + val instance = createInstance() + + instance.updateSubmissionConsent(defaultTest, true) shouldBe defaultTest.copy( + isAdvancedConsentGiven = true + ) + instance.updateSubmissionConsent(defaultTest, false) shouldBe defaultTest.copy( + isAdvancedConsentGiven = false + ) + } } 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 74e69ef1cec8fe8f5cfc717809c6cf9c114fb6aa..a3180a0da2b84bb6dd675e47ec62bc713c02374c 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 @@ -43,6 +43,15 @@ class RapidAntigenProcessorTest : BaseTest() { private val nowUTC = Instant.parse("2021-03-15T05:45:00.000Z") + private val defaultTest = RACoronaTest( + identifier = "identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = nowUTC, + registrationToken = "regtoken", + testResult = RAT_PENDING, + testedAt = Instant.EPOCH, + ) + @BeforeEach fun setup() { MockKAnnotations.init(this) @@ -85,7 +94,7 @@ class RapidAntigenProcessorTest : BaseTest() { } } - fun createInstance() = RapidAntigenProcessor( + fun createInstance() = RAProcessor( timeStamper = timeStamper, submissionService = submissionService, analyticsKeySubmissionCollector = analyticsKeySubmissionCollector, @@ -118,13 +127,8 @@ class RapidAntigenProcessorTest : BaseTest() { fun `if we receive a pending result 60 days after registration, we map to REDEEMED`() = runBlockingTest { val instance = createInstance() - val raTest = RACoronaTest( - identifier = "identifier", - lastUpdatedAt = Instant.EPOCH, - registeredAt = nowUTC, - registrationToken = "regtoken", + val raTest = defaultTest.copy( testResult = RAT_POSITIVE, - testedAt = Instant.EPOCH, ) instance.pollServer(raTest).testResult shouldBe PCR_OR_RAT_PENDING @@ -189,13 +193,8 @@ class RapidAntigenProcessorTest : BaseTest() { val instance = createInstance() - val raTest = RACoronaTest( - identifier = "identifier", - lastUpdatedAt = Instant.EPOCH, - registeredAt = nowUTC, - registrationToken = "regtoken", + val raTest = defaultTest.copy( testResult = RAT_POSITIVE, - testedAt = Instant.EPOCH, ) values().forEach { @@ -227,13 +226,9 @@ class RapidAntigenProcessorTest : BaseTest() { val instance = createInstance() - val raTest = RACoronaTest( - identifier = "identifier", - lastUpdatedAt = Instant.EPOCH, + val raTest = defaultTest.copy( registeredAt = nowUTC.minus(Duration.standardDays(22)), - registrationToken = "regtoken", testResult = RAT_REDEEMED, - testedAt = Instant.EPOCH, ) // Older than 21 days and already redeemed @@ -273,4 +268,16 @@ class RapidAntigenProcessorTest : BaseTest() { lastError shouldBe null } } + + @Test + fun `giving submission consent`() = runBlockingTest { + val instance = createInstance() + + instance.updateSubmissionConsent(defaultTest, true) shouldBe defaultTest.copy( + isAdvancedConsentGiven = true + ) + instance.updateSubmissionConsent(defaultTest, false) shouldBe defaultTest.copy( + isAdvancedConsentGiven = false + ) + } }