feat(k1): add local connection matrix

This commit is contained in:
DCCONSTRUCTIONS
2026-07-19 09:52:22 +03:00
parent fecb5885d0
commit 57b2208cae
20 changed files with 1405 additions and 115 deletions
+10 -6
View File
@@ -88,12 +88,12 @@ def test_repository_catalog_exposes_xgrids_model() -> None:
item for item in plugins if item["metadata"]["id"] == "nodedc.device.xgrids-lixelkity-k1"
)
assert plugin["apiVersion"] == "missioncore.nodedc/v1alpha2"
assert plugin["metadata"]["version"] == "0.5.0"
assert plugin["metadata"]["version"] == "0.6.0"
assert plugin["spec"]["hostApiRange"] == "v1alpha2"
assert plugin["spec"]["compatibilityProfiles"] == [
{
"profileId": "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1",
"path": "profiles/fw-3.0.2/direct-lan.v1.json",
"profileId": "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2",
"path": "profiles/fw-3.0.2/local-network.v2.json",
"modelId": "xgrids.lixelkity-k1",
}
]
@@ -125,14 +125,14 @@ def test_repository_catalog_exposes_xgrids_model() -> None:
} <= action_ids
assert next(item for item in models if item["id"] == "xgrids.lixelkity-k1") == {
"pluginId": "nodedc.device.xgrids-lixelkity-k1",
"pluginVersion": "0.5.0",
"pluginVersion": "0.6.0",
"id": "xgrids.lixelkity-k1",
"vendor": "XGRIDS",
"displayName": "XGRIDS LixelKity K1",
"category": "Мобильный лидарный сканер",
"description": (
"Проверенный локальный профиль: BLE-настройка Wi-Fi, MQTT-приём, "
"облако точек, поза и raw-first запись."
"Локальный профиль Bridge, Quick Connect и Direct Connect: "
"BLE/CoreWLAN, MQTT, камеры и raw-first запись."
),
"verified": True,
"capabilities": [
@@ -141,6 +141,10 @@ def test_repository_catalog_exposes_xgrids_model() -> None:
"id": "device.provisioning.wifi-over-ble",
"label": "Wi-Fi через BLE",
},
{
"id": "host.network.wifi-associate-local",
"label": "Подключение к точке доступа K1",
},
{"id": "spatial.point-cloud.live", "label": "Облако точек"},
{"id": "spatial.pose.live", "label": "Траектория"},
{"id": "device.modeling.live", "label": "Метрики маршрута"},
+207 -5
View File
@@ -33,6 +33,16 @@ ATTESTATION = CompatibilityAttestationRequest(
topology="direct-lan",
verification="live-device-info",
)
QUICK_CONNECT_ATTESTATION = CompatibilityAttestationRequest(
firmware_version="3.0.2",
topology="device-ap",
verification="live-device-info",
)
DIRECT_CONNECT_ATTESTATION = CompatibilityAttestationRequest(
firmware_version="3.0.2",
topology="controller-hotspot",
verification="live-device-info",
)
PRIMARY_TEST_CREDENTIAL = "x" * 24
SECONDARY_TEST_CREDENTIAL = "y" * 24
PROJECT_NAME = "K1 lifecycle test"
@@ -225,15 +235,48 @@ def test_project_name_is_normalized_and_control_characters_are_rejected() -> Non
host="192.168.1.20",
compatibility_attestation=ATTESTATION,
)
def test_only_physically_accepted_configuration_values_are_admitted() -> None:
def test_connection_modes_require_their_exact_topology_attestation() -> None:
assert ConnectRequest(
device_id="synthetic-device",
ssid="synthetic-network",
password=SecretStr(PRIMARY_TEST_CREDENTIAL),
connection_mode="quick-connect",
compatibility_attestation=QUICK_CONNECT_ATTESTATION,
).connection_mode == "quick-connect"
assert ConnectRequest(
device_id="synthetic-device",
ssid="synthetic-network",
password=SecretStr(PRIMARY_TEST_CREDENTIAL),
connection_mode="direct-connect",
compatibility_attestation=DIRECT_CONNECT_ATTESTATION,
).connection_mode == "direct-connect"
with pytest.raises(ValidationError):
ConnectRequest(
device_id="synthetic-device",
ssid="synthetic-network",
password=SecretStr(PRIMARY_TEST_CREDENTIAL),
connection_mode="quick-connect", # type: ignore[arg-type]
connection_mode="quick-connect",
compatibility_attestation=ATTESTATION,
)
with pytest.raises(ValidationError, match="32 UTF-8 bytes"):
ConnectRequest(
device_id="synthetic-device",
ssid="🛰️" * 9,
password=SecretStr(PRIMARY_TEST_CREDENTIAL),
compatibility_attestation=ATTESTATION,
)
with pytest.raises(ValidationError, match="64 UTF-8 bytes"):
ConnectRequest(
device_id="synthetic-device",
ssid="synthetic-network",
password=SecretStr("🔒" * 17),
compatibility_attestation=ATTESTATION,
)
def test_only_physically_accepted_mount_and_gnss_values_are_admitted() -> None:
with pytest.raises(ValidationError):
PrepareAcquisitionRequest(
project_name=PROJECT_NAME,
@@ -1372,15 +1415,15 @@ def test_exact_profile_is_inactive_until_selected_for_live_device_info_verificat
)
def test_prepare_rejects_device_ap_fallback_as_direct_lan_target(tmp_path: Path) -> None:
def test_prepare_rejects_device_ap_without_completed_quick_connect(tmp_path: Path) -> None:
service, _ = service_with_fake_runtime(tmp_path)
with pytest.raises(ValueError, match="точки доступа"):
with pytest.raises(ValueError, match="connection flow"):
service.prepare_acquisition(
PrepareAcquisitionRequest(
project_name=PROJECT_NAME,
host="192.168.56.1",
compatibility_attestation=ATTESTATION,
compatibility_attestation=QUICK_CONNECT_ATTESTATION,
)
)
@@ -1812,6 +1855,165 @@ def test_network_provisioning_is_single_flight_and_secret_is_unwrapped_only_at_b
assert {item["status"] for item in provision_operations} == {"succeeded", "failed"}
def test_quick_connect_associates_the_host_without_a_ble_write(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
service, _ = service_with_fake_runtime(tmp_path)
service._devices = [{"device_id": "k1-a"}] # noqa: SLF001
association_calls: list[tuple[Path, str, str]] = []
def fake_associate(
helper_path: Path,
ssid: str,
password: str,
**_: object,
) -> dict[str, Any]:
association_calls.append((helper_path, ssid, password))
return {
"schema_version": 1,
"adapter": "CoreWLAN",
"outcome": "associated",
"already_associated": False,
}
async def forbidden_ble_write(*_: object, **__: object) -> dict[str, Any]:
raise AssertionError("Quick Connect must not provision the K1 over BLE")
monkeypatch.setattr(facade_module, "associate_with_wifi_once", fake_associate)
monkeypatch.setattr(facade_module, "provision_wifi_once", forbidden_ble_write)
monkeypatch.setattr(facade_module, "_target_is_local_ipv4", lambda _target: False)
state = asyncio.run(
service.connect(
ConnectRequest(
device_id="k1-a",
ssid="XGR-TEST",
password=SecretStr(PRIMARY_TEST_CREDENTIAL),
connection_mode="quick-connect",
compatibility_attestation=QUICK_CONNECT_ATTESTATION,
)
)
)
assert len(association_calls) == 1
assert association_calls[0][0].name == "associate_wifi.swift"
assert association_calls[0][1:] == ("XGR-TEST", PRIMARY_TEST_CREDENTIAL)
assert state["connection_mode"] == "quick-connect"
assert state["k1_ip"] == "192.168.56.1"
assert state["compatibility"]["attestation"]["topology"] == "device-ap"
quick_sessions = sorted(service.evidence_root.glob("*viewer_k1_ap_association*"))
assert len(quick_sessions) == 1
assert not (quick_sessions[0] / "provisioning.sensitive.json").exists()
redacted_manifest = (quick_sessions[0] / "manifest.redacted.json").read_text(
encoding="utf-8"
)
assert PRIMARY_TEST_CREDENTIAL not in redacted_manifest
assert "XGR-TEST" not in redacted_manifest
assert service._camera_target_for_session( # noqa: SLF001
state["device_session"]["device_session_id"]
) == "192.168.56.1"
prepared = service.prepare_acquisition(
PrepareAcquisitionRequest(
project_name=PROJECT_NAME,
compatibility_attestation=QUICK_CONNECT_ATTESTATION,
)
)
assert prepared["acquisition"]["target_host"] == "192.168.56.1"
def test_direct_connect_reuses_the_reviewed_ble_provisioning_frame(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
service, _ = service_with_fake_runtime(tmp_path)
service._devices = [{"device_id": "k1-a"}] # noqa: SLF001
provisioning_calls: list[tuple[str, str, str]] = []
async def fake_provision(
device_id: str,
ssid: str,
password: str,
**_: object,
) -> dict[str, Any]:
provisioning_calls.append((device_id, ssid, password))
return {
"started_at_utc": "2026-07-19T01:00:00Z",
"completed_at_utc": "2026-07-19T01:00:01Z",
"profile_id": "xgrids-k1-fw3-wifi-v1",
"outcome": "lan_address_observed",
"observations": [{"status": {"ipv4": "172.20.10.2"}}],
}
def forbidden_host_association(*_: object, **__: object) -> dict[str, Any]:
raise AssertionError("Direct Connect must not switch the host Wi-Fi network")
monkeypatch.setattr(facade_module, "provision_wifi_once", fake_provision)
monkeypatch.setattr(
facade_module,
"associate_with_wifi_once",
forbidden_host_association,
)
monkeypatch.setattr(facade_module, "_target_is_local_ipv4", lambda _target: False)
state = asyncio.run(
service.connect(
ConnectRequest(
device_id="k1-a",
ssid="controller-hotspot",
password=SecretStr(PRIMARY_TEST_CREDENTIAL),
connection_mode="direct-connect",
compatibility_attestation=DIRECT_CONNECT_ATTESTATION,
)
)
)
assert provisioning_calls == [
("k1-a", "controller-hotspot", PRIMARY_TEST_CREDENTIAL)
]
assert state["connection_mode"] == "direct-connect"
assert state["k1_ip"] == "172.20.10.2"
assert state["compatibility"]["attestation"]["topology"] == (
"controller-hotspot"
)
def test_failed_connection_change_revokes_the_previous_route(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
service, _ = service_with_fake_runtime(tmp_path)
service._devices = [{"device_id": "k1-a"}] # noqa: SLF001
service._selected_device_id = "previous-k1" # noqa: SLF001
service._k1_ip = "192.168.1.20" # noqa: SLF001
service._connection_mode = "bridge" # noqa: SLF001
def failed_association(*_: object, **__: object) -> dict[str, Any]:
raise RuntimeError("offline association fixture failed")
monkeypatch.setattr(facade_module, "associate_with_wifi_once", failed_association)
with pytest.raises(RuntimeError, match="offline association fixture failed"):
asyncio.run(
service.connect(
ConnectRequest(
device_id="k1-a",
ssid="XGR-OFFLINE",
password=SecretStr(PRIMARY_TEST_CREDENTIAL),
connection_mode="quick-connect",
compatibility_attestation=QUICK_CONNECT_ATTESTATION,
)
)
)
state = service.state()
assert state["selected_device_id"] is None
assert state["k1_ip"] is None
assert state["connection_mode"] is None
assert state["compatibility"]["attestation"] is None
def test_network_provisioning_rejects_an_ipv4_owned_by_the_local_host(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
+9
View File
@@ -275,6 +275,15 @@ def test_acceptance_transport_rejects_the_k1_access_point_fallback() -> None:
ReviewedApplicationMqttTransport("192.168.56.1")
def test_acceptance_transport_admits_the_k1_ap_only_with_an_explicit_gate() -> None:
transport = ReviewedApplicationMqttTransport(
"192.168.56.1",
allow_device_ap=True,
)
assert transport.snapshot().state == "new"
def test_retained_control_subscription_batches_remain_exact_and_separate_from_points() -> None:
assert [len(group) for group in CONTROL_SUBSCRIPTION_GROUPS] == [9, 5, 42]
assert CONTROL_SUBSCRIPTION_GROUPS[0][-1] == (DEVICE_INFO_RESPONSE_TOPIC, 0)
+30 -4
View File
@@ -45,15 +45,29 @@ def _boolean_write_flags(value: Any) -> list[bool]:
def test_xgrids_compatibility_profile_loads_exact_firmware_and_sources() -> None:
profile = LOADER.load_compatibility_profile()
assert profile["profile_id"] == "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1"
assert profile["profile_id"] == "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2"
assert profile["scope"]["vendor"] == "XGRIDS"
assert profile["scope"]["model"] == "LixelKity K1"
assert profile["scope"]["platform_type"] == "A4"
assert profile["scope"]["firmware"] == {"match": "exact", "version": "3.0.2"}
assert profile["scope"]["topology"] == "direct-lan"
assert profile["scope"]["topology"] == "local-network-matrix"
modes = _by_id(profile["scope"]["connection_modes"])
assert {
mode_id: mode["topology"] for mode_id, mode in modes.items()
} == {
"bridge": "direct-lan",
"direct-connect": "controller-hotspot",
"quick-connect": "device-ap",
}
assert LOADER.matches_target(profile, firmware="3.0.2", topology="direct-lan")
assert LOADER.matches_target(profile, firmware="3.0.2", topology="device-ap")
assert LOADER.matches_target(
profile,
firmware="3.0.2",
topology="controller-hotspot",
)
assert not LOADER.matches_target(profile, firmware="3.0.3", topology="direct-lan")
assert not LOADER.matches_target(profile, firmware="3.0.2", topology="device-ap")
assert not LOADER.matches_target(profile, firmware="3.0.2", topology="usb")
for source in profile["evidence_sources"]:
assert (REPOSITORY_ROOT / source["path"]).is_file()
@@ -119,7 +133,7 @@ def test_xgrids_compatibility_profile_declares_only_reviewed_transports() -> Non
assert ble["evidence"]["physical_verified"] is True
assert ble["evidence"]["write_enabled"] is False
mqtt = transports["mqtt.direct-lan.fw3.v1"]
mqtt = transports["mqtt.local-ipv4.fw3.v1"]
assert mqtt["protocol"] == "MQTT 3.1.1"
assert mqtt["network"]["transport"] == "TCP"
assert mqtt["network"]["port"] == 1883
@@ -205,6 +219,18 @@ def test_xgrids_compatibility_profile_rejects_vendor_write_promotion() -> None:
LOADER.validate_compatibility_profile(modified)
def test_xgrids_compatibility_profile_rejects_connection_matrix_drift() -> None:
profile = LOADER.load_compatibility_profile()
modified = copy.deepcopy(profile)
modes = _by_id(modified["scope"]["connection_modes"])
modes["quick-connect"]["device_network_action"] = (
"unreviewed-ble-credential-read"
)
with pytest.raises(LOADER.CompatibilityProfileError, match="differs"):
LOADER.validate_compatibility_profile(modified)
def test_xgrids_compatibility_profile_rejects_noncanonical_modeling_header() -> None:
profile = LOADER.load_compatibility_profile()
modified = copy.deepcopy(profile)
+108
View File
@@ -0,0 +1,108 @@
from __future__ import annotations
import json
import subprocess
from pathlib import Path
import pytest
from k1link.device_plugins.xgrids_k1 import macos_wifi
TEST_PASSWORD = "fixture-only-network-secret"
def _helper(tmp_path: Path) -> Path:
helper = tmp_path / "associate_wifi.swift"
helper.write_text("// offline fixture\n", encoding="utf-8")
return helper
def test_association_passes_secret_only_through_stdin(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
monkeypatch.setattr(macos_wifi.sys, "platform", "darwin")
calls: list[dict[str, object]] = []
live_inputs: list[bytearray] = []
def fake_runner(argv: list[str], **kwargs: object) -> subprocess.CompletedProcess[bytes]:
assert isinstance(kwargs["input"], bytearray)
live_inputs.append(kwargs["input"])
calls.append(
{
"argv": argv,
**kwargs,
"input": bytes(kwargs["input"]),
}
)
return subprocess.CompletedProcess(
argv,
0,
stdout=b'{"ok":true,"already_associated":false}',
stderr=b"",
)
result = macos_wifi.associate_with_wifi_once(
_helper(tmp_path),
"XGR-OFFLINE",
TEST_PASSWORD,
runner=fake_runner,
)
assert result == {
"schema_version": 1,
"adapter": "CoreWLAN",
"outcome": "associated",
"already_associated": False,
}
assert len(calls) == 1
call = calls[0]
assert call["argv"][:2] == ["/usr/bin/xcrun", "swift"]
assert TEST_PASSWORD not in " ".join(call["argv"])
request = json.loads(bytes(call["input"]).decode("utf-8"))
assert request == {"ssid": "XGR-OFFLINE", "password": TEST_PASSWORD}
assert call["check"] is False
assert call["timeout"] == 45.0
assert live_inputs and not any(live_inputs[0])
def test_association_reports_only_sanitized_helper_reason(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
monkeypatch.setattr(macos_wifi.sys, "platform", "darwin")
def fake_runner(argv: list[str], **_: object) -> subprocess.CompletedProcess[bytes]:
return subprocess.CompletedProcess(
argv,
1,
stdout=b'{"ok":false,"reason_code":"network-not-found"}',
stderr=f"private diagnostic {TEST_PASSWORD}".encode(),
)
with pytest.raises(
macos_wifi.HostWifiAssociationError,
match="network-not-found",
) as raised:
macos_wifi.associate_with_wifi_once(
_helper(tmp_path),
"XGR-OFFLINE",
TEST_PASSWORD,
runner=fake_runner,
)
assert TEST_PASSWORD not in str(raised.value)
def test_association_is_macos_only(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(macos_wifi.sys, "platform", "linux")
with pytest.raises(
macos_wifi.HostWifiAssociationError,
match="unsupported-platform",
):
macos_wifi.associate_with_wifi_once(
_helper(tmp_path),
"XGR-OFFLINE",
TEST_PASSWORD,
)