feat(sessions): add durable observation archive and replay API
This commit is contained in:
@@ -16,6 +16,14 @@ def _write_native(path: Path, topic: str, payload: bytes) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _append_native(path: Path, topic: str, payload: bytes) -> None:
|
||||
topic_raw = topic.encode()
|
||||
with path.open("ab") as stream:
|
||||
stream.write(FRAME_HEADER.pack(len(topic_raw), len(payload)))
|
||||
stream.write(topic_raw)
|
||||
stream.write(payload)
|
||||
|
||||
|
||||
def test_native_replay_uses_aligned_metadata_timing(tmp_path: Path) -> None:
|
||||
capture = tmp_path / "mqtt.raw.k1mqtt"
|
||||
_write_native(capture, "lixel/application/report/lio_pose", b"pose")
|
||||
@@ -39,6 +47,35 @@ def test_native_replay_uses_aligned_metadata_timing(tmp_path: Path) -> None:
|
||||
assert message.received_monotonic_ns == 456
|
||||
|
||||
|
||||
def test_native_replay_stops_at_crash_truncated_metadata_tail(tmp_path: Path) -> None:
|
||||
capture = tmp_path / "mqtt.raw.k1mqtt"
|
||||
_write_native(capture, "RealtimePointcloud", b"first")
|
||||
_append_native(capture, "RealtimePointcloud", b"uncommitted-tail")
|
||||
first_record = {
|
||||
"record_type": "message",
|
||||
"sequence": 1,
|
||||
"received_at_epoch_ns": 123_000_000_000,
|
||||
"received_monotonic_ns": 456,
|
||||
}
|
||||
(tmp_path / "mqtt.metadata.jsonl").write_text(
|
||||
json.dumps(first_record) + "\n" + '{"record_type":"message"',
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
messages = list(iter_replay_messages(capture))
|
||||
|
||||
assert [message.payload for message in messages] == [b"first"]
|
||||
|
||||
|
||||
def test_native_replay_rejects_newline_terminated_metadata_corruption(tmp_path: Path) -> None:
|
||||
capture = tmp_path / "mqtt.raw.k1mqtt"
|
||||
_write_native(capture, "RealtimePointcloud", b"frame")
|
||||
(tmp_path / "mqtt.metadata.jsonl").write_text("{corrupt\n", encoding="utf-8")
|
||||
|
||||
with pytest.raises(ReplayFormatError, match="not valid JSON"):
|
||||
list(iter_replay_messages(capture))
|
||||
|
||||
|
||||
def test_legacy_tsv_replay_validates_and_decodes_payload(tmp_path: Path) -> None:
|
||||
capture = tmp_path / "payloads.tsv"
|
||||
capture.write_bytes(b"1784124315.186225000\tRealtimePath\t4\t0001aaff\n")
|
||||
|
||||
Reference in New Issue
Block a user