feat: synchronize CH1 vehicle control and feedback

This commit is contained in:
vee
2026-08-11 10:42:13 +08:00
parent 3701192a11
commit 32379a1573
50 changed files with 13636 additions and 736 deletions
@@ -0,0 +1,58 @@
from __future__ import annotations
from pathlib import Path
import unittest
REPOSITORY_ROOT = Path(__file__).resolve().parents[2]
SOURCE_PATH = REPOSITORY_ROOT / "apps" / "web-console" / "self-extension.js"
class SelfExtensionFrontendDelayContractTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.source = SOURCE_PATH.read_text(encoding="utf-8")
hydrate_start = cls.source.index(" function hydratePlan(steps) {")
hydrate_end = cls.source.index("\n function setExecutionState", hydrate_start)
cls.hydrate_source = cls.source[hydrate_start:hydrate_end]
execute_start = cls.source.index(" async function executePlan(plan) {")
execute_end = cls.source.index("\n function addHistory", execute_start)
cls.execute_source = cls.source[execute_start:execute_end]
def test_hydration_recognizes_and_strictly_validates_delay_steps(self) -> None:
self.assertIn('if (step.kind === "delay")', self.hydrate_source)
self.assertIn("Number.isInteger(step.delay_ms)", self.hydrate_source)
self.assertIn("step.delay_ms < MIN_PLAN_DELAY_MS", self.hydrate_source)
self.assertIn("step.delay_ms > MAX_PLAN_DELAY_MS", self.hydrate_source)
self.assertIn("totalDelayMs > MAX_TOTAL_PLAN_DELAY_MS", self.hydrate_source)
self.assertIn("index === 0 || index === plan.length - 1", self.hydrate_source)
self.assertIn('plan[index - 1].kind === "delay"', self.hydrate_source)
self.assertIn("step.delay_ms !== undefined && step.delay_ms !== 0", self.hydrate_source)
self.assertNotIn(".filter(Boolean)", self.hydrate_source)
def test_hydration_displays_a_canonical_five_second_label(self) -> None:
self.assertIn("`等待 ${step.delay_ms / 1000} 秒`", self.hydrate_source)
self.assertIn("delayMs: step.delay_ms", self.hydrate_source)
def test_execution_routes_delay_only_to_the_timer(self) -> None:
delay_branch = self.execute_source.index('if (step.kind === "delay")')
vehicle_branch = self.execute_source.index('else if (step.kind === "vehicle")')
peripheral_branch = self.execute_source.index('await sendGatewayCommand("emit"')
self.assertLess(delay_branch, vehicle_branch)
self.assertLess(vehicle_branch, peripheral_branch)
between_delay_and_vehicle = self.execute_source[delay_branch:vehicle_branch]
self.assertIn("await delay(step.delayMs);", between_delay_and_vehicle)
self.assertNotIn("executeVehicle", between_delay_and_vehicle)
self.assertNotIn("sendGatewayCommand", between_delay_and_vehicle)
def test_explicit_delay_does_not_stack_with_default_action_spacing(self) -> None:
self.assertIn("const nextStep = plan[index + 1];", self.execute_source)
self.assertIn(
'nextStep && step.kind !== "delay" && nextStep.kind !== "delay"',
self.execute_source,
)
self.assertIn("await delay(state.settings.actionDelayMs);", self.execute_source)
if __name__ == "__main__":
unittest.main()