diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt index 27de6bffab3d6d14dd588f322d370025b3efdb51..3c51b6df3aff83058514b6825a8497d602190d8e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt @@ -1,16 +1,23 @@ package de.rki.coronawarnapp.vaccination.core.repository -import androidx.annotation.VisibleForTesting import dagger.Reusable import de.rki.coronawarnapp.util.coroutine.AppScope +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.flow.HotDataFlow import de.rki.coronawarnapp.vaccination.core.repository.storage.ValueSetsStorage import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationServer import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet +import de.rki.coronawarnapp.vaccination.core.server.valueset.emptyVaccinationValueSet import de.rki.coronawarnapp.vaccination.core.server.valueset.isEmpty import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.distinctUntilChangedBy +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.plus import timber.log.Timber import java.util.Locale import javax.inject.Inject @@ -19,55 +26,65 @@ import javax.inject.Inject class ValueSetsRepository @Inject constructor( private val vaccinationServer: VaccinationServer, private val valueSetsStorage: ValueSetsStorage, - @AppScope private val scope: CoroutineScope + @AppScope private val scope: CoroutineScope, + dispatcherProvider: DispatcherProvider ) { - val latestValueSet: Flow<VaccinationValueSet> = valueSetsStorage.valueSet.flow.distinctUntilChanged() + private fun Flow<VaccinationValueSet>.distinctUntilChangedByHash() = distinctUntilChangedBy { it.hashCode() } - fun triggerUpdateValueSet(languageCode: Locale) = scope.launch { + private val internalData: HotDataFlow<VaccinationValueSet> = HotDataFlow( + loggingTag = TAG, + scope = scope, + coroutineContext = dispatcherProvider.IO, + sharingBehavior = SharingStarted.Lazily, + startValueProvider = { + valueSetsStorage.vaccinationValueSet.also { Timber.v("Loaded initial value set %s", it) } + } + ) + + init { + internalData.data + .distinctUntilChangedByHash() + .onStart { Timber.d("Observing value set") } + .onEach { valueSetsStorage.vaccinationValueSet = it } + .catch { Timber.e(it, "Storing new value set failed.") } + .launchIn(scope + dispatcherProvider.IO) + } + + val latestValueSet: Flow<VaccinationValueSet> = internalData.data.distinctUntilChangedByHash() + + fun triggerUpdateValueSet(languageCode: Locale) { Timber.d("triggerUpdateValueSet(languageCode=%s)", languageCode) + internalData.updateAsync( + onUpdate = { getValueSetFromServer(languageCode = languageCode) ?: this }, + onError = { Timber.e(it, "Updating value set failed") } + ) + } + + private suspend fun getValueSetFromServer(languageCode: Locale): VaccinationValueSet? { + Timber.v("getValueSetFromServer(languageCode=%s)", languageCode) var valueSet = vaccinationServer.getVaccinationValueSets(languageCode = languageCode) - if (valueSet == null && valueSetsStorage.valueSet.value.isEmpty()) { + if (valueSet.isEmpty()) { Timber.d( - "Got no value set from server for %s and local value set is empty... " + - "Try fallback to value set for en", + "Got no value set from server for %s and local value set is empty... Try fallback to value set for en", languageCode.language ) valueSet = vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } - valueSet - .also { Timber.d("Value set is %s", it) } - ?.let { newValueSet -> valueSetsStorage.valueSet.update { newValueSet.toStoredVaccinationValueSet() } } + return valueSet + .also { Timber.v("New value set %s", it) } } - fun clear() { + suspend fun clear() { Timber.d("Clearing value sets") vaccinationServer.clear() - valueSetsStorage.clear() + internalData.updateBlocking { + Timber.v("Resetting value set to an empty value set") + emptyVaccinationValueSet + } } } -@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) -fun VaccinationValueSet.toStoredVaccinationValueSet(): ValueSetsStorage.StoredVaccinationValueSet = - ValueSetsStorage.StoredVaccinationValueSet( - languageCode = languageCode, - vp = vp.toStoredValueSet(), - mp = mp.toStoredValueSet(), - ma = ma.toStoredValueSet() - ) - -@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) -fun VaccinationValueSet.ValueSet.toStoredValueSet(): ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet = - ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet( - items = items.map { it.toStoredItem() } - ) - -@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) -fun VaccinationValueSet.ValueSet.Item.toStoredItem(): - ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet.StoredItem = - ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet.StoredItem( - key = key, - displayText = displayText - ) +private const val TAG = "ValueSetsRepository" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt index bb81b8a8261a55e8bfa6cdcba1ed2f20ecb86064..63c2246e457b96b251dae2590186c4b5e108a214 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt @@ -3,15 +3,15 @@ package de.rki.coronawarnapp.vaccination.core.repository.storage import android.content.Context import android.content.SharedPreferences import androidx.annotation.Keep +import androidx.annotation.VisibleForTesting +import androidx.core.content.edit import com.google.gson.Gson import com.google.gson.annotations.SerializedName import dagger.Reusable 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.preferences.createFlowPreference import de.rki.coronawarnapp.util.serialization.BaseGson import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet +import de.rki.coronawarnapp.vaccination.core.server.valueset.emptyVaccinationValueSet import timber.log.Timber import java.util.Locale import javax.inject.Inject @@ -26,25 +26,52 @@ class ValueSetsStorage @Inject constructor( context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) } - var valueSet: FlowPreference<StoredVaccinationValueSet> = prefs.createFlowPreference( - key = PKEY_VALUE_SETS_PREFIX, - reader = FlowPreference.gsonReader(gson = gson, createEmptyValueSet()), - writer = FlowPreference.gsonWriter(gson = gson) - ) + var vaccinationValueSet: VaccinationValueSet + get() = getValueSet() + set(value) = setValueSet(value) - fun clear() { - Timber.d("Clearing local storage") - prefs.clearAndNotify() + private fun getValueSet(): VaccinationValueSet { + Timber.v("Loading value set") + val valueSetString = prefs.getString(PKEY_VALUE_SETS_PREFIX, null) + return when (valueSetString != null) { + true -> gson.fromJson(valueSetString, StoredVaccinationValueSet::class.java) + else -> emptyVaccinationValueSet + }.also { loaded -> Timber.v("Loaded value set %s", loaded) } } - private fun createEmptyValueSet() = StoredVaccinationValueSet( - languageCode = Locale.ENGLISH, - vp = StoredVaccinationValueSet.StoredValueSet(items = emptyList()), - mp = StoredVaccinationValueSet.StoredValueSet(items = emptyList()), - ma = StoredVaccinationValueSet.StoredValueSet(items = emptyList()) - ) + private fun setValueSet(value: VaccinationValueSet) { + Timber.v("Saving value set %s", value) + prefs.edit { + val json = gson.toJson(value.toStoredVaccinationValueSet(), StoredVaccinationValueSet::class.java) + Timber.v("Writing %s to prefs", json) + putString(PKEY_VALUE_SETS_PREFIX, json) + } + } + + @VisibleForTesting + fun VaccinationValueSet.toStoredVaccinationValueSet(): StoredVaccinationValueSet = + StoredVaccinationValueSet( + languageCode = languageCode, + vp = vp.toStoredValueSet(), + mp = mp.toStoredValueSet(), + ma = ma.toStoredValueSet() + ) + + @VisibleForTesting + fun VaccinationValueSet.ValueSet.toStoredValueSet(): StoredVaccinationValueSet.StoredValueSet = + StoredVaccinationValueSet.StoredValueSet( + items = items.map { it.toStoredItem() } + ) + + @VisibleForTesting + fun VaccinationValueSet.ValueSet.Item.toStoredItem(): + StoredVaccinationValueSet.StoredValueSet.StoredItem = + StoredVaccinationValueSet.StoredValueSet.StoredItem( + key = key, + displayText = displayText + ) - @Keep + @VisibleForTesting data class StoredVaccinationValueSet( @SerializedName("languageCode") override val languageCode: Locale, @SerializedName("vp") override val vp: StoredValueSet, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/DefaultVaccinationValueSet.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/DefaultVaccinationValueSet.kt index 1962cf330dbe26a192497bc4eeaeeb0e052b00d9..c7635321913b9267d867927ad2359afb41f7968d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/DefaultVaccinationValueSet.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/DefaultVaccinationValueSet.kt @@ -19,3 +19,12 @@ data class DefaultVaccinationValueSet( ) : VaccinationValueSet.ValueSet.Item } } + +internal val emptyVaccinationValueSet: VaccinationValueSet by lazy { + DefaultVaccinationValueSet( + languageCode = Locale.ENGLISH, + vp = DefaultVaccinationValueSet.DefaultValueSet(items = emptyList()), + mp = DefaultVaccinationValueSet.DefaultValueSet(items = emptyList()), + ma = DefaultVaccinationValueSet.DefaultValueSet(items = emptyList()) + ) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt index 08618fc008c487cd471b334a15fe6df1d039a348..23d5350fad4c4018dd2691784541febcb8ff42ad 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt @@ -24,6 +24,6 @@ fun VaccinationValueSet.getDisplayText(key: String): String? = fun VaccinationValueSet.ValueSet.getDisplayText(key: String): String? = items.find { key == it.key }?.displayText -fun VaccinationValueSet.isEmpty(): Boolean = vp.isEmpty() && mp.isEmpty() && ma.isEmpty() +fun VaccinationValueSet?.isEmpty(): Boolean = (this == null) || (vp.isEmpty() && mp.isEmpty() && ma.isEmpty()) fun VaccinationValueSet.ValueSet.isEmpty(): Boolean = items.isEmpty() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/ValueSetTestData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/ValueSetTestData.kt new file mode 100644 index 0000000000000000000000000000000000000000..67e7a01f66f51fee0b09e1f2ea82cdec55a1a1af --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/ValueSetTestData.kt @@ -0,0 +1,87 @@ +package de.rki.coronawarnapp.vaccination.core + +import de.rki.coronawarnapp.vaccination.core.repository.storage.ValueSetsStorage +import de.rki.coronawarnapp.vaccination.core.server.valueset.DefaultVaccinationValueSet +import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet +import io.kotest.matchers.shouldBe +import java.util.Locale + +object ValueSetTestData { + + val vpItemDe = "1119305005" to "Impfstoff-Name" + val mpItemDe = "EU/1/21/1529" to "Arzneimittel-Name" + val maItemDe = "ORG-100001699" to "Hersteller-Name" + + val vpItemEn = vpItemDe.copy(second = "Vaccine-Name") + val mpItemEn = mpItemDe.copy(second = "MedicalProduct-Name") + val maItemEn = maItemDe.copy(second = "Manufactorer-Name") + + val storedValueSetDe = ValueSetsStorage.StoredVaccinationValueSet( + languageCode = Locale.GERMAN, + vp = createStoredValueSet(vpItemDe), + mp = createStoredValueSet(mpItemDe), + ma = createStoredValueSet(maItemDe) + ) + + val storedValueSetEn = ValueSetsStorage.StoredVaccinationValueSet( + languageCode = Locale.ENGLISH, + vp = createStoredValueSet(vpItemEn), + mp = createStoredValueSet(mpItemEn), + ma = createStoredValueSet(maItemEn) + ) + + val emptyStoredValueSet = ValueSetsStorage.StoredVaccinationValueSet( + languageCode = Locale.ENGLISH, + vp = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = emptyList()), + mp = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = emptyList()), + ma = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = emptyList()) + ) + + val valueSetDe = DefaultVaccinationValueSet( + languageCode = Locale.GERMAN, + vp = createValueSet(vpItemDe), + mp = createValueSet(mpItemDe), + ma = createValueSet(maItemDe) + ) + + val valueSetEn = DefaultVaccinationValueSet( + languageCode = Locale.ENGLISH, + vp = createValueSet(vpItemEn), + mp = createValueSet(mpItemEn), + ma = createValueSet(maItemEn) + ) + + val emptyValueSetEn = emptyStoredValueSet + + fun createStoredValueSet(keyText: Pair<String, String>): ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet { + val item = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet.StoredItem( + key = keyText.first, + displayText = keyText.second + ) + return ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = listOf(item)) + } + + fun createValueSet(keyText: Pair<String, String>): DefaultVaccinationValueSet.DefaultValueSet { + val item = DefaultVaccinationValueSet.DefaultValueSet.DefaultItem( + key = keyText.first, + displayText = keyText.second + ) + return DefaultVaccinationValueSet.DefaultValueSet(items = listOf(item)) + } +} + +fun VaccinationValueSet.validateValues(v2: VaccinationValueSet) { + languageCode shouldBe v2.languageCode + vp.validateValues(v2.vp) + mp.validateValues(v2.mp) + ma.validateValues(v2.ma) +} + +fun VaccinationValueSet.ValueSet.validateValues(v2: VaccinationValueSet.ValueSet) { + items.forEachIndexed { index, item1 -> + val item2 = v2.items[index] + + item1.key shouldBe item2.key + item1.displayText shouldBe item2.displayText + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt index 374831b938ca7e4384a0ad152b7c144bdf7b1766..3ac9576d02d0740117dafee40677906c2aed214d 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt @@ -1,11 +1,11 @@ package de.rki.coronawarnapp.vaccination.core.repository -import de.rki.coronawarnapp.util.preferences.FlowPreference +import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.emptyValueSetEn +import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.valueSetDe +import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.valueSetEn import de.rki.coronawarnapp.vaccination.core.repository.storage.ValueSetsStorage -import de.rki.coronawarnapp.vaccination.core.server.valueset.DefaultVaccinationValueSet import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationServer -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet -import io.kotest.matchers.shouldBe +import de.rki.coronawarnapp.vaccination.core.validateValues import io.mockk.MockKAnnotations import io.mockk.Ordering import io.mockk.coEvery @@ -14,13 +14,13 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just import io.mockk.runs +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first -import kotlinx.coroutines.test.TestCoroutineScope -import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest -import testhelpers.preferences.mockFlowPreference +import testhelpers.TestDispatcherProvider +import testhelpers.coroutines.runBlockingTest2 import java.util.Locale class ValueSetsRepositoryTest : BaseTest() { @@ -28,124 +28,68 @@ class ValueSetsRepositoryTest : BaseTest() { @MockK lateinit var vaccinationServer: VaccinationServer @MockK lateinit var valueSetsStorage: ValueSetsStorage - private val testScope = TestCoroutineScope() - - private val emptyValueSetEN = createValueSet(languageCode = Locale.ENGLISH) - private val valueSetPref: FlowPreference<ValueSetsStorage.StoredVaccinationValueSet> = - mockFlowPreference(emptyValueSetEN.toStoredVaccinationValueSet()) - - private val valueSetEN = createValueSet( - languageCode = Locale.ENGLISH, - vpItems = listOf( - DefaultVaccinationValueSet.DefaultValueSet.DefaultItem( - key = "1119305005", - displayText = "Vaccine-Name" - ) - ), - mpItems = listOf( - DefaultVaccinationValueSet.DefaultValueSet.DefaultItem( - key = "EU/1/21/1529", - displayText = "MedicalProduct-Name" - ) - ), - maItems = listOf( - DefaultVaccinationValueSet.DefaultValueSet.DefaultItem( - key = "ORG-100001699", - displayText = "Manufactorer-Name" - ) - ) - ) - - private val valueSetDE = createValueSet( - languageCode = Locale.GERMAN, - vpItems = listOf( - DefaultVaccinationValueSet.DefaultValueSet.DefaultItem( - key = "1119305005", - displayText = "Impfstoff-Name" - ) - ), - mpItems = listOf( - DefaultVaccinationValueSet.DefaultValueSet.DefaultItem( - key = "EU/1/21/1529", - displayText = "Arzneimittel-Name" - ) - ), - maItems = listOf( - DefaultVaccinationValueSet.DefaultValueSet.DefaultItem( - key = "ORG-100001699", - displayText = "Hersteller-Name" - ) - ) - ) - - private fun createValueSet( - languageCode: Locale, - vpItems: List<DefaultVaccinationValueSet.DefaultValueSet.DefaultItem> = emptyList(), - mpItems: List<DefaultVaccinationValueSet.DefaultValueSet.DefaultItem> = emptyList(), - maItems: List<DefaultVaccinationValueSet.DefaultValueSet.DefaultItem> = emptyList() - ) = DefaultVaccinationValueSet( - languageCode = languageCode, - vp = DefaultVaccinationValueSet.DefaultValueSet(items = vpItems), - mp = DefaultVaccinationValueSet.DefaultValueSet(items = mpItems), - ma = DefaultVaccinationValueSet.DefaultValueSet(items = maItems) - ) + private val testDispatcherProvider = TestDispatcherProvider() - private fun createInstance() = ValueSetsRepository( + private fun createInstance(scope: CoroutineScope) = ValueSetsRepository( vaccinationServer = vaccinationServer, valueSetsStorage = valueSetsStorage, - scope = testScope + scope = scope, + dispatcherProvider = testDispatcherProvider ) @BeforeEach fun setUp() { MockKAnnotations.init(this) coEvery { vaccinationServer.getVaccinationValueSets(any()) } returns null - coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } returns valueSetEN - coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } returns valueSetDE + coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } returns valueSetEn + coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } returns valueSetDe every { vaccinationServer.clear() } just runs - every { valueSetsStorage.valueSet } returns valueSetPref - every { valueSetsStorage.clear() } just runs + every { valueSetsStorage.vaccinationValueSet = any() } just runs + every { valueSetsStorage.vaccinationValueSet } returns emptyValueSetEn } @Test - fun `successful update for de`() = runBlockingTest { - createInstance().run { + fun `successful update for de`() = runBlockingTest2(ignoreActive = true) { + createInstance(this).run { triggerUpdateValueSet(languageCode = Locale.GERMAN) latestValueSet.first() - }.also { it.validateValues(valueSetDE) } + }.also { it.validateValues(valueSetDe) } - coVerify(exactly = 1) { + coVerify { vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) + valueSetsStorage.vaccinationValueSet = valueSetDe } coVerify(exactly = 0) { vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) + valueSetsStorage.vaccinationValueSet = valueSetEn } } @Test - fun `fallback to en`() = runBlockingTest { - createInstance().run { + fun `fallback to en`() = runBlockingTest2(ignoreActive = true) { + createInstance(this).run { triggerUpdateValueSet(languageCode = Locale.FRENCH) latestValueSet.first() - }.also { it.validateValues(valueSetEN) } + }.also { it.validateValues(valueSetEn) } coVerify(ordering = Ordering.ORDERED) { vaccinationServer.getVaccinationValueSets(languageCode = Locale.FRENCH) vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) + valueSetsStorage.vaccinationValueSet = valueSetEn } } @Test - fun `server returns nothing`() = runBlockingTest { + fun `server returns nothing`() = runBlockingTest2(ignoreActive = true) { coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } returns null coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } returns null - createInstance().run { + createInstance(this).run { triggerUpdateValueSet(languageCode = Locale.GERMAN) latestValueSet.first() - }.also { it.validateValues(emptyValueSetEN) } + }.also { it.validateValues(emptyValueSetEn) } coVerify(ordering = Ordering.ORDERED) { vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) @@ -154,37 +98,16 @@ class ValueSetsRepositoryTest : BaseTest() { } @Test - fun `mapping is to stored version is correct`() { - valueSetDE.also { it.toStoredVaccinationValueSet().validateValues(it) } - valueSetEN.also { it.toStoredVaccinationValueSet().validateValues(it) } - emptyValueSetEN.also { it.toStoredVaccinationValueSet().validateValues(it) } - } - - @Test - fun `clear data of server and local storage`() { - createInstance().run { + fun `clear data of server and local storage`() = runBlockingTest2(ignoreActive = true) { + createInstance(this).run { clear() - coVerify(exactly = 1) { + latestValueSet.first().validateValues(emptyValueSetEn) + + coVerify { vaccinationServer.clear() - valueSetsStorage.clear() + valueSetsStorage.vaccinationValueSet = emptyValueSetEn } } } - - private fun VaccinationValueSet.validateValues(v2: VaccinationValueSet) { - languageCode shouldBe v2.languageCode - vp.validateValues(v2.vp) - mp.validateValues(v2.mp) - ma.validateValues(v2.ma) - } - - private fun VaccinationValueSet.ValueSet.validateValues(v2: VaccinationValueSet.ValueSet) { - items.forEachIndexed { index, item1 -> - val item2 = v2.items[index] - - item1.key shouldBe item2.key - item1.displayText shouldBe item2.displayText - } - } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt index e333cef8a7f2eb2ba48fd036fb38a1840c1c1f58..e79a54c44aca09f9ae6ca0c3fd02fe9e03c93d62 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt @@ -2,17 +2,21 @@ package de.rki.coronawarnapp.vaccination.core.repository.storage import android.content.Context import de.rki.coronawarnapp.util.serialization.SerializationModule +import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.emptyStoredValueSet +import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.emptyValueSetEn +import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.storedValueSetDe +import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.storedValueSetEn +import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.valueSetDe +import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.valueSetEn +import de.rki.coronawarnapp.vaccination.core.validateValues import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest import testhelpers.preferences.MockSharedPreferences -import java.util.Locale class ValueSetsStorageTest : BaseTest() { @@ -21,28 +25,6 @@ class ValueSetsStorageTest : BaseTest() { private val gson = SerializationModule().baseGson() - private val storedValueSetDE = ValueSetsStorage.StoredVaccinationValueSet( - languageCode = Locale.GERMAN, - vp = createValueSet(key = "1119305005", displayText = "Impfstoff-Name"), - mp = createValueSet(key = "EU/1/21/1529", displayText = "Arzneimittel-Name"), - ma = createValueSet(key = "ORG-100001699", displayText = "Hersteller-Name") - ) - - private val storedValueSetEN = ValueSetsStorage.StoredVaccinationValueSet( - languageCode = Locale.ENGLISH, - vp = createValueSet(key = "1119305005", displayText = "Vaccine-Name"), - mp = createValueSet(key = "EU/1/21/1529", displayText = "MedicalProduct-Name"), - ma = createValueSet(key = "ORG-100001699", displayText = "Manufactorer-Name") - ) - - private fun createValueSet(key: String, displayText: String): ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet { - val item = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet.StoredItem( - key = key, - displayText = displayText - ) - return ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = listOf(item)) - } - @BeforeEach fun setup() { MockKAnnotations.init(this) @@ -56,40 +38,42 @@ class ValueSetsStorageTest : BaseTest() { ) @Test - fun `Default value is empty value set`() { - createInstance().valueSet.value.run { - languageCode shouldBe Locale.ENGLISH - vp.items shouldBe emptyList() - mp.items shouldBe emptyList() - ma.items shouldBe emptyList() - } + fun `Default value is an empty value set`() { + createInstance().vaccinationValueSet.validateValues(emptyValueSetEn) } @Test - fun `Clear resets value set`() { + fun `Updates values`() { createInstance().run { - valueSet.update { storedValueSetDE } - clear() + vaccinationValueSet = storedValueSetDe + vaccinationValueSet shouldBe storedValueSetDe - valueSet.value.also { - it.languageCode shouldBe Locale.ENGLISH - it.vp.items shouldBe emptyList() - it.mp.items shouldBe emptyList() - it.ma.items shouldBe emptyList() - } + vaccinationValueSet = storedValueSetEn + vaccinationValueSet shouldBe storedValueSetEn } } @Test - fun `Updates values`() = runBlockingTest { - createInstance().valueSet.run { - update { storedValueSetDE } - value shouldBe storedValueSetDE - flow.first() shouldBe storedValueSetDE + fun `Check mapping is correct`() { + createInstance().run { + storedValueSetDe.also { it.toStoredVaccinationValueSet() shouldBe it } + + storedValueSetEn.also { it.toStoredVaccinationValueSet() shouldBe it } + + valueSetDe.also { + it.toStoredVaccinationValueSet() shouldBe storedValueSetDe + it.toStoredVaccinationValueSet().validateValues(it) + } - update { storedValueSetEN } - value shouldBe storedValueSetEN - flow.first() shouldBe storedValueSetEN + valueSetEn.also { + it.toStoredVaccinationValueSet() shouldBe storedValueSetEn + it.toStoredVaccinationValueSet().validateValues(it) + } + + emptyValueSetEn.also { + it.toStoredVaccinationValueSet() shouldBe emptyStoredValueSet + it.toStoredVaccinationValueSet().validateValues(it) + } } } }