优化后端

This commit is contained in:
Miayui
2026-09-11 17:56:56 +08:00
parent 7eed11b774
commit 812e67422d
7299 changed files with 1744959 additions and 112 deletions
+10
View File
@@ -164,3 +164,13 @@ class TestMutationRequestSecurity:
handler.headers["X-Forwarded-For"] = "203.0.113.10"
assert handler._hardware_access_bypass_allowed() is False
def test_wireless_passive_state_bypass_does_not_widen_control_bypass(self) -> None:
handler = object.__new__(SeatDemoHandler)
handler.client_address = ("127.0.0.1", 12345)
handler.headers = Message()
handler.auth = _auth(enabled=False)
assert handler._passive_state_access_bypass_allowed("wireless_mqtt") is True
assert handler._passive_state_access_bypass_allowed("wired_can") is False
assert handler._hardware_access_bypass_allowed() is False
+97 -20
View File
@@ -135,6 +135,32 @@ class SeatWebApiTests(unittest.TestCase):
unauthenticated.open(f"{self.base_url}/api/runtime", timeout=3)
self.assertEqual(caught.exception.code, 401)
with self.assertRaises(urllib.error.HTTPError) as state_denied:
unauthenticated.open(
f"{self.base_url}/api/hardware/vehicle-state?"
"conversation_id=conv-unauthenticated-state&"
"control_transport=wireless_mqtt",
timeout=3,
)
self.assertEqual(state_denied.exception.code, 401)
control_request = urllib.request.Request(
f"{self.base_url}/api/can/commands",
method="POST",
headers={"Content-Type": "application/json"},
data=json.dumps(
{
"commands": ["reading_light power on"],
"conversation_id": "conv-unauthenticated-control",
"execution_mode": "hardware",
"control_transport": "wireless_mqtt",
}
).encode("utf-8"),
)
with self.assertRaises(urllib.error.HTTPError) as control_denied:
unauthenticated.open(control_request, timeout=3)
self.assertEqual(control_denied.exception.code, 401)
def test_n7_mode_scene_catalog_is_public_and_read_only(self) -> None:
unauthenticated = urllib.request.build_opener()
@@ -667,18 +693,18 @@ 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-131", timeout=3) as response:
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-135", 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-9", timeout=3
) as response:
gesture_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/console-v2.js?v=local-first-66", timeout=3
f"{self.base_url}/console-v2.js?v=local-first-70", timeout=3
) as response:
console_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/console-v2.css?v=delivery-state-68", timeout=3
f"{self.base_url}/console-v2.css?v=delivery-state-69", timeout=3
) as response:
console_styles = response.read().decode("utf-8")
with self.opener.open(
@@ -694,7 +720,7 @@ operations:
) as response:
speech_worklet = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/self-extension.js?v=gscu-tcp-43", timeout=3
f"{self.base_url}/self-extension.js?v=gscu-tcp-44", timeout=3
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
@@ -702,7 +728,7 @@ operations:
) as response:
extension_styles = response.read().decode("utf-8").replace("\r\n", "\n")
with self.opener.open(
f"{self.base_url}/assistant-chat.js?v=codex-assistant-27", timeout=3
f"{self.base_url}/assistant-chat.js?v=codex-assistant-32", timeout=3
) as response:
assistant_script = response.read().decode("utf-8")
with self.opener.open(
@@ -743,12 +769,12 @@ 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-131"', html)
self.assertIn('src="/app.js?v=dual-seat-135"', html)
self.assertIn('href="/styles.css?v=dual-seat-23"', html)
self.assertIn('src="/gesture-control.js?v=android-pad-9"', html)
self.assertNotIn('class="vehicle-system-hud"', html)
self.assertIn('src="/console-v2.js?v=local-first-66"', html)
self.assertIn('href="/console-v2.css?v=delivery-state-68"', html)
self.assertIn('src="/console-v2.js?v=local-first-70"', html)
self.assertIn('href="/console-v2.css?v=delivery-state-69"', html)
self.assertIn('src="/speech-input.js?v=sensevoice-13"', html)
self.assertIn('href="/speech-input.css?v=sensevoice-7"', html)
self.assertIn('id="speechDictationButton"', html)
@@ -756,7 +782,7 @@ operations:
self.assertIn('<body class="console-v2">', html)
self.assertIn('id="v2BootScreen"', html)
self.assertIn('正在加载车机控制与实时状态', html)
self.assertIn('src="/self-extension.js?v=gscu-tcp-43"', html)
self.assertIn('src="/self-extension.js?v=gscu-tcp-44"', html)
self.assertIn('href="/self-extension.css?v=gscu-tcp-43"', html)
self.assertNotIn("self-calibration", html)
self.assertNotIn("self-repair", html)
@@ -764,8 +790,8 @@ operations:
self.assertNotIn("滚动查看更多", html)
self.assertIn('aria-label="发送消息"', html)
self.assertIn('id="assistantTranscript"', html)
self.assertIn('href="/assistant-chat.css?v=codex-assistant-26"', html)
self.assertIn('src="/assistant-chat.js?v=codex-assistant-27"', html)
self.assertIn('href="/assistant-chat.css?v=codex-assistant-28"', html)
self.assertIn('src="/assistant-chat.js?v=codex-assistant-32"', html)
self.assertIn('href="/energy-monitor.css?v=energy-monitor-4"', html)
self.assertIn('src="/energy-monitor.js?v=energy-monitor-4"', html)
self.assertNotIn('"admin"', console_script)
@@ -803,8 +829,12 @@ operations:
'if (ui.activeModule === "self-extension" && role !== "user"',
console_script,
)
self.assertIn('511工程-智能座舱', console_script)
self.assertIn('网页控制链路', console_script)
self.assertNotIn('511工程-智能座舱', console_script)
self.assertNotIn('网页控制链路', console_script)
self.assertIn('function buildModuleSwitcher(mainPanel, modeRail)', console_script)
self.assertIn('if (modeRailBar) modeRailBar.prepend(switcher)', console_script)
self.assertNotIn('class="v2-sync-sidebar-heading"', console_script)
self.assertIn('function appendDispatchedControls(message, result = {})', assistant_script)
self.assertIn('["self-orchestration", "self-extension"]', console_script)
self.assertNotIn('label: "自诊断"', console_script)
self.assertNotIn('runtimeControls.id = "v2RuntimeControls"', console_script)
@@ -1043,14 +1073,14 @@ operations:
self.assertIn("CAN 指令已提交,等待车辆状态确认", console_script)
self.assertIn('canvas.addEventListener("webglcontextlost"', app_script)
self.assertIn('"cognitive-agent:module-change"', app_script)
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v282"', service_worker)
self.assertIn('/console-v2.css?v=delivery-state-68', service_worker)
self.assertIn('/console-v2.js?v=local-first-66', service_worker)
self.assertIn('/assistant-chat.css?v=codex-assistant-26', service_worker)
self.assertIn('/assistant-chat.js?v=codex-assistant-27', service_worker)
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v290"', service_worker)
self.assertIn('/console-v2.css?v=delivery-state-69', service_worker)
self.assertIn('/console-v2.js?v=local-first-70', service_worker)
self.assertIn('/assistant-chat.css?v=codex-assistant-28', service_worker)
self.assertIn('/assistant-chat.js?v=codex-assistant-32', service_worker)
self.assertIn('/energy-monitor.css?v=energy-monitor-4', service_worker)
self.assertIn('/energy-monitor.js?v=energy-monitor-4', service_worker)
self.assertIn('"/app.js?v=dual-seat-131"', service_worker)
self.assertIn('"/app.js?v=dual-seat-135"', service_worker)
self.assertIn('"/speech-input.js?v=sensevoice-13"', service_worker)
self.assertIn('"/speech-input.css?v=sensevoice-7"', service_worker)
self.assertIn('"/speech-audio-worklet.js?v=sensevoice-5"', service_worker)
@@ -1507,7 +1537,7 @@ operations:
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/app.js?v=dual-seat-131", timeout=3
f"{self.base_url}/app.js?v=dual-seat-135", timeout=3
) as response:
app_script = response.read().decode("utf-8")
@@ -3100,6 +3130,53 @@ operations:
self.assertEqual(payload["source"], "wireless_mqtt_ch1")
self.assertEqual(payload["capture_sequence"], 17)
def test_wireless_state_http_bypasses_only_the_physical_bench_lease(self) -> None:
cases = (
("seat-state", "synchronize_hardware_state", "conv-http-mqtt-seat-state"),
("vehicle-state", "synchronize_vehicle_state", "conv-http-mqtt-vehicle-state"),
)
for endpoint, method_name, conversation_id in cases:
with self.subTest(endpoint=endpoint):
with patch.object(
self.service,
method_name,
return_value={"status": "succeeded", "source": "wireless_mqtt"},
) as synchronize:
request = urllib.request.Request(
f"{self.base_url}/api/hardware/{endpoint}?"
f"conversation_id={conversation_id}&vehicle_id=denza_n7&"
"control_transport=wireless_mqtt&"
"hardware_client_id=hwc-e2e-state-reader",
headers={"X-Forwarded-For": "203.0.113.25"},
)
with self.opener.open(request, timeout=3) as response:
self.assertEqual(response.status, 200)
synchronize.assert_called_once_with(
conversation_id,
"denza_n7",
control_transport="wireless_mqtt",
hardware_client_id="hwc-e2e-state-reader",
hardware_access_bypass=True,
)
with patch.object(
self.service,
"synchronize_vehicle_state",
return_value={"status": "succeeded", "source": "physical_can_ch1"},
) as synchronize_wired:
request = urllib.request.Request(
f"{self.base_url}/api/hardware/vehicle-state?"
"conversation_id=conv-http-wired-state&vehicle_id=denza_n7&"
"control_transport=wired_can&"
"hardware_client_id=hwc-e2e-state-reader",
headers={"X-Forwarded-For": "203.0.113.25"},
)
with self.opener.open(request, timeout=3) as response:
self.assertEqual(response.status, 200)
self.assertFalse(synchronize_wired.call_args.kwargs["hardware_access_bypass"])
def test_gpt_http_failure_falls_back_without_returning_502(self) -> None:
original_api_key = self.service.openai.api_key
self.service.openai.api_key = "test-key"
@@ -309,6 +309,30 @@ test("assistant bridges request start and validated local controls to the consol
value: "0x1DE · 00 00 00 00 17 00 00 00",
}],
},
{
operation: {
id: "reading-light-1",
capability: "interior_light.set_reading_power",
target: { instance: "all" },
parameters: { state: "off" },
parse_source: "local_rule",
},
channel: "CAN1",
actual_command: "light reading all off",
frames: [],
},
{
operation: {
id: "roof-shade-1",
capability: "roof.set_shade_motion",
target: { instance: "shade" },
parameters: { motion: "close" },
parse_source: "local_rule",
},
channel: "CAN1",
actual_command: "shade close",
frames: [],
},
],
trace_events: [{ stage: "compile", duration_ms: 2 }],
};
@@ -338,6 +362,25 @@ test("assistant bridges request start and validated local controls to the consol
assert.deepEqual(JSON.parse(JSON.stringify(validated)), [payload]);
assert.equal(harness.requestCalls.length, 1);
assert.equal(JSON.parse(harness.requestCalls[0].options.body).execution_mode, "dry_run");
const assistantArticle = harness.messages.children[1];
const cards = assistantArticle.querySelectorAll(".assistant-dispatched-controls");
assert.equal(cards.length, 1);
assert.equal(cards[0].open, true);
assert.equal(
cards[0].querySelector(".assistant-dispatched-controls-count").textContent,
"3",
);
const rows = cards[0].querySelector(".assistant-dispatched-controls-list").children;
assert.deepEqual(
rows.map((row) => row.querySelector(".assistant-dispatched-control-label").textContent),
["空调温度", "阅读灯", "遮阳帘"],
);
assert.deepEqual(
rows.map((row) => row.querySelector(".assistant-dispatched-control-value").textContent),
["23°C", "关闭", "关闭"],
);
assert.match(rows[0].title, /实际控制指令:ac temp driver 23/);
});
test("unsupported-control suggestions render once and click through as a new command", async () => {
+30 -5
View File
@@ -13,15 +13,15 @@ def test_console_contains_ai_assistant_surface():
assert 'aria-label="AI助手对话"' in html
assert 'placeholder="向 AI助手提问或交代工作"' in html
assert 'aria-label="发送消息"' in html
assert 'href="/assistant-chat.css?v=codex-assistant-26"' in html
assert 'src="/assistant-chat.js?v=codex-assistant-27"' in html
assert 'href="/assistant-chat.css?v=codex-assistant-28"' in html
assert 'src="/assistant-chat.js?v=codex-assistant-32"' in html
def test_codex_assistant_assets_are_versioned_together_in_the_service_worker():
service_worker = (ROOT / "apps/web-console/service-worker.js").read_text(encoding="utf-8")
assert 'const CACHE_VERSION = "cognitive-agent-pad-v282"' in service_worker
assert '"/assistant-chat.css?v=codex-assistant-26"' in service_worker
assert '"/assistant-chat.js?v=codex-assistant-27"' in service_worker
assert 'const CACHE_VERSION = "cognitive-agent-pad-v290"' in service_worker
assert '"/assistant-chat.css?v=codex-assistant-28"' in service_worker
assert '"/assistant-chat.js?v=codex-assistant-32"' in service_worker
def test_codex_frontend_uses_one_streaming_endpoint_and_agent_work_events():
@@ -73,6 +73,31 @@ def test_unsupported_control_suggestions_are_clickable_accessible_and_deduplicat
assert ".assistant-control-suggestion.is-sending" in css
def test_validated_controls_are_mirrored_into_a_compact_assistant_card():
script = (ROOT / "apps/web-console/assistant-chat.js").read_text(encoding="utf-8")
css = (ROOT / "apps/web-console/assistant-chat.css").read_text(encoding="utf-8")
assert "function appendDispatchedControls(message, result = {})" in script
assert "window.CognitiveAgentConsoleV2?.getParsedCanCommands?.(result)" in script
assert "fallbackAssistantControlCommands(result)" in script
assert 'card.className = "assistant-dispatched-controls"' in script
assert 'heading.textContent = "下发控制指令"' in script
assert 'count.className = "assistant-dispatched-controls-count"' in script
assert 'list.className = "assistant-dispatched-controls-list"' in script
assert "value.textContent = assistantControlValue(item)" in script
assert "语义控制指令:" in script
assert "实际控制指令:" in script
assert "appendDispatchedControls(message, event.payload)" in script
assert "function announceModeExecution(" in script
assert "announceModeExecution," in script
assert '"ambient_light.set_illumination": "氛围灯颜色与亮度"' in script
assert '.replaceAll("_", "-")' in script
assert ".assistant-dispatched-controls > summary" in css
assert ".assistant-dispatched-controls-list > li" in css
assert ".assistant-dispatched-control-value" in css
assert ".assistant-dispatched-controls[open]" in css
def test_codex_request_keeps_trusted_vehicle_context_without_legacy_model_routing():
script = (ROOT / "apps/web-console/assistant-chat.js").read_text(encoding="utf-8")
request_body = script.split("function requestBody(message)", 1)[1].split(
@@ -35,10 +35,10 @@ def test_energy_monitor_assets_are_versioned_and_cached() -> None:
):
assert asset in html
assert f'"{asset}"' in service_worker
assert html.index("/assistant-chat.js?v=codex-assistant-27") < html.index(
assert html.index("/assistant-chat.js?v=codex-assistant-32") < html.index(
"/energy-monitor.js?v=energy-monitor-4"
)
assert 'const CACHE_VERSION = "cognitive-agent-pad-v282"' in service_worker
assert 'const CACHE_VERSION = "cognitive-agent-pad-v290"' in service_worker
def test_energy_monitor_uses_the_fifth_shared_dock_icon_and_starts_closed() -> None:
+2 -2
View File
@@ -41,13 +41,13 @@ def test_theme_asset_versions_are_consistent_with_the_pwa_shell() -> None:
for asset in (
"/styles.css?v=dual-seat-23",
"/console-v2.css?v=delivery-state-68",
"/console-v2.css?v=delivery-state-69",
"/self-extension.css?v=gscu-tcp-43",
"/speech-input.css?v=sensevoice-7",
):
assert asset in html
assert f'"{asset}"' in service_worker
assert 'const CACHE_VERSION = "cognitive-agent-pad-v282"' in service_worker
assert 'const CACHE_VERSION = "cognitive-agent-pad-v290"' in service_worker
def test_vehicle_twin_icons_and_nested_overlays_use_semantic_tokens() -> None:
@@ -259,6 +259,39 @@ class FrontendVehicleStateSyncContractTests(unittest.TestCase):
)
self.assertNotIn("passenger: mergedActuatorState.driver", render_source)
def test_state_polling_surfaces_structured_403_reason(self) -> None:
passenger_source = function_source(
self.app_source,
"async function synchronizePassengerSeatState() {",
"\nasync function synchronizeDriverSeatState()",
)
driver_source = function_source(
self.app_source,
"async function synchronizeDriverSeatState() {",
"\nasync function synchronizePhysicalSeatState()",
)
live_status_source = function_source(
self.app_source,
"function renderCabinLiveStatus(",
"\nfunction refreshIcons",
)
telemetry_source = function_source(
self.app_source,
"function recordVehicleStateTelemetry(payload) {",
"\nfunction applyAuthoritativeVehicleState",
)
self.assertIn("throw await apiResponseError(response)", passenger_source)
self.assertIn("throw await apiResponseError(response)", driver_source)
self.assertIn("errorStatus: Number(error.status) || null", driver_source)
self.assertIn("errorReason: error.serverMessage", driver_source)
self.assertIn("snapshot?.errorStatus === 403", live_status_source)
self.assertIn("`${transportLabel} · 403:${reason}`", live_status_source)
self.assertIn('telemetryState.textContent = "403 拒绝"', live_status_source)
self.assertIn('setSummarySystem("403 · 状态读取被拒绝", "warning")', live_status_source)
self.assertIn("errorStatus: null", telemetry_source)
self.assertIn('errorReason: ""', telemetry_source)
def test_persistent_linux_monitor_uses_backend_selected_fast_polling(self) -> None:
interval_source = function_source(
self.app_source,
+95 -18
View File
@@ -250,7 +250,32 @@ def test_top_mode_action_formatter_uses_device_target_action_and_parameter_value
}
def test_mode_cards_open_assistant_and_submit_enter_mode_command() -> None:
def test_mode_chip_scroller_keeps_clicks_and_only_captures_real_drags() -> None:
app = (WEB / "app.js").read_text(encoding="utf-8")
start = app.index("function installModeChipScroller")
end = app.index("\n\nfunction ensureModeActionPopover", start)
snippet = app[start:end]
threshold = snippet.index('if (!dragged && Math.abs(distance) < 5) return;')
capture = snippet.index("group.setPointerCapture(pointerId);")
assert capture > threshold
assert "let pointerCaptured = false;" in snippet
assert "if (pointerCaptured) group.releasePointerCapture?.(pointerId);" in snippet
assert 'event.stopImmediatePropagation();' in snippet
def test_top_mode_triangle_is_an_explicit_execution_target() -> None:
app = (WEB / "app.js").read_text(encoding="utf-8")
vue_ui = (WEB / "vue-ui.js").read_text(encoding="utf-8")
handlers = app[app.index('document.addEventListener("DOMContentLoaded"') :]
assert '"data-mode-run": "true"' in vue_ui
assert 'runIcon.dataset.modeRun = "true";' in app
assert 'const runControl = target?.closest("[data-mode-run]");' in handlers
assert '(runControl || target)?.closest("button[data-mode-command]")' in handlers
def test_saved_mode_cards_open_assistant_and_execute_the_saved_command_flow() -> None:
app = (WEB / "app.js").read_text(encoding="utf-8")
start = app.index("function applyModeCommand")
end = app.index("\n\nfunction applyRuntimeTargetPolicy", start)
@@ -258,12 +283,6 @@ def test_mode_cards_open_assistant_and_submit_enter_mode_command() -> None:
script = "const snippet = " + json.dumps(snippet) + ";\n" + r"""
const vm = require("node:vm");
const calls = [];
const input = {
value: "",
dataset: { speechStage: "final", speechUtteranceId: "old", speechValue: "old" },
dispatchEvent(event) { calls.push(["input", event.type, event.bubbles, this.value]); },
};
const form = { requestSubmit() { calls.push(["submit", input.value]); } };
const runButton = { disabled: false };
const savedMode = {
id: "reading-user",
@@ -283,11 +302,14 @@ const context = {
},
modeProfiles() { return [savedMode]; },
setModeRailSelection(mode) { calls.push(["select", mode?.id]); },
setModeExecutionState(modeId, status) { calls.push(["state", modeId, status]); },
closeModeGallery(options) { calls.push(["close", options.restoreFocus]); },
showToast(message) { calls.push(["toast", message]); },
executeSavedModeProfile(mode, options) {
calls.push(["execute", mode.id, options.command]);
return Promise.resolve(true);
},
$(selector) {
return { "#utterance": input, "#requestForm": form, "#runButton": runButton }[selector];
return { "#runButton": runButton }[selector];
},
};
vm.createContext(context);
@@ -295,8 +317,6 @@ vm.runInContext(snippet + '\nthis.result = applyModeCommand(" 进入阅读模式
console.log(JSON.stringify({
result: context.result,
calls,
value: input.value,
speechDataset: input.dataset,
pendingModeProfileId: context.state.pendingModeProfileId,
}));
"""
@@ -314,14 +334,71 @@ console.log(JSON.stringify({
["select", "reading-user"],
["close", False],
["open"],
["input", "input", True, "进入阅读模式"],
["state", "reading-user", "running"],
["submit", "进入阅读模式"],
["execute", "reading-user", "进入阅读模式"],
]
assert result["value"] == "进入阅读模式"
assert result["speechDataset"] == {}
assert result["pendingModeProfileId"] == "reading-user"
assert "executeSavedModeProfile" not in snippet
assert result["pendingModeProfileId"] is None
assert "executeSavedModeProfile" in snippet
def test_mode_card_without_saved_flow_falls_back_to_assistant_submission() -> None:
app = (WEB / "app.js").read_text(encoding="utf-8")
start = app.index("function applyModeCommand")
end = app.index("\n\nfunction applyRuntimeTargetPolicy", start)
snippet = app[start:end]
script = "const snippet = " + json.dumps(snippet) + ";\n" + r"""
const vm = require("node:vm");
const calls = [];
const input = {
value: "",
dataset: { speechStage: "final", speechUtteranceId: "old", speechValue: "old" },
dispatchEvent(event) { calls.push(["input", event.type, event.bubbles, this.value]); },
};
const form = { requestSubmit() { calls.push(["submit", input.value]); } };
const runButton = { disabled: false };
const unsavedMode = { id: "future-mode", name: "未来模式" };
const context = {
state: { pendingModeProfileId: null },
Event: class Event {
constructor(type, options = {}) { this.type = type; this.bubbles = options.bubbles; }
},
window: {
CognitiveAgentAssistant: {
isPending() { return false; },
open() { calls.push(["open"]); },
},
},
modeProfiles() { return [unsavedMode]; },
setModeRailSelection(mode) { calls.push(["select", mode?.id]); },
setModeExecutionState(modeId, status) { calls.push(["state", modeId, status]); },
closeModeGallery(options) { calls.push(["close", options.restoreFocus]); },
showToast(message) { calls.push(["toast", message]); },
$(selector) {
return { "#utterance": input, "#requestForm": form, "#runButton": runButton }[selector];
},
};
vm.createContext(context);
vm.runInContext(snippet + '\nthis.result = applyModeCommand(" 进入未来模式 ", "future-mode");', context);
console.log(JSON.stringify({ result: context.result, calls, value: input.value }));
"""
completed = subprocess.run(
[node_runtime(), "-e", script],
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
result = json.loads(completed.stdout)
assert result["result"] is True
assert result["calls"] == [
["select", "future-mode"],
["close", False],
["open"],
["input", "input", True, "进入未来模式"],
["state", "future-mode", "running"],
["submit", "进入未来模式"],
]
assert result["value"] == "进入未来模式"
def test_mode_cards_do_not_submit_while_assistant_is_busy() -> None:
+3 -2
View File
@@ -741,8 +741,9 @@ class MqttCanTransportTests(unittest.TestCase):
by_id["0x41A"]["domain_state"]["roof"]["shade_position_pct"],
92,
)
self.assertIsNone(
by_id["0x42E"]["domain_state"]["ambient_light"]["front_color_index"]
self.assertEqual(
by_id["0x42E"]["domain_state"]["ambient_light"]["front_color_index"],
100,
)
self.assertEqual(
by_id["0x3FF"]["domain_state"]["interior_light"]["ambient_illuminance_lux"],
+16 -2
View File
@@ -100,9 +100,23 @@ class NativeCanFeedbackTests(unittest.TestCase):
self.assertFalse(feedback_matches("indoor_light:on", feedback))
self.assertTrue(feedback_matches("ambient_power:off", feedback))
def test_can1_matrix_rejects_reserved_ambient_color_values(self) -> None:
def test_n7_profile_accepts_full_seven_bit_ambient_color_index(self) -> None:
decoded = decode_feedback(
self.frame(0x42E, "11 64 32 03 90 02 00 C6")
self.frame(0x42E, "11 C1 20 03 D0 02 00 C6")
)["domain_state"]["ambient_light"]
self.assertEqual(decoded["front_color_index"], 65)
self.assertEqual(decoded["rear_color_index"], 65)
self.assertTrue(
feedback_matches(
"ambient_color:65",
self.frame(0x42E, "11 C1 20 03 D0 02 00 C6"),
)
)
def test_can1_matrix_rejects_zero_ambient_color_index(self) -> None:
decoded = decode_feedback(
self.frame(0x42E, "11 00 00 03 D0 02 00 C6")
)["domain_state"]["ambient_light"]
self.assertIsNone(decoded["front_color_index"])
+2 -2
View File
@@ -60,7 +60,7 @@ def test_unified_console_exposes_device_id_based_nfc_authorization() -> None:
assert '.v2-nfc-command-list' in styles
assert '.v2-nfc-command-option' in styles
assert '.v2-nfc-select-all' in styles
assert 'self-extension.js?v=gscu-tcp-43' in html
assert 'self-extension.js?v=gscu-tcp-44' in html
assert 'self-extension.css?v=gscu-tcp-43' in html
@@ -140,5 +140,5 @@ def test_main_parsed_control_panel_renders_validated_infrared_operations() -> No
assert "下发控制指令" in console
assert "已解析控制指令" not in console
assert 'li[data-can-channel="IR"]' in styles
assert "console-v2.js?v=local-first-66" in html
assert "console-v2.js?v=local-first-70" in html
assert 'speech_semantic: "语音语义"' in console
@@ -30,7 +30,7 @@ def test_frontend_only_exposes_user_orchestration_and_extension() -> None:
assert not (web_root / removed).exists()
assert removed not in html
assert "511工程-智能座舱" in console
assert "511工程-智能座舱" not in console
assert '["self-orchestration", "self-extension"]' in console
assert 'id="vehicleExtensionSync"' in console
assert 'getRole: () => "user"' in console
@@ -174,6 +174,20 @@ def test_vehicle_device_status_cards_prioritize_seats_and_expose_manual_controls
assert ".v2-device-control-apply" in styles
def test_ambient_status_uses_its_own_power_feedback_not_combined_light_badge() -> None:
console = source("console-v2.js")
ambient_start = console.index('id: "ambient", name: "氛围灯"')
ambient_end = console.index('id: "headlight", name: "前大灯"', ambient_start)
ambient = console[ambient_start:ambient_end]
assert 'function ambientPowerState()' in console
assert 'dataset.ambientPower' in console
assert 'const powerState = ambientPowerState();' in ambient
assert 'powerState === "on" ? "开启"' in ambient
assert 'ambientPowerState() === "off" ? "off" : "on"' in ambient
assert 'readText("#ambientPower")' not in ambient
def test_passenger_can0_status_ui_uses_only_seat_ota_four_axis_contract() -> None:
app = source("app.js")
console = source("console-v2.js")
@@ -207,7 +221,8 @@ def test_left_sidebar_exposes_collapsed_parsed_can_command_panel() -> None:
assert "getParsedCanCommands: (result) => parsedCanCommands(result)" in console
assert "const configurations = result.function_configs || {};" in console
assert "if (!configurationEntries.length && result.function_config)" in console
assert 'deliveryLabel: submitted ? "下发" : "未下发"' in console
assert "const safetyBlocked = executionFailed && !submitted" in console
assert 'safetyBlocked\n ? "安全阻止"' in console
assert 'data-delivery-state="${escapeHtml(item.deliveryState)}"' in console
assert 'const command = (category, text, channel = "CAN1", frame = "")' in console
assert 'data-can-channel="${escapeHtml(item.channel)}"' in console
@@ -247,6 +262,9 @@ def test_left_sidebar_exposes_collapsed_parsed_can_command_panel() -> None:
"light reading all ${onOff}",
"light ambient color ${color}",
"light ambient brightness ${level}",
"Ambient_RGB_Control_32B_2B(color=${color}, brightness=${brightness}%)",
"Ambient_Mode_Control_496_04(mode=${mode})",
"Climate_Control_064_04(${target}=${temperature}°C)",
"shade position ${position}",
"seat passenger ${axis[1]} position ${position}",
"seat passenger dynamic ${dynamic}",
@@ -705,6 +723,42 @@ def test_parsed_command_delivery_is_independent_of_transport_and_target() -> Non
}
}
}
blocked_shade = {
"function_configs": {
"native_roof": {
"spec": {
"operations": [
{
"id": "shade-position",
"capability": "roof.set_shade_position",
"target": {"zone": "vehicle"},
"parameters": {"position_pct": 30},
"parse_source": "local_rule",
}
]
}
}
},
"execution_mode": "hardware",
"control_transport": "wireless_mqtt",
"execution_report": {
"spec": {
"results": [
{
"ref": "shade-position",
"status": "failed",
"message": (
"no fresh valid 0x41A shade position; "
"refusing blind motion"
),
"telemetry": {
"verification_status": "failed_or_blocked"
},
}
]
}
},
}
samples = [
{**base, "execution_mode": "dry_run", "control_transport": "wired_can"},
{
@@ -744,6 +798,7 @@ def test_parsed_command_delivery_is_independent_of_transport_and_target() -> Non
}
},
},
blocked_shade,
]
script = (
f"{helpers}\n"
@@ -761,19 +816,35 @@ def test_parsed_command_delivery_is_independent_of_transport_and_target() -> Non
)
parsed = json.loads(completed.stdout)
assert [len(commands) for commands in parsed] == [1, 1, 1, 1]
assert [commands[0]["command"] for commands in parsed] == ["ac on"] * 4
assert [len(commands) for commands in parsed] == [1, 1, 1, 1, 1]
assert [commands[0]["command"] for commands in parsed] == [
"ac on",
"ac on",
"ac on",
"ac on",
"shade position 30",
]
assert [commands[0]["deliveryLabel"] for commands in parsed] == [
"未下发",
"未下发",
"下发",
"下发",
"安全阻止",
]
assert [commands[0]["deliveryState"] for commands in parsed] == [
"not_submitted",
"not_submitted",
"submitted",
"submitted",
"blocked",
]
assert "0x41A 实时位置" in parsed[-1][0]["deliveryDetail"]
assert [commands[0]["deliveryTransport"] for commands in parsed] == [
"有线 CAN",
"无线 CAN",
"有线 CAN",
"无线 CAN",
"无线 CAN",
]
@@ -803,9 +874,11 @@ def test_mode_editor_renders_readable_commands_with_expandable_mqtt_sequences()
assert "if (isDispatchTask(task))" in console
assert "下发控制指令" in console
assert "已解析控制指令" not in console
assert "function executeSavedModeProfile(mode)" in app
assert 'async function executeSavedModeProfile(mode, { command = "" } = {})' in app
assert "/api/mode-profiles/${encodeURIComponent(mode.id)}/execute" in app
assert '$("#utterance").value = mode.name || mode.id;' in app
assert "assistant?.announceModeExecution?.(mode, payload" in app
assert "void executeSavedModeProfile(mode, { command: modeCommand });" in app
assert "announceModeExecution" in source("assistant-chat.js")
assert 'const savedModeExecution = result.orchestration_route === "saved_mode_command_flow";' in app
assert "`${result.mode_profile.name}执行完成`" in app
@@ -1021,6 +1094,14 @@ def test_interactive_console_uses_one_top_right_icon_dock_for_all_five_panels()
assert console.index('id="v2DevicePanel"') < console.index('id="v2ExtensionPanel"')
assert 'class="v2-side-status-stack"' in console
assert 'class="v2-panel-control-dock"' in console
assert "function buildModuleSwitcher(mainPanel, modeRail)" in console
assert 'const modeRailBar = $(".mode-rail-bar", modeRail);' in console
assert "if (modeRailBar) modeRailBar.prepend(switcher);" in console
assert 'const homepageHeader = moduleSwitcher.closest(".v2-mode-rail") || moduleSwitcher;' in extension
assert 'homepageHeader.insertAdjacentElement("afterend", root);' in extension
assert 'class="v2-unified-brand"' not in console
assert "网页控制链路" not in console
assert '<div class="v2-sync-sidebar-heading">' not in console
assert 'panelDock.append(sidebarToggle)' in console
assert 'panelDock.append(deviceToggle, extensionToggle)' in console
assert 'class="v2-side-panel-dock"' not in console
@@ -1046,12 +1127,22 @@ def test_interactive_console_uses_one_top_right_icon_dock_for_all_five_panels()
assert '.v2-panel-control-button[aria-pressed="true"]' in styles
assert "background: var(--primary);" in styles
assert "width: 38px !important;" in styles
assert "Unified homepage header: panel controls and function modes share one row." in styles
assert "grid-template-columns: auto auto minmax(0, 1fr) auto;" in styles
final_styles = styles.split("/* AI assistant light-blue final override", 1)[1]
assert 'data-open-panels="none"' in final_styles
assert "grid-template-columns: minmax(0, 1fr);" in final_styles
assert ".v2-unified-right-rail[hidden]" in final_styles
def test_runtime_hardware_coverage_defines_the_linked_readiness_state() -> None:
app = source("app.js")
definition = "const linkedHardwareReady = seatHardwareReady && vehicleHardwareReady;"
assert definition in app
assert app.index(definition) < app.index(": linkedHardwareReady")
def test_left_sync_rail_can_collapse_and_log_aligns_with_lower_workspace() -> None:
console = source("console-v2.js")
styles = source("console-v2.css")
@@ -1210,7 +1301,11 @@ def test_compact_mode_strip_supports_wheel_drag_and_keyboard_navigation() -> Non
assert 'group.addEventListener("pointerdown"' in scroller
assert 'event.pointerType !== "mouse"' in scroller
assert "group.setPointerCapture?.(pointerId)" in scroller
assert "group.setPointerCapture(pointerId)" in scroller
assert scroller.index("group.setPointerCapture(pointerId)") > scroller.index(
"Math.abs(distance) < 5"
)
assert "let pointerCaptured = false;" in scroller
assert 'group.addEventListener("pointermove"' in scroller
assert "Math.abs(distance) < 5" in scroller
assert 'group.classList.add("is-dragging")' in scroller
@@ -0,0 +1,98 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
WEB = ROOT / "apps" / "web-console"
def source(name: str) -> str:
return (WEB / name).read_text(encoding="utf-8")
def test_spatial_ui_contract_is_loaded_last_and_cached() -> None:
html = source("index.html")
service_worker = source("service-worker.js")
asset = "/ui-consistency.css?v=spatial-ui-4"
assert asset in html
assert f'"{asset}"' in service_worker
assert html.index("/energy-monitor.css") < html.index(asset)
def test_same_kind_surfaces_share_geometry_tokens() -> None:
styles = source("ui-consistency.css")
for token in (
"--ui-control-height-compact:",
"--ui-control-height:",
"--ui-radius-control:",
"--ui-radius-item:",
"--ui-radius-section:",
"--ui-radius-panel:",
"--ui-radius-modal:",
"--ui-shadow-item:",
"--ui-shadow-panel:",
"--ui-shadow-modal:",
):
assert token in styles
for selector in (
".v2-operation-shell",
".joint-twin-shell > .twin-band",
"#v2DevicePanel",
"#v2ExtensionPanel .v2-extension-sync-mount",
".mode-gallery",
".mode-editor",
".energy-monitor-page",
".v2-synced-device",
".v2-extension-gateway",
".mode-gallery-card",
".energy-kpi",
):
assert selector in styles
def test_same_kind_controls_share_sizes_and_focus_treatment() -> None:
styles = source("ui-consistency.css")
for selector in (
"#v2PanelControlDock > .v2-panel-control-button",
".v2-mode-rail .mode-rail-actions > button",
".v2-device-adjust-toggle",
".v2-extension-nfc-launch",
".energy-trend-tabs button",
".mode-gallery-close",
".mode-editor-header > button",
"#energyMonitorClose",
):
assert selector in styles
assert "box-shadow: var(--ui-focus-ring)" in styles
assert "outline: none" in styles
def test_compact_sidebar_overrides_cannot_break_shared_geometry() -> None:
styles = source("ui-consistency.css")
assert ".v2-sync-execution-field," in styles
assert ".v2-can-command-panel" in styles
assert "border-radius: var(--ui-radius-item) !important" in styles
assert ".v2-sync-log {" in styles
assert "border-radius: var(--ui-radius-section) !important" in styles
def test_assistant_composer_hint_stays_inside_narrow_input() -> None:
styles = source("ui-consistency.css")
assert ".v2-request-form .composer-hint" in styles
assert "left: 16px !important" in styles
assert "right: auto !important" in styles
assert "max-width: calc(100% - 132px)" in styles
def test_failed_delivery_uses_one_semantic_status_treatment() -> None:
styles = source("ui-consistency.css")
assert '[data-delivery-state="blocked"]' in styles
assert '[data-delivery-state="failed"]' in styles
assert "color: var(--destructive)" in styles
+85
View File
@@ -0,0 +1,85 @@
from __future__ import annotations
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
WEB = ROOT / "apps" / "web-console"
def source(name: str) -> str:
return (WEB / name).read_text(encoding="utf-8")
def test_vue_presentation_assets_are_local_ordered_and_cached() -> None:
html = source("index.html")
service_worker = source("service-worker.js")
runtime = "/vendor/vue/3.5.21/vue.runtime.global.prod.js"
presentation = "/vue-ui.js?v=vue-presentation-3"
assert html.index(runtime) < html.index(presentation) < html.index(
"/app.js?v=dual-seat-135"
)
assert html.index(presentation) < html.index(
"/assistant-chat.js?v=codex-assistant-32"
)
assert f'"{runtime}"' in service_worker
assert f'"{presentation}"' in service_worker
assert 'const CACHE_VERSION = "cognitive-agent-pad-v290"' in service_worker
assert "unpkg.com" not in html
assert "cdn.jsdelivr.net" not in html
def test_vendored_vue_runtime_and_license_are_present() -> None:
runtime = WEB / "vendor" / "vue" / "3.5.21" / "vue.runtime.global.prod.js"
license_file = WEB / "vendor" / "vue" / "3.5.21" / "LICENSE"
assert runtime.is_file()
assert runtime.stat().st_size > 50_000
assert "vue v3.5.21" in runtime.read_text(encoding="utf-8")[:500]
assert license_file.is_file()
assert "The MIT License" in license_file.read_text(encoding="utf-8")
def test_vue_islands_render_modes_and_dispatched_controls() -> None:
vue_ui = source("vue-ui.js")
app = source("app.js")
assistant = source("assistant-chat.js")
styles = source("assistant-chat.css")
assert 'name: "N7ModeChipRail"' in vue_ui
assert 'name: "N7DispatchedControlsCard"' in vue_ui
assert 'container.dataset.vueSurface = "mode-chips"' in vue_ui
assert 'container.dataset.vueSurface = "dispatched-controls"' in vue_ui
assert "renderModeChips" in vue_ui
assert "renderDispatchedControls" in vue_ui
assert "window.CognitiveAgentVueUI = Object.freeze" in vue_ui
assert '"data-mode-run": "true"' in vue_ui
assert "function vueModeChipEntries(modes)" in app
assert "window.CognitiveAgentVueUI?.renderModeChips?.(" in app
assert "window.CognitiveAgentVueUI?.renderDispatchedControls" in assistant
assert 'host.className = "assistant-dispatched-controls-vue-host"' in assistant
assert "deliveryLabel: String(item.deliveryLabel" in assistant
assert "deliveryDetail: String(item.deliveryDetail" in assistant
assert "下发状态:" in vue_ui
assert ".assistant-dispatched-controls-vue-host" in styles
def test_existing_dom_renderers_remain_as_no_vue_fallbacks() -> None:
app = source("app.js")
assistant = source("assistant-chat.js")
mode_vue_call = app.index("window.CognitiveAgentVueUI?.renderModeChips?.(")
mode_fallback = app.index("const runtimeModeIds = new Set", mode_vue_call)
assert mode_fallback > mode_vue_call
command_vue_call = assistant.index(
"window.CognitiveAgentVueUI?.renderDispatchedControls"
)
command_fallback = assistant.index(
'document.createElement("details")', command_vue_call
)
assert command_fallback > command_vue_call
assert "renderDispatchedControls: () => false" in source("vue-ui.js")
assert "renderModeChips: () => false" in source("vue-ui.js")