update
This commit is contained in:
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user