feat: upload alarm snapshots to webhook payloads
This commit is contained in:
@@ -9,11 +9,13 @@ from pathlib import Path
|
||||
from cold_display_guard.cases import CaseStore
|
||||
from cold_display_guard.main import (
|
||||
case_sink_path,
|
||||
capture_runtime_alarm_snapshot,
|
||||
deliver_runtime_webhooks,
|
||||
persist_case_updates,
|
||||
restore_runtime_state,
|
||||
webhook_retry_sink_path,
|
||||
)
|
||||
from cold_display_guard.vision import Frame
|
||||
from cold_display_guard.webhooks import load_retry_snapshots
|
||||
|
||||
|
||||
@@ -114,6 +116,29 @@ class RuntimeRestoreTests(unittest.TestCase):
|
||||
self.assertEqual(deliveries[0][1]["kind"], "batch_event")
|
||||
self.assertEqual(deliveries[1][1]["kind"], "case_event")
|
||||
|
||||
def test_capture_runtime_alarm_snapshot_uses_current_frame_for_alert_events(self) -> None:
|
||||
frame = Frame(width=1, height=1, rgb=b"\x01\x02\x03")
|
||||
result = capture_runtime_alarm_snapshot(
|
||||
frame,
|
||||
[
|
||||
{
|
||||
"event": "time_alarm",
|
||||
"severity": "alarm",
|
||||
"batch_id": "batch_000001",
|
||||
"camera_id": "cam_01",
|
||||
"zone_id": "1",
|
||||
"ts": datetime(2026, 6, 9, 9, 0, tzinfo=UTC).isoformat(),
|
||||
}
|
||||
],
|
||||
{"alarm_snapshot_upload": {"enabled": True}},
|
||||
now=datetime(2026, 6, 9, 9, 0, tzinfo=UTC),
|
||||
jpeg_encoder=lambda frame, timeout_seconds: b"jpeg",
|
||||
uploader=lambda image_bytes, **kwargs: {"status": "uploaded", "object_key": "uploads/alarms/a.jpg", "file_name": "a.jpg"},
|
||||
)
|
||||
|
||||
self.assertEqual(result["status"], "uploaded")
|
||||
self.assertEqual(result["object_key"], "uploads/alarms/a.jpg")
|
||||
|
||||
def test_deliver_runtime_webhooks_enqueues_failure_and_drains_due_retry(self) -> None:
|
||||
attempts = {"count": 0}
|
||||
|
||||
@@ -169,6 +194,53 @@ class RuntimeRestoreTests(unittest.TestCase):
|
||||
self.assertEqual(retries[-1]["status"], "delivered")
|
||||
self.assertEqual(retries[-1]["attempt_count"], 2)
|
||||
|
||||
def test_deliver_runtime_webhooks_includes_snapshot_path_in_alert_payloads(self) -> None:
|
||||
deliveries: list[dict[str, object]] = []
|
||||
|
||||
def fake_post(url: str, payload: dict[str, object], timeout: tuple[float, float]) -> tuple[int, str]:
|
||||
deliveries.append(payload)
|
||||
return 200, "ok"
|
||||
|
||||
deliver_runtime_webhooks(
|
||||
[
|
||||
{
|
||||
"event": "time_alarm",
|
||||
"ts": datetime(2026, 6, 9, 9, 0, tzinfo=UTC).isoformat(),
|
||||
"batch_id": "batch_000001",
|
||||
"camera_id": "cam_01",
|
||||
"zone_id": "1",
|
||||
"zone_label": "区域 1",
|
||||
"severity": "alarm",
|
||||
"state": "alerted",
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"case_id": "case_batch_000001",
|
||||
"batch_id": "batch_000001",
|
||||
"case_type": "time_alarm",
|
||||
"case_status": "open",
|
||||
"source_event": "time_alarm",
|
||||
"handled_source": "",
|
||||
"created_at": datetime(2026, 6, 9, 9, 0, tzinfo=UTC).isoformat(),
|
||||
"updated_at": datetime(2026, 6, 9, 9, 0, tzinfo=UTC).isoformat(),
|
||||
}
|
||||
],
|
||||
{
|
||||
"webhooks": {
|
||||
"enabled": True,
|
||||
"event_url": "https://example.com/events",
|
||||
"case_url": "https://example.com/cases",
|
||||
}
|
||||
},
|
||||
Path(tempfile.mkdtemp()) / "webhook_delivery.jsonl",
|
||||
http_post=fake_post,
|
||||
snapshot_upload={"status": "uploaded", "object_key": "uploads/alarms/a.jpg", "batch_ids": ["batch_000001"]},
|
||||
)
|
||||
|
||||
self.assertEqual(deliveries[0]["snapshot_object_key"], "uploads/alarms/a.jpg")
|
||||
self.assertEqual(deliveries[1]["snapshot_object_key"], "uploads/alarms/a.jpg")
|
||||
|
||||
def test_restore_runtime_state_uses_stable_occupancy_when_raw_metrics_flicker(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
diagnostics_path = Path(tmpdir) / "runtime_diagnostics.jsonl"
|
||||
|
||||
Reference in New Issue
Block a user