from __future__ import annotations import os from pathlib import Path import tempfile from unittest.mock import patch import pytest from cognitive_agent.application.direct_can_control import build_direct_can_intent from cognitive_agent.application.seat_service import REPOSITORY_ROOT, SeatDemoService def test_driver_device_batch_keeps_each_exact_control_independent() -> None: commands = [ "seat driver vent low", "seat driver heat off", "seat driver massage mode stretch", "seat driver massage strength strong", ] normalized, parsed = build_direct_can_intent(commands) assert normalized == commands assert [item["capability"] for item in parsed["native_can_actions"]] == [ "seat.set_driver_ventilation_native", "seat.set_driver_heating_native", "seat.set_driver_massage_native", "seat.set_driver_massage_native", ] assert [item["parameters"] for item in parsed["native_can_actions"]] == [ {"state": "on", "level": 2}, {"state": "off"}, {"action": "mode", "mode": "stretch"}, {"action": "strength", "strength": 3}, ] assert { item["parse_source"] for item in parsed["native_can_actions"] } == {"direct_control"} def test_passenger_device_batch_builds_can0_position_action() -> None: normalized, parsed = build_direct_can_intent( ["seat passenger backrest position 36"] ) assert normalized == ["seat passenger backrest position 36"] assert parsed["native_can_actions"] == [] assert parsed["actions"] == [ { "target": "passenger", "action_type": "position", "axis": "backrest_recline", "position_pct": 36, "parse_source": "direct_control", "source_span": "seat passenger backrest position 36", } ] def test_passenger_can0_canonical_absolute_relative_thermal_and_dynamic() -> None: context = { "devices": { "passenger": { "position": { "longitudinal": {"position_pct": 40}, "cushion_tilt": {"position_pct": 50}, "backrest_recline": {"position_pct": 60}, "legrest_height": {"position_pct": 70}, } } } } commands = [ "seat passenger horizontal position 35", "seat passenger basin relative up 10", "seat passenger vent medium", "seat passenger dynamic rocking-horse", "seat passenger dynamic stop", ] normalized, parsed = build_direct_can_intent(commands, context) assert normalized == commands assert parsed["detected_domains"] == ["seat"] assert parsed["actions"][0]["axis"] == "longitudinal" assert parsed["actions"][0]["position_pct"] == 35 assert parsed["actions"][1]["axis"] == "cushion_tilt" assert parsed["actions"][1]["position_pct"] == 40 assert parsed["actions"][1]["motion_intent"] == { "mode": "relative", "direction": "decrease", "start_position_pct": 50, "delta_position_pct": 10, } assert parsed["actions"][2]["function"] == "ventilation" assert parsed["actions"][2]["requested_level"] == 2 assert parsed["actions"][3]["action_type"] == "dynamic_mode" assert parsed["actions"][3]["mode_id"] == 1 assert parsed["actions"][4]["action_type"] == "dynamic_mode_stop" def test_passenger_can0_relative_command_requires_current_position() -> None: with pytest.raises(ValueError, match="requires a valid current"): build_direct_can_intent( ["seat passenger horizontal relative forward 10"], {"devices": {"passenger": {"position": {}}}}, ) @pytest.mark.parametrize( "commands", ( [], ["seat driver vent medium"], ["seat passenger height position 101"], ["headlight low-beam on"], ), ) def test_direct_batch_rejects_non_whitelisted_commands(commands: list[str]) -> None: with pytest.raises(ValueError): build_direct_can_intent(commands) def test_service_direct_batch_bypasses_natural_language_interpreter() -> None: with tempfile.TemporaryDirectory() as directory, patch.dict( os.environ, { "COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED": "false", "COGNITIVE_AGENT_VEHICLE_CAN_ENABLED": "false", }, clear=False, ): service = SeatDemoService(REPOSITORY_ROOT) service.artifact_root = Path(directory) / "artifacts" with patch.object( service, "_interpret", side_effect=AssertionError("natural-language parsing must not run"), ): result = service.process_can_commands( [ "ac on", "ac temp driver 24", "ac fan 2", "ac cycle internal", ], execution_mode="dry_run", control_transport="wireless_mqtt", ) assert result["status"] == "succeeded" assert result["provider_used"] == "direct_can_control" assert result["control_transport"] == "wireless_mqtt" assert result["can_commands"] == [ "ac on", "ac temp driver 24", "ac fan 2", "ac cycle internal", ] operations = [ operation for configuration in result["function_configs"].values() for operation in configuration["spec"]["operations"] ] assert [item["capability"] for item in operations] == [ "climate.set_power", "climate.set_temperature", "climate.set_fan_level", "climate.set_cycle_mode", ] assert {item["parse_source"] for item in operations} == {"direct_control"} def test_service_direct_batch_combines_can0_and_can1_semantics() -> None: with tempfile.TemporaryDirectory() as directory, patch.dict( os.environ, { "COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED": "false", "COGNITIVE_AGENT_VEHICLE_CAN_ENABLED": "false", }, clear=False, ): service = SeatDemoService(REPOSITORY_ROOT) service.artifact_root = Path(directory) / "artifacts" result = service.process_can_commands( [ "seat passenger basin relative up 10", "seat passenger vent medium", "seat passenger dynamic rocking-horse", "ac fan 2", "light ambient on", ], conversation_id="conv-direct-can0-can1", execution_mode="dry_run", ) assert result["status"] == "succeeded" assert result["can_commands"] == [ "seat passenger basin relative up 10", "seat passenger vent medium", "seat passenger dynamic rocking-horse", "ac fan 2", "light ambient on", ] operations = [ operation for configuration in result["function_configs"].values() for operation in configuration["spec"]["operations"] ] by_capability = {item["capability"]: item for item in operations} assert by_capability["seat.set_cushion_tilt"]["parameters"] == { "position_pct": 40 } assert by_capability["seat.set_cushion_tilt"]["motion"] == { "mode": "relative", "direction": "decrease", "start_position_pct": 50, "delta_position_pct": 10, } assert by_capability["seat.start_dynamic_mode"]["parameters"] == { "mode_id": 1 } assert by_capability["seat.set_ventilation"]["parameters"] == { "state": "on", "level": 2, } assert by_capability["climate.set_fan_level"]["parameters"] == {"level": 2} assert by_capability["ambient_light.set_power"]["parameters"] == { "state": "on" } assert {item["parse_source"] for item in operations} == {"direct_control"}