Files
NODEDC_MISSION_CORE/tests/fleet/test_vesc_archive.py
T

75 lines
3.1 KiB
Python

import base64
import copy
import hashlib
from datetime import UTC, datetime
from uuid import uuid4
import pytest
from k1link.device_plugins.vesc.archive import Archive
def backup():
uid = "01" * 12
configs = {}
for name, command in (("motor", 14), ("application", 17)):
raw = bytes([command]) + b"test-synthetic-config"
configs[name] = {"encoding": "base64", "payload": base64.b64encode(raw).decode(),
"bytes": len(raw), "sha256": hashlib.sha256(raw).hexdigest(),
"signature_hex": raw[1:5].hex()}
return {"schema": "missioncore.vesc.config-backup/v1", "operation_id": "op_" + uuid4().hex,
"device_id": "vesc_" + hashlib.sha256(("uuid:" + uid).encode()).hexdigest()[:32],
"identity": {"uuid": uid, "version": "6.06"}, "configs": configs,
"observed_at": datetime.now(UTC).isoformat()}
def test_immutable_versions_survive_restart_and_do_not_cross_node_scope(tmp_path):
archive = Archive(tmp_path / "archive")
value = backup()
archive.add("node-a", value)
archive.add("node-a", copy.deepcopy(value))
reopened = Archive(tmp_path / "archive")
assert len(reopened.listing("node-a", value["device_id"])["items"]) == 1
assert reopened.read("node-a", value["device_id"], value["operation_id"]) == value
assert reopened.listing("node-b", value["device_id"])["items"] == []
with pytest.raises(ValueError):
reopened.read("node-b", value["device_id"], value["operation_id"])
changed = copy.deepcopy(value)
changed["observed_at"] = datetime.now(UTC).isoformat()
with pytest.raises(ValueError, match="immutable"):
reopened.add("node-a", changed)
def test_corrupt_or_misattributed_payload_is_never_archived(tmp_path):
archive = Archive(tmp_path / "archive")
value = backup()
value["configs"]["motor"]["sha256"] = "0" * 64
with pytest.raises(ValueError, match="integrity"):
archive.add("a", value)
value = backup()
value["device_id"] = "vesc_" + "0" * 32
with pytest.raises(ValueError, match="mismatch"):
archive.add("a", value)
assert archive.export("a", 0)["items"] == []
def test_bounded_replication_replays_without_losing_versions_and_paginates(tmp_path):
board, core = Archive(tmp_path / "board"), Archive(tmp_path / "core")
values = [backup() for _ in range(53)]
for value in values:
board.add("local", value)
after = 0
while True:
batch = board.export("local", after)
assert len(batch["items"]) <= 4
assert core.receive("paired-node", batch) == batch["next"]
assert core.receive("paired-node", batch) == batch["next"]
if batch["next"] == after:
break
after = batch["next"]
page = core.listing("paired-node", values[0]["device_id"])
assert len(page["items"]) == 50 and page["next"]
older = core.listing("paired-node", values[0]["device_id"], int(page["next"]))
assert len(older["items"]) == 3 and older["next"] is None
assert len({item["id"] for item in page["items"] + older["items"]}) == 53