feat(k1): complete canonical control lifecycle

This commit is contained in:
DCCONSTRUCTIONS
2026-07-19 01:07:02 +03:00
parent d7a2c22faf
commit ffffee1879
42 changed files with 3577 additions and 556 deletions
+94 -26
View File
@@ -1,5 +1,8 @@
from __future__ import annotations
import json
from pathlib import Path
import pytest
from k1link.device_plugins.xgrids_k1.protocol.application_bootstrap import (
@@ -16,6 +19,14 @@ 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"
CAPTURE_FACTS = json.loads(
(
Path(__file__).parent
/ "fixtures"
/ "xgrids_k1"
/ "device_info_fw3_0_2_semantics.json"
).read_text(encoding="utf-8")
)
def _varint(value: int) -> bytes:
@@ -47,11 +58,11 @@ def _binding(**changes: object) -> LiveDeviceControlBinding:
values = {
"vendor_device_id": VENDOR_DEVICE_ID,
"device_serial": "K1SERIAL01",
"software_version": "V3.0.2-20260101-release",
"system_version": "V3.0.2",
"device_model": "LixelKity K1",
"device_type": "K1",
"is_activated": True,
"software_version": CAPTURE_FACTS["software_version"],
"system_version": CAPTURE_FACTS["system_version"],
"device_model": CAPTURE_FACTS["device_model"],
"device_type": CAPTURE_FACTS["platform_type"],
"is_activated": CAPTURE_FACTS["is_activated"],
}
values.update(changes)
return LiveDeviceControlBinding(**values) # type: ignore[arg-type]
@@ -62,13 +73,13 @@ def _device_info_response(*, session_id: str = ":DeviceInfoRequest") -> bytes:
base_info = b"".join(
(
_text(1, "2026-01-01T00:00:00"),
_text(2, "V3.0.2-20260101-release"),
_text(3, "V3.0.2"),
_text(2, CAPTURE_FACTS["software_version"]),
_text(3, CAPTURE_FACTS["system_version"]),
_text(4, "V1.2.3"),
_text(5, "V1.0"),
_text(6, "LixelKity K1"),
_text(6, CAPTURE_FACTS["device_model"]),
_text(7, "K1SERIAL01"),
_text(8, "K1"),
_text(8, CAPTURE_FACTS["platform_type"]),
)
)
working_status = _uint(1, 1) + _uint(2, 0)
@@ -149,6 +160,27 @@ def test_bootstrap_headers_switch_from_unbound_to_live_device_identity() -> None
)
def test_device_config_timestamp_uses_captured_sint64_zigzag_wire_semantics() -> None:
captured_epoch_seconds = 1_784_215_030
plan = build_shadow_bootstrap(
_authority(),
_binding(),
epoch_seconds=captured_epoch_seconds,
timezone_name="Europe/Moscow",
)
config_request = plan.requests[3]
top_fields = {field.number: field.value for field in iter_fields(config_request.payload)}
time_config = top_fields[4]
assert isinstance(time_config, bytes)
time_fields = {field.number: field.value for field in iter_fields(time_config)}
assert time_fields == {
1: 3_568_430_060,
2: b"Europe/Moscow",
}
def test_canonical_post_start_observation_preserves_retained_operations_12_to_14() -> None:
plan = build_canonical_post_start_observation(_authority(), _binding())
@@ -169,6 +201,7 @@ def test_device_info_response_produces_live_binding_without_a_saved_device_profi
assert response.result_code == OPENAPI_SUCCESS
assert response.binding.ready_for_reviewed_profile
assert response.binding.matches_reviewed_device
assert response.binding.matches_reviewed_firmware
assert response.binding.is_activated
assert VENDOR_DEVICE_ID not in repr(response)
@@ -181,7 +214,6 @@ def test_device_info_response_correlation_and_profile_attestation_fail_closed()
_device_info_response(session_id=":AnotherRequest"),
_authority(),
)
with pytest.raises(ApplicationBootstrapError, match="does not attest"):
build_shadow_bootstrap(
_authority(),
@@ -191,6 +223,37 @@ def test_device_info_response_correlation_and_profile_attestation_fail_closed()
)
@pytest.mark.parametrize(
("device_model", "device_type"),
(
("lixelkity K1", "A4"),
("LixelKity K1 ", "A4"),
("LixelKity K11", "A4"),
("LixelKity K1", "a4"),
("LixelKity K1", " A4"),
("LixelKity K1", "K1"),
),
)
def test_reviewed_profile_rejects_non_exact_device_model_and_type(
device_model: str,
device_type: str,
) -> None:
binding = _binding(device_model=device_model, device_type=device_type)
assert binding.matches_reviewed_firmware
assert not binding.matches_reviewed_device
assert not binding.ready_for_reviewed_profile
with pytest.raises(ApplicationBootstrapError, match="does not attest"):
build_shadow_bootstrap(
_authority(),
binding,
epoch_seconds=1_752_680_000,
timezone_name="Europe/Moscow",
)
with pytest.raises(ApplicationBootstrapError, match="does not attest"):
build_canonical_post_start_observation(_authority(), binding)
def test_response_barrier_orchestrator_emits_each_retained_batch_once() -> None:
orchestrator = ShadowApplicationBootstrapOrchestrator(
_authority(),
@@ -202,35 +265,37 @@ def test_response_barrier_orchestrator_emits_each_retained_batch_once() -> None:
assert [request.ordinal for request in identity] == [1]
with pytest.raises(ApplicationBootstrapError, match="already issued"):
orchestrator.next_batch()
orchestrator.accept_response(identity[0].response_topic, _device_info_response())
initial_reads = orchestrator.next_batch()
assert [request.ordinal for request in initial_reads] == [2, 3]
assert [request.response_required for request in initial_reads] == [False, True]
orchestrator.accept_response(
initial_reads[1].response_topic,
_generic_response(initial_reads[1].session_id),
"bootstrap:1:DeviceInfoRequest",
_device_info_response(),
)
preparation = orchestrator.next_batch()
assert [request.ordinal for request in preparation] == [4, 5, 6]
device_info = preparation[1]
initial_reads = orchestrator.next_batch()
assert [request.ordinal for request in initial_reads] == [2, 3, 4, 5, 6]
assert [request.response_required for request in initial_reads] == [
False,
True,
True,
True,
True,
]
device_info = initial_reads[3]
orchestrator.accept_response(
device_info.response_topic,
"bootstrap:5:DeviceInfoRequest",
_device_info_response(session_id=device_info.session_id),
)
with pytest.raises(ApplicationBootstrapError, match="already issued"):
orchestrator.next_batch()
for request in (preparation[2], preparation[0]):
for request in (initial_reads[4], initial_reads[1], initial_reads[2]):
orchestrator.accept_response(
request.response_topic,
f"bootstrap:{request.ordinal}:{request.message_type}",
_generic_response(request.session_id),
)
ntrip = orchestrator.next_batch()
assert [request.ordinal for request in ntrip] == [7]
orchestrator.accept_response(
ntrip[0].response_topic,
"bootstrap:7:GetNtripProfileRequest",
_generic_response(ntrip[0].session_id),
)
@@ -242,7 +307,10 @@ def test_response_barrier_orchestrator_emits_each_retained_batch_once() -> None:
if request.message_type == "DeviceInfoRequest"
else _generic_response(request.session_id)
)
orchestrator.accept_response(request.response_topic, payload)
orchestrator.accept_response(
f"bootstrap:{request.ordinal}:{request.message_type}",
payload,
)
snapshot = orchestrator.snapshot()
assert snapshot.bootstrap_complete
@@ -264,7 +332,7 @@ def test_orchestrator_rejects_unexpected_response_without_advancing() -> None:
with pytest.raises(ApplicationBootstrapError, match="not required"):
orchestrator.accept_response(
"lixel/application/response/get_rtk_advance",
"bootstrap:3:GetRtkAdvanceRequest",
_generic_response(":GetRtkAdvanceRequest"),
)