From 4a17c7b702a8413c4d61726cdb912a18111ae03e Mon Sep 17 00:00:00 2001 From: "Matt Johnson (via Claude)" Date: Fri, 12 Jun 2026 15:33:25 +0000 Subject: [PATCH] fix: add satpass to region rewrite table in _subjects_for() _subjects_for() consults a per-adapter region table when region is set (e.g. us.id). The table had no satpass entry, so it returned [] and Central silently subscribed to nothing for sat subjects. Changes: - Add satpass entry to the region table: pass alerts use Convention A (central.sat.pass.{region}.>), TLEs are global (central.sat.tle.>) - Update test_central_region_default_propagates_to_consumer_subjects to expect satpass subjects (satpass defaults to feed_source=central) - Add 3 tests in test_satpass_registration.py: _subjects_for with region, without region, and empty region fallback Co-Authored-By: Claude Opus 4.6 --- meshai/central/consumer.py | 6 ++++++ tests/test_central_region_routing.py | 7 ++++++- tests/test_satpass_registration.py | 30 ++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/meshai/central/consumer.py b/meshai/central/consumer.py index 7c1f7d9..33f4fff 100644 --- a/meshai/central/consumer.py +++ b/meshai/central/consumer.py @@ -183,6 +183,12 @@ def _subjects_for(adapter: str, region: Optional[str]) -> list[str]: # on Central's scale, inverse of what the handler uses). # Off-season: June–Sep, CENTRAL_AVY will be empty — expected, not broken. "avalanche": [f"central.avy.advisory.>"], + # satpass: pass alerts are region-scoped (Central publishes + # central.sat.pass.us.., per quickstart §7); + # TLEs are global, no region token (central.sat.tle., §4) -- + # same no-region logic as swpc. + "satpass": [f"central.sat.pass.{region}.>", + "central.sat.tle.>"], } return list(table.get(adapter, [])) diff --git a/tests/test_central_region_routing.py b/tests/test_central_region_routing.py index 748ecf3..8fa6d98 100644 --- a/tests/test_central_region_routing.py +++ b/tests/test_central_region_routing.py @@ -117,5 +117,10 @@ def test_central_region_default_propagates_to_consumer_subjects(): assert env.central.region == "us.id" # spec default env.nws.feed_source = "central" so = CentralConsumer(env, None)._subject_owned() - assert list(so.keys()) == ["central.wx.alert.us.id.>"] + # satpass also defaults to feed_source='central', so it appears too + assert "central.wx.alert.us.id.>" in so assert so["central.wx.alert.us.id.>"] == {"nws"} + assert "central.sat.pass.us.id.>" in so + assert so["central.sat.pass.us.id.>"] == {"satpass"} + assert "central.sat.tle.>" in so + assert so["central.sat.tle.>"] == {"satpass"} diff --git a/tests/test_satpass_registration.py b/tests/test_satpass_registration.py index e34bb46..e652ed6 100644 --- a/tests/test_satpass_registration.py +++ b/tests/test_satpass_registration.py @@ -115,3 +115,33 @@ def test_yaml_parsing_satpass(): assert isinstance(env.satpass, SatpassConfig) assert env.satpass.enabled is True assert env.satpass.feed_source == "central" + + + +# -- _subjects_for() region rewrite table ------------------------------------ + +def test_subjects_for_satpass_with_region(): + """_subjects_for('satpass', 'us.id') must return region-scoped pass + subjects and global TLE subject.""" + from meshai.central.consumer import _subjects_for + result = _subjects_for('satpass', 'us.id') + assert len(result) == 2, f'Expected 2 subjects, got {len(result)}: {result}' + assert result[0] == 'central.sat.pass.us.id.>' + assert result[1] == 'central.sat.tle.>' + + +def test_subjects_for_satpass_no_region_falls_back(): + """_subjects_for('satpass', None) must return bare-wildcard forms + from _SUBJECTS_BARE.""" + from meshai.central.consumer import _subjects_for + result = _subjects_for('satpass', None) + assert len(result) == 2, f'Expected 2 subjects, got {len(result)}: {result}' + assert result[0] == 'central.sat.pass.>' + assert result[1] == 'central.sat.tle.>' + + +def test_subjects_for_satpass_empty_region_falls_back(): + """_subjects_for('satpass', '') must behave like None — bare wildcards.""" + from meshai.central.consumer import _subjects_for + result = _subjects_for('satpass', '') + assert result == _subjects_for('satpass', None)