test_runtime.py (2338B)
1 """Test pure manual-watering state rules without Home Assistant dependencies.""" 2 3 import sys 4 from datetime import UTC, datetime, timedelta 5 from importlib.util import module_from_spec, spec_from_file_location 6 from pathlib import Path 7 8 import pytest 9 10 _RUNTIME_PATH = Path(__file__).parents[1] / "custom_components/hunter_btt/runtime.py" 11 _SPEC = spec_from_file_location("hunter_btt_runtime", _RUNTIME_PATH) 12 assert _SPEC is not None and _SPEC.loader is not None 13 _RUNTIME = module_from_spec(_SPEC) 14 sys.modules[_SPEC.name] = _RUNTIME 15 _SPEC.loader.exec_module(_RUNTIME) 16 17 ManualControlError = _RUNTIME.ManualControlError 18 command_end_time = _RUNTIME.command_end_time 19 ensure_manual_start_allowed = _RUNTIME.ensure_manual_start_allowed 20 should_send_manual_stop = _RUNTIME.should_send_manual_stop 21 22 23 def test_start_uses_requested_duration_after_zone_confirms_watering() -> None: 24 """Avoid FF8A's immediate stale-duration response after an HA start.""" 25 now = datetime(2026, 8, 31, 12, tzinfo=UTC) 26 stale_reported_end = now + timedelta(minutes=10) 27 28 end_time = command_end_time(True, now, 5 * 60, stale_reported_end) 29 30 assert end_time == now + timedelta(minutes=5) 31 32 33 def test_command_end_time_keeps_device_value_without_confirmed_start() -> None: 34 """Use device data for a stop or an unconfirmed start.""" 35 now = datetime(2026, 8, 31, 12, tzinfo=UTC) 36 reported_end = now + timedelta(minutes=10) 37 38 assert command_end_time(False, now, 5 * 60, reported_end) == reported_end 39 assert command_end_time(True, now, None, reported_end) == reported_end 40 41 42 @pytest.mark.parametrize("is_closed", [False, None]) 43 def test_start_requires_a_confirmed_closed_valve(is_closed: bool | None) -> None: 44 """Do not silently restart a run or command a valve with unknown state.""" 45 with pytest.raises(ManualControlError): 46 ensure_manual_start_allowed(is_closed) 47 48 49 def test_start_is_allowed_when_valve_is_confirmed_closed() -> None: 50 """Allow the expected idle-to-watering transition.""" 51 ensure_manual_start_allowed(True) 52 53 54 def test_stop_only_writes_when_valve_is_confirmed_open() -> None: 55 """Keep close idempotent but reject a stop with unknown state.""" 56 assert should_send_manual_stop(False) 57 assert not should_send_manual_stop(True) 58 with pytest.raises(ManualControlError): 59 should_send_manual_stop(None)