mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
fix(activity-log): stamp source_event_table for native adapter broadcasts + UI label fallback + backfill recent orphans (#99)
Backend: add _SOURCE_TO_TABLE class constant in Dispatcher mapping event.source
("nws", "nifc", "wzdx", "traffic", "511") to canonical audit table names.
_post_broadcast_commit now falls back to this map when _broadcast_audit is
absent/None, so native env adapter sends (nws.py, fires.py, wzdx.py,
roads511.py, traffic.py) write a non-NULL source_event_table instead of NULL.
Existing _broadcast_audit paths (Central handlers, scheduled broadcasts) are
unchanged.
Frontend: replace naive familyLabel() string transform with explicit
TABLE_LABELS lookup (10 known tables → friendly names) plus a TEXT_HINTS
emoji-prefix heuristic for legacy NULL-source rows, so historical orphan rows
still display a meaningful label before/after backfill.
Tests: three new unit tests in test_dispatcher_persistence.py covering the
fallback path (nws→nws_alerts), the full _SOURCE_TO_TABLE map, and that
explicit _broadcast_audit is never overridden by the fallback.
Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d988868257
commit
40fcbf88e8
3 changed files with 179 additions and 8 deletions
|
|
@ -501,3 +501,114 @@ def test_dispatcher_construct_when_db_unavailable(monkeypatch, tmp_path):
|
|||
d = Dispatcher(cfg, factory) # must not raise
|
||||
assert d._stale_dropped == 0
|
||||
assert d._first_event_at is None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# _SOURCE_TO_TABLE fallback — native adapter events without _broadcast_audit
|
||||
# ============================================================================
|
||||
|
||||
def test_source_to_table_fallback_stamps_audit_row(db_path):
|
||||
"""_post_broadcast_commit must write source_event_table via _SOURCE_TO_TABLE
|
||||
when the event has no _broadcast_audit. Covers the core Fix 1 regression
|
||||
guard: event.source='nws' → source_event_table='nws_alerts'.
|
||||
"""
|
||||
from unittest.mock import MagicMock
|
||||
from meshai.notifications.events import make_event
|
||||
from meshai.notifications.pipeline.dispatcher import Dispatcher
|
||||
from meshai.persistence import get_db
|
||||
|
||||
cfg = _build_config(cold_start_grace=0)
|
||||
factory, _ = _mk_channel_factory()
|
||||
d = Dispatcher(cfg, factory)
|
||||
|
||||
# Build a native NWS event — no _broadcast_audit, no data dict at all.
|
||||
ev = make_event(
|
||||
source="nws",
|
||||
category="weather_alert",
|
||||
severity="priority",
|
||||
region="US-ID",
|
||||
title="⚠️ Winter Weather Advisory",
|
||||
lat=43.6, lon=-116.2,
|
||||
)
|
||||
|
||||
# Fake a minimal rule + payload for the internal commit call.
|
||||
rule = MagicMock()
|
||||
rule.broadcast_channel = 1
|
||||
rule.delivery_types = ["mesh_broadcast"]
|
||||
|
||||
payload = MagicMock()
|
||||
payload.message = "⚠️ Winter Weather Advisory — test"
|
||||
|
||||
# Call _post_broadcast_commit directly — does NOT transmit anything.
|
||||
d._post_broadcast_commit(ev, payload, rule, "mesh_broadcast", success=True)
|
||||
|
||||
conn = get_db()
|
||||
row = conn.execute(
|
||||
"SELECT source_event_table, source_event_pk, transport "
|
||||
"FROM mesh_broadcasts_out ORDER BY id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
assert row is not None, "No audit row was written"
|
||||
assert row["source_event_table"] == "nws_alerts", (
|
||||
f"Expected 'nws_alerts', got {row['source_event_table']!r}"
|
||||
)
|
||||
assert row["source_event_pk"] is None, "pk should be NULL for fallback path"
|
||||
|
||||
|
||||
def test_source_to_table_fallback_all_native_sources(db_path):
|
||||
"""Verify _SOURCE_TO_TABLE covers all five native adapter sources."""
|
||||
from meshai.notifications.pipeline.dispatcher import Dispatcher
|
||||
expected = {
|
||||
"nws": "nws_alerts",
|
||||
"nifc": "fires",
|
||||
"wzdx": "traffic_events",
|
||||
"traffic": "traffic_events",
|
||||
"511": "traffic_events",
|
||||
}
|
||||
cfg = _build_config()
|
||||
factory, _ = _mk_channel_factory()
|
||||
d = Dispatcher(cfg, factory)
|
||||
assert d._SOURCE_TO_TABLE == expected, (
|
||||
f"Map mismatch: {d._SOURCE_TO_TABLE!r}"
|
||||
)
|
||||
|
||||
|
||||
def test_broadcast_audit_present_not_overridden(db_path):
|
||||
"""When _broadcast_audit IS stamped, _post_broadcast_commit must use its
|
||||
table/pk, NOT the _SOURCE_TO_TABLE fallback."""
|
||||
from unittest.mock import MagicMock
|
||||
from meshai.notifications.events import make_event
|
||||
from meshai.notifications.pipeline.dispatcher import Dispatcher
|
||||
from meshai.persistence import get_db
|
||||
|
||||
cfg = _build_config(cold_start_grace=0)
|
||||
factory, _ = _mk_channel_factory()
|
||||
d = Dispatcher(cfg, factory)
|
||||
|
||||
ev = make_event(
|
||||
source="nws",
|
||||
category="weather_alert",
|
||||
severity="priority",
|
||||
region="US-ID",
|
||||
title="⚠️ Test explicit audit",
|
||||
lat=43.6, lon=-116.2,
|
||||
)
|
||||
# Stamp explicit audit (as Central handlers do).
|
||||
ev.data["_broadcast_audit"] = {"table": "nws_alerts", "pk": "CAP-XYZ-001"}
|
||||
|
||||
rule = MagicMock()
|
||||
rule.broadcast_channel = 1
|
||||
rule.delivery_types = ["mesh_broadcast"]
|
||||
payload = MagicMock()
|
||||
payload.message = "⚠️ Test explicit audit"
|
||||
|
||||
d._post_broadcast_commit(ev, payload, rule, "mesh_broadcast", success=True)
|
||||
|
||||
conn = get_db()
|
||||
row = conn.execute(
|
||||
"SELECT source_event_table, source_event_pk "
|
||||
"FROM mesh_broadcasts_out ORDER BY id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
assert row["source_event_table"] == "nws_alerts"
|
||||
assert row["source_event_pk"] == "CAP-XYZ-001", (
|
||||
f"Expected pk='CAP-XYZ-001', got {row['source_event_pk']!r}"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue