Files
full-stack-fastapi/backend/tests/mqtt/test_parser_worker.py
魏风 e81aadcd99
All checks were successful
Deploy to Production / deploy (push) Successful in 1m10s
feat: add realtime GNSS monitor with telemetry websocket
2026-03-20 11:48:04 +08:00

150 lines
4.9 KiB
Python

from sqlmodel import Session, SQLModel, create_engine
from app.models import TerminalDeviceState, TerminalMessageRaw
from app.mqtt.parser_worker import process_pending_batch
from app.mqtt.repository import insert_raw_message
def _session() -> Session:
engine = create_engine("sqlite://")
SQLModel.metadata.create_all(engine)
return Session(engine)
def test_insert_raw_message_sets_unparsed_defaults() -> None:
with _session() as session:
row = insert_raw_message(
session=session,
device_id="device-001",
topic="terminal/device-001/raw",
payload=b"$GPRMC,123520,A,3723.2475,N,12158.3416,W,0.13,309.62,120598,,,A*74\r\n",
qos=1,
retain=False,
)
assert row.parsed_at is None
assert row.parse_error is None
assert row.payload_size > 0
assert len(row.payload_sha256) == 64
def test_worker_marks_success_and_updates_state() -> None:
with _session() as session:
raw = insert_raw_message(
session=session,
device_id="device-001",
topic="terminal/device-001/raw",
payload=b"$GPGGA,123519,3723.2475,N,12158.3416,W,1,12,0.8,10.0,M,-25.0,M,,*65\r\n",
qos=1,
retain=False,
)
processed = process_pending_batch(session=session, batch_size=10)
assert processed == 1
refreshed = session.get(TerminalMessageRaw, raw.id)
assert refreshed is not None
assert refreshed.parsed_at is not None
assert refreshed.parse_error is None
state = session.get(TerminalDeviceState, "device-001")
assert state is not None
assert state.lat is not None
assert state.lon is not None
def test_worker_marks_failed_without_retry_schedule(monkeypatch) -> None:
def _raise_parse_error(_payload: bytes) -> list[dict[str, object]]:
raise ValueError("boom")
monkeypatch.setattr(
"app.mqtt.parser_worker.parse_mixed_payload", _raise_parse_error
)
with _session() as session:
raw = insert_raw_message(
session=session,
device_id="device-err",
topic="terminal/device-err/raw",
payload=b"$GPGGA,broken*00\r\n",
qos=1,
retain=False,
)
processed = process_pending_batch(session=session, batch_size=10)
assert processed == 1
refreshed = session.get(TerminalMessageRaw, raw.id)
assert refreshed is not None
assert refreshed.parsed_at is not None
assert refreshed.parse_error == "boom"
processed_again = process_pending_batch(session=session, batch_size=10)
assert processed_again == 0
def test_schema_excludes_terminal_observation_event_table() -> None:
assert "terminal_observation_event" not in SQLModel.metadata.tables
def test_ingest_and_parse_real_nmea_sample() -> None:
from pathlib import Path
sample_path = Path(__file__).parent / "nmea.txt"
payload = sample_path.read_bytes()
with _session() as session:
raw = insert_raw_message(
session=session,
device_id="device-real-sample",
topic="terminal/device-real-sample/raw",
payload=payload,
qos=1,
retain=False,
)
processed = process_pending_batch(session=session, batch_size=10)
assert processed == 1
refreshed = session.get(TerminalMessageRaw, raw.id)
assert refreshed is not None
assert refreshed.parsed_at is not None
assert refreshed.parse_error is None
state = session.get(TerminalDeviceState, "device-real-sample")
assert state is not None
assert state.lat is not None
assert state.lon is not None
def test_worker_builds_satellite_distribution_summary() -> None:
payload = (
b"$GNGGA,123519,3723.2475,N,12158.3416,W,4,12,0.8,10.0,M,-25.0,M,,*65\r\n"
b"$GPGSA,A,3,02,05,12,,,,,,,,,,1.50,0.90,1.20*00\r\n"
b"$GPGSV,1,1,04,02,17,213,40,05,29,121,37,12,66,314,44,25,14,048,39*71\r\n"
)
with _session() as session:
insert_raw_message(
session=session,
device_id="device-gsv",
topic="terminal/device-gsv/raw",
payload=payload,
qos=1,
retain=False,
)
processed = process_pending_batch(session=session, batch_size=10)
assert processed == 1
state = session.get(TerminalDeviceState, "device-gsv")
assert state is not None
assert state.fix_status == "4"
summary = state.satellite_signal_summary
assert summary is not None
assert summary["satellites_in_view"] == 4
assert summary["snr_values"] == [40, 37, 44, 39]
assert summary["used_satellite_ids"] == ["02", "05", "12"]
assert summary["constellations"]["GPS"]["count"] == 4
assert summary["constellations"]["GPS"]["used_in_navigation"] == 3