From e5668585da1cfa326e6db5a8dbd05d9e1e81e72c Mon Sep 17 00:00:00 2001 From: corescope-bot Date: Thu, 4 Jun 2026 16:46:01 +0000 Subject: [PATCH] test(ingestor): regression test for #1534 default_scope overwrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds TestBuildPacketDataScopeMatchingNoMatch covering the case where a transport-scoped advert from a non-matching region carries ScopeName="". Extracts the guard at cmd/ingestor/main.go:719 into shouldUpdateDefaultScope so it can be unit-tested. The helper currently mirrors the buggy guard (IsTransportScoped only), so the new test FAILS — this is the red commit. Refs #1534 --- cmd/ingestor/main.go | 12 ++++++++++-- cmd/ingestor/main_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/cmd/ingestor/main.go b/cmd/ingestor/main.go index 3569bed2..8c5ad706 100644 --- a/cmd/ingestor/main.go +++ b/cmd/ingestor/main.go @@ -715,8 +715,8 @@ func handleMessage(store *Store, tag string, source MQTTSource, m mqtt.Message, log.Printf("MQTT [%s] node telemetry update error: %v", tag, err) } } - // Update default_scope when advert carries a matched transport scope (#899) - if pktData.IsTransportScoped { + // Update default_scope when advert carries a matched transport scope (#899, #1534) + if shouldUpdateDefaultScope(pktData) { if err := store.UpdateNodeDefaultScope(decoded.Payload.PubKey, pktData.ScopeName); err != nil { log.Printf("MQTT [%s] node default_scope update error: %v", tag, err) } @@ -1356,3 +1356,11 @@ func init() { os.Exit(0) } } + +// shouldUpdateDefaultScope returns true when the packet carries a transport +// scope whose region key matched (#1534). Without the ScopeName non-empty +// guard, transport-scoped adverts from non-matching regions would overwrite +// previously-correct default_scope values with the empty string. +func shouldUpdateDefaultScope(pktData *PacketData) bool { + return pktData.IsTransportScoped +} diff --git a/cmd/ingestor/main_test.go b/cmd/ingestor/main_test.go index bf1a8c02..73c9a974 100644 --- a/cmd/ingestor/main_test.go +++ b/cmd/ingestor/main_test.go @@ -1053,3 +1053,38 @@ func TestHandleMessageObserverIATAWhitelist(t *testing.T) { t.Errorf("observer from whitelisted IATA ARN should be accepted, got count=%d", count) } } + +// TestBuildPacketDataScopeMatchingNoMatch covers the #1534 regression: a +// transport-scoped advert from a non-matching region carries +// IsTransportScoped=true and ScopeName="". The default_scope update guard +// must skip these packets so previously-correct scopes aren't overwritten +// with the empty string. +func TestBuildPacketDataScopeMatchingNoMatch(t *testing.T) { + // Code1=2AB5 is the precomputed code for region "#test" (payload="hello", + // payloadType=5). Build a region-key map for a DIFFERENT region so + // matchScope() finds no match and returns "". + const rawHex = "142AB500000068656C6C6F" + otherKey, _ := hex.DecodeString("aabbccddeeff00112233445566778899") + regionKeys := map[string][]byte{"#other": otherKey} + + decoded, err := DecodePacket(rawHex, nil, false) + if err != nil { + t.Fatalf("DecodePacket: %v", err) + } + msg := &MQTTPacketMessage{Raw: rawHex} + pktData := BuildPacketData(msg, decoded, "obs1", "region1", regionKeys) + + if !pktData.IsTransportScoped { + t.Fatalf("precondition: IsTransportScoped should be true (Code1 != 0000)") + } + if pktData.ScopeName != "" { + t.Fatalf("precondition: ScopeName should be empty (no region match), got %q", pktData.ScopeName) + } + + // Regression assertion: when ScopeName is empty, the guard must skip the + // UpdateNodeDefaultScope call so an empty value never overwrites a + // previously-correct default_scope (#1534). + if shouldUpdateDefaultScope(pktData) { + t.Errorf("shouldUpdateDefaultScope = true for empty ScopeName; want false (would overwrite default_scope with \"\")") + } +}