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()