From a2e41a0c1a443d7c60425471629a0c865d6b488b Mon Sep 17 00:00:00 2001 From: atsunatsu Date: Thu, 24 Sep 2026 11:11:04 +0800 Subject: [PATCH] fix: don't crash the AMSAT list update on unknown satellite names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolvePerName crashed with NoSuchElementException (all.first()) when an AMSAT live-page name had no matching local entry (TEVEL2-1..9, RS95S, any brand-new sat not yet in the local TLE). The exception was swallowed by updateAmSatLiveLists' runCatching, so the FM/Linear lists were NEVER rewritten — they stayed at the stale pre-whitelist snapshot containing all five ISS modules, which is why ISS kept showing as DESTINY despite the 15.4/15.5/15.6 disambiguation fixes. Fix: return an empty set for names with no local match (correct skip semantics) so the rest of the page still resolves and the ISS module collapse runs. Regression test covers the real AMSAT page mix (ISS + unknown names). --- .../core/data/repository/DatabaseRepo.kt | 6 +++ .../core/data/repository/DatabaseRepoTest.kt | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+) 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 597946d6..4d59aef9 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 @@ -158,6 +158,12 @@ class DatabaseRepo( val all = nameToCatnum.filter { (localName, _) -> dataParser.matchesAmSatName(localName, keys) }.values.toSet() + // No local entry matches this AMSAT name (e.g. a brand-new + // satellite not yet in the local TLE, like TEVEL2/RS95S): + // skip it. Returning empty is correct — crashing here (via + // all.first()) aborted the WHOLE list update and kept the + // stale pre-whitelist FM list with all five ISS modules. + if (all.isEmpty()) return emptySet() if (all.size <= 1) return all val preferred = all.intersect(activeCatnums) if (preferred.isNotEmpty()) return preferred 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 14eac89c..d950a44b 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 @@ -244,6 +244,43 @@ class DatabaseRepoTest { assertEquals(setOf(25544), settingsRepo.amSatFm) } + @Test + fun `amsat fm list survives unknown satellite names and still disambiguates ISS`() = runTest(dispatcher) { + // Regression: AMSAT pages list satellites absent from the local TLE + // (TEVEL2-1..9, RS95S). resolvePerName used to crash on the empty + // match set (all.first()), aborting the WHOLE list update and keeping + // the stale pre-whitelist FM list with all five ISS modules. + val satnogsUrl = Sources.satelliteDataUrls.getValue("SatNOGS") + val localSource = FakeLocalSource() + val remoteSource = FakeRemoteSource().apply { + networkStreams[satnogsUrl] = { issModulesCsvStream() } + networkStreams[Sources.amSatLiveUrls.getValue("FM")] = { amsatFmPageWithUnknownStream() } + } + val settingsRepo = FakeSettingsRepo( + dataSources = DataSourcesSettings( + satelliteUrls = listOf(satnogsUrl), + transceiversUrls = emptyList() + ) + ).apply { amSatActive = setOf(25544) } + val repository = DatabaseRepo(dispatcher, dataParser, localSource, remoteSource, settingsRepo) + + repository.updateFromRemote() + + // No crash; ISS still collapses to the primary ZARYA (25544). + assertEquals(setOf(25544), settingsRepo.amSatFm) + } + + private fun amsatFmPageWithUnknownStream(): InputStream = """ + + + + + + + +
SatelliteUplinkDownlinkComment
ISS145.990 MHz437.800 MHz
TEVEL2-1 thru TEVEL2-9145.970 MHz436.400 MHz
RS95S(QMR-KWT-2)145.920 MHz436.950 MHz
+ """.trimIndent().byteInputStream() + private fun issModulesCsvStream(): 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