This commit is contained in:
vee
2026-08-14 15:01:29 +08:00
parent 6d768aaef4
commit f77dda70fe
20 changed files with 1125 additions and 122 deletions
+9 -9
View File
@@ -177,7 +177,7 @@ operations:
def test_static_console_and_runtime_endpoint(self) -> None:
with self.opener.open(f"{self.base_url}/", timeout=3) as response:
html = response.read().decode("utf-8")
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-81", timeout=3) as response:
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-82", timeout=3) as response:
app_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/gesture-control.js?v=android-pad-7", timeout=3
@@ -192,11 +192,11 @@ operations:
) as response:
console_styles = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/self-extension.js?v=extension-20", timeout=3
f"{self.base_url}/self-extension.js?v=extension-21", timeout=3
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/self-extension.css?v=extension-18", timeout=3
f"{self.base_url}/self-extension.css?v=extension-19", timeout=3
) as response:
extension_styles = response.read().decode("utf-8").replace("\r\n", "\n")
with self.opener.open(
@@ -223,14 +223,14 @@ operations:
self.assertIn("CMD 0x431 · FB 0x41A · CH1", html)
self.assertIn("CMD 0x431 · FB 0x388 · CH1", html)
self.assertNotIn("透明车体 · 实体设备", html)
self.assertIn('src="/app.js?v=dual-seat-81"', html)
self.assertIn('src="/app.js?v=dual-seat-82"', html)
self.assertIn('href="/styles.css?v=dual-seat-9"', html)
self.assertIn('src="/gesture-control.js?v=android-pad-7"', html)
self.assertNotIn('class="vehicle-system-hud"', html)
self.assertIn('src="/console-v2.js?v=desktop-refactor-35"', html)
self.assertIn('href="/console-v2.css?v=desktop-refactor-106"', html)
self.assertIn('src="/self-extension.js?v=extension-20"', html)
self.assertIn('href="/self-extension.css?v=extension-18"', html)
self.assertIn('src="/self-extension.js?v=extension-21"', html)
self.assertIn('href="/self-extension.css?v=extension-19"', html)
self.assertNotIn("滚动查看更多", html)
self.assertIn('aria-label="执行车舱控制指令"', html)
self.assertIn('sessionStorage.getItem("cognitive-agent.demo-role")', console_script)
@@ -455,7 +455,7 @@ operations:
self.assertIn("window.requestAnimationFrame(initSeatTwin)", app_script)
self.assertIn('canvas.addEventListener("webglcontextlost"', app_script)
self.assertIn('"cognitive-agent:module-change"', app_script)
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v117"', service_worker)
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v119"', service_worker)
self.assertIn(
'"/assets/models/n7_seat.glb?v=e7f50f03"', service_worker
)
@@ -664,11 +664,11 @@ operations:
def test_self_extension_vehicle_success_contract_is_strict(self) -> None:
with self.opener.open(
f"{self.base_url}/self-extension.js?v=extension-20", timeout=3
f"{self.base_url}/self-extension.js?v=extension-21", timeout=3
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/app.js?v=dual-seat-81", timeout=3
f"{self.base_url}/app.js?v=dual-seat-82", timeout=3
) as response:
app_script = response.read().decode("utf-8")
+12 -4
View File
@@ -122,16 +122,24 @@ class TestSelfCalibrationApi:
stopped = json.loads(response.read().decode("utf-8"))["state"]
assert not stopped["controller"]["armed"]
with urllib.request.urlopen(
self.request("/api/calibration/session/reset", method="POST", payload={}),
timeout=3,
) as response:
reset = json.loads(response.read().decode("utf-8"))["state"]
assert reset["transport"]["state"] == "disconnected"
assert [axis["permille"] for axis in reset["axes"]] == [0, 0, 0, 0]
def test_frontend_and_original_model_are_served_by_existing_service(self) -> None:
with urllib.request.urlopen(f"{self.base_url}/", timeout=3) as response:
html = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/self-calibration.js?v=calibration-6",
f"{self.base_url}/self-calibration.js?v=calibration-8",
timeout=3,
) as response:
script = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/self-calibration.css?v=calibration-10",
f"{self.base_url}/self-calibration.css?v=calibration-12",
timeout=3,
) as response:
styles = response.read().decode("utf-8")
@@ -160,8 +168,8 @@ class TestSelfCalibrationApi:
timeout=3,
)
assert 'href="/self-calibration.css?v=calibration-10"' in html
assert 'src="/self-calibration.js?v=calibration-6"' in html
assert 'href="/self-calibration.css?v=calibration-12"' in html
assert 'src="/self-calibration.js?v=calibration-8"' in html
assert 'data-calibration-page="live"' in script
assert 'data-calibration-view="live"' in script
assert 'data-calibration-page="hall"' not in script
+4 -4
View File
@@ -146,7 +146,7 @@ class TestSelfRepairApi:
with urllib.request.urlopen(f"{self.base_url}/", timeout=3) as response:
html = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/self-repair.js?v=repair-20", timeout=3
f"{self.base_url}/self-repair.js?v=repair-21", timeout=3
) as response:
script = response.read().decode("utf-8").replace("\r\n", "\n")
with urllib.request.urlopen(
@@ -154,7 +154,7 @@ class TestSelfRepairApi:
) as response:
styles = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/self-repair-seat3d.js?v=repair-19", timeout=3
f"{self.base_url}/self-repair-seat3d.js?v=repair-21", timeout=3
) as response:
seat3d = response.read().decode("utf-8")
with urllib.request.urlopen(
@@ -163,12 +163,12 @@ class TestSelfRepairApi:
console_script = response.read().decode("utf-8")
assert 'href="/self-repair.css?v=repair-21"' in html
assert 'src="/self-repair.js?v=repair-20"' in html
assert 'src="/self-repair.js?v=repair-21"' in html
assert 'class="v2-module-heading"' in script
assert 'SELF-REPAIR' in script
assert 'padding: var(--v2-module-title-block) 0 0' in styles
assert 'margin-inline: var(--v2-module-title-inline)' in styles
assert 'src="/self-repair-seat3d.js?v=repair-19"' in html
assert 'src="/self-repair-seat3d.js?v=repair-21"' in html
assert "量产故障注入" in script
assert "运行与自修复日志" in script
assert "启动座椅运行" in script
+46
View File
@@ -356,6 +356,52 @@ operations:
[{"name": "transmittance_pct", "value": 20}],
)
def test_session_overlay_ignores_stale_repository_user_modes(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory) / "profiles"
session_root = Path(directory) / "session"
stale = root / "vehicles" / "denza_n7" / "user" / "stale.yaml"
stale.parent.mkdir(parents=True)
stale.write_text(
"""
id: stale
name: 上次运行模式
scope:
vehicle_ids: [denza_n7]
operations:
- domain: roof
capability: roof.set_transmittance
target_scope: vehicle
parameters:
transmittance_pct: 20
""".strip() + "\n",
encoding="utf-8",
)
registry = ModeProfileRegistry(root, user_root=session_root)
self.assertEqual(registry.public_modes("denza_n7"), [])
created = registry.save_mode(
"本次运行模式",
[
{
"domain": "roof",
"capability": "roof.set_transmittance",
"target_scope": "vehicle",
"parameters": {"transmittance_pct": 35},
}
],
vehicle_id="denza_n7",
)
self.assertTrue(Path(created.source).is_relative_to(session_root))
self.assertEqual(
{mode["id"] for mode in registry.public_modes("denza_n7")},
{created.id},
)
registry.delete_user_mode(created.id, "denza_n7")
self.assertFalse(Path(created.source).exists())
self.assertEqual(registry.public_modes("denza_n7"), [])
def test_delete_user_mode_removes_yaml_and_protects_presets(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
@@ -0,0 +1,57 @@
from __future__ import annotations
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def source(name: str) -> str:
return (ROOT / "apps" / "web-console" / name).read_text(encoding="utf-8")
def test_calibration_twin_tracks_labels_and_commits_direct_axis_drag() -> None:
calibration = source("self-calibration.js")
seat3d = source("self-repair-seat3d.js")
styles = source("self-calibration.css")
assert 'data-seat-axis-label="backrest"' in calibration
assert 'data-seat-label-line="legrest"' in calibration
assert 'window.addEventListener("calibration-seat-axis-preview"' in calibration
assert 'window.addEventListener("calibration-seat-axis-commit"' in calibration
assert "function layoutSeatLabels()" in seat3d
assert "makePanel(legrestPivot, cushionShape(0.08)" in seat3d
assert "function legrestShape(" not in seat3d
assert 'enableAxisDrag: true' in seat3d
assert 'function startAxisDrag(axis, event, source = "model")' in seat3d
assert 'startAxisDrag(item.axis, event, "label")' in seat3d
assert "calculateAxisDragVector(axis, value)" in seat3d
assert "Math.max(0, Math.min(100," in seat3d
assert 'new CustomEvent("calibration-seat-axis-commit"' in seat3d
assert ".v2-calibration-label-lines line" in styles
assert '.v2-axis-tag[data-control-enabled="true"]' in styles
assert "pointer-events: auto;" in styles
def test_extension_pet_and_transient_vehicle_state_restart_contract() -> None:
extension = source("self-extension.js")
styles = source("self-extension.css")
assert 'id="extensionDesktopPet"' in extension
assert "function initializeDesktopPet()" in extension
assert "saved.vehicleExecutions = {};" in extension
assert "saved.lastProtocol = null;" in extension
assert ".v2-extension-pet-body" in styles
assert "@keyframes extension-pet-blink" in styles
def test_virtual_modules_reset_but_physical_sessions_remain_authoritative() -> None:
orchestration = source("app.js")
calibration = source("self-calibration.js")
repair = source("self-repair.js")
assert 'sessionStorage.removeItem("cognitive-agent.conversation-id")' in orchestration
assert 'api("/session/reset")' in calibration
assert 'if (state.transport?.mode === "simulation")' in calibration
assert 'if (backendMode === "bench")' in repair
assert 'await apiPost("/runtime/reset-statistics")' in repair
+28 -1
View File
@@ -7,7 +7,7 @@ from pathlib import Path
import pytest
from cognitive_agent.application.calibration import protocol
from cognitive_agent.application.calibration.service import CalibrationService
from cognitive_agent.application.calibration.service import ApiError, CalibrationService
def test_protocol_matches_android_golden_vectors() -> None:
@@ -89,3 +89,30 @@ def test_simulation_uses_real_wire_boundary_and_generates_pose() -> None:
assert not stopped["controller"]["armed"]
finally:
service.close()
def test_virtual_session_restart_resets_axes_without_touching_hardware() -> None:
with tempfile.TemporaryDirectory() as directory:
service = CalibrationService(Path(directory))
service.start()
try:
service.connect_can({"mode": "simulation"})
service.move(1, 720)
time.sleep(0.15)
reset = service.reset_virtual_session()
assert reset["transport"]["mode"] == "simulation"
assert reset["transport"]["state"] == "disconnected"
assert reset["controller"]["armed"] is False
assert [axis["permille"] for axis in reset["axes"]] == [0, 0, 0, 0]
assert reset["operation"]["phase"] == protocol.PHASE_IDLE
finally:
service.close()
def test_virtual_session_restart_rejects_real_bench_state() -> None:
with tempfile.TemporaryDirectory() as directory:
service = CalibrationService(Path(directory))
with pytest.raises(ApiError, match="真实台架会话"):
service.reset_virtual_session()