fix(k1): harden live handoff and camera recovery

This commit is contained in:
DCCONSTRUCTIONS
2026-08-22 13:09:24 +03:00
parent eaad9deda1
commit 85035fa07b
26 changed files with 1478 additions and 170 deletions
+76 -2
View File
@@ -11,6 +11,7 @@ import pytest
from fastapi import APIRouter
from fastapi.routing import APIRoute
from pydantic import ValidationError
from starlette.requests import Request
from k1link.web.runtime_diagnostics import (
SCANNER_LOGGER_NAME,
@@ -34,6 +35,13 @@ def _endpoint(router: APIRouter, path: str, method: str) -> Callable[..., Any]:
raise AssertionError(f"{method} {path} route is missing")
def _request(*, ui_build_id: str | None = None) -> Request:
headers = []
if ui_build_id is not None:
headers.append((b"x-missioncore-ui-build", ui_build_id.encode("ascii")))
return Request({"type": "http", "method": "GET", "path": "/", "headers": headers})
def test_private_scanner_diagnostics_are_durable_structured_and_bounded(
tmp_path: Path,
) -> None:
@@ -76,6 +84,11 @@ def test_private_scanner_diagnostics_are_durable_structured_and_bounded(
"device_write_performed": False,
"preferred_port": 9876,
"selected_port": 9877,
"camera_queue_bytes": 12_000_000,
"camera_queue_segments": 96,
"camera_retry_count": 3,
"websocket_close_code": 4_008,
"transport_epoch": 7,
"unapproved_secret_field": "must-not-be-written",
},
)
@@ -118,6 +131,11 @@ def test_private_scanner_diagnostics_are_durable_structured_and_bounded(
assert document["device_write_performed"] is False
assert document["preferred_port"] == 9876
assert document["selected_port"] == 9877
assert document["camera_queue_bytes"] == 12_000_000
assert document["camera_queue_segments"] == 96
assert document["camera_retry_count"] == 3
assert document["websocket_close_code"] == 4_008
assert document["transport_epoch"] == 7
assert "unapproved_secret_field" not in document
parent = logging.getLogger(SCANNER_LOGGER_NAME)
@@ -180,6 +198,40 @@ def test_live_viewer_diagnostic_endpoint_accepts_only_bounded_events(
)
assert fallback.failure_stage is None
camera_restart = LiveViewerDiagnosticEvent(
schema_version="missioncore.live-viewer-diagnostic/v2",
event_code="live_camera_transport_restart_requested",
ui_build_id=expected_build,
document_instance_id="00000000-0000-4000-8000-000000000001",
viewer_instance_id="00000000-0000-4000-8000-000000000003",
lifecycle_generation=2,
failure_stage="camera-queue-capacity",
stream_id="camera-preview-2",
camera_queue_bytes=12_000_000,
camera_queue_segments=96,
camera_retry_count=3,
websocket_close_code=4_008,
transport_epoch=7,
camera_append_error_name="InvalidStateError",
camera_media_source_state="open",
camera_video_error_code=3,
)
with caplog.at_level(
logging.INFO,
logger="k1link.device_plugins.xgrids_k1.viewer_receiver",
):
camera_response = endpoint(camera_restart)
assert camera_response.status_code == 204
assert caplog.records[-1].failure_stage == "camera-queue-capacity"
assert caplog.records[-1].camera_queue_bytes == 12_000_000
assert caplog.records[-1].camera_queue_segments == 96
assert caplog.records[-1].camera_retry_count == 3
assert caplog.records[-1].websocket_close_code == 4_008
assert caplog.records[-1].transport_epoch == 7
assert caplog.records[-1].camera_append_error_name == "InvalidStateError"
assert caplog.records[-1].camera_media_source_state == "open"
assert caplog.records[-1].camera_video_error_code == 3
def test_live_viewer_diagnostic_rejects_stale_build_before_logging(
caplog: pytest.LogCaptureFixture,
@@ -212,7 +264,7 @@ def test_live_viewer_client_contract_is_no_store_and_exact_build() -> None:
router = build_viewer_diagnostics_router(expected_ui_build_id=lambda: expected_build)
endpoint = _endpoint(router, "/api/v1/viewer/client-contract", "GET")
response = endpoint()
response = endpoint(_request(ui_build_id=expected_build))
assert response.status_code == 200
assert response.headers["cache-control"] == "no-store"
@@ -225,11 +277,33 @@ def test_live_viewer_client_contract_is_no_store_and_exact_build() -> None:
}
def test_live_viewer_client_contract_logs_suppressed_stale_build_reload(
caplog: pytest.LogCaptureFixture,
) -> None:
loaded_build = "/assets/index-abcdefgh.js"
expected_build = "/assets/index-ijklmnop.js"
router = build_viewer_diagnostics_router(expected_ui_build_id=lambda: expected_build)
endpoint = _endpoint(router, "/api/v1/viewer/client-contract", "GET")
with caplog.at_level(
logging.INFO,
logger="k1link.device_plugins.xgrids_k1.viewer_receiver",
):
response = endpoint(_request(ui_build_id=loaded_build))
assert response.status_code == 200
assert caplog.records[-1].event_code == "ui_build_drift_reload_suppressed"
assert caplog.records[-1].ui_build_id == loaded_build
assert caplog.records[-1].expected_ui_build_id == expected_build
assert caplog.records[-1].device_write_performed is False
assert caplog.records[-1].automatic_retry is False
def test_live_viewer_client_contract_no_dist_is_retryable_without_reload_header() -> None:
router = build_viewer_diagnostics_router(expected_ui_build_id=lambda: None)
endpoint = _endpoint(router, "/api/v1/viewer/client-contract", "GET")
response = endpoint()
response = endpoint(_request())
assert response.status_code == 503
assert response.headers["cache-control"] == "no-store"