aima_phone_catcher/tests/test_config.py

111 lines
4.2 KiB
Python

import pytest
from lingkong_motor_service.config import ConfigError, Settings
def test_defaults_match_connected_ms3008_installation() -> None:
settings = Settings()
assert settings.can_channel == 0
assert settings.can_device == 0
assert settings.can_bitrate == 1_000_000
assert settings.motor_ids == (1, 2)
assert settings.open_control_raw == 400
assert settings.close_control_raw == 400
assert settings.broadcast_frequency_hz == 10
assert settings.derate_after_s == 5
assert settings.derated_control_raw == 100
assert settings.temperature_limit_c == 80
assert settings.temperature_poll_interval_s == 0.5
assert settings.http_host == "0.0.0.0"
assert settings.api_token is None
@pytest.mark.parametrize("field", ["open_control_raw", "close_control_raw"])
@pytest.mark.parametrize("value", [851, 0, True, 1.5])
def test_rejects_invalid_open_loop_magnitude(field: str, value: object) -> None:
with pytest.raises(ConfigError, match="1..850"):
Settings(**{field: value}).validate()
@pytest.mark.parametrize("motor_ids", [(), (1, 1), (0,), (3,), (5,), (True,)])
def test_motor_ids_are_nonempty_unique_and_in_broadcast_range(
motor_ids: tuple[int, ...],
) -> None:
with pytest.raises(ConfigError, match="MOTOR_IDS"):
Settings(motor_ids=motor_ids).validate()
def test_environment_values_are_parsed(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("MOTOR_CAN_CHANNEL", "1")
monkeypatch.setenv("MOTOR_CAN_DEVICE", "2")
monkeypatch.setenv("MOTOR_IDS", "2, 1")
monkeypatch.setenv("MOTOR_OPEN_CONTROL_RAW", "123")
monkeypatch.setenv("MOTOR_CLOSE_CONTROL_RAW", "234")
monkeypatch.setenv("MOTOR_DERATE_AFTER_S", "6")
monkeypatch.setenv("MOTOR_DERATED_CONTROL_RAW", "90")
monkeypatch.setenv("MOTOR_TEMPERATURE_LIMIT_C", "75")
monkeypatch.setenv("MOTOR_TEMPERATURE_POLL_INTERVAL_S", "0.25")
settings = Settings.from_env()
assert settings.can_channel == 1
assert settings.can_device == 2
assert settings.motor_ids == (2, 1)
assert settings.open_control_raw == 123
assert settings.close_control_raw == 234
assert settings.derate_after_s == 6
assert settings.derated_control_raw == 90
assert settings.temperature_limit_c == 75
assert settings.temperature_poll_interval_s == 0.25
@pytest.mark.parametrize("channel", [-1, 2, "CAN1", True])
def test_canalystii_channel_is_physical_zero_or_one(channel: object) -> None:
with pytest.raises(ConfigError, match="panel CAN1"):
Settings(can_channel=channel).validate()
@pytest.mark.parametrize("device", [-1, True, "0"])
def test_canalystii_device_index_is_non_negative_integer(device: object) -> None:
with pytest.raises(ConfigError, match="MOTOR_CAN_DEVICE"):
Settings(can_device=device).validate()
def test_broadcast_frequency_is_loaded_from_environment(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("MOTOR_BROADCAST_FREQUENCY_HZ", "250")
assert Settings.from_env().broadcast_frequency_hz == 250
@pytest.mark.parametrize("frequency", [False, float("nan"), 0.0, 1001.0])
def test_broadcast_frequency_must_be_finite_and_bounded(frequency: float) -> None:
with pytest.raises(ConfigError, match="MOTOR_BROADCAST_FREQUENCY_HZ"):
Settings(broadcast_frequency_hz=frequency).validate()
def test_external_http_binding_does_not_require_api_token() -> None:
Settings(http_host="0.0.0.0", api_token=None).validate()
@pytest.mark.parametrize(
("field", "value", "message"),
[
("derate_after_s", 0, "MOTOR_DERATE_AFTER_S"),
("derated_control_raw", 851, "MOTOR_DERATED_CONTROL_RAW"),
("temperature_limit_c", 128, "MOTOR_TEMPERATURE_LIMIT_C"),
("temperature_poll_interval_s", 0, "MOTOR_TEMPERATURE_POLL_INTERVAL_S"),
],
)
def test_power_and_temperature_settings_are_bounded(
field: str, value: object, message: str
) -> None:
with pytest.raises(ConfigError, match=message):
Settings(**{field: value}).validate()
def test_derated_value_cannot_increase_the_configured_output() -> None:
with pytest.raises(ConfigError, match="must not exceed"):
Settings(open_control_raw=50, derated_control_raw=100).validate()