refactor: move source tree into work/, multi-stage Docker build, fix satpass

- Move all application source (meshai/, dashboard-frontend/, tests/,
  config/, docs/, Dockerfile, etc.) into work/ directory
- Add Node.js multi-stage build to Dockerfile for frontend compilation;
  remove compiled static assets from git tracking
- Fix satpass missing time windows: consolidation was splitting wire on
  newline and only putting line 1 in event.title, dropping the time
  window line that the composer uses for precomposed broadcasts
- Fix satpass burst flooding: stagger consolidation timers (+60s per
  pending pass) so Central batch publishes don't blast the mesh
- Update CI workflow build context to work/
- Anchor lib/ and data/ gitignore patterns to repo root to prevent
  false matches on nested directories
- Add dashboard-frontend/node_modules/ to .dockerignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ubuntu 2026-06-16 03:40:31 +00:00
commit 2e1fb325f7
283 changed files with 109402 additions and 483 deletions

View file

@ -0,0 +1,175 @@
"""Tests for v0.5.10 USGS earthquakes handler."""
import pytest
from meshai.central.quake_handler import (
handle_quake,
within_250mi_of_idaho,
)
from meshai.persistence import close_thread_connection, init_db
from meshai.persistence import db as persistence_db
@pytest.fixture
def mem_db(monkeypatch, tmp_path):
db_path = str(tmp_path / "quake-test.sqlite")
monkeypatch.setenv("MESHAI_DB_PATH", db_path)
persistence_db._initialised.clear()
close_thread_connection()
conn = init_db()
yield conn
close_thread_connection()
persistence_db._initialised.discard(db_path)
def _quake_env(*, event_id="uu80141266", mag=3.5, depth_km=9.0,
place="9 km SW of Stanley, Idaho",
lat=44.094, lon=-115.962,
tsunami=0, alert=None,
time_ms=1780006952030,
category="quake.event.minor"):
return {
"id": event_id, "subject": "central.quake.event.minor.unknown",
"data": {
"id": event_id, "adapter": "usgs_quake", "category": category,
"severity": 0,
"geo": {"centroid": [lon, lat], "primary_region": None},
"data": {
"id": event_id, "magnitude": mag, "place": place,
"depth_km": depth_km, "time_ms": time_ms,
"tsunami": tsunami, "alert": alert, "status": "reviewed",
},
},
}
def _commit(data, t):
data["_on_broadcast_committed"](float(t))
# ---- magnitude floor ----
def test_m3_anywhere_broadcasts(mem_db):
env = _quake_env(mag=3.5, lat=37.0, lon=-122.0) # SF Bay area, outside Idaho
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert wire is not None
assert "M3.5" in wire
def test_m25_inside_idaho_broadcasts(mem_db):
env = _quake_env(mag=2.7, lat=44.094, lon=-115.962, event_id="uu1")
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert wire is not None
assert "M2.7" in wire
def test_m25_outside_idaho_skipped(mem_db):
# San Francisco -- well outside 250mi of Idaho centroid.
env = _quake_env(mag=2.7, lat=37.0, lon=-122.0, event_id="uu2")
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert wire is None
def test_below_25_skipped(mem_db):
env = _quake_env(mag=1.01, lat=44.0, lon=-114.0, event_id="uu3")
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert wire is None
# ---- tsunami special ----
def test_tsunami_any_magnitude_broadcasts(mem_db):
env = _quake_env(mag=4.5, lat=10.0, lon=140.0, tsunami=1, event_id="japan1")
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert wire is not None
assert "TSUNAMI WARNING" in wire
assert wire.startswith("🚨")
# ---- PAGER alert ----
def test_pager_orange_broadcasts(mem_db):
env = _quake_env(mag=2.0, lat=37.0, lon=-122.0, alert="orange",
event_id="pager1")
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert wire is not None
def test_pager_red_broadcasts(mem_db):
env = _quake_env(mag=2.0, lat=37.0, lon=-122.0, alert="red",
event_id="pager2")
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert wire is not None
# ---- wire format ----
def test_uses_usgs_place_string(mem_db):
env = _quake_env(mag=4.1, place="9 km SW of Stanley, Idaho",
event_id="usgs1")
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert "9 km SW of Stanley, Idaho" in wire
def test_m5_uses_warning_emoji(mem_db):
env = _quake_env(mag=5.2, lat=44.0, lon=-114.0, event_id="big1")
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert wire.startswith("⚠️")
def test_wire_includes_depth_and_coords(mem_db):
env = _quake_env(mag=4.1, depth_km=9.0, lat=44.094, lon=-115.962,
event_id="d1")
wire = handle_quake(env, env["subject"], data={}, now=1_000_000)
assert "Depth: 9 km" in wire
assert "@ 44.094, -115.962" in wire
# ---- per-event dedup ----
def test_per_event_id_dedup_no_reissue(mem_db):
env = _quake_env(mag=4.0, event_id="dedup1")
data1 = {}
handle_quake(env, env["subject"], data=data1, now=1_000_000)
_commit(data1, 1_000_001)
# Same event_id republishes (magnitude revision). Should NOT re-broadcast.
env_rev = _quake_env(mag=4.2, event_id="dedup1") # higher mag, same id
wire2 = handle_quake(env_rev, env_rev["subject"], data={}, now=1_000_300)
assert wire2 is None
# ---- distance helper ----
def test_within_250mi_of_idaho_boundary():
# Boise, ID -- inside
assert within_250mi_of_idaho(43.6, -116.2) is True
# San Francisco -- outside
assert within_250mi_of_idaho(37.0, -122.0) is False
# Seattle -- inside (250mi from Idaho centroid; verify the boundary)
assert within_250mi_of_idaho(47.6, -122.3) is False
# Boundary edge case (Idaho center)
assert within_250mi_of_idaho(44.36, -114.61) is True
# ---- commit callback ----
def test_commit_callback_updates_last_broadcast(mem_db):
env = _quake_env(mag=4.0, event_id="cb1")
data = {}
handle_quake(env, env["subject"], data=data, now=1_000_000)
pre = mem_db.execute(
"SELECT last_broadcast_at FROM quake_events WHERE event_id='cb1'"
).fetchone()
assert pre["last_broadcast_at"] is None
_commit(data, 1_000_001)
post = mem_db.execute(
"SELECT last_broadcast_at FROM quake_events WHERE event_id='cb1'"
).fetchone()
assert post["last_broadcast_at"] == 1_000_001