diff --git a/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepo.kt b/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepo.kt index 05a6891c..99dd5b10 100644 --- a/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepo.kt +++ b/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepo.kt @@ -107,12 +107,17 @@ class DatabaseRepo( // launch all network requests concurrently val tleJobs = tleUrls.values.map { url -> async { url to remoteSource.getNetworkStream(url) } } val radioJobs = radioUrls.values.map { url -> async { url to remoteSource.getNetworkStream(url) } } - // Count successful sources: zero successes = update failed (timestamp untouched, exception surfaced in the UI) val tleResults = tleJobs.awaitAll() val radioResults = radioJobs.awaitAll() - val successCount = tleResults.count { it.second != null } + radioResults.count { it.second != null } - if (successCount == 0) { - throw java.io.IOException("All data sources failed to download") + // Orbital elements are counted on their own. A combined count let a successful transceivers + // fetch stand in for a failed orbital one: with a custom TLE URL there are two requests + // rather than 28, so if that URL was down and SatNOGS answered, the total was 1, no + // exception was raised, and setUpdateSuccessful stamped a fresh timestamp for an update + // that refreshed no orbital data at all - which also suppressed the 48-hour auto-update + // retry that keys off that timestamp. The failure existed before but 26 other sources hid + // it; replacing them made it easy to hit. + if (tleResults.none { it.second != null }) { + throw java.io.IOException("No orbital data source could be downloaded") } // parse fetched data concurrently and associate with types val importedEntries = tleResults.flatMap { (url, stream) -> diff --git a/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepoTest.kt b/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepoTest.kt index 46467c9b..03c193be 100644 --- a/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepoTest.kt +++ b/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepoTest.kt @@ -159,6 +159,42 @@ class DatabaseRepoTest { ) } + /** + * A dead custom URL must fail the update even when the transceivers source answers. + * + * The counts used to be added together, so one transceivers success covered a total orbital + * failure: no exception, and a fresh "updated successfully" timestamp for an update that + * refreshed nothing. Replacing the built-in sources shrank the denominator from 28 to 2 and + * made that easy to hit. + */ + @Test + fun `a dead custom url fails the update even if transceivers succeed`() = runTest(dispatcher) { + val localSource = FakeLocalSource() + val remoteSource = FakeRemoteSource().apply { + Sources.transceiversDataUrls.values.filter { it.isNotBlank() } + .forEach { networkStreams[it] = { "[]".byteInputStream() } } + } + val settingsRepo = FakeSettingsRepo( + dataSources = DataSourcesSettings( + useCustomTLE = true, + useCustomTransceivers = false, + tleUrl = "https://example.com/dead.csv", + transceiversUrl = "" + ) + ) + val repository = DatabaseRepo(dispatcher, dataParser, localSource, remoteSource, settingsRepo) + + var threw = false + try { + repository.updateFromRemote() + } catch (_: java.io.IOException) { + threw = true + } + + assertTrue("a total orbital failure must raise", threw) + assertTrue("no entries may be inserted", localSource.insertedEntries.isEmpty()) + } + private fun validCsvStream(): InputStream = """ OBJECT_NAME,OBJECT_ID,EPOCH,MEAN_MOTION,ECCENTRICITY,INCLINATION,RA_OF_ASC_NODE,ARG_OF_PERICENTER,MEAN_ANOMALY,EPHEMERIS_TYPE,CLASSIFICATION_TYPE,NORAD_CAT_ID,ELEMENT_SET_NO,REV_AT_EPOCH,BSTAR,MEAN_MOTION_DOT,MEAN_MOTION_DDOT ISS (ZARYA),1998-067A,2021-11-16T12:28:09.322176,15.48582035,.0004694,51.6447,309.4881,203.6966,299.8876,0,U,25544,999,31220,.31985E-4,.1288E-4,0 diff --git a/core/presentation/src/main/res/values-zh/strings.xml b/core/presentation/src/main/res/values-zh/strings.xml index 5e83387e..b75408a7 100644 --- a/core/presentation/src/main/res/values-zh/strings.xml +++ b/core/presentation/src/main/res/values-zh/strings.xml @@ -263,6 +263,7 @@ 已存入本地日志 已上传 %1$d 条, %2$d 条仍在队列 已上传 %1$d 条 + 没有待上传的联络 未配置 WaveLog 服务器 读不到站点信息 - 请检查站点 ID 与密钥权限 上传失败 diff --git a/core/presentation/src/main/res/values/strings.xml b/core/presentation/src/main/res/values/strings.xml index 50d016de..a44bc0f4 100644 --- a/core/presentation/src/main/res/values/strings.xml +++ b/core/presentation/src/main/res/values/strings.xml @@ -294,6 +294,7 @@ Saved to local log Uploaded %1$d, %2$d still queued Uploaded %1$d QSO + Nothing waiting to upload WaveLog server not configured Cannot read the station profile - check the station ID and key permissions Upload failed diff --git a/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsViewModel.kt index adc82c0e..16eadec6 100644 --- a/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/java/com/rtbishop/look4sat/feature/settings/SettingsViewModel.kt @@ -313,6 +313,8 @@ class SettingsViewModel( showToast(R.string.wavelog_no_station) result.failedCount > 0 -> showToast(R.string.wavelog_upload_done, result.successCount, result.failedCount) + // "Uploaded 0" is accurate but reads oddly when the queue was already clear. + result.successCount == 0 -> showToast(R.string.wavelog_nothing_to_upload) else -> showToast(R.string.wavelog_upload_all_ok, result.successCount) } }