From 6db10b5b723663301190496d7b523784c33022c0 Mon Sep 17 00:00:00 2001 From: QIU Date: Wed, 26 Aug 2026 04:56:44 +0000 Subject: [PATCH] fix(sources): a dead custom URL no longer counts as a successful update Found by an audit of the replace-semantics commit rather than by the change itself. The success count added orbital and transceivers sources together, so one could stand in for the other. With the built-in sources replaced there are two requests instead of 28: if the operator's TLE URL was down and SatNOGS answered, the count was 1, no exception was raised, and setUpdateSuccessful stamped a fresh timestamp for an update that refreshed no orbital elements at all. That also suppressed the 48-hour auto-update retry, which keys off that timestamp - so the operator was left with stale orbits, a screen saying the update worked, and nothing scheduled to correct it. The failure existed before this rebuild, but 26 other sources masked it. Narrowing the source set made it easy to hit, which is why it belongs with these commits rather than in a backlog. Orbital sources are now counted on their own. A test covers the exact case: transceivers answers, the custom TLE URL does not, and the update must raise rather than record success. Also: an upload that found nothing waiting said "Uploaded 0 QSO". Accurate, but it reads oddly when the queue was already clear, so that case has its own wording now. --- .../core/data/repository/DatabaseRepo.kt | 13 ++++--- .../core/data/repository/DatabaseRepoTest.kt | 36 +++++++++++++++++++ .../src/main/res/values-zh/strings.xml | 1 + .../src/main/res/values/strings.xml | 1 + .../feature/settings/SettingsViewModel.kt | 2 ++ 5 files changed, 49 insertions(+), 4 deletions(-) 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) } }