fix(aprs): do not tell a mistyped passcode it is in receive-only mode

The previous commit derived "wants receive-only" from AprsPasscode.canTransmit, which is
a boolean over four cases. Both a deliberate -1 and a mistyped passcode return false, so
fixing the mis-diagnosis in one direction introduced it in the other: measured against
the shipped algorithm, three entries - a passcode off by one digit, an arbitrary number,
and a non-numeric entry - were all told they were connected in receive-only mode, when
what they needed to hear was that the passcode does not match the callsign.

classify already distinguishes these; only its ReceiveOnly case counts as a deliberate
choice. A test now pins the distinction, including the fact that all four entries are
equally unable to transmit - which is exactly why the boolean was not enough.

Found by probe before review, not by the suite, which had no test for the reporter's use
of this and still does not.
This commit is contained in:
mckero committed 2026-08-25 15:25:32 +00:00
1 parent eb66a77cae
commit 0208a577c4
2 files changed
+32 -4

No files matched your search

@@ -64,6 +64,31 @@ class AprsPasscodeTest {
assertEquals(AprsPasscode.RECEIVE_ONLY, AprsPasscode.loginValue(callsign, wrong))
}
/**
* The three-way distinction the reporter depends on, and why it must not use canTransmit for
* it: that collapses a deliberate receive-only choice and a typo into one boolean. Telling an
* operator who mistyped their passcode that they are in receive-only mode is the same
* mis-diagnosis as telling a deliberate receive-only user their passcode is wrong, pointing
* the other way.
*/
@Test
fun `a deliberate receive-only entry is distinguishable from a typo`() {
val deliberate = AprsPasscode.classify(callsign, "-1")
val blank = AprsPasscode.classify(callsign, "")
val typo = AprsPasscode.classify(callsign, (correct + 1).toString())
val garbage = AprsPasscode.classify(callsign, "abcde")
assertTrue(deliberate is AprsPasscode.Entry.ReceiveOnly)
assertTrue(blank is AprsPasscode.Entry.ReceiveOnly)
assertFalse("a typo must not read as receive-only", typo is AprsPasscode.Entry.ReceiveOnly)
assertFalse("garbage must not read as receive-only", garbage is AprsPasscode.Entry.ReceiveOnly)
// All four are equally unable to transmit, which is why the boolean is not enough.
for (entry in listOf("-1", "", (correct + 1).toString(), "abcde")) {
assertFalse(AprsPasscode.canTransmit(callsign, entry))
}
}
@Test
fun `something that is not a number is its own case`() {
assertEquals(AprsPasscode.Entry.NotANumber, AprsPasscode.classify(callsign, "abcde"))