"""Tests for RudderSensorConfig and DualRudderSensorConfig — Sprint 4.""" from __future__ import annotations import pytest from pydantic import ValidationError from arautopilot.core.sensor_config import ( DualRudderSensorConfig, RudderSensorConfig, RudderSensorType, ) def _primary( sensor_type: RudderSensorType = RudderSensorType.AS5048A_SPI, cs: int = 10, fsd: float = 35.0, ) -> RudderSensorConfig: return RudderSensorConfig(type=sensor_type, spi_cs_gpio=cs, full_scale_deg=fsd) def _redundant(cs: int = 11) -> RudderSensorConfig: return RudderSensorConfig( type=RudderSensorType.AS5048A_SPI, label="Redundant – actuator arm", spi_cs_gpio=cs, full_scale_deg=35.0, ) class TestRudderSensorConfig: def test_as5048a_defaults(self) -> None: s = _primary() assert s.type == RudderSensorType.AS5048A_SPI assert s.spi_cs_gpio == 10 assert s.full_scale_deg == 35.0 assert s.zero_offset_deg == 0.0 assert s.divergence_alarm_deg == 3.0 assert s.divergence_failover_deg == 6.0 def test_potentiometer_type(self) -> None: s = _primary(sensor_type=RudderSensorType.POTENTIOMETER) assert s.type == RudderSensorType.POTENTIOMETER def test_gpio_bounds(self) -> None: with pytest.raises(ValidationError): RudderSensorConfig(type=RudderSensorType.AS5048A_SPI, spi_cs_gpio=40) with pytest.raises(ValidationError): RudderSensorConfig(type=RudderSensorType.AS5048A_SPI, spi_cs_gpio=-1) def test_full_scale_bounds(self) -> None: with pytest.raises(ValidationError): _primary(fsd=0.0) with pytest.raises(ValidationError): _primary(fsd=46.0) def test_label_optional(self) -> None: s = RudderSensorConfig(type=RudderSensorType.AS5048A_SPI) assert s.label == "" def test_extra_fields_forbidden(self) -> None: with pytest.raises(ValidationError): RudderSensorConfig( type=RudderSensorType.AS5048A_SPI, unknown_field="boom", # type: ignore[call-arg] ) def test_roundtrip_json(self) -> None: s = _primary() restored = RudderSensorConfig.model_validate_json(s.model_dump_json()) assert restored == s class TestDualRudderSensorConfig: def test_single_sensor_mode(self) -> None: d = DualRudderSensorConfig(primary=_primary()) assert d.redundant is None assert not d.has_redundancy def test_dual_sensor_mode(self) -> None: d = DualRudderSensorConfig(primary=_primary(), redundant=_redundant()) assert d.redundant is not None assert d.has_redundancy def test_different_cs_pins(self) -> None: d = DualRudderSensorConfig(primary=_primary(cs=10), redundant=_redundant(cs=11)) assert d.primary.spi_cs_gpio == 10 assert d.redundant is not None assert d.redundant.spi_cs_gpio == 11 def test_roundtrip_yaml(self) -> None: import yaml d = DualRudderSensorConfig(primary=_primary(), redundant=_redundant()) data = yaml.safe_dump(d.model_dump(mode="json")) restored = DualRudderSensorConfig.model_validate(yaml.safe_load(data)) assert restored == d