121 lines
4.5 KiB
Python
121 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import unittest
|
|
from unittest.mock import Mock, patch
|
|
|
|
from cognitive_agent.application.self_extension import (
|
|
GatewayError,
|
|
SelfExtensionService,
|
|
SimulationGateway,
|
|
build_gateway_command,
|
|
validate_slot,
|
|
)
|
|
|
|
|
|
class SelfExtensionTests(unittest.TestCase):
|
|
def test_gateway_command_contracts_and_slot_validation(self) -> None:
|
|
command = build_gateway_command("learn", {"slot": 17})
|
|
self.assertEqual(command["type"], "infrared")
|
|
self.assertEqual(command["action"], "learn")
|
|
self.assertEqual(command["data"], {"no": 17})
|
|
self.assertTrue(str(command["messageId"]).startswith("bridge-"))
|
|
|
|
self.assertEqual(validate_slot("248"), 248)
|
|
with self.assertRaises(GatewayError):
|
|
validate_slot(0)
|
|
with self.assertRaises(GatewayError):
|
|
build_gateway_command("erase", {"confirm": "wrong"})
|
|
|
|
def test_simulation_gateway_executes_without_physical_confirmation(self) -> None:
|
|
service = SelfExtensionService(SimulationGateway())
|
|
result = service.execute_gateway("emit", {"slot": 3, "repeat": 2})
|
|
|
|
self.assertTrue(result["success"])
|
|
self.assertEqual(result["transport"], "simulation")
|
|
self.assertFalse(result["physical_state_confirmed"])
|
|
self.assertEqual(len(result["device_response"]), 2)
|
|
|
|
def test_plan_only_accepts_learned_catalog_capabilities(self) -> None:
|
|
service = SelfExtensionService(SimulationGateway())
|
|
proposed = {
|
|
"summary": "先开玩具灯,再打开阅读灯",
|
|
"needs_confirmation": False,
|
|
"question": "",
|
|
"steps": [
|
|
{
|
|
"kind": "peripheral",
|
|
"capability_id": "cap-light-on",
|
|
"command": "",
|
|
"label": "ignored",
|
|
},
|
|
{
|
|
"kind": "peripheral",
|
|
"capability_id": "invented-capability",
|
|
"command": "",
|
|
"label": "ignored",
|
|
},
|
|
{
|
|
"kind": "vehicle",
|
|
"capability_id": "",
|
|
"command": "打开全部阅读灯",
|
|
"label": "车内灯光 · 阅读灯",
|
|
},
|
|
],
|
|
}
|
|
with patch.dict(os.environ, {"OPENAI_API_KEY": "test-key"}, clear=False):
|
|
with patch.object(
|
|
service,
|
|
"_request_gpt_plan",
|
|
return_value=json.dumps(proposed, ensure_ascii=False),
|
|
):
|
|
result = service.plan(
|
|
{
|
|
"utterance": "打开玩具灯和阅读灯",
|
|
"peripherals": [
|
|
{
|
|
"id": "cap-light-on",
|
|
"status": "learned",
|
|
"device_name": "灯光测试玩具",
|
|
"action": "开启",
|
|
"risk": "low",
|
|
}
|
|
],
|
|
"vehicle_devices": [],
|
|
}
|
|
)
|
|
|
|
self.assertEqual(len(result["steps"]), 2)
|
|
self.assertEqual(result["steps"][0]["capability_id"], "cap-light-on")
|
|
self.assertEqual(result["steps"][1]["command"], "打开全部阅读灯")
|
|
|
|
def test_vehicle_execution_reuses_the_same_orchestration_service(self) -> None:
|
|
service = SelfExtensionService(SimulationGateway())
|
|
vehicle_service = Mock()
|
|
vehicle_service.process.return_value = {
|
|
"status": "succeeded",
|
|
"provider_used": "local_rules",
|
|
"request_id": "req-extension-1",
|
|
"decision_summary": ["打开阅读灯"],
|
|
"validation": [],
|
|
"execution_report": {"spec": {"results": [{"status": "succeeded"}]}},
|
|
"deferred_requests": [],
|
|
}
|
|
|
|
result = service.execute_vehicle(
|
|
{"command": "打开全部阅读灯", "vehicle_id": "denza_n7"},
|
|
vehicle_service,
|
|
hardware_access_bypass=True,
|
|
)
|
|
|
|
self.assertTrue(result["success"])
|
|
self.assertEqual(result["request_id"], "req-extension-1")
|
|
vehicle_service.process.assert_called_once()
|
|
self.assertEqual(vehicle_service.process.call_args.args[0], "打开全部阅读灯")
|
|
self.assertTrue(vehicle_service.process.call_args.kwargs["hardware_access_bypass"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|