from copy import deepcopy from dataclasses import replace from pathlib import Path import pytest from cognitive_agent.application.n7_seat_preset import ( N7_SEAT_PRESET_CAPABILITIES, expand_n7_seat_preset, ) from cognitive_agent.application.vehicle_profile import VehicleProfileRegistry ROOT = Path(__file__).resolve().parents[2] def preset(targets=None): return { "domain": "local_vehicle", "capability": "local_scene.seat_preset", "target_scope": "vehicle", "operation": "set", "at_ms": 0, "parameters": {"targets_pulse": [246, 112, 405, 342] if targets is None else targets}, } @pytest.fixture def vehicle(): return VehicleProfileRegistry(ROOT / "config" / "vehicles").get("denza_n7") def test_preset_uses_vehicle_calibration_and_keeps_original_pulses(vehicle): action = preset() original = deepcopy(action) operations = expand_n7_seat_preset(action, vehicle) assert [item["capability"] for item in operations] == list(N7_SEAT_PRESET_CAPABILITIES) assert [item["parameters"]["position_pct"] for item in operations] == [51, 30, 44, 53] assert all(item["target_scope"] == "passenger" for item in operations) assert action == original calibrated = replace(vehicle, seat_motors={ **vehicle.seat_motors, "longitudinal": replace(vehicle.seat_motors["longitudinal"], hall_total_pulses=500), }) assert expand_n7_seat_preset(action, calibrated)[0]["parameters"] == {"position_pct": 49} @pytest.mark.parametrize("targets", [ [], [1, 2, 3], [1, 2, 3, 4, 5], "246,112,405,342", [True, 112, 405, 342], [246.5, 112, 405, 342], ["246", 112, 405, 342], [-1, 112, 405, 342], [481, 112, 405, 342], [246, 375, 405, 342], [246, 112, 918, 342], [246, 112, 405, 648], ]) def test_invalid_pulses_are_rejected_without_clamping(vehicle, targets): with pytest.raises(ValueError): expand_n7_seat_preset(preset(targets), vehicle) @pytest.mark.parametrize("field,value", [ ("target_scope", "driver"), ("domain", "seat"), ("operation", "toggle"), ("parameters", {"targets_pulse": [246, 112, 405, 342], "speed": 100}), ]) def test_unrecognized_target_or_contract_cannot_move_a_seat(vehicle, field, value): action = preset() action[field] = value with pytest.raises(ValueError): expand_n7_seat_preset(action, vehicle) def test_zero_and_small_targets_do_not_accidentally_home(vehicle): assert [item["parameters"]["position_pct"] for item in expand_n7_seat_preset( preset([0, 0, 0, 0]), vehicle )] == [0, 0, 0, 0] assert [item["parameters"]["position_pct"] for item in expand_n7_seat_preset( preset([1, 1, 1, 1]), vehicle )] == [1, 1, 1, 1] assert [item["parameters"]["position_pct"] for item in expand_n7_seat_preset( preset([480, 374, 917, 647]), vehicle )] == [100, 100, 100, 100] def test_delayed_actions_keep_the_vehicle_timing(vehicle): action = preset() action["at_ms"] = 3000 assert expand_n7_seat_preset(action, vehicle) is None def test_wrong_vehicle_or_missing_calibration_is_rejected(vehicle): with pytest.raises(ValueError): expand_n7_seat_preset(preset(), replace(vehicle, id="other_vehicle")) with pytest.raises(ValueError): expand_n7_seat_preset(preset(), replace(vehicle, seat_motors={}))