From 971580a89f5ca720b2f8cf720e961428dc4a6543 Mon Sep 17 00:00:00 2001
From: Matthias Urhahn <matthias.urhahn@sap.com>
Date: Mon, 12 Oct 2020 13:29:59 +0200
Subject: [PATCH] Fix behavior of last 3 hours test mode switch
 (EXPOSUREAPP-3179) (#1375)

* Improve test readability, create helper methods for locations,days,hours.

* Prepare better test data that allow for future 3+hours tests.

* Fix last 3 hours mode downloading key package files from yesterday instead of today,
due to being based of the latest server index day files. We need to get them for the latest day index file,
plus 1 day.

* Fix hour formatting, server uses hour format without leading zeros

Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com>
---
 .../download/KeyFileDownloader.kt             |  15 +-
 .../server/DiagnosisKeyServer.kt              |   2 +-
 .../download/KeyFileDownloaderTest.kt         | 426 ++++++++----------
 .../server/DiagnosisKeyServerTest.kt          |  51 ++-
 4 files changed, 240 insertions(+), 254 deletions(-)

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyFileDownloader.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyFileDownloader.kt
index 0d18d7a27..9b8830415 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyFileDownloader.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyFileDownloader.kt
@@ -92,6 +92,7 @@ class KeyFileDownloader @Inject constructor(
                         Timber.tag(TAG).w("Missing keyfile for : %s", keyInfo)
                         null
                     } else {
+                        Timber.tag(TAG).v("Providing available key: %s", keyInfo)
                         path
                     }
                 }
@@ -171,11 +172,21 @@ class KeyFileDownloader @Inject constructor(
         availableCountries: List<LocationCode>,
         itemLimit: Int
     ): List<CountryHours> {
-
         val availableHours = availableCountries.flatMap { location ->
             var remainingItems = itemLimit
             // Descending because we go backwards newest -> oldest
-            keyServer.getDayIndex(location).sortedDescending().mapNotNull { day ->
+            val indexWithToday = keyServer.getDayIndex(location).let {
+                val lastDayInIndex = it.maxOrNull()
+                Timber.tag(TAG).v("Last day in index: %s", lastDayInIndex)
+                if (lastDayInIndex != null) {
+                    it.plus(lastDayInIndex.plusDays(1))
+                } else {
+                    it
+                }
+            }
+            Timber.tag(TAG).v("Day index with (fake) today entry: %s", indexWithToday)
+
+            indexWithToday.sortedDescending().mapNotNull { day ->
                 // Limit reached, return null (filtered out) instead of new CountryHours object
                 if (remainingItems <= 0) return@mapNotNull null
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/server/DiagnosisKeyServer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/server/DiagnosisKeyServer.kt
index 7114ff634..decf192f7 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/server/DiagnosisKeyServer.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/server/DiagnosisKeyServer.kt
@@ -110,6 +110,6 @@ class DiagnosisKeyServer @Inject constructor(
     companion object {
         private val TAG = DiagnosisKeyServer::class.java.simpleName
         private val DAY_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd")
-        private val HOUR_FORMATTER = DateTimeFormat.forPattern("HH")
+        private val HOUR_FORMATTER = DateTimeFormat.forPattern("H")
     }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyFileDownloaderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyFileDownloaderTest.kt
index a39102dbe..22f91097f 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyFileDownloaderTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyFileDownloaderTest.kt
@@ -5,6 +5,7 @@ import de.rki.coronawarnapp.diagnosiskeys.server.DiagnosisKeyServer
 import de.rki.coronawarnapp.diagnosiskeys.server.DownloadInfo
 import de.rki.coronawarnapp.diagnosiskeys.server.LocationCode
 import de.rki.coronawarnapp.diagnosiskeys.storage.CachedKeyInfo
+import de.rki.coronawarnapp.diagnosiskeys.storage.CachedKeyInfo.Type
 import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository
 import de.rki.coronawarnapp.diagnosiskeys.storage.legacy.LegacyKeyCacheMigration
 import de.rki.coronawarnapp.storage.AppSettings
@@ -52,7 +53,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
     private lateinit var legacyMigration: LegacyKeyCacheMigration
 
     @MockK
-    private lateinit var diagnosisKeyServer: DiagnosisKeyServer
+    private lateinit var server: DiagnosisKeyServer
 
     @MockK
     private lateinit var deviceStorage: DeviceStorage
@@ -63,6 +64,10 @@ class KeyFileDownloaderTest : BaseIOTest() {
     private val testDir = File(IO_TEST_BASEDIR, this::class.simpleName!!)
     private val keyRepoData = mutableMapOf<String, CachedKeyInfo>()
 
+    private val String.loc get() = LocationCode(this)
+    private val String.day get() = LocalDate.parse(this)
+    private val String.hour get() = LocalTime.parse(this)
+
     @BeforeEach
     fun setup() {
         MockKAnnotations.init(this)
@@ -71,44 +76,38 @@ class KeyFileDownloaderTest : BaseIOTest() {
 
         every { settings.isLast3HourModeEnabled } returns false
 
-        coEvery { diagnosisKeyServer.getCountryIndex() } returns listOf(
-            LocationCode("DE"),
-            LocationCode("NL")
-        )
+        coEvery { server.getCountryIndex() } returns listOf("DE".loc, "NL".loc)
         coEvery { deviceStorage.requireSpacePrivateStorage(any()) } returns mockk<DeviceStorage.CheckResult>().apply {
             every { isSpaceAvailable } returns true
         }
 
-        coEvery { diagnosisKeyServer.getCountryIndex() } returns listOf(
-            LocationCode("DE"), LocationCode("NL")
-        )
-        coEvery { diagnosisKeyServer.getDayIndex(LocationCode("DE")) } returns listOf(
-            LocalDate.parse("2020-09-01"), LocalDate.parse("2020-09-02")
+        coEvery { server.getDayIndex("DE".loc) } returns listOf(
+            "2020-09-01".day, "2020-09-02".day
         )
         coEvery {
-            diagnosisKeyServer.getHourIndex(LocationCode("DE"), LocalDate.parse("2020-09-01"))
-        } returns listOf(
-            LocalTime.parse("18"), LocalTime.parse("19"), LocalTime.parse("20")
-        )
+            server.getHourIndex("DE".loc, "2020-09-01".day)
+        } returns (0..23).map { "$it".hour }
         coEvery {
-            diagnosisKeyServer.getHourIndex(LocationCode("DE"), LocalDate.parse("2020-09-02"))
-        } returns listOf(
-            LocalTime.parse("20"), LocalTime.parse("21")
-        )
-        coEvery { diagnosisKeyServer.getDayIndex(LocationCode("NL")) } returns listOf(
-            LocalDate.parse("2020-09-02"), LocalDate.parse("2020-09-03")
-        )
+            server.getHourIndex("DE".loc, "2020-09-02".day)
+        } returns (0..23).map { "$it".hour }
         coEvery {
-            diagnosisKeyServer.getHourIndex(LocationCode("NL"), LocalDate.parse("2020-09-02"))
-        } returns listOf(
-            LocalTime.parse("20"), LocalTime.parse("21"), LocalTime.parse("22")
+            server.getHourIndex("DE".loc, "2020-09-03".day)
+        } returns (0..12).map { "$it".hour }
+
+        coEvery { server.getDayIndex("NL".loc) } returns listOf(
+            "2020-09-01".day, "2020-09-02".day
         )
         coEvery {
-            diagnosisKeyServer.getHourIndex(LocationCode("NL"), LocalDate.parse("2020-09-03"))
-        } returns listOf(
-            LocalTime.parse("22"), LocalTime.parse("23")
-        )
-        coEvery { diagnosisKeyServer.downloadKeyFile(any(), any(), any(), any(), any()) } answers {
+            server.getHourIndex("NL".loc, "2020-09-01".day)
+        } returns (0..23).map { "$it".hour }
+        coEvery {
+            server.getHourIndex("NL".loc, "2020-09-02".day)
+        } returns (0..23).map { "$it".hour }
+        coEvery {
+            server.getHourIndex("NL".loc, "2020-09-03".day)
+        } returns (0..12).map { "$it".hour }
+
+        coEvery { server.downloadKeyFile(any(), any(), any(), any(), any()) } answers {
             mockDownloadServerDownload(
                 locationCode = arg(0),
                 day = arg(1),
@@ -125,7 +124,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
             mockKeyCacheUpdateComplete(arg(0), arg(1))
         }
         coEvery { keyCache.getEntriesForType(any()) } answers {
-            val type = arg<CachedKeyInfo.Type>(0)
+            val type = arg<Type>(0)
             keyRepoData.values.filter { it.type == type }.map { it to File(testDir, it.id) }
         }
         coEvery { keyCache.getAllCachedKeys() } answers {
@@ -149,7 +148,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
     }
 
     private fun mockKeyCacheCreateEntry(
-        type: CachedKeyInfo.Type,
+        type: Type,
         location: LocationCode,
         dayIdentifier: LocalDate,
         hourIdentifier: LocalTime?
@@ -172,7 +171,8 @@ class KeyFileDownloaderTest : BaseIOTest() {
         checksum: String
     ) {
         keyRepoData[keyInfo.id] = keyInfo.copy(
-            isDownloadComplete = checksum != null, checksumMD5 = checksum
+            isDownloadComplete = true,
+            checksumMD5 = checksum
         )
     }
 
@@ -193,7 +193,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
     }
 
     private fun mockAddData(
-        type: CachedKeyInfo.Type,
+        type: Type,
         location: LocationCode,
         day: LocalDate,
         hour: LocalTime?,
@@ -215,7 +215,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
     private fun createDownloader(): KeyFileDownloader {
         val downloader = KeyFileDownloader(
             deviceStorage = deviceStorage,
-            keyServer = diagnosisKeyServer,
+            keyServer = server,
             keyCache = keyCache,
             legacyKeyCache = legacyMigration,
             settings = settings
@@ -226,7 +226,6 @@ class KeyFileDownloaderTest : BaseIOTest() {
 
     @Test
     fun `wanted country list is empty, day mode`() = flakyTest {
-
         val downloader = createDownloader()
         runBlocking {
             downloader.asyncFetchKeyFiles(emptyList()) shouldBe emptyList()
@@ -253,7 +252,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
 
         runBlocking {
             shouldThrow<InsufficientStorageException> {
-                downloader.asyncFetchKeyFiles(listOf(LocationCode("DE")))
+                downloader.asyncFetchKeyFiles(listOf("DE".loc))
             }
         }
     }
@@ -270,20 +269,20 @@ class KeyFileDownloaderTest : BaseIOTest() {
 
         runBlocking {
             shouldThrow<InsufficientStorageException> {
-                downloader.asyncFetchKeyFiles(listOf(LocationCode("DE")))
+                downloader.asyncFetchKeyFiles(listOf("DE".loc))
             }
         }
     }
 
     @Test
     fun `error during country index fetch`() = flakyTest {
-        coEvery { diagnosisKeyServer.getCountryIndex() } throws IOException()
+        coEvery { server.getCountryIndex() } throws IOException()
 
         val downloader = createDownloader()
 
         runBlocking {
             shouldThrow<IOException> {
-                downloader.asyncFetchKeyFiles(listOf(LocationCode("DE")))
+                downloader.asyncFetchKeyFiles(listOf("DE".loc))
             }
         }
     }
@@ -293,34 +292,32 @@ class KeyFileDownloaderTest : BaseIOTest() {
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
-            ).size shouldBe 4
+            downloader.asyncFetchKeyFiles(listOf("DE".loc, "NL".loc)).size shouldBe 4
         }
 
         coVerify {
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-01"),
+                type = Type.COUNTRY_DAY,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-01".day,
                 hourIdentifier = null
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
+                type = Type.COUNTRY_DAY,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-02".day,
                 hourIdentifier = null
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
+                type = Type.COUNTRY_DAY,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-01".day,
                 hourIdentifier = null
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-03"),
+                type = Type.COUNTRY_DAY,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-02".day,
                 hourIdentifier = null
             )
         }
@@ -332,17 +329,16 @@ class KeyFileDownloaderTest : BaseIOTest() {
     @Test
     fun `day fetch with existing data`() = flakyTest {
         mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_DAY,
-            location = LocationCode("DE"),
-            day = LocalDate.parse("2020-09-01"),
+            type = Type.COUNTRY_DAY,
+            location = "DE".loc,
+            day = "2020-09-01".day,
             hour = null,
             isCompleted = true
         )
-
         mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_DAY,
-            location = LocationCode("NL"),
-            day = LocalDate.parse("2020-09-02"),
+            type = Type.COUNTRY_DAY,
+            location = "NL".loc,
+            day = "2020-09-02".day,
             hour = null,
             isCompleted = true
         )
@@ -350,22 +346,20 @@ class KeyFileDownloaderTest : BaseIOTest() {
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
-            ).size shouldBe 4
+            downloader.asyncFetchKeyFiles(listOf("DE".loc, "NL".loc)).size shouldBe 4
         }
 
         coVerify {
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
+                type = Type.COUNTRY_DAY,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-02".day,
                 hourIdentifier = null
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-03"),
+                type = Type.COUNTRY_DAY,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-01".day,
                 hourIdentifier = null
             )
         }
@@ -378,21 +372,19 @@ class KeyFileDownloaderTest : BaseIOTest() {
 
     @Test
     fun `day fetch deletes stale data`() = flakyTest {
-        coEvery { diagnosisKeyServer.getDayIndex(LocationCode("DE")) } returns listOf(
-            LocalDate.parse("2020-09-02")
-        )
+        coEvery { server.getDayIndex("DE".loc) } returns listOf("2020-09-02".day)
         val (staleKeyInfo, _) = mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_DAY,
-            location = LocationCode("DE"),
-            day = LocalDate.parse("2020-09-01"),
+            type = Type.COUNTRY_DAY,
+            location = "DE".loc,
+            day = "2020-09-01".day,
             hour = null,
             isCompleted = true
         )
 
         mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_DAY,
-            location = LocationCode("NL"),
-            day = LocalDate.parse("2020-09-02"),
+            type = Type.COUNTRY_DAY,
+            location = "NL".loc,
+            day = "2020-09-02".day,
             hour = null,
             isCompleted = true
         )
@@ -400,22 +392,20 @@ class KeyFileDownloaderTest : BaseIOTest() {
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
-            ).size shouldBe 3
+            downloader.asyncFetchKeyFiles(listOf("DE".loc, "NL".loc)).size shouldBe 3
         }
 
         coVerify {
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
+                type = Type.COUNTRY_DAY,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-02".day,
                 hourIdentifier = null
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-03"),
+                type = Type.COUNTRY_DAY,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-01".day,
                 hourIdentifier = null
             )
         }
@@ -427,7 +417,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
     @Test
     fun `day fetch skips single download failures`() = flakyTest {
         var dlCounter = 0
-        coEvery { diagnosisKeyServer.downloadKeyFile(any(), any(), any(), any(), any()) } answers {
+        coEvery { server.downloadKeyFile(any(), any(), any(), any(), any()) } answers {
             dlCounter++
             if (dlCounter == 2) throw IOException("Timeout")
             mockDownloadServerDownload(
@@ -442,9 +432,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
-            ).size shouldBe 3
+            downloader.asyncFetchKeyFiles(listOf("DE".loc, "NL".loc)).size shouldBe 3
         }
 
         // We delete the entry for the failed download
@@ -458,48 +446,46 @@ class KeyFileDownloaderTest : BaseIOTest() {
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
-            ).size shouldBe 6
+            downloader.asyncFetchKeyFiles(listOf("DE".loc, "NL".loc)).size shouldBe 6
         }
 
         coVerify {
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
-                hourIdentifier = LocalTime.parse("21")
+                type = Type.COUNTRY_HOUR,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "12".hour
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
-                hourIdentifier = LocalTime.parse("20")
+                type = Type.COUNTRY_HOUR,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "11".hour
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-01"),
-                hourIdentifier = LocalTime.parse("20")
+                type = Type.COUNTRY_HOUR,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "10".hour
             )
 
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-03"),
-                hourIdentifier = LocalTime.parse("23")
+                type = Type.COUNTRY_HOUR,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "12".hour
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-03"),
-                hourIdentifier = LocalTime.parse("22")
+                type = Type.COUNTRY_HOUR,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "11".hour
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
-                hourIdentifier = LocalTime.parse("22")
+                type = Type.COUNTRY_HOUR,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "10".hour
             )
         }
         coVerify(exactly = 6) { keyCache.markKeyComplete(any(), any()) }
@@ -515,17 +501,17 @@ class KeyFileDownloaderTest : BaseIOTest() {
         every { settings.isLast3HourModeEnabled } returns true
 
         mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_HOUR,
-            location = LocationCode("DE"),
-            day = LocalDate.parse("2020-09-01"),
-            hour = LocalTime.parse("20"),
+            type = Type.COUNTRY_HOUR,
+            location = "DE".loc,
+            day = "2020-09-03".day,
+            hour = "11".hour,
             isCompleted = true
         )
         mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_HOUR,
-            location = LocationCode("NL"),
-            day = LocalDate.parse("2020-09-02"),
-            hour = LocalTime.parse("22"),
+            type = Type.COUNTRY_HOUR,
+            location = "NL".loc,
+            day = "2020-09-03".day,
+            hour = "11".hour,
             isCompleted = true
         )
 
@@ -533,45 +519,39 @@ class KeyFileDownloaderTest : BaseIOTest() {
 
         runBlocking {
             downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
+                listOf("DE".loc, "NL".loc)
             ).size shouldBe 6
         }
 
         coVerify {
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
-                hourIdentifier = LocalTime.parse("21")
+                type = Type.COUNTRY_HOUR,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "12".hour
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
-                hourIdentifier = LocalTime.parse("20")
+                type = Type.COUNTRY_HOUR,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "10".hour
             )
 
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-03"),
-                hourIdentifier = LocalTime.parse("23")
+                type = Type.COUNTRY_HOUR,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "12".hour
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-03"),
-                hourIdentifier = LocalTime.parse("22")
+                type = Type.COUNTRY_HOUR,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "10".hour
             )
         }
         coVerify(exactly = 4) {
-            diagnosisKeyServer.downloadKeyFile(
-                any(),
-                any(),
-                any(),
-                any(),
-                any()
-            )
+            server.downloadKeyFile(any(), any(), any(), any(), any())
         }
         coVerify { deviceStorage.requireSpacePrivateStorage(90112L) }
     }
@@ -581,79 +561,71 @@ class KeyFileDownloaderTest : BaseIOTest() {
         every { settings.isLast3HourModeEnabled } returns true
 
         val (staleKey1, _) = mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_HOUR,
-            location = LocationCode("NL"),
-            day = LocalDate.parse("2020-09-02"),
-            hour = LocalTime.parse("12"), // Stale hour
+            type = Type.COUNTRY_HOUR,
+            location = "DE".loc,
+            day = "2020-09-02".day,
+            hour = "01".hour, // Stale hour
             isCompleted = true
         )
 
         val (staleKey2, _) = mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_HOUR,
-            location = LocationCode("NL"),
-            day = LocalDate.parse("2020-09-01"), // Stale day
-            hour = LocalTime.parse("22"),
+            type = Type.COUNTRY_HOUR,
+            location = "NL".loc,
+            day = "2020-09-02".day, // Stale day
+            hour = "01".hour,
             isCompleted = true
         )
 
         mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_HOUR,
-            location = LocationCode("DE"),
-            day = LocalDate.parse("2020-09-01"),
-            hour = LocalTime.parse("20"),
+            type = Type.COUNTRY_HOUR,
+            location = "DE".loc,
+            day = "2020-09-03".day,
+            hour = "10".hour,
             isCompleted = true
         )
         mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_HOUR,
-            location = LocationCode("NL"),
-            day = LocalDate.parse("2020-09-02"),
-            hour = LocalTime.parse("22"),
+            type = Type.COUNTRY_HOUR,
+            location = "NL".loc,
+            day = "2020-09-03".day,
+            hour = "10".hour,
             isCompleted = true
         )
 
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
-            ).size shouldBe 6
+            downloader.asyncFetchKeyFiles(listOf("DE".loc, "NL".loc)).size shouldBe 6
         }
 
         coVerify {
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
-                hourIdentifier = LocalTime.parse("21")
+                type = Type.COUNTRY_HOUR,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "12".hour
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-02"),
-                hourIdentifier = LocalTime.parse("20")
+                type = Type.COUNTRY_HOUR,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "11".hour
             )
 
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-03"),
-                hourIdentifier = LocalTime.parse("23")
+                type = Type.COUNTRY_HOUR,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "12".hour
             )
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_HOUR,
-                location = LocationCode("NL"),
-                dayIdentifier = LocalDate.parse("2020-09-03"),
-                hourIdentifier = LocalTime.parse("22")
+                type = Type.COUNTRY_HOUR,
+                location = "NL".loc,
+                dayIdentifier = "2020-09-03".day,
+                hourIdentifier = "11".hour
             )
         }
         coVerify(exactly = 4) {
-            diagnosisKeyServer.downloadKeyFile(
-                any(),
-                any(),
-                any(),
-                any(),
-                any()
-            )
+            server.downloadKeyFile(any(), any(), any(), any(), any())
         }
         coVerify(exactly = 1) { keyCache.delete(listOf(staleKey1, staleKey2)) }
     }
@@ -663,7 +635,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
         every { settings.isLast3HourModeEnabled } returns true
 
         var dlCounter = 0
-        coEvery { diagnosisKeyServer.downloadKeyFile(any(), any(), any(), any(), any()) } answers {
+        coEvery { server.downloadKeyFile(any(), any(), any(), any(), any()) } answers {
             dlCounter++
             if (dlCounter == 2) throw IOException("Timeout")
             mockDownloadServerDownload(
@@ -678,9 +650,7 @@ class KeyFileDownloaderTest : BaseIOTest() {
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
-            ).size shouldBe 5
+            downloader.asyncFetchKeyFiles(listOf("DE".loc, "NL".loc)).size shouldBe 5
         }
 
         // We delete the entry for the failed download
@@ -690,9 +660,9 @@ class KeyFileDownloaderTest : BaseIOTest() {
     @Test
     fun `not completed cache entries are overwritten`() = flakyTest {
         mockAddData(
-            type = CachedKeyInfo.Type.COUNTRY_DAY,
-            location = LocationCode("DE"),
-            day = LocalDate.parse("2020-09-01"),
+            type = Type.COUNTRY_DAY,
+            location = "DE".loc,
+            day = "2020-09-01".day,
             hour = null,
             isCompleted = false
         )
@@ -700,16 +670,14 @@ class KeyFileDownloaderTest : BaseIOTest() {
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
-            ).size shouldBe 4
+            downloader.asyncFetchKeyFiles(listOf("DE".loc, "NL".loc)).size shouldBe 4
         }
 
         coVerify {
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-01"),
+                type = Type.COUNTRY_DAY,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-01".day,
                 hourIdentifier = null
             )
         }
@@ -727,42 +695,30 @@ class KeyFileDownloaderTest : BaseIOTest() {
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"), LocationCode("NL"))
-            ).size shouldBe 3
+            downloader.asyncFetchKeyFiles(listOf("DE".loc, "NL".loc)).size shouldBe 3
         }
 
         coVerify(exactly = 4) {
-            diagnosisKeyServer.downloadKeyFile(
-                any(),
-                any(),
-                any(),
-                any(),
-                any()
-            )
+            server.downloadKeyFile(any(), any(), any(), any(), any())
         }
     }
 
     @Test
     fun `store server md5`() = flakyTest {
-        coEvery { diagnosisKeyServer.getCountryIndex() } returns listOf(LocationCode("DE"))
-        coEvery { diagnosisKeyServer.getDayIndex(LocationCode("DE")) } returns listOf(
-            LocalDate.parse("2020-09-01")
-        )
+        coEvery { server.getCountryIndex() } returns listOf("DE".loc)
+        coEvery { server.getDayIndex("DE".loc) } returns listOf("2020-09-01".day)
 
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"))
-            ).size shouldBe 1
+            downloader.asyncFetchKeyFiles(listOf("DE".loc)).size shouldBe 1
         }
 
         coVerify {
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-01"),
+                type = Type.COUNTRY_DAY,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-01".day,
                 hourIdentifier = null
             )
         }
@@ -775,11 +731,9 @@ class KeyFileDownloaderTest : BaseIOTest() {
 
     @Test
     fun `use local MD5 as fallback if there is none available from the server`() = flakyTest {
-        coEvery { diagnosisKeyServer.getCountryIndex() } returns listOf(LocationCode("DE"))
-        coEvery { diagnosisKeyServer.getDayIndex(LocationCode("DE")) } returns listOf(
-            LocalDate.parse("2020-09-01")
-        )
-        coEvery { diagnosisKeyServer.downloadKeyFile(any(), any(), any(), any(), any()) } answers {
+        coEvery { server.getCountryIndex() } returns listOf("DE".loc)
+        coEvery { server.getDayIndex("DE".loc) } returns listOf("2020-09-01".day)
+        coEvery { server.downloadKeyFile(any(), any(), any(), any(), any()) } answers {
             mockDownloadServerDownload(
                 locationCode = arg(0),
                 day = arg(1),
@@ -793,16 +747,14 @@ class KeyFileDownloaderTest : BaseIOTest() {
         val downloader = createDownloader()
 
         runBlocking {
-            downloader.asyncFetchKeyFiles(
-                listOf(LocationCode("DE"))
-            ).size shouldBe 1
+            downloader.asyncFetchKeyFiles(listOf("DE".loc)).size shouldBe 1
         }
 
         coVerify {
             keyCache.createCacheEntry(
-                type = CachedKeyInfo.Type.COUNTRY_DAY,
-                location = LocationCode("DE"),
-                dayIdentifier = LocalDate.parse("2020-09-01"),
+                type = Type.COUNTRY_DAY,
+                location = "DE".loc,
+                dayIdentifier = "2020-09-01".day,
                 hourIdentifier = null
             )
         }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/server/DiagnosisKeyServerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/server/DiagnosisKeyServerTest.kt
index bce772243..08aed5e5a 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/server/DiagnosisKeyServerTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/server/DiagnosisKeyServerTest.kt
@@ -81,7 +81,7 @@ class DiagnosisKeyServerTest : BaseIOTest() {
     fun `download hour index for country and day`() {
         val downloadServer = createDownloadServer()
         coEvery { api.getHourIndex("DE", "2000-01-01") } returns listOf(
-            "20", "21"
+            "1", "2", "20", "21"
         )
 
         runBlocking {
@@ -89,7 +89,7 @@ class DiagnosisKeyServerTest : BaseIOTest() {
                 LocationCode("DE"),
                 LocalDate.parse("2000-01-01")
             ) shouldBe listOf(
-                "20:00", "21:00"
+                "01:00", "02:00", "20:00", "21:00"
             ).map { LocalTime.parse(it) }
         }
 
@@ -122,28 +122,51 @@ class DiagnosisKeyServerTest : BaseIOTest() {
     }
 
     @Test
-    fun `download key files for hour`() {
+    fun `download key files for hour and check hour format`() {
         val downloadServer = createDownloadServer()
-        coEvery {
-            api.downloadKeyFileForHour(
-                "DE",
-                "2000-01-01",
-                "01"
-            )
-        } returns Response.success("testdata-hour".toResponseBody())
-
-        val targetFile = File(testDir, "hour-keys")
 
         runBlocking {
+            coEvery {
+                api.downloadKeyFileForHour(
+                    "DE",
+                    "2000-01-01",
+                    "1" // no leading ZEROS!
+                )
+            } returns Response.success("testdata-hour".toResponseBody())
+
+            val targetFile = File(testDir, "hour-keys")
+
             downloadServer.downloadKeyFile(
                 locationCode = LocationCode("DE"),
                 day = LocalDate.parse("2000-01-01"),
                 hour = LocalTime.parse("01:00"),
                 saveTo = targetFile
             )
+
+            targetFile.exists() shouldBe true
+            targetFile.readText() shouldBe "testdata-hour"
         }
 
-        targetFile.exists() shouldBe true
-        targetFile.readText() shouldBe "testdata-hour"
+        runBlocking {
+            coEvery {
+                api.downloadKeyFileForHour(
+                    "DE",
+                    "2000-01-01",
+                    "13"
+                )
+            } returns Response.success("testdata-hour".toResponseBody())
+
+            val targetFile = File(testDir, "hour-keys")
+
+            downloadServer.downloadKeyFile(
+                locationCode = LocationCode("DE"),
+                day = LocalDate.parse("2000-01-01"),
+                hour = LocalTime.parse("13:00"),
+                saveTo = targetFile
+            )
+
+            targetFile.exists() shouldBe true
+            targetFile.readText() shouldBe "testdata-hour"
+        }
     }
 }
-- 
GitLab