feat: refine vehicle console and self-repair workflow
This commit is contained in:
+118
-16
@@ -177,28 +177,28 @@ 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-67", timeout=3) as response:
|
||||
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-80", 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
|
||||
) as response:
|
||||
gesture_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/console-v2.js?v=desktop-refactor-28", timeout=3
|
||||
f"{self.base_url}/console-v2.js?v=desktop-refactor-34", timeout=3
|
||||
) as response:
|
||||
console_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/console-v2.css?v=desktop-refactor-87", timeout=3
|
||||
f"{self.base_url}/console-v2.css?v=desktop-refactor-106", timeout=3
|
||||
) as response:
|
||||
console_styles = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-extension.js?v=extension-18", timeout=3
|
||||
f"{self.base_url}/self-extension.js?v=extension-20", timeout=3
|
||||
) as response:
|
||||
extension_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/self-extension.css?v=extension-12", timeout=3
|
||||
f"{self.base_url}/self-extension.css?v=extension-18", timeout=3
|
||||
) as response:
|
||||
extension_styles = response.read().decode("utf-8")
|
||||
extension_styles = response.read().decode("utf-8").replace("\r\n", "\n")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/api/extension/health", timeout=3
|
||||
) as response:
|
||||
@@ -219,22 +219,39 @@ 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-67"', html)
|
||||
self.assertIn('src="/app.js?v=dual-seat-80"', 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-28"', html)
|
||||
self.assertIn('href="/console-v2.css?v=desktop-refactor-87"', html)
|
||||
self.assertIn('src="/self-extension.js?v=extension-18"', html)
|
||||
self.assertIn('href="/self-extension.css?v=extension-12"', html)
|
||||
self.assertIn('src="/console-v2.js?v=desktop-refactor-34"', 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.assertNotIn("滚动查看更多", html)
|
||||
self.assertIn('aria-label="执行车舱控制指令"', html)
|
||||
self.assertIn('sessionStorage.getItem("cognitive-agent.demo-role")', console_script)
|
||||
self.assertIn('id="v2SidebarToggle"', console_script)
|
||||
self.assertIn('id = "v2ModuleSwitcher"', console_script)
|
||||
self.assertIn(
|
||||
'panelHeading.classList.add("v2-operation-heading", "v2-module-heading")',
|
||||
console_script,
|
||||
)
|
||||
self.assertIn('kicker.textContent = "SELF-ORCHESTRATION"', console_script)
|
||||
self.assertIn(
|
||||
'if (ui.activeModule === "self-extension" && ui.role !== "user")',
|
||||
console_script,
|
||||
)
|
||||
self.assertIn(
|
||||
'if (ui.activeModule === "self-extension" && role !== "user" && !allowModuleTransition)',
|
||||
console_script,
|
||||
)
|
||||
self.assertIn('label: "自编排", available: true', console_script)
|
||||
self.assertIn('id: "self-calibration"', console_script)
|
||||
self.assertIn('label: "自标定"', console_script)
|
||||
self.assertLess(
|
||||
console_script.index('label: "自标定"'),
|
||||
console_script.index('label: "自编排"'),
|
||||
)
|
||||
self.assertIn("adminOnly: true", console_script)
|
||||
self.assertIn(
|
||||
"$('.v2-module-tab[data-module=\"self-calibration\"]')",
|
||||
@@ -245,6 +262,16 @@ operations:
|
||||
self.assertIn('<span>Hi,</span><strong>user</strong>', console_script)
|
||||
self.assertIn('id: "self-repair"', console_script)
|
||||
self.assertIn('label: "自修复"', console_script)
|
||||
self.assertIn('id: "self-evolution", label: "自进化", available: true', console_script)
|
||||
self.assertIn(
|
||||
'evolution.setAttribute("aria-label", "自进化能力日志")',
|
||||
console_script,
|
||||
)
|
||||
self.assertIn('<span class="section-kicker">SELF-EVOLUTION</span>', console_script)
|
||||
self.assertIn('v2-evolution-heading v2-module-heading', console_script)
|
||||
self.assertIn('data-evolution-index', console_script)
|
||||
self.assertIn('NEXT EVOLUTION · v0.6', console_script)
|
||||
self.assertIn('["self-orchestration", "self-extension", "self-evolution"]', console_script)
|
||||
self.assertIn('roleSelect.disabled = ui.activeModule === "self-repair"', console_script)
|
||||
self.assertNotIn('label: "自诊断"', console_script)
|
||||
self.assertIn('<small>手势模型</small>', console_script)
|
||||
@@ -262,7 +289,37 @@ operations:
|
||||
self.assertIn('grid-template-columns: clamp(245px, 18.5vw, 310px)', console_styles)
|
||||
self.assertIn('.v2-control-switch {', console_styles)
|
||||
self.assertIn('.v2-module-switcher {', console_styles)
|
||||
self.assertIn('grid-template-columns: repeat(4, minmax(0, 1fr))', console_styles)
|
||||
self.assertIn('grid-template-columns: repeat(5, minmax(0, 1fr))', console_styles)
|
||||
self.assertIn('[data-module="self-evolution"] .v2-self-evolution', console_styles)
|
||||
self.assertIn(
|
||||
'.console-v2 .v2-module-heading.v2-module-heading h1',
|
||||
console_styles,
|
||||
)
|
||||
self.assertIn('font-size: clamp(24px, 2vw, 31px)', console_styles)
|
||||
self.assertIn('--v2-module-title-block: clamp(12px, 2vh, 22px)', console_styles)
|
||||
self.assertIn('--v2-module-title-inline: clamp(20px, 2.3vw, 34px)', console_styles)
|
||||
self.assertIn(
|
||||
'grid-template-rows: 36px clamp(236px, 33dvh, 246px) minmax(0, 1fr)',
|
||||
console_styles,
|
||||
)
|
||||
self.assertIn(
|
||||
'[data-module="self-calibration"] .v2-self-calibration',
|
||||
console_styles,
|
||||
)
|
||||
self.assertIn(
|
||||
'[data-module="self-repair"] .v2-self-repair',
|
||||
console_styles,
|
||||
)
|
||||
self.assertIn('.v2-evolution-version.active', console_styles)
|
||||
self.assertIn('[data-evolution-scope="admin"]', console_styles)
|
||||
self.assertIn(
|
||||
'color-mix(in srgb, var(--v2-accent-soft) 86%, white)',
|
||||
console_styles,
|
||||
)
|
||||
self.assertNotIn(
|
||||
'.cabin-channel-legend [data-channel="ch0"]',
|
||||
console_styles,
|
||||
)
|
||||
self.assertIn('margin-inline: calc(0px - var(--v2-main-pad))', console_styles)
|
||||
self.assertIn('background: color-mix(in srgb, var(--v2-accent) 30%, var(--v2-accent-soft))', console_styles)
|
||||
self.assertIn('width: clamp(48px, 45%, 72px)', console_styles)
|
||||
@@ -333,8 +390,10 @@ operations:
|
||||
self.assertIn('root.id = "v2SelfExtension"', extension_script)
|
||||
self.assertIn('data-extension-mode="study"', extension_script)
|
||||
self.assertIn('data-extension-mode="control"', extension_script)
|
||||
self.assertIn('外设玩具能力一览与学习', extension_script)
|
||||
self.assertIn('说一句,联动外设玩具与车端设备', extension_script)
|
||||
self.assertIn('外设玩具能力学习', extension_script)
|
||||
self.assertIn('外设玩具与车端联动', extension_script)
|
||||
self.assertGreaterEqual(extension_script.count('class="v2-module-heading"'), 2)
|
||||
self.assertGreaterEqual(extension_script.count('SELF-EXTENSION'), 2)
|
||||
self.assertIn('id="extensionExecuteButton"', extension_script)
|
||||
self.assertIn('data-lucide="play"', extension_script)
|
||||
self.assertNotIn('id="extensionControlHealth"', extension_script)
|
||||
@@ -363,6 +422,10 @@ operations:
|
||||
self.assertIn('.v2-extension-twin-host > .twin-band {', extension_styles)
|
||||
self.assertIn('.v2-extension-type-card[data-extension-connected="true"]', extension_styles)
|
||||
self.assertIn('.v2-extension-type-card[data-extension-connected="false"]', extension_styles)
|
||||
self.assertIn(
|
||||
'.v2-extension-control\n.v2-extension-learned-action {\n --extension-card-color: var(--v2-accent);',
|
||||
extension_styles,
|
||||
)
|
||||
self.assertIn('grid-template-columns: repeat(3, minmax(0, 1fr))', extension_styles)
|
||||
self.assertTrue(extension_health["success"])
|
||||
self.assertIn(extension_health["transport"], {"tcp", "mqtt", "simulation"})
|
||||
@@ -444,6 +507,45 @@ operations:
|
||||
self.assertIn('id="vehicleSpeed"', html)
|
||||
self.assertNotIn('data-domain-panel="control_link"', html)
|
||||
self.assertIn('id="cabinStateTitle">整车设备状态</h3>', html)
|
||||
self.assertIn('id="cabinCarouselPrev"', app_script)
|
||||
self.assertIn('id="cabinCarouselNext"', app_script)
|
||||
self.assertIn('aria-label="左右浏览设备卡片"', app_script)
|
||||
self.assertNotIn('class="cabin-category-tabs"', app_script)
|
||||
self.assertIn('<span class="section-kicker">LIVE VEHICLE DIGITAL TWIN</span>', html)
|
||||
self.assertIn('.joint-twin-shell > .twin-band {', console_styles)
|
||||
self.assertIn('grid-template-rows: 62px minmax(0, 1fr)', console_styles)
|
||||
self.assertIn('grid-template-columns: repeat(2, minmax(0, 1fr))', console_styles)
|
||||
self.assertIn('--cabin-cards-per-view: 2', console_styles)
|
||||
self.assertIn('const cabinCardsPerView = () => 2', app_script)
|
||||
self.assertIn('scroll-snap-type: x mandatory', console_styles)
|
||||
self.assertIn('data-device-power-value', app_script)
|
||||
self.assertIn('查看${name}功率详情', app_script)
|
||||
self.assertIn('name: "车内灯光", measurable: false', app_script)
|
||||
self.assertIn('.device-power-entry.unavailable', console_styles)
|
||||
self.assertIn('powerDrawer.id = "devicePowerDrawer"', app_script)
|
||||
self.assertIn('window.updateDevicePowerTelemetry = updatePowerTelemetry', app_script)
|
||||
self.assertIn('["longitudinal_motor", "水平电机"]', app_script)
|
||||
self.assertIn('["cushion_motor", "坐盆电机"]', app_script)
|
||||
self.assertIn('["backrest_motor", "座椅靠背电机"]', app_script)
|
||||
self.assertIn('["legrest_motor", "腿托电机"]', app_script)
|
||||
self.assertIn('["ventilation_control", "通风控制"]', app_script)
|
||||
self.assertIn('["heating_control", "加热控制"]', app_script)
|
||||
self.assertIn('["compressor", "压缩机"]', app_script)
|
||||
self.assertIn('["electronic_fan", "电子风扇"]', app_script)
|
||||
self.assertIn('["blower", "鼓风机"]', app_script)
|
||||
self.assertIn('["thermal_management_module", "空调热管理集成模块"]', app_script)
|
||||
self.assertIn('["compressor_controller", "压缩机电控"]', app_script)
|
||||
self.assertIn('["electronic_cooling_pump", "电控冷却水泵"]', app_script)
|
||||
self.assertIn('["front_left_window", "左前车窗"]', app_script)
|
||||
self.assertIn('["front_right_window", "右前车窗"]', app_script)
|
||||
self.assertIn('["rear_left_window", "左后车窗"]', app_script)
|
||||
self.assertIn('["rear_right_window", "右后车窗"]', app_script)
|
||||
self.assertIn('["front_left_combination_lamp", "左前组合灯"]', app_script)
|
||||
self.assertIn('["front_right_combination_lamp", "右前组合灯"]', app_script)
|
||||
self.assertIn('name: "主驾座椅", measurable: true, components: []', app_script)
|
||||
self.assertIn('name: "遮阳帘", measurable: true, components: []', app_script)
|
||||
self.assertIn("仅支持设备总功率", app_script)
|
||||
self.assertIn('.device-power-drawer {', console_styles)
|
||||
self.assertNotIn('class="roof-visual"', html)
|
||||
self.assertNotIn('class="ambient-visual"', html)
|
||||
self.assertIn('id="driverAirDirection"', html)
|
||||
@@ -536,11 +638,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-18", timeout=3
|
||||
f"{self.base_url}/self-extension.js?v=extension-20", timeout=3
|
||||
) as response:
|
||||
extension_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/app.js?v=dual-seat-67", timeout=3
|
||||
f"{self.base_url}/app.js?v=dual-seat-80", timeout=3
|
||||
) as response:
|
||||
app_script = response.read().decode("utf-8")
|
||||
|
||||
|
||||
@@ -126,17 +126,17 @@ class TestSelfCalibrationApi:
|
||||
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-3",
|
||||
f"{self.base_url}/self-calibration.js?v=calibration-6",
|
||||
timeout=3,
|
||||
) as response:
|
||||
script = response.read().decode("utf-8")
|
||||
with urllib.request.urlopen(
|
||||
f"{self.base_url}/self-calibration.css?v=calibration-6",
|
||||
f"{self.base_url}/self-calibration.css?v=calibration-10",
|
||||
timeout=3,
|
||||
) as response:
|
||||
styles = response.read().decode("utf-8")
|
||||
with urllib.request.urlopen(
|
||||
f"{self.base_url}/console-v2.js?v=desktop-refactor-28",
|
||||
f"{self.base_url}/console-v2.js?v=desktop-refactor-31",
|
||||
timeout=3,
|
||||
) as response:
|
||||
console_script = response.read().decode("utf-8")
|
||||
@@ -148,24 +148,30 @@ class TestSelfCalibrationApi:
|
||||
content_type = response.headers["Content-Type"]
|
||||
content_security_policy = response.headers["Content-Security-Policy"]
|
||||
|
||||
assert 'href="/self-calibration.css?v=calibration-6"' in html
|
||||
assert 'src="/self-calibration.js?v=calibration-3"' in html
|
||||
assert 'href="/self-calibration.css?v=calibration-10"' in html
|
||||
assert 'src="/self-calibration.js?v=calibration-6"' in html
|
||||
assert 'data-calibration-page="live"' in script
|
||||
assert 'data-calibration-view="live"' in script
|
||||
assert 'data-calibration-page="hall"' not in script
|
||||
assert 'data-calibration-page="stereo"' not in script
|
||||
assert "座椅—双目联合标定" in script
|
||||
assert "座椅与双目联合标定" in script
|
||||
assert 'class="v2-module-heading"' in script
|
||||
assert 'SELF-CALIBRATION' in script
|
||||
assert 'padding: var(--v2-module-title-block) 0 0' in styles
|
||||
assert 'margin-inline: var(--v2-module-title-inline)' in styles
|
||||
assert "实时座椅姿态" in script
|
||||
assert "四轴实时控制" in script
|
||||
assert "ZED 实时画面" in script
|
||||
assert "双目位姿数据" in script
|
||||
assert "双目位姿数据" not in script
|
||||
assert 'class="v2-calibration-panel v2-calibration-mqtt-panel"' not in script
|
||||
assert "grid-template-columns: minmax(420px, 1.06fr) minmax(380px, 0.94fr)" in styles
|
||||
assert "标定仿真台架" in script
|
||||
assert "X-Cognitive-Agent-Role" in script
|
||||
assert "--v2-sidebar: #dae3f5" in styles
|
||||
assert "--v2-sidebar-deep: #b6c7ea" in styles
|
||||
assert "--v2-accent-strong: #1e386b" in styles
|
||||
assert '["self-calibration", "self-repair"].includes(module)' in console_script
|
||||
assert 'setRole("admin")' in console_script
|
||||
assert 'setRole("admin", { allowModuleTransition: true })' in console_script
|
||||
assert len(model) == 845_992
|
||||
assert content_type.startswith("model/gltf-binary")
|
||||
assert "img-src 'self' data: blob:;" in content_security_policy
|
||||
|
||||
@@ -95,6 +95,17 @@ class TestSelfRepairApi:
|
||||
assert len(state["scenarios"]) == 6
|
||||
assert state["hardware"]["connector_enabled_count"] == 21
|
||||
|
||||
with urllib.request.urlopen(
|
||||
self.request(
|
||||
"/api/self-repair/runtime",
|
||||
method="POST",
|
||||
payload={"active": True, "execution_mode": "shadow"},
|
||||
),
|
||||
timeout=3,
|
||||
) as response:
|
||||
runtime = json.loads(response.read().decode("utf-8"))["runtime"]
|
||||
assert runtime["active"] is True
|
||||
|
||||
with urllib.request.urlopen(
|
||||
self.request(
|
||||
"/api/self-repair/healing/start",
|
||||
@@ -135,26 +146,37 @@ 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-17", timeout=3
|
||||
f"{self.base_url}/self-repair.js?v=repair-20", timeout=3
|
||||
) as response:
|
||||
script = response.read().decode("utf-8")
|
||||
script = response.read().decode("utf-8").replace("\r\n", "\n")
|
||||
with urllib.request.urlopen(
|
||||
f"{self.base_url}/self-repair.css?v=repair-17", timeout=3
|
||||
f"{self.base_url}/self-repair.css?v=repair-21", timeout=3
|
||||
) as response:
|
||||
styles = response.read().decode("utf-8")
|
||||
with urllib.request.urlopen(
|
||||
f"{self.base_url}/self-repair-seat3d.js?v=repair-17", timeout=3
|
||||
f"{self.base_url}/self-repair-seat3d.js?v=repair-18", timeout=3
|
||||
) as response:
|
||||
seat3d = response.read().decode("utf-8")
|
||||
with urllib.request.urlopen(
|
||||
f"{self.base_url}/console-v2.js?v=desktop-refactor-28", timeout=3
|
||||
f"{self.base_url}/console-v2.js?v=desktop-refactor-31", timeout=3
|
||||
) as response:
|
||||
console_script = response.read().decode("utf-8")
|
||||
|
||||
assert 'href="/self-repair.css?v=repair-17"' in html
|
||||
assert 'src="/self-repair.js?v=repair-17"' in html
|
||||
assert 'src="/self-repair-seat3d.js?v=repair-17"' in html
|
||||
assert "量产故障用例" in script
|
||||
assert 'href="/self-repair.css?v=repair-21"' in html
|
||||
assert 'src="/self-repair.js?v=repair-20"' 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-18"' in html
|
||||
assert "量产故障注入" in script
|
||||
assert "运行与自修复日志" in script
|
||||
assert "启动座椅运行" in script
|
||||
assert "开始正常任务" not in script
|
||||
assert 'id="repairSeatCanvas"' in script
|
||||
assert 'id="repairSeatViewer"' in script
|
||||
assert "self-repair-seat-reset" in script
|
||||
assert "seat-healing-state" in script
|
||||
assert 'id="repairConfirmDialog"' in script
|
||||
assert "confirmAction(" in script
|
||||
assert "window.confirm" not in script
|
||||
@@ -163,10 +185,15 @@ class TestSelfRepairApi:
|
||||
assert "connectorRows" in script
|
||||
assert "Polling refreshes data only" in script
|
||||
assert "renderRecords();\n showView(activeView);" not in script
|
||||
assert 'let latest = fallbackState();' in script
|
||||
assert 'renderAll();\n window.lucide?.createIcons' in script
|
||||
assert '"WDT复位后失能"' in script
|
||||
assert "--repair-deep-blue" in styles
|
||||
assert '.console-v2[data-module="self-repair"] .section-kicker' in styles
|
||||
assert 'grid-template-areas: "twin cases timeline"' in styles
|
||||
assert "background: color-mix(in srgb, var(--v2-accent-soft) 82%, white)" in styles
|
||||
assert 'grid-template-areas: "twin cases logs"' in styles
|
||||
assert ".v2-repair-event-stream" in styles
|
||||
assert "background: rgba(255, 255, 255, 0.94)" in styles
|
||||
assert "border-color: color-mix(in srgb, var(--v2-accent) 18%, white)" in styles
|
||||
assert ".v2-repair-toolbar button.primary:hover:not(:disabled)" in styles
|
||||
assert "color: #fff;" in styles
|
||||
assert "seat-healing-state" in seat3d
|
||||
@@ -175,7 +202,7 @@ class TestSelfRepairApi:
|
||||
assert 'cssThemeColor("--v2-accent", "#315eaa")' in seat3d
|
||||
assert 'viewer.addEventListener("pointerdown"' in seat3d
|
||||
assert 'window.addEventListener("pointermove"' in seat3d
|
||||
assert 'window.addEventListener("self-repair-seat-reset"' in seat3d
|
||||
assert "window.addEventListener(resetEvent" in seat3d
|
||||
assert '["self-calibration", "self-repair"].includes(module)' in console_script
|
||||
assert 'roleSelect.disabled = ui.activeModule === "self-repair"' in console_script
|
||||
|
||||
|
||||
@@ -47,6 +47,49 @@ class FrontendVehicleStateSyncContractTests(unittest.TestCase):
|
||||
self.extension_source,
|
||||
)
|
||||
|
||||
def test_shared_twin_theme_tracks_demo_role_and_extension_is_always_user(self) -> None:
|
||||
self.assertIn("function applyTwinSceneTheme(", self.app_source)
|
||||
self.assertIn('canvas.dataset.themeRole = themeRole', self.app_source)
|
||||
self.assertIn(
|
||||
'canvas.dataset.themeAccent = `#${palette.accent.toString(16).padStart(6, "0")}`',
|
||||
self.app_source,
|
||||
)
|
||||
self.assertIn("accent: 0x0e7773", self.app_source)
|
||||
self.assertIn("accent: 0x315eaa", self.app_source)
|
||||
self.assertIn(
|
||||
'if (document.body.dataset.module === "self-extension") return "user"',
|
||||
self.app_source,
|
||||
)
|
||||
self.assertIn(
|
||||
'const themeRole = document.body.dataset.module === "self-extension"',
|
||||
self.app_source,
|
||||
)
|
||||
self.assertIn(
|
||||
'window.addEventListener("cognitive-agent:demo-role-change"',
|
||||
self.app_source,
|
||||
)
|
||||
|
||||
def test_shared_twin_ground_uses_theme_radial_alpha_texture(self) -> None:
|
||||
self.assertIn("function createTwinGroundTexture(", self.app_source)
|
||||
self.assertIn("new THREE.PlaneGeometry(30, 30)", self.app_source)
|
||||
self.assertIn(
|
||||
'surfaceGradient.addColorStop(0, "rgba(255, 255, 255, 1)")',
|
||||
self.app_source,
|
||||
)
|
||||
self.assertIn(
|
||||
'surfaceGradient.addColorStop(1, "rgba(255, 255, 255, 0)")',
|
||||
self.app_source,
|
||||
)
|
||||
self.assertIn("const outerRadius = size * 0.48", self.app_source)
|
||||
self.assertIn(
|
||||
"const gridColor = accent.clone().lerp(new THREE.Color(0xffffff), 0.52)",
|
||||
self.app_source,
|
||||
)
|
||||
self.assertIn("gridContext.lineWidth = major ? 0.9 : 0.45", self.app_source)
|
||||
self.assertIn('gridContext.globalCompositeOperation = "destination-in"', self.app_source)
|
||||
self.assertIn("applyTwinGroundTheme(twin, palette)", self.app_source)
|
||||
self.assertNotIn("new THREE.GridHelper(10, 20", self.app_source)
|
||||
|
||||
def test_lan_http_origin_does_not_require_crypto_random_uuid(self) -> None:
|
||||
id_source = function_source(
|
||||
self.app_source,
|
||||
|
||||
@@ -4,6 +4,8 @@ import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from cognitive_agent.application.self_repair.healing_model import (
|
||||
PHASE_DIAGNOSED,
|
||||
PHASE_INJECTED,
|
||||
@@ -75,6 +77,30 @@ def test_service_is_safe_without_machine_specific_tool_paths() -> None:
|
||||
assert service.run_self_test() is True
|
||||
|
||||
|
||||
def test_runtime_must_start_before_fault_injection() -> None:
|
||||
service = SelfRepairService()
|
||||
|
||||
with pytest.raises(ValueError, match="请先启动座椅连续运行"):
|
||||
service.start_healing({
|
||||
"case_id": "FI-FIELD-01",
|
||||
"execution_mode": "shadow",
|
||||
})
|
||||
|
||||
runtime = service.set_runtime({"active": True, "execution_mode": "shadow"})
|
||||
assert runtime["active"] is True
|
||||
assert runtime["motion_profile"] == "四电机协同自由运动"
|
||||
assert runtime["events"][0]["title"] == "座椅自由运动任务已启动"
|
||||
|
||||
healing = service.start_healing({
|
||||
"case_id": "FI-FIELD-01",
|
||||
"execution_mode": "shadow",
|
||||
})
|
||||
assert healing["case_id"] == "FI-FIELD-01"
|
||||
|
||||
snapshot = service.snapshot()
|
||||
assert snapshot["runtime"]["injected_count"] == 1
|
||||
|
||||
|
||||
def test_bridge_manager_does_not_arm_visible_tooling_by_default(tmp_path: Path) -> None:
|
||||
manager = ZcanBridgeManager(
|
||||
tmp_path / "bridge.json",
|
||||
|
||||
Reference in New Issue
Block a user