feat: add seat firmware workbench and OTA delivery
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import threading
|
||||
import urllib.request
|
||||
from http.server import ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
|
||||
from cognitive_agent.firmware_ota_server import FirmwareOtaHandler, FirmwareOtaRepository
|
||||
|
||||
|
||||
class TestFirmwareOtaServer:
|
||||
def test_latest_download_head_and_resume_are_read_only(self, tmp_path: Path) -> None:
|
||||
release_id = "fw-rel-123456789abc"
|
||||
filename = "PassengerSeat_test.zip"
|
||||
package = (b"vehicle-firmware-package" * 128) + b"end"
|
||||
release_root = tmp_path / "releases" / release_id
|
||||
release_root.mkdir(parents=True)
|
||||
package_path = release_root / filename
|
||||
package_path.write_bytes(package)
|
||||
manifest = {
|
||||
"schema": "n7-ota-release/v1",
|
||||
"release_id": release_id,
|
||||
"firmware_filename": filename,
|
||||
"size_bytes": len(package),
|
||||
"sha256": hashlib.sha256(package).hexdigest(),
|
||||
"capabilities": ["时间控制", "座椅加热"],
|
||||
"published_at": "2026-09-16T13:17:17+08:00",
|
||||
"status": "published",
|
||||
"download_url": "http://stale-address.invalid/download",
|
||||
}
|
||||
(release_root / "release.json").write_text(
|
||||
json.dumps(manifest, ensure_ascii=False), encoding="utf-8"
|
||||
)
|
||||
(tmp_path / "latest.json").write_text(
|
||||
json.dumps(manifest, ensure_ascii=False), encoding="utf-8"
|
||||
)
|
||||
older_release_id = "fw-rel-000000000001"
|
||||
older_root = tmp_path / "releases" / older_release_id
|
||||
older_root.mkdir(parents=True)
|
||||
older_filename = "PassengerSeat_older.zip"
|
||||
older_package = b"older-firmware-package"
|
||||
(older_root / older_filename).write_bytes(older_package)
|
||||
older_manifest = {
|
||||
**manifest,
|
||||
"release_id": older_release_id,
|
||||
"firmware_filename": older_filename,
|
||||
"size_bytes": len(older_package),
|
||||
"sha256": hashlib.sha256(older_package).hexdigest(),
|
||||
"published_at": "2026-09-15T13:17:17+08:00",
|
||||
}
|
||||
(older_root / "release.json").write_text(
|
||||
json.dumps(older_manifest, ensure_ascii=False), encoding="utf-8"
|
||||
)
|
||||
|
||||
class Handler(FirmwareOtaHandler):
|
||||
repository = FirmwareOtaRepository(
|
||||
tmp_path, "https://n7-ota.example.ts.net/api/firmware-workbench/releases"
|
||||
)
|
||||
|
||||
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
base_url = f"http://127.0.0.1:{server.server_port}"
|
||||
try:
|
||||
with urllib.request.urlopen(
|
||||
f"{base_url}/api/firmware-workbench/releases/latest", timeout=3
|
||||
) as response:
|
||||
latest = json.loads(response.read().decode("utf-8"))
|
||||
assert latest["download_url"] == (
|
||||
f"https://n7-ota.example.ts.net/api/firmware-workbench/releases/"
|
||||
f"{release_id}/download"
|
||||
)
|
||||
|
||||
with urllib.request.urlopen(
|
||||
f"{base_url}/api/firmware-workbench/releases", timeout=3
|
||||
) as response:
|
||||
releases = json.loads(response.read().decode("utf-8"))
|
||||
assert releases["schema"] == "n7-ota-release-list/v1"
|
||||
assert releases["count"] == 2
|
||||
assert releases["latest_release_id"] == release_id
|
||||
assert [item["release_id"] for item in releases["releases"]] == [
|
||||
release_id,
|
||||
older_release_id,
|
||||
]
|
||||
assert releases["releases"][1]["download_url"].endswith(
|
||||
f"/{older_release_id}/download"
|
||||
)
|
||||
|
||||
request = urllib.request.Request(
|
||||
f"{base_url}/api/firmware-workbench/releases/{release_id}/download",
|
||||
headers={"Range": "bytes=8-31"},
|
||||
)
|
||||
with urllib.request.urlopen(request, timeout=3) as response:
|
||||
assert response.status == 206
|
||||
assert response.headers["Content-Range"] == f"bytes 8-31/{len(package)}"
|
||||
assert response.headers["X-Firmware-SHA256"] == manifest["sha256"]
|
||||
assert response.read() == package[8:32]
|
||||
|
||||
head = urllib.request.Request(
|
||||
f"{base_url}/api/firmware-workbench/releases/{release_id}/download",
|
||||
method="HEAD",
|
||||
)
|
||||
with urllib.request.urlopen(head, timeout=3) as response:
|
||||
assert response.status == 200
|
||||
assert int(response.headers["Content-Length"]) == len(package)
|
||||
assert response.read() == b""
|
||||
finally:
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
thread.join(timeout=3)
|
||||
Reference in New Issue
Block a user