diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt index c082b5b64e30e99ac9d2229f7db75ef77c8121b5..9bf3595fcd5690c4b7c42ed3faf752775f2af139 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt @@ -10,7 +10,7 @@ import com.squareup.inject.assisted.AssistedInject import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.appconfig.ConfigData import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask -import de.rki.coronawarnapp.diagnosiskeys.download.KeyPackageSyncSettings +import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysSettings import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.nearby.modules.detectiontracker.latestSubmission @@ -57,7 +57,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( private val testSettings: TestSettings, private val timeStamper: TimeStamper, private val exposureDetectionTracker: ExposureDetectionTracker, - private val keyPackageSyncSettings: KeyPackageSyncSettings, + private val downloadDiagnosisKeysSettings: DownloadDiagnosisKeysSettings, private val submissionRepository: SubmissionRepository ) : CWAViewModel( dispatcherProvider = dispatcherProvider @@ -241,7 +241,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( Timber.d("Clearing key cache") launch { keyCacheRepository.clear() - keyPackageSyncSettings.clear() + downloadDiagnosisKeysSettings.clear() exposureDetectionTracker.clear() dataResetEvent.postValue("Download & Submission related data reset.") diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/internal/AppConfigSource.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/internal/AppConfigSource.kt index 1e5cc4959e860fec7548e6e1a1954451487261f8..e8d3ff9192777c40b494970353b95b0cdbd002ae 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/internal/AppConfigSource.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/internal/AppConfigSource.kt @@ -40,7 +40,7 @@ class AppConfigSource @Inject constructor( localConfig } else -> { - Timber.tag(TAG).w("Remote & Local config available! Returning DEFAULT!") + Timber.tag(TAG).w("Remote & Local config unavailable! Returning DEFAULT!") defaultAppConfigSource.getConfigData() } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncSettings.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysSettings.kt similarity index 58% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncSettings.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysSettings.kt index 767788b052571ee50d306089dec02aba0a7513d6..942c9407578f4fc65ea739b83d8a14992dc48c8f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncSettings.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysSettings.kt @@ -2,17 +2,20 @@ package de.rki.coronawarnapp.diagnosiskeys.download import android.annotation.SuppressLint import android.content.Context +import androidx.core.content.edit import com.google.gson.Gson +import de.rki.coronawarnapp.environment.BuildConfigWrap import de.rki.coronawarnapp.util.di.AppContext import de.rki.coronawarnapp.util.preferences.FlowPreference import de.rki.coronawarnapp.util.preferences.clearAndNotify import de.rki.coronawarnapp.util.serialization.BaseGson import org.joda.time.Instant +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton -class KeyPackageSyncSettings @Inject constructor( +class DownloadDiagnosisKeysSettings @Inject constructor( @AppContext private val context: Context, @BaseGson private val gson: Gson ) { @@ -34,6 +37,12 @@ class KeyPackageSyncSettings @Inject constructor( writer = FlowPreference.gsonWriter(gson) ) + var lastVersionCode: Long + get() = prefs.getLong(KEY_LAST_VERSION_CODE, -1L) + set(value) = prefs.edit { + putLong(KEY_LAST_VERSION_CODE, value) + } + @SuppressLint("ApplySharedPref") fun clear() { prefs.clearAndNotify() @@ -45,4 +54,21 @@ class KeyPackageSyncSettings @Inject constructor( val successful: Boolean = false, val newData: Boolean = false ) + + companion object { + private const val KEY_LAST_VERSION_CODE = "download.task.last.versionCode" + internal const val VERSION_CODE_FIRST_POSSIBLE_1_8_RELEASE = 1080000 + } +} + +/** + * true if, and only if, since last runtime the app was updated from 1.7 (or earlier) to a version >= 1.8.0 + */ +val DownloadDiagnosisKeysSettings.isUpdateToEnfV2: Boolean + get() = lastVersionCode < DownloadDiagnosisKeysSettings.VERSION_CODE_FIRST_POSSIBLE_1_8_RELEASE + +fun DownloadDiagnosisKeysSettings.updateLastVersionCodeToCurrent() { + lastVersionCode = BuildConfigWrap.VERSION_CODE.also { + Timber.d("lastVersionCode updated to %d", it) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt index cd5bc298faa82904ddbf2c2610ed954ed4a98750..8f4f32f0d97dc6e9d5887232b292d152635b58ee 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt @@ -6,7 +6,6 @@ import de.rki.coronawarnapp.appconfig.ExposureDetectionConfig import de.rki.coronawarnapp.diagnosiskeys.server.LocationCode import de.rki.coronawarnapp.environment.EnvironmentSetup import de.rki.coronawarnapp.nearby.ENFClient -import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetection import de.rki.coronawarnapp.risk.RollbackItem import de.rki.coronawarnapp.task.Task @@ -31,7 +30,8 @@ class DownloadDiagnosisKeysTask @Inject constructor( private val environmentSetup: EnvironmentSetup, private val appConfigProvider: AppConfigProvider, private val keyPackageSyncTool: KeyPackageSyncTool, - private val timeStamper: TimeStamper + private val timeStamper: TimeStamper, + private val settings: DownloadDiagnosisKeysSettings ) : Task<DownloadDiagnosisKeysTask.Progress, Task.Result> { private val internalProgress = ConflatedBroadcastChannel<Progress>() @@ -51,7 +51,7 @@ class DownloadDiagnosisKeysTask @Inject constructor( * in a background job. Also it acts as a failure catch in case the orchestration code did * not check in before. */ - if (!InternalExposureNotificationClient.asyncIsEnabled()) { + if (!enfClient.isTracingEnabled.first()) { Timber.tag(TAG).w("EN is not enabled, skipping RetrieveDiagnosisKeys") return object : Task.Result {} } @@ -72,7 +72,6 @@ class DownloadDiagnosisKeysTask @Inject constructor( val keySyncResult = getAvailableKeyFiles(requestedCountries) throwIfCancelled() - val trackedExposureDetections = enfClient.latestTrackedExposureDetection().first() val now = timeStamper.nowUTC if (exposureConfig.maxExposureDetectionsPerUTCDay == 0) { @@ -80,13 +79,17 @@ class DownloadDiagnosisKeysTask @Inject constructor( return object : Task.Result {} } - if (wasLastDetectionPerformedRecently(now, exposureConfig, trackedExposureDetections)) { + val trackedExposureDetections = enfClient.latestTrackedExposureDetection().first() + val isUpdateToEnfV2 = settings.isUpdateToEnfV2 + + Timber.tag(TAG).d("isUpdateToEnfV2: %b", isUpdateToEnfV2) + if (!isUpdateToEnfV2 && wasLastDetectionPerformedRecently(now, exposureConfig, trackedExposureDetections)) { // At most one detection every 6h Timber.tag(TAG).i("task aborted, because detection was performed recently") return object : Task.Result {} } - if (hasRecentDetectionAndNoNewFiles(now, keySyncResult, trackedExposureDetections)) { + if (!isUpdateToEnfV2 && hasRecentDetectionAndNoNewFiles(now, keySyncResult, trackedExposureDetections)) { Timber.tag(TAG).i("task aborted, last check was within 24h, and there are no new files") return object : Task.Result {} } @@ -101,6 +104,9 @@ class DownloadDiagnosisKeysTask @Inject constructor( ) ) + // remember version code of this execution for next time + settings.updateLastVersionCodeToCurrent() + Timber.tag(TAG).d("Attempting submission to ENF") val isSubmissionSuccessful = enfClient.provideDiagnosisKeys( availableKeyFiles, @@ -161,7 +167,9 @@ class DownloadDiagnosisKeysTask @Inject constructor( } } - private suspend fun getAvailableKeyFiles(requestedCountries: List<String>?): KeyPackageSyncTool.Result { + private suspend fun getAvailableKeyFiles( + requestedCountries: List<String>? + ): KeyPackageSyncTool.Result { val wantedLocations = if (environmentSetup.useEuropeKeyPackageFiles) { listOf("EUR") } else { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncTool.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncTool.kt index 9c8189c90e5015085a0b360b8d23999f8e662cd6..5388f534123f19c118d9a8583b7c749ba420d5cd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncTool.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncTool.kt @@ -15,7 +15,7 @@ class KeyPackageSyncTool @Inject constructor( private val keyCache: KeyCacheRepository, private val dayPackageSyncTool: DayPackageSyncTool, private val hourPackageSyncTool: HourPackageSyncTool, - private val syncSettings: KeyPackageSyncSettings, + private val syncSettings: DownloadDiagnosisKeysSettings, private val timeStamper: TimeStamper, private val networkStateProvider: NetworkStateProvider ) { @@ -77,7 +77,7 @@ class KeyPackageSyncTool @Inject constructor( Timber.tag(TAG).d("Synchronizing available days (lastDownload=%s).", lastDownload) syncSettings.lastDownloadDays.update { - KeyPackageSyncSettings.LastDownload(startedAt = timeStamper.nowUTC) + DownloadDiagnosisKeysSettings.LastDownload(startedAt = timeStamper.nowUTC) } val syncResult = dayPackageSyncTool.syncMissingDayPackages( @@ -104,7 +104,7 @@ class KeyPackageSyncTool @Inject constructor( Timber.tag(TAG).d("Synchronizing available hours (lastDownload=%s).", lastDownload) syncSettings.lastDownloadHours.update { - KeyPackageSyncSettings.LastDownload( + DownloadDiagnosisKeysSettings.LastDownload( startedAt = timeStamper.nowUTC ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/BuildConfigWrap.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/BuildConfigWrap.kt index 8fc88e77f9cb9b5ce3f49404a59b1d16a5386d04..bc8252d1b7b923721b0e77cd6c85c08f43c4e678 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/BuildConfigWrap.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/BuildConfigWrap.kt @@ -8,4 +8,6 @@ object BuildConfigWrap { val ENVIRONMENT_JSONDATA = BuildConfig.ENVIRONMENT_JSONDATA val ENVIRONMENT_TYPE_DEFAULT = BuildConfig.ENVIRONMENT_TYPE_DEFAULT + + val VERSION_CODE: Long = BuildConfig.VERSION_CODE.toLong() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/legacy/RiskLevelResultMigrator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/legacy/RiskLevelResultMigrator.kt index 0f60e67edfc2b0e1533ad3325acee636ef1aa86f..07f3a3fb3f6e6578f0c341aface0383151d020ef 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/legacy/RiskLevelResultMigrator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/legacy/RiskLevelResultMigrator.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.risk.storage.legacy +import android.content.Context import android.content.SharedPreferences import androidx.annotation.VisibleForTesting import com.google.android.gms.nearby.exposurenotification.ExposureWindow @@ -7,8 +8,11 @@ import dagger.Lazy import de.rki.coronawarnapp.risk.RiskLevelResult import de.rki.coronawarnapp.risk.RiskState import de.rki.coronawarnapp.risk.result.AggregatedRiskResult +import de.rki.coronawarnapp.storage.AppDatabase import de.rki.coronawarnapp.storage.EncryptedPreferences import de.rki.coronawarnapp.util.TimeStamper +import de.rki.coronawarnapp.util.di.AppContext +import org.joda.time.Duration import org.joda.time.Instant import timber.log.Timber import javax.inject.Inject @@ -22,60 +26,93 @@ import javax.inject.Singleton @Singleton class RiskLevelResultMigrator @Inject constructor( @EncryptedPreferences encryptedPreferences: Lazy<SharedPreferences>, - private val timeStamper: TimeStamper + private val timeStamper: TimeStamper, + @AppContext + private val context: Context ) { private val prefs by lazy { encryptedPreferences.get() } private fun lastTimeRiskLevelCalculation(): Instant? { prefs.getLong("preference_timestamp_risk_level_calculation", -1L).also { + Timber.tag(TAG).d("preference_timestamp_risk_level_calculation=$it") return if (it < 0) null else Instant.ofEpochMilli(it) } } private fun lastCalculatedRiskLevel(): RiskState? { val rawRiskLevel = prefs.getInt("preference_risk_level_score", -1) + Timber.tag(TAG).d("preference_risk_level_score=$rawRiskLevel") return if (rawRiskLevel != -1) mapRiskLevelConstant(rawRiskLevel) else null } private fun lastSuccessfullyCalculatedRiskLevel(): RiskState? { val rawRiskLevel = prefs.getInt("preference_risk_level_score_successful", -1) + Timber.tag(TAG).d("preference_risk_level_score_successful=$rawRiskLevel") return if (rawRiskLevel != -1) mapRiskLevelConstant(rawRiskLevel) else null } - fun getLegacyResults(): List<RiskLevelResult> = try { + private suspend fun lastEncounterAt(): Instant? { + return try { + val daysSinceLastExposure = + AppDatabase.getInstance(context) + .exposureSummaryDao() + .getLatestExposureSummary()?.daysSinceLastExposure + if (daysSinceLastExposure == null) { + null + } else { + timeStamper.nowUTC.minus(Duration.standardDays(daysSinceLastExposure.toLong())) + } + } catch (exception: Exception) { + Timber.tag(TAG).w(exception, "failed to select exposure summary dao from enf v1") + null + } + } + + suspend fun getLegacyResults(): List<RiskLevelResult> = try { val legacyResults = mutableListOf<RiskLevelResult>() lastCalculatedRiskLevel()?.let { legacyResults.add( LegacyResult( riskState = it, - calculatedAt = lastTimeRiskLevelCalculation() ?: timeStamper.nowUTC + calculatedAt = lastTimeRiskLevelCalculation() ?: timeStamper.nowUTC, + lastEncounterAt = lastEncounterAt() ) ) } lastSuccessfullyCalculatedRiskLevel()?.let { - legacyResults.add(LegacyResult(riskState = it, calculatedAt = timeStamper.nowUTC)) + legacyResults.add( + LegacyResult( + riskState = it, + calculatedAt = timeStamper.nowUTC, + lastEncounterAt = lastEncounterAt() + ) + ) } + Timber.tag(TAG).d("legacyResults=$legacyResults") legacyResults } catch (e: Exception) { - Timber.e(e, "Failed to parse legacy risklevel data.") + Timber.tag(TAG).e(e, "Failed to parse legacy risklevel data.") emptyList() } data class LegacyResult( override val riskState: RiskState, - override val calculatedAt: Instant + override val calculatedAt: Instant, + private val lastEncounterAt: Instant? ) : RiskLevelResult { override val failureReason: RiskLevelResult.FailureReason? = null override val aggregatedRiskResult: AggregatedRiskResult? = null override val exposureWindows: List<ExposureWindow>? = null override val matchedKeyCount: Int = 0 override val daysWithEncounters: Int = 0 + override val lastRiskEncounterAt: Instant? = lastEncounterAt } companion object { + private const val TAG = "RiskLevelResultMigrator" @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal fun mapRiskLevelConstant(value: Int): RiskState = when (value) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardState.kt index 8495f92558bb58fb6246e5b2e6ae2d942bbfd633..8faedf9b4348988f5127213fc396a84b2e13ce60 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardState.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardState.kt @@ -86,7 +86,9 @@ data class TracingCardState( "" } riskState == INCREASED_RISK && daysWithEncounters == 0 -> { - c.getString(R.string.risk_card_high_risk_no_encounters_body) + // LEGACY MIGRATION CASE FROM 1.7.x -> 1.8.x ('days with encounter' doesn't exit in 1.7.x) + // see RiskLevelResultMigrator.kt + "" } riskState == INCREASED_RISK -> { c.resources.getQuantityString( @@ -96,6 +98,8 @@ data class TracingCardState( ) } riskState == LOW_RISK && daysWithEncounters == 0 -> { + // caution! is 0 after migration from 1.7.x -> 1.8.x + // see RiskLevelResultMigrator.kt c.getString(R.string.risk_card_low_risk_no_encounters_body) } riskState == LOW_RISK -> { @@ -126,10 +130,13 @@ data class TracingCardState( */ fun getRiskContactLast(c: Context): String = when { isTracingOff() -> "" - riskState == INCREASED_RISK -> { - val formattedDate = lastEncounterAt?.toLocalDate()?.toString(DateTimeFormat.mediumDate()) - c.getString(R.string.risk_card_high_risk_most_recent_body, formattedDate) - } + riskState == INCREASED_RISK && lastEncounterAt != null -> + // caution! lastEncounterAt is null after migration from 1.7.x -> 1.8.x + // see RiskLevelResultMigrator.kt + c.getString( + R.string.risk_card_high_risk_most_recent_body, + lastEncounterAt.toLocalDate().toString(DateTimeFormat.mediumDate()) + ) else -> "" } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt index 7673f4e16733553d6e9817bc3f604e55aa7206c5..e5438192ad55b436bd559dc31e998c7e162facd2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt @@ -22,7 +22,7 @@ package de.rki.coronawarnapp.util import android.annotation.SuppressLint import android.content.Context import de.rki.coronawarnapp.appconfig.AppConfigProvider -import de.rki.coronawarnapp.diagnosiskeys.download.KeyPackageSyncSettings +import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysSettings import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.risk.storage.RiskLevelStorage @@ -49,7 +49,7 @@ class DataReset @Inject constructor( private val interoperabilityRepository: InteroperabilityRepository, private val submissionRepository: SubmissionRepository, private val exposureDetectionTracker: ExposureDetectionTracker, - private val keyPackageSyncSettings: KeyPackageSyncSettings, + private val downloadDiagnosisKeysSettings: DownloadDiagnosisKeysSettings, private val riskLevelStorage: RiskLevelStorage ) { @@ -75,7 +75,7 @@ class DataReset @Inject constructor( appConfigProvider.clear() interoperabilityRepository.clear() exposureDetectionTracker.clear() - keyPackageSyncSettings.clear() + downloadDiagnosisKeysSettings.clear() riskLevelStorage.clear() Timber.w("CWA LOCAL DATA DELETION COMPLETED.") diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml index dcece355b579c93069c886ac2f35c3f659082cda..ec76626547627e9547298defceb1262937dc7a71 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -175,8 +175,6 @@ <item quantity="many">"Begegnungen mit niedrigem Risiko an %1$d Tagen"</item> </plurals> - <!-- XTXT: risk card - High risk state - No days with high risk encounters --> - <string name="risk_card_high_risk_no_encounters_body">Keine Risiko-Begegnungen</string> <!-- XTXT: risk card - High risk state - Days with high risk encounters --> <plurals name="risk_card_high_risk_encounter_days_body"> <item quantity="one">"Begegnungen an einem Tag mit erhöhtem Risiko"</item> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParserTest.kt index 22f65553bf124132ecd7df87bc67b821afbbbc2e..17ac5297ed7f48bccf82fec5fda51897c1655df0 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParserTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParserTest.kt @@ -1,9 +1,13 @@ package de.rki.coronawarnapp.appconfig.mapping +import com.google.protobuf.InvalidProtocolBufferException import de.rki.coronawarnapp.appconfig.CWAConfig import de.rki.coronawarnapp.appconfig.ExposureDetectionConfig import de.rki.coronawarnapp.appconfig.ExposureWindowRiskCalculationConfig import de.rki.coronawarnapp.appconfig.KeyDownloadConfig +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe import io.mockk.MockKAnnotations import io.mockk.clearAllMocks import io.mockk.every @@ -15,6 +19,7 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest +import java.io.File class ConfigParserTest : BaseTest() { @MockK lateinit var cwaConfigMapper: CWAConfig.Mapper @@ -22,6 +27,9 @@ class ConfigParserTest : BaseTest() { @MockK lateinit var exposureDetectionConfigMapper: ExposureDetectionConfig.Mapper @MockK lateinit var exposureWindowRiskCalculationConfigMapper: ExposureWindowRiskCalculationConfig.Mapper + private val appConfig171 = File("src/test/resources/appconfig_1_7_1.bin") + private val appConfig180 = File("src/test/resources/appconfig_1_8_0.bin") + @BeforeEach fun setup() { MockKAnnotations.init(this) @@ -30,6 +38,9 @@ class ConfigParserTest : BaseTest() { every { keyDownloadConfigMapper.map(any()) } returns mockk() every { exposureDetectionConfigMapper.map(any()) } returns mockk() every { exposureWindowRiskCalculationConfigMapper.map(any()) } returns mockk() + + appConfig171.exists() shouldBe true + appConfig180.exists() shouldBe true } @AfterEach @@ -57,6 +68,22 @@ class ConfigParserTest : BaseTest() { } } + @Test + fun `parsing the 1_7_1 config throws exception`() { + // We don't want to use the 1.7.x config as fallback once we are on 1.8.x + // If parsing cached config data fails, we will fallback to the default config of 1.8.x + shouldThrow<InvalidProtocolBufferException> { + createInstance().parse(appConfig171.readBytes()) + } + } + + @Test + fun `parsing the 1_8_0 config does not throw an exception`() { + createInstance().parse(appConfig180.readBytes()).apply { + rawConfig shouldNotBe null + } + } + companion object { private val APPCONFIG_RAW = ( "081f101f1a0e0a0c0a0872657365727665641001220244452a061" + diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/DownloadDiagnosisKeysSettingsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/DownloadDiagnosisKeysSettingsTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..44f67e089885700329b608a9d448f36ce2b8609f --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/DownloadDiagnosisKeysSettingsTest.kt @@ -0,0 +1,79 @@ +package de.rki.coronawarnapp.diagnosiskeys + +import android.content.Context +import androidx.core.content.edit +import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysSettings +import de.rki.coronawarnapp.diagnosiskeys.download.isUpdateToEnfV2 +import de.rki.coronawarnapp.diagnosiskeys.download.updateLastVersionCodeToCurrent +import de.rki.coronawarnapp.environment.BuildConfigWrap +import de.rki.coronawarnapp.util.serialization.SerializationModule +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockkObject +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 DownloadDiagnosisKeysSettingsTest : BaseTest() { + @MockK lateinit var context: Context + lateinit var preferences: MockSharedPreferences + + private val baseGson = SerializationModule().baseGson() + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + preferences = MockSharedPreferences() + every { context.getSharedPreferences("keysync_localdata", Context.MODE_PRIVATE) } returns preferences + + mockkObject(BuildConfigWrap) + } + + @AfterEach + fun tearDown() { + clearAllMocks() + } + + fun createInstance() = DownloadDiagnosisKeysSettings( + context = context, + gson = baseGson + ) + + @Test + fun `lastVersionCode default values`() { + val instance = createInstance() + instance.lastVersionCode shouldBe -1L + instance.isUpdateToEnfV2 shouldBe true + } + + @Test + fun `lastVersionCode on 1_8_x`() { + preferences.edit { + putLong("download.task.last.versionCode", 1080000) + } + val instance = createInstance() + instance.lastVersionCode shouldBe 1080000L + instance.isUpdateToEnfV2 shouldBe false + } + + @Test + fun `update the last versionCode`() { + val instance = createInstance() + instance.lastVersionCode = 99 + instance.isUpdateToEnfV2 shouldBe true + instance.lastVersionCode shouldBe 99 + instance.isUpdateToEnfV2 shouldBe true + + every { BuildConfigWrap.VERSION_CODE } returns 1080000 + + instance.updateLastVersionCodeToCurrent() + instance.lastVersionCode shouldBe 1080000 + instance.isUpdateToEnfV2 shouldBe false + preferences.dataMapPeek["download.task.last.versionCode"] shouldBe 1080000 + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt index 4aec1691f060b27ae4a8ff466967840a4c80475c..666493af418fd8f43791eff09ad33f5807743b95 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt @@ -1,15 +1,34 @@ package de.rki.coronawarnapp.diagnosiskeys.download +import com.google.android.gms.nearby.exposurenotification.DiagnosisKeysDataMapping import de.rki.coronawarnapp.appconfig.AppConfigProvider +import de.rki.coronawarnapp.appconfig.ConfigData +import de.rki.coronawarnapp.diagnosiskeys.storage.CachedKey +import de.rki.coronawarnapp.environment.BuildConfigWrap import de.rki.coronawarnapp.environment.EnvironmentSetup import de.rki.coronawarnapp.nearby.ENFClient +import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetection import de.rki.coronawarnapp.util.TimeStamper import io.mockk.MockKAnnotations +import io.mockk.Runs import io.mockk.clearAllMocks +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.coVerifySequence +import io.mockk.every import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.mockkObject +import io.mockk.verify +import kotlinx.coroutines.flow.flowOf +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 java.io.File class DownloadDiagnosisKeysTaskTest : BaseTest() { @@ -18,10 +37,60 @@ class DownloadDiagnosisKeysTaskTest : BaseTest() { @MockK lateinit var appConfigProvider: AppConfigProvider @MockK lateinit var keyPackageSyncTool: KeyPackageSyncTool @MockK lateinit var timeStamper: TimeStamper + @MockK lateinit var downloadSettings: DownloadDiagnosisKeysSettings + + @MockK lateinit var appConfig: ConfigData + @MockK lateinit var syncResult: KeyPackageSyncTool.Result + @MockK lateinit var diagnosisKeyDataMapping: DiagnosisKeysDataMapping + + @MockK lateinit var availableKey1: CachedKey + @MockK lateinit var newKey1: CachedKey + + @MockK lateinit var latestTrackedDetection: TrackedExposureDetection @BeforeEach fun setup() { MockKAnnotations.init(this) + + mockkObject(BuildConfigWrap) + every { BuildConfigWrap.VERSION_CODE } returns 1080005 + + availableKey1.apply { + every { path } returns File("availableKey1") + } + newKey1.apply { + every { path } returns File("newKey1") + } + + appConfig.apply { + every { maxExposureDetectionsPerUTCDay } returns 5 + every { minTimeBetweenDetections } returns Duration.standardHours(24 / 6) + every { diagnosisKeysDataMapping } returns diagnosisKeyDataMapping + } + coEvery { appConfigProvider.getAppConfig() } returns appConfig + + downloadSettings.apply { + every { lastVersionCode } returns 1080000 + every { lastVersionCode = any() } just Runs + } + + every { enfClient.isTracingEnabled } returns flowOf(true) + every { timeStamper.nowUTC } returns Instant.EPOCH.plus(Duration.standardHours(5)) + every { environmentSetup.useEuropeKeyPackageFiles } returns true + + coEvery { keyPackageSyncTool.syncKeyFiles(any()) } returns syncResult.apply { + every { availableKeys } returns listOf(availableKey1) + every { newKeys } returns listOf(newKey1) + } + + enfClient.apply { + latestTrackedDetection.apply { + every { startedAt } returns Instant.EPOCH + every { isSuccessful } returns true + } + coEvery { latestTrackedExposureDetection() } returns flowOf(listOf(latestTrackedDetection)) + coEvery { provideDiagnosisKeys(any(), any()) } returns true + } } @AfterEach @@ -34,6 +103,102 @@ class DownloadDiagnosisKeysTaskTest : BaseTest() { environmentSetup = environmentSetup, appConfigProvider = appConfigProvider, keyPackageSyncTool = keyPackageSyncTool, - timeStamper = timeStamper + timeStamper = timeStamper, + settings = downloadSettings ) + + @Test + fun `enf v1 to v2 change flag is checked and set`() = runBlockingTest { + every { downloadSettings.lastVersionCode } returns -1L + + val task = createInstance() + + task.run(DownloadDiagnosisKeysTask.Arguments()) + + coVerifySequence { + enfClient.isTracingEnabled + enfClient.latestTrackedExposureDetection() + enfClient.provideDiagnosisKeys(any(), any()) + } + verify(exactly = 0) { + appConfig.minTimeBetweenDetections + syncResult.newKeys + } + } + + @Test + fun `normal execution on first run`() = runBlockingTest { + val task = createInstance() + + task.run(DownloadDiagnosisKeysTask.Arguments()) + + coVerifySequence { + enfClient.isTracingEnabled + enfClient.latestTrackedExposureDetection() + enfClient.provideDiagnosisKeys(any(), any()) + } + } + + @Test + fun `execution is skipped if last detection was recent via`() = runBlockingTest { + // Last detection was at T+2h + every { timeStamper.nowUTC } returns Instant.EPOCH.plus(Duration.standardHours(2)) + + createInstance().run(DownloadDiagnosisKeysTask.Arguments()) + + coVerifySequence { + enfClient.isTracingEnabled + enfClient.latestTrackedExposureDetection() + } + + coVerify(exactly = 0) { + enfClient.provideDiagnosisKeys(any(), any()) + } + } + + @Test + fun `wasLastDetectionPerformedRecently honors paramters from config`() = runBlockingTest { + every { timeStamper.nowUTC } returns Instant.EPOCH.plus(Duration.standardHours(4)) + + createInstance().run(DownloadDiagnosisKeysTask.Arguments()) + + coVerifySequence { + enfClient.isTracingEnabled + enfClient.latestTrackedExposureDetection() + enfClient.provideDiagnosisKeys(any(), any()) + } + } + + @Test + fun `hasRecentDetectionAndNoNewFiles checks for new files`() = runBlockingTest { + every { timeStamper.nowUTC } returns Instant.EPOCH.plus(Duration.standardHours(4)) + + every { syncResult.newKeys } returns emptyList() + + createInstance().run(DownloadDiagnosisKeysTask.Arguments()) + + coVerifySequence { + enfClient.isTracingEnabled + enfClient.latestTrackedExposureDetection() + } + + coVerify(exactly = 0) { + enfClient.provideDiagnosisKeys(any(), any()) + } + } + + @Test + fun `hasRecentDetectionAndNoNewFiles ignores amount of files if we didn't update for a day`() = runBlockingTest { + every { timeStamper.nowUTC } returns Instant.EPOCH.plus(Duration.standardHours(25)) + + every { syncResult.newKeys } returns emptyList() + + createInstance().run(DownloadDiagnosisKeysTask.Arguments()) + + coVerifySequence { + enfClient.isTracingEnabled + enfClient.latestTrackedExposureDetection() + enfClient.provideDiagnosisKeys(any(), any()) + } + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncToolTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncToolTest.kt index 4579828228e565c51d5e07f3738ba8ca657e9b00..18a42f38981624f677ba2feefab59801f98bb30c 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncToolTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncToolTest.kt @@ -35,19 +35,19 @@ class KeyPackageSyncToolTest : BaseIOTest() { @MockK lateinit var keyCache: KeyCacheRepository @MockK lateinit var dayPackageSyncTool: DayPackageSyncTool @MockK lateinit var hourPackageSyncTool: HourPackageSyncTool - @MockK lateinit var syncSettings: KeyPackageSyncSettings + @MockK lateinit var syncSettings: DownloadDiagnosisKeysSettings @MockK lateinit var timeStamper: TimeStamper @MockK lateinit var networkStateProvider: NetworkStateProvider @MockK lateinit var networkState: NetworkStateProvider.State - private val lastDownloadDays: FlowPreference<KeyPackageSyncSettings.LastDownload?> = mockFlowPreference( - KeyPackageSyncSettings.LastDownload( + private val lastDownloadDays: FlowPreference<DownloadDiagnosisKeysSettings.LastDownload?> = mockFlowPreference( + DownloadDiagnosisKeysSettings.LastDownload( startedAt = Instant.EPOCH, finishedAt = Instant.EPOCH, successful = true ) ) - private val lastDownloadHours: FlowPreference<KeyPackageSyncSettings.LastDownload?> = mockFlowPreference( - KeyPackageSyncSettings.LastDownload( + private val lastDownloadHours: FlowPreference<DownloadDiagnosisKeysSettings.LastDownload?> = mockFlowPreference( + DownloadDiagnosisKeysSettings.LastDownload( startedAt = Instant.EPOCH, finishedAt = Instant.EPOCH, successful = true @@ -202,14 +202,14 @@ class KeyPackageSyncToolTest : BaseIOTest() { @Test fun `failed last download causes force sync`() = runBlockingTest { lastDownloadDays.update { - KeyPackageSyncSettings.LastDownload( + DownloadDiagnosisKeysSettings.LastDownload( startedAt = Instant.EPOCH, finishedAt = Instant.EPOCH, successful = false ) } lastDownloadHours.update { - KeyPackageSyncSettings.LastDownload( + DownloadDiagnosisKeysSettings.LastDownload( startedAt = Instant.EPOCH, finishedAt = Instant.EPOCH, successful = false diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt index 8e9fd7d52bcb1d29a2e35bfe010fc7a053bd10dc..c6b172de4f6454dc7da0688aaa1776131ed90fdc 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt @@ -50,7 +50,7 @@ class BaseRiskLevelStorageTest : BaseTest() { every { database.exposureWindows() } returns exposureWindowTables every { database.clearAllTables() } just Runs - every { riskLevelResultMigrator.getLegacyResults() } returns emptyList() + coEvery { riskLevelResultMigrator.getLegacyResults() } returns emptyList() every { riskResultTables.allEntries() } returns emptyFlow() coEvery { riskResultTables.insertEntry(any()) } just Runs @@ -124,7 +124,7 @@ class BaseRiskLevelStorageTest : BaseTest() { @Test fun `if no risk level results are available we try to get legacy results`() { - every { riskLevelResultMigrator.getLegacyResults() } returns listOf(mockk(), mockk()) + coEvery { riskLevelResultMigrator.getLegacyResults() } returns listOf(mockk(), mockk()) every { riskResultTables.allEntries() } returns flowOf(emptyList()) every { exposureWindowTables.allEntries() } returns flowOf(emptyList()) @@ -132,7 +132,7 @@ class BaseRiskLevelStorageTest : BaseTest() { val instance = createInstance() instance.riskLevelResults.first().size shouldBe 2 - verify { riskLevelResultMigrator.getLegacyResults() } + coVerify { riskLevelResultMigrator.getLegacyResults() } } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/legacy/RiskLevelResultMigratorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/legacy/RiskLevelResultMigratorTest.kt index 453bde1aa8e43131120df75ff2854a59eca99c08..3b95ea47cd3b7e0de90976cd3eb853be85c48d65 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/legacy/RiskLevelResultMigratorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/legacy/RiskLevelResultMigratorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.risk.storage.legacy +import android.content.Context import androidx.core.content.edit import de.rki.coronawarnapp.risk.RiskState import de.rki.coronawarnapp.util.TimeStamper @@ -8,6 +9,7 @@ import io.mockk.MockKAnnotations import io.mockk.clearAllMocks import io.mockk.every import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.runBlocking import org.joda.time.Instant import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach @@ -18,6 +20,7 @@ import testhelpers.preferences.MockSharedPreferences class RiskLevelResultMigratorTest : BaseTest() { @MockK lateinit var timeStamper: TimeStamper + @MockK lateinit var context: Context private val mockPreferences = MockSharedPreferences() @BeforeEach @@ -34,11 +37,12 @@ class RiskLevelResultMigratorTest : BaseTest() { fun createInstance() = RiskLevelResultMigrator( timeStamper = timeStamper, - encryptedPreferences = { mockPreferences } + encryptedPreferences = { mockPreferences }, + context = context ) @Test - fun `normal case with full values`() { + fun `normal case with full values`() = runBlocking { mockPreferences.edit { putInt("preference_risk_level_score", MigrationRiskLevelConstants.INCREASED_RISK) putInt("preference_risk_level_score_successful", MigrationRiskLevelConstants.LOW_LEVEL_RISK) @@ -58,13 +62,13 @@ class RiskLevelResultMigratorTest : BaseTest() { } @Test - fun `empty list if no previous data was available`() { + fun `empty list if no previous data was available`() = runBlocking { mockPreferences.dataMapPeek.isEmpty() shouldBe true createInstance().getLegacyResults() shouldBe emptyList() } @Test - fun `if no timestamp is available we use the current time`() { + fun `if no timestamp is available we use the current time`() = runBlocking { mockPreferences.edit { putInt("preference_risk_level_score", MigrationRiskLevelConstants.INCREASED_RISK) putInt("preference_risk_level_score_successful", MigrationRiskLevelConstants.LOW_LEVEL_RISK) @@ -83,7 +87,7 @@ class RiskLevelResultMigratorTest : BaseTest() { } @Test - fun `last successful is null`() { + fun `last successful is null`() = runBlocking { mockPreferences.edit { putInt("preference_risk_level_score_successful", MigrationRiskLevelConstants.INCREASED_RISK) } @@ -98,7 +102,7 @@ class RiskLevelResultMigratorTest : BaseTest() { } @Test - fun `last successfully calculated is null`() { + fun `last successfully calculated is null`() = runBlocking { mockPreferences.edit { putInt("preference_risk_level_score", MigrationRiskLevelConstants.INCREASED_RISK) putLong("preference_timestamp_risk_level_calculation", 1234567890L) @@ -114,7 +118,7 @@ class RiskLevelResultMigratorTest : BaseTest() { } @Test - fun `exceptions are handled gracefully`() { + fun `exceptions are handled gracefully`() = runBlocking { mockPreferences.edit { putInt("preference_risk_level_score", MigrationRiskLevelConstants.INCREASED_RISK) } diff --git a/Corona-Warn-App/src/test/resources/appconfig_1_7_1.bin b/Corona-Warn-App/src/test/resources/appconfig_1_7_1.bin new file mode 100644 index 0000000000000000000000000000000000000000..372f95657fc5b88c39d4f2baac77cec169e20ba6 Binary files /dev/null and b/Corona-Warn-App/src/test/resources/appconfig_1_7_1.bin differ diff --git a/Corona-Warn-App/src/test/resources/appconfig_1_8_0.bin b/Corona-Warn-App/src/test/resources/appconfig_1_8_0.bin new file mode 100644 index 0000000000000000000000000000000000000000..8b4b13ce8bfb73231da85ec22602b208ece7f226 Binary files /dev/null and b/Corona-Warn-App/src/test/resources/appconfig_1_8_0.bin differ diff --git a/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt b/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt index 09b586353b1c3d5f3e1b28887814710876fc388e..5085f60d184d9cacd3706cc50c32c5708dd5f8f0 100644 --- a/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt +++ b/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt @@ -75,7 +75,7 @@ class DefaultRiskLevelStorageTest : BaseTest() { every { database.exposureWindows() } returns exposureWindowTables every { database.clearAllTables() } just Runs - every { riskLevelResultMigrator.getLegacyResults() } returns emptyList() + coEvery { riskLevelResultMigrator.getLegacyResults() } returns emptyList() every { riskResultTables.allEntries() } returns flowOf(listOf(testRiskLevelResultDao)) coEvery { riskResultTables.insertEntry(any()) } just Runs diff --git a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt index 396eee55921cc00a270a2aacbaba4b20fbe18372..85da20abc73c054d9c9442c9f7f3c0e94fbf3c95 100644 --- a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt +++ b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt @@ -75,7 +75,7 @@ class DefaultRiskLevelStorageTest : BaseTest() { every { database.exposureWindows() } returns exposureWindowTables every { database.clearAllTables() } just Runs - every { riskLevelResultMigrator.getLegacyResults() } returns emptyList() + coEvery { riskLevelResultMigrator.getLegacyResults() } returns emptyList() every { riskResultTables.allEntries() } returns flowOf(listOf(testRiskLevelResultDao)) coEvery { riskResultTables.insertEntry(any()) } just Runs