diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServer.kt index 0d108cd2aac3b3b439cf8a679a82e029eb8d788e..5e064692b159c9123f431138dbbc4520a33228f3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServer.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServer.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.vaccination.core.server.valueset +import androidx.annotation.VisibleForTesting import dagger.Lazy import dagger.Reusable import de.rki.coronawarnapp.server.protocols.internal.dgc.ValueSetsOuterClass @@ -15,6 +16,7 @@ import okhttp3.ResponseBody import retrofit2.HttpException import retrofit2.Response import timber.log.Timber +import java.io.InputStream import java.util.Locale import javax.inject.Inject @@ -50,8 +52,12 @@ class VaccinationServer @Inject constructor( apiV1.get().getValueSets(languageCode = languageCode) } - private fun ResponseBody.parseBody(): ValueSetsOuterClass.ValueSets { - val fileMap = this.byteStream().unzip().readIntoMap() + private fun ResponseBody.parseBody(): ValueSetsOuterClass.ValueSets = + parseBody(byteStream()) + + @VisibleForTesting + internal fun parseBody(inputStream: InputStream): ValueSetsOuterClass.ValueSets { + val fileMap = inputStream.unzip().readIntoMap() val exportBinary = fileMap[EXPORT_BINARY_FILE_NAME] val exportSignature = fileMap[EXPORT_SIGNATURE_FILE_NAME] diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..0c27822e117164f0baa327bb8b978c7e72b688b4 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServerTest.kt @@ -0,0 +1,109 @@ +package de.rki.coronawarnapp.vaccination.core.server.valueset + +import dagger.Lazy +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.security.SignatureValidation +import de.rki.coronawarnapp.vaccination.core.server.valueset.internal.ValueSetInvalidSignatureException +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.runs +import io.mockk.verify +import okhttp3.Cache +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import java.io.File + +class VaccinationServerTest : BaseTest() { + + /** + * contains both binary and signature + */ + private val exportZip = File("src/test/resources/vaccination/valueset_default.zip") + + /** + * binary is missing + */ + private val invalidExportZip = File("src/test/resources/vaccination/valueset_invalid.zip") + + @MockK lateinit var cache: Cache + @MockK lateinit var apiV1: Lazy<VaccinationValueSetApiV1> + @MockK lateinit var dispatcherProvider: DispatcherProvider + @MockK lateinit var signatureValidation: SignatureValidation + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + exportZip.exists() shouldBe true + invalidExportZip.exists() shouldBe true + } + + private fun createInstance() = VaccinationServer( + cache, + apiV1, + dispatcherProvider, + signatureValidation + ) + + @Test + fun `valid export data`() { + every { signatureValidation.hasValidSignature(any(), any()) } returns true + exportZip.inputStream().use { + createInstance().parseBody(it).apply { + this shouldNotBe null + ma.apply { + itemsCount shouldBe 1 + getItems(0).apply { + key shouldBe "maKey" + displayText shouldBe "maDisplayText" + } + } + mp.apply { + itemsCount shouldBe 1 + getItems(0).apply { + key shouldBe "mpKey" + displayText shouldBe "mpDisplayText" + } + } + vp.apply { + itemsCount shouldBe 1 + getItems(0).apply { + key shouldBe "vpKey" + displayText shouldBe "vpDisplayText" + } + } + } + } + } + + @Test + fun `invalid signature`() { + every { signatureValidation.hasValidSignature(any(), any()) } returns false + exportZip.inputStream().use { + shouldThrow<ValueSetInvalidSignatureException> { + createInstance().parseBody(it) + } + } + } + + @Test + fun `a file is missing`() { + invalidExportZip.inputStream().use { + shouldThrow<ValueSetInvalidSignatureException> { + createInstance().parseBody(it) + } + } + } + + @Test + fun `call to clear invalidates cache`() { + every { cache.evictAll() } just runs + createInstance().clear() + verify { cache.evictAll() } + } +} diff --git a/Corona-Warn-App/src/test/resources/vaccination/valueset_default.zip b/Corona-Warn-App/src/test/resources/vaccination/valueset_default.zip new file mode 100755 index 0000000000000000000000000000000000000000..07bab18cc3ed089272171b18702eda8d62cab2d9 Binary files /dev/null and b/Corona-Warn-App/src/test/resources/vaccination/valueset_default.zip differ diff --git a/Corona-Warn-App/src/test/resources/vaccination/valueset_invalid.zip b/Corona-Warn-App/src/test/resources/vaccination/valueset_invalid.zip new file mode 100755 index 0000000000000000000000000000000000000000..7a574548e639ff20f9d465304ff3c03c3ed8437d Binary files /dev/null and b/Corona-Warn-App/src/test/resources/vaccination/valueset_invalid.zip differ