from __future__ import annotations import json from collections import deque from datetime import UTC, datetime from pathlib import Path from types import SimpleNamespace from typing import Any import paho.mqtt.client as mqtt import pytest from k1link.device_plugins.xgrids_k1.calibration_snapshot import ( seal_factory_calibration_snapshot, ) from k1link.device_plugins.xgrids_k1.protocol.application_bootstrap import ( ApplicationControlAuthority, LiveDeviceControlBinding, ) from k1link.device_plugins.xgrids_k1.protocol.calibration_file import ( CALIBRATION_FILE_READ_COMMAND, CALIBRATION_FILE_REQUEST_TOPIC, CALIBRATION_FILE_RESPONSE_TOPIC, FACTORY_CALIBRATION_PATHS, FACTORY_CAMERA_CALIBRATION_PATH, FACTORY_CAMERA_LIDAR_EXTRINSIC_PATH, CalibrationFileProtocolError, CalibrationFileRejected, build_factory_calibration_file_read, decode_factory_calibration_file_response, ) from k1link.device_plugins.xgrids_k1.protocol.calibration_mqtt import ( CALIBRATION_READ_SUBSCRIPTIONS, FactoryCalibrationReadResult, ReviewedCalibrationMqttReader, ) from k1link.device_plugins.xgrids_k1.protocol.modeling_control import OPENAPI_SUCCESS from k1link.device_plugins.xgrids_k1.protocol.protobuf_wire import iter_fields APPLICATION_KEY = "11111111-2222-3333-4444-555555555555" VENDOR_DEVICE_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" DEVICE_SERIAL = "K1SERIAL01" PROFILE_ID = "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2" CALIBRATION_FIXTURE_ROOT = Path(__file__).parent / "fixtures" / "k1" / "calibration" def _authority() -> ApplicationControlAuthority: return ApplicationControlAuthority(openapi_key=APPLICATION_KEY) def _binding(**changes: object) -> LiveDeviceControlBinding: values = { "vendor_device_id": VENDOR_DEVICE_ID, "device_serial": DEVICE_SERIAL, "software_version": "V3.0.2", "system_version": "V3.0.2", "device_model": "LixelKity K1", "device_type": "A4", "is_activated": True, } values.update(changes) return LiveDeviceControlBinding(**values) # type: ignore[arg-type] def _varint(value: int) -> bytes: encoded = bytearray() while value > 0x7F: encoded.append((value & 0x7F) | 0x80) value >>= 7 encoded.append(value) return bytes(encoded) def _uint(number: int, value: int) -> bytes: return _varint(number << 3) + _varint(value) def _bytes(number: int, value: bytes) -> bytes: return _varint((number << 3) | 2) + _varint(len(value)) + value def _text(number: int, value: str) -> bytes: return _bytes(number, value.encode("utf-8")) def _header(session_id: str) -> bytes: return b"".join( ( _text(4, VENDOR_DEVICE_ID), _text(5, session_id), _text(6, APPLICATION_KEY), ) ) def _device_info_response() -> bytes: base_info = b"".join( ( _text(2, "V3.0.2"), _text(3, "V3.0.2"), _text(6, "LixelKity K1"), _text(7, DEVICE_SERIAL), _text(8, "A4"), ) ) working_status = _uint(1, 1) device_info = _bytes(2, base_info) + _bytes(7, working_status) return b"".join( ( _bytes(1, _header(":DeviceInfoRequest")), _bytes(2, device_info), _bytes(15, _uint(1, OPENAPI_SUCCESS)), ) ) def _calibration_response( path: str, content: bytes, *, command: int = CALIBRATION_FILE_READ_COMMAND, result_code: int = OPENAPI_SUCCESS, session_id: str | None = None, ) -> bytes: return b"".join( ( _bytes( 1, _header( session_id or f"{VENDOR_DEVICE_ID}:CalibFileRequest" ), ), _uint(2, command), _text(3, path), _bytes(4, content), _bytes(15, _uint(1, result_code)), ) ) def _content_for(path: str) -> bytes: if path == FACTORY_CAMERA_CALIBRATION_PATH: return (CALIBRATION_FIXTURE_ROOT / "camera.yaml").read_bytes() return (CALIBRATION_FIXTURE_ROOT / "extrinsic_camera_lidar.yaml").read_bytes() def test_factory_file_request_is_command_five_and_has_no_content_field() -> None: request = build_factory_calibration_file_read( _authority(), _binding(), FACTORY_CAMERA_CALIBRATION_PATH, ) fields = {field.number: field.value for field in iter_fields(request.payload)} header = fields[1] assert isinstance(header, bytes) header_fields = {field.number: field.value for field in iter_fields(header)} assert request.topic == CALIBRATION_FILE_REQUEST_TOPIC assert request.response_topic == CALIBRATION_FILE_RESPONSE_TOPIC assert request.mutates_device is False assert request.automatic_retry is False assert fields[2] == 5 assert fields[3] == FACTORY_CAMERA_CALIBRATION_PATH.encode() assert 4 not in fields assert header_fields[4] == VENDOR_DEVICE_ID.encode() assert header_fields[5] == f"{VENDOR_DEVICE_ID}:CalibFileRequest".encode() assert header_fields[6] == APPLICATION_KEY.encode() @pytest.mark.parametrize( "path", ( "/mnt/system/factory-data/config/../Lixel.yaml", "/mnt/system/factory-data/config/extrinsic_camera_motor.yaml", "/tmp/camera.yaml", "", ), ) def test_factory_file_request_rejects_every_path_outside_two_file_allowlist( path: str, ) -> None: with pytest.raises(CalibrationFileProtocolError, match="two-file allowlist"): build_factory_calibration_file_read(_authority(), _binding(), path) def test_factory_file_request_rejects_a_nonreviewed_live_binding() -> None: with pytest.raises(CalibrationFileProtocolError, match="reviewed activated"): build_factory_calibration_file_read( _authority(), _binding(system_version="V3.0.3"), FACTORY_CAMERA_CALIBRATION_PATH, ) def test_correlated_factory_file_response_returns_exact_utf8_bytes() -> None: request = build_factory_calibration_file_read( _authority(), _binding(), FACTORY_CAMERA_CALIBRATION_PATH, ) content = _content_for(request.path) result = decode_factory_calibration_file_response( _calibration_response(request.path, content), request, _authority(), _binding(), ) assert result.path == request.path assert result.content == content assert result.content_bytes == len(content) assert len(result.content_sha256) == 64 assert result.result_code == OPENAPI_SUCCESS def test_factory_file_response_rejects_write_command_path_drift_and_duplicates() -> None: request = build_factory_calibration_file_read( _authority(), _binding(), FACTORY_CAMERA_CALIBRATION_PATH, ) with pytest.raises(CalibrationFileProtocolError, match="not a file-read"): decode_factory_calibration_file_response( _calibration_response(request.path, b"yaml", command=6), request, _authority(), _binding(), ) with pytest.raises(CalibrationFileProtocolError, match="path mismatch"): decode_factory_calibration_file_response( _calibration_response(FACTORY_CAMERA_LIDAR_EXTRINSIC_PATH, b"yaml"), request, _authority(), _binding(), ) duplicate_path = _calibration_response(request.path, b"yaml") + _text(3, request.path) with pytest.raises(CalibrationFileProtocolError, match="duplicated"): decode_factory_calibration_file_response( duplicate_path, request, _authority(), _binding(), ) def test_factory_file_response_rejects_device_error_and_non_utf8_content() -> None: request = build_factory_calibration_file_read( _authority(), _binding(), FACTORY_CAMERA_CALIBRATION_PATH, ) with pytest.raises(CalibrationFileRejected) as rejected: decode_factory_calibration_file_response( _calibration_response(request.path, b"yaml", result_code=123), request, _authority(), _binding(), ) assert rejected.value.result_code == 123 with pytest.raises(CalibrationFileProtocolError, match="valid UTF-8"): decode_factory_calibration_file_response( _calibration_response(request.path, b"\xff"), request, _authority(), _binding(), ) class FakeReasonCode: def __init__(self, *, failure: bool = False) -> None: self.is_failure = failure class FakeCalibrationClient: def __init__(self) -> None: self.on_connect: Any = None self.on_subscribe: Any = None self.on_publish: Any = None self.on_message: Any = None self.on_disconnect: Any = None self.connect_timeout = 0.0 self.events: deque[tuple[str, object]] = deque() self.connect_calls: list[tuple[str, int, int]] = [] self.subscribe_calls: list[list[tuple[str, int]]] = [] self.publish_calls: list[tuple[str, bytes, int, bool]] = [] self.unsubscribe_calls: list[list[str]] = [] self.next_mid = 20 def connect(self, host: str, port: int, keepalive: int) -> mqtt.MQTTErrorCode: self.connect_calls.append((host, port, keepalive)) self.events.append(("connect", FakeReasonCode())) return mqtt.MQTT_ERR_SUCCESS def subscribe(self, topics: list[tuple[str, int]]) -> tuple[mqtt.MQTTErrorCode, int]: self.subscribe_calls.append(topics) self.events.append(("subscribe", 7)) return mqtt.MQTT_ERR_SUCCESS, 7 def publish( self, topic: str, payload: bytes, qos: int, retain: bool, ) -> SimpleNamespace: self.publish_calls.append((topic, payload, qos, retain)) mid = self.next_mid self.next_mid += 1 self.events.append(("publish", mid)) if topic == "lixel/application/request/device_info": response_topic = "lixel/application/response/device_info" response_payload = _device_info_response() else: fields = {field.number: field.value for field in iter_fields(payload)} raw_path = fields[3] assert isinstance(raw_path, bytes) path = raw_path.decode() response_topic = CALIBRATION_FILE_RESPONSE_TOPIC response_payload = _calibration_response(path, _content_for(path)) self.events.append(("message", (response_topic, response_payload))) return SimpleNamespace(rc=mqtt.MQTT_ERR_SUCCESS, mid=mid) def loop(self, timeout: float) -> mqtt.MQTTErrorCode: del timeout if not self.events: return mqtt.MQTT_ERR_SUCCESS kind, value = self.events.popleft() if kind == "connect": self.on_connect(self, None, SimpleNamespace(), value, None) elif kind == "subscribe": self.on_subscribe( self, None, value, [FakeReasonCode() for _topic in CALIBRATION_READ_SUBSCRIPTIONS], None, ) elif kind == "publish": self.on_publish(self, None, value, FakeReasonCode(), None) elif kind == "message": topic, payload = value self.on_message(self, None, SimpleNamespace(topic=topic, payload=payload)) return mqtt.MQTT_ERR_SUCCESS def unsubscribe(self, topics: list[str]) -> tuple[mqtt.MQTTErrorCode, int]: self.unsubscribe_calls.append(topics) return mqtt.MQTT_ERR_SUCCESS, 50 def disconnect(self) -> mqtt.MQTTErrorCode: return mqtt.MQTT_ERR_SUCCESS def test_reviewed_mqtt_reader_performs_one_identity_read_then_two_exact_file_reads() -> None: client = FakeCalibrationClient() reader = ReviewedCalibrationMqttReader( "10.255.254.54", client_factory=lambda: client, # type: ignore[arg-type] ) result = reader.read_factory_calibration(_authority()) assert result.binding == _binding() assert [item.path for item in result.files] == list(FACTORY_CALIBRATION_PATHS) assert client.subscribe_calls == [list(CALIBRATION_READ_SUBSCRIPTIONS)] assert [topic for topic, _payload, _qos, _retain in client.publish_calls] == [ "lixel/application/request/device_info", CALIBRATION_FILE_REQUEST_TOPIC, CALIBRATION_FILE_REQUEST_TOPIC, ] assert all(qos == 2 and not retain for _topic, _payload, qos, retain in client.publish_calls) assert result.transport["publish_attempts"] == 3 assert result.transport["correlated_responses"] == 3 assert result.transport["write_command_available"] is False def test_private_snapshot_is_new_only_and_hashes_both_exact_artifacts(tmp_path: Path) -> None: files = tuple( decode_factory_calibration_file_response( _calibration_response(path, _content_for(path)), build_factory_calibration_file_read(_authority(), _binding(), path), _authority(), _binding(), ) for path in FACTORY_CALIBRATION_PATHS ) result = FactoryCalibrationReadResult( binding=_binding(), files=(files[0], files[1]), transport={"automatic_retry": False, "publish_attempts": 3}, ) snapshot = seal_factory_calibration_snapshot( result, evidence_root=tmp_path, compatibility_profile_id=PROFILE_ID, captured_at=datetime(2026, 7, 20, 10, 30, tzinfo=UTC), ) assert snapshot["status"] == "available" internal = snapshot["device_internal_calibration"] assert isinstance(internal, dict) snapshot_path = Path(str(internal["private_snapshot_path"])) assert snapshot_path.is_dir() assert (snapshot_path / "camera.yaml").read_bytes() == _content_for( FACTORY_CAMERA_CALIBRATION_PATH ) assert (snapshot_path / "extrinsic_camera_lidar.yaml").read_bytes() == _content_for( FACTORY_CAMERA_LIDAR_EXTRINSIC_PATH ) manifest = json.loads((snapshot_path / "manifest.json").read_text(encoding="utf-8")) assert manifest["source"]["request_command"] == 5 assert manifest["source"]["write_command_available"] is False assert manifest["normalized_calibration"]["transform_notation"] == ( "T_destination_from_source" ) assert [item["source_path"] for item in manifest["artifacts"]] == list( FACTORY_CALIBRATION_PATHS ) assert snapshot_path.stat().st_mode & 0o777 == 0o700 assert all(path.stat().st_mode & 0o777 == 0o600 for path in snapshot_path.iterdir()) assert internal["camera_stream_mapping"]["status"] == "firmware-profile-verified"