feat(fleet): add board observation center with live sources and maps
Add adaptive per-vehicle layouts, full-panel dragging, source controls and automatic preview recovery for RealSense, Insta360 X4 and XGRIDS K1 observation views. Integrate cached Cesium map layers through the private NAS gateway link, retain bounded sensor command receipts, and document validation and operational handoff.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import copy
|
||||
import threading
|
||||
import time
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from k1link.fleet import sensors
|
||||
from k1link.fleet.trust import PairingError
|
||||
|
||||
|
||||
class Fleet:
|
||||
def __init__(self):
|
||||
self.lock = threading.RLock()
|
||||
self.row = {"sensor_state": {"items": [{"id": "synthetic-camera", "snapshot": {"context": {"session_id": "synthetic-session"}}}]}}
|
||||
|
||||
def find(self, _):
|
||||
return self.row
|
||||
|
||||
def public(self, _):
|
||||
return {"connectivity": "online"}
|
||||
|
||||
def save(self, _):
|
||||
pass
|
||||
|
||||
|
||||
def request():
|
||||
now = datetime.now(UTC)
|
||||
identifier = "op_" + uuid4().hex
|
||||
return {"api_version": "missioncore.nodedc/plugin-sdk/v0alpha2", "kind": "OperationRequest", "operation_id": identifier, "idempotency_key": identifier, "session": {"device_id": "synthetic-camera", "session_id": "synthetic-session"}, "requested_at": now.isoformat(), "deadline_at": (now + timedelta(seconds=60)).isoformat(), "action_id": "preview.stop", "parameters": {}}
|
||||
|
||||
|
||||
def test_completed_viewing_receipts_do_not_block_stop_and_remain_idempotent():
|
||||
fleet = Fleet()
|
||||
for _ in range(32):
|
||||
value = request()
|
||||
receipt = sensors.submit(fleet, "vehicle", value)
|
||||
receipt["state"] = "complete"
|
||||
assert sensors.submit(fleet, "vehicle", value) is receipt
|
||||
assert sensors.submit(fleet, "vehicle", request())["state"] == "queued"
|
||||
|
||||
|
||||
def test_pending_commands_still_have_the_original_32_slot_limit():
|
||||
fleet = Fleet()
|
||||
for _ in range(32):
|
||||
sensors.submit(fleet, "vehicle", request())
|
||||
with pytest.raises(PairingError, match="Слишком много"):
|
||||
sensors.submit(fleet, "vehicle", request())
|
||||
|
||||
|
||||
def test_receipt_storage_is_bounded_and_only_expired_terminal_receipts_are_reclaimable():
|
||||
fleet = Fleet()
|
||||
for _ in range(sensors.MAX_COMMAND_RECEIPTS):
|
||||
value = request()
|
||||
receipt = sensors.submit(fleet, "vehicle", value)
|
||||
receipt["state"] = "complete"
|
||||
with pytest.raises(PairingError, match="Слишком много"):
|
||||
sensors.submit(fleet, "vehicle", request())
|
||||
expired = next(iter(fleet.row["sensor_commands"].values()))
|
||||
expired["command"]["requested_at"] = (datetime.now(UTC) - timedelta(seconds=61)).isoformat()
|
||||
expired["command"]["deadline_at"] = (datetime.now(UTC) - timedelta(seconds=1)).isoformat()
|
||||
expired["updated_at"] = time.time() - 60
|
||||
replay = copy.deepcopy(expired["command"])
|
||||
sensors.submit(fleet, "vehicle", request())
|
||||
assert len(fleet.row["sensor_commands"]) == sensors.MAX_COMMAND_RECEIPTS
|
||||
with pytest.raises(PairingError, match="Срок команды"):
|
||||
sensors.submit(fleet, "vehicle", replay)
|
||||
@@ -0,0 +1,60 @@
|
||||
import importlib.util
|
||||
import plistlib
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
SCRIPT = Path(__file__).parents[1] / "scripts/manage_map_gateway_link.py"
|
||||
spec = importlib.util.spec_from_file_location("map_gateway_link", SCRIPT)
|
||||
assert spec and spec.loader
|
||||
link = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(link)
|
||||
|
||||
|
||||
def test_link_is_local_only_and_cannot_execute_remote_commands(tmp_path):
|
||||
value = plistlib.loads(link.payload("operator@nas.example", tmp_path))
|
||||
argv = value["ProgramArguments"]
|
||||
assert argv[-3:] == ["-L", "127.0.0.1:18103:127.0.0.1:18103", "operator@nas.example"]
|
||||
for flag in ("-N", "-T", "StrictHostKeyChecking=yes", "BatchMode=yes",
|
||||
"ExitOnForwardFailure=yes", "ForwardAgent=no", "PermitLocalCommand=no"):
|
||||
assert flag in argv
|
||||
assert "-R" not in argv and "-D" not in argv
|
||||
assert value["KeepAlive"] and value["ThrottleInterval"] == 30
|
||||
for target in ("-oProxyCommand=evil", "operator@host;touch x", "operator@host\ncommand"):
|
||||
with pytest.raises(ValueError):
|
||||
link.payload(target, tmp_path)
|
||||
|
||||
|
||||
def test_stale_plan_cannot_change_runtime(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(link.Path, "home", lambda: tmp_path)
|
||||
run = Mock()
|
||||
monkeypatch.setattr(link.subprocess, "run", run)
|
||||
monkeypatch.setattr("sys.argv", [str(SCRIPT), "apply", "--target", "operator@nas.example",
|
||||
"--expected-current-sha256", "absent",
|
||||
"--expected-desired-sha256", "stale"])
|
||||
with pytest.raises(ValueError, match="plan changed"):
|
||||
link.main()
|
||||
run.assert_not_called()
|
||||
assert not (tmp_path / "Library").exists()
|
||||
|
||||
|
||||
def test_failed_first_install_removes_candidate_agent(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(link.Path, "home", lambda: tmp_path)
|
||||
runtime = tmp_path / "Library/Logs/NODEDC/MissionCore/map-gateway-link"
|
||||
expected = link.digest(link.payload("operator@nas.example", runtime))
|
||||
monkeypatch.setattr("sys.argv", [str(SCRIPT), "apply", "--target", "operator@nas.example",
|
||||
"--expected-current-sha256", "absent",
|
||||
"--expected-desired-sha256", expected])
|
||||
monkeypatch.setattr(link, "loaded", lambda: False)
|
||||
unload = Mock()
|
||||
monkeypatch.setattr(link, "unload", unload)
|
||||
socket = Mock()
|
||||
socket.__enter__ = Mock(return_value=Mock())
|
||||
socket.__exit__ = Mock(return_value=False)
|
||||
monkeypatch.setattr(link.socket, "socket", lambda: socket)
|
||||
monkeypatch.setattr(link.subprocess, "run", Mock(side_effect=RuntimeError("bootstrap failed")))
|
||||
with pytest.raises(RuntimeError, match="bootstrap failed"):
|
||||
link.main()
|
||||
unload.assert_called_once()
|
||||
assert not (tmp_path / "Library/LaunchAgents" / f"{link.LABEL}.plist").exists()
|
||||
Reference in New Issue
Block a user