| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- from datetime import date, datetime, time
- from unittest.mock import AsyncMock, MagicMock, patch
- import pytest
- from homeassistant.core import HomeAssistant
- from homeassistant.exceptions import HomeAssistantError
- from pytest_homeassistant_custom_component.common import MockConfigEntry
- from custom_components.room_cal_ha.backends import BackendAuthError, BackendConnectError
- from custom_components.room_cal_ha.const import (
- CONF_API_BASE_URL,
- CONF_API_TOKEN,
- CONF_CALDAV_URL,
- CONF_CALENDAR_NAME,
- CONF_SYNC_MODE,
- DOMAIN,
- STORE_CURRENT,
- STORE_NEXT,
- SYNC_MODE_API,
- SYNC_MODE_CALDAV,
- SYNC_MODE_MANUAL,
- )
- from custom_components.room_cal_ha.coordinator import RoomCalCoordinator
- from custom_components.room_cal_ha.models import ApiStatusData, OverrideData, ReservationData
- SAMPLE = ReservationData(
- guest_name="Alice",
- check_in_date=date(2026, 7, 1),
- check_in_time=time(14, 0),
- check_out_date=date(2026, 7, 5),
- check_out_time=time(12, 0),
- wifi_key="stone-angel-market",
- door_code="0472",
- )
- _BASE_DATA = {
- "room_state_name": "Room State",
- "current_name": "Current",
- "next_name": "Next",
- }
- def _make_entry(hass, options: dict) -> MockConfigEntry:
- entry = MockConfigEntry(domain=DOMAIN, data=_BASE_DATA, options=options)
- entry.add_to_hass(hass)
- return entry
- @pytest.fixture
- async def coordinator(hass: HomeAssistant, mock_entry):
- mock_entry.add_to_hass(hass)
- coord = RoomCalCoordinator(hass, mock_entry)
- await coord.async_setup()
- return coord
- # ---------------------------------------------------------------------------
- # Manual mode
- # ---------------------------------------------------------------------------
- async def test_manual_mode_serves_empty(coordinator: RoomCalCoordinator):
- await coordinator.async_refresh()
- assert coordinator.data.current == ReservationData()
- assert coordinator.data.next == ReservationData()
- assert coordinator.data.current_last_refresh is None
- assert coordinator.data.next_last_refresh is None
- assert coordinator.data.api_status is None
- # ---------------------------------------------------------------------------
- # CalDAV mode
- # ---------------------------------------------------------------------------
- async def test_caldav_fetch_writes_store_and_timestamps(hass: HomeAssistant):
- entry = _make_entry(hass, {CONF_SYNC_MODE: SYNC_MODE_CALDAV, CONF_CALDAV_URL: "https://cal.example.com", CONF_CALENDAR_NAME: "Room"})
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- with patch("custom_components.room_cal_ha.coordinator.CalDavBackend") as MockBackend:
- MockBackend.return_value.async_fetch = AsyncMock(return_value=(SAMPLE, None))
- await coord.async_refresh()
- assert coord.data.current.guest_name == "Alice"
- assert coord.data.current.door_code == "0472"
- assert coord.data.next == ReservationData()
- assert coord.data.current_last_refresh is not None
- assert coord.data.next_last_refresh is not None
- async def test_caldav_empty_result_clears_store(hass: HomeAssistant):
- entry = _make_entry(hass, {CONF_SYNC_MODE: SYNC_MODE_CALDAV, CONF_CALDAV_URL: "https://cal.example.com", CONF_CALENDAR_NAME: "Room"})
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- await coord.store.async_set(STORE_CURRENT, SAMPLE)
- with patch("custom_components.room_cal_ha.coordinator.CalDavBackend") as MockBackend:
- MockBackend.return_value.async_fetch = AsyncMock(return_value=(None, None))
- await coord.async_refresh()
- assert coord.data.current == ReservationData()
- assert coord.store.get(STORE_CURRENT) is None
- async def test_caldav_connect_error_preserves_store(hass: HomeAssistant):
- entry = _make_entry(hass, {CONF_SYNC_MODE: SYNC_MODE_CALDAV, CONF_CALDAV_URL: "https://cal.example.com", CONF_CALENDAR_NAME: "Room"})
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- await coord.store.async_set(STORE_CURRENT, SAMPLE)
- with patch("custom_components.room_cal_ha.coordinator.CalDavBackend") as MockBackend:
- MockBackend.return_value.async_fetch = AsyncMock(side_effect=BackendConnectError("timeout"))
- await coord.async_refresh()
- assert coord.data.current.guest_name == "Alice"
- assert coord.data.current_last_refresh is None
- async def test_caldav_auth_error_triggers_reauth(hass: HomeAssistant):
- entry = _make_entry(hass, {CONF_SYNC_MODE: SYNC_MODE_CALDAV, CONF_CALDAV_URL: "https://cal.example.com", CONF_CALENDAR_NAME: "Room"})
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- with (
- patch("custom_components.room_cal_ha.coordinator.CalDavBackend") as MockBackend,
- patch.object(entry, "async_start_reauth") as mock_reauth,
- ):
- MockBackend.return_value.async_fetch = AsyncMock(side_effect=BackendAuthError)
- await coord.async_refresh()
- mock_reauth.assert_called_once_with(hass)
- # ---------------------------------------------------------------------------
- # API mode
- # ---------------------------------------------------------------------------
- SAMPLE_STATUS = ApiStatusData(
- status="ok",
- checks={"caldav_connection": {"ok": True}, "calendar": {"ok": True}},
- )
- async def test_api_fetch_writes_store_and_status(hass: HomeAssistant):
- entry = _make_entry(hass, {CONF_SYNC_MODE: SYNC_MODE_API, CONF_API_BASE_URL: "https://api.example.com"})
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- with patch("custom_components.room_cal_ha.coordinator.RoomCalApiBackend") as MockBackend:
- instance = MockBackend.return_value
- instance.async_fetch = AsyncMock(return_value=(SAMPLE, None))
- instance.async_fetch_status = AsyncMock(return_value=SAMPLE_STATUS)
- instance.async_fetch_services = AsyncMock(return_value={})
- await coord.async_refresh()
- assert coord.data.current.guest_name == "Alice"
- assert coord.data.api_status.status == "ok"
- assert coord.data.current_last_refresh is not None
- async def test_api_connect_error_preserves_store_and_still_fetches_status(hass: HomeAssistant):
- entry = _make_entry(hass, {CONF_SYNC_MODE: SYNC_MODE_API, CONF_API_BASE_URL: "https://api.example.com"})
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- await coord.store.async_set(STORE_CURRENT, SAMPLE)
- degraded = ApiStatusData(status="error")
- with patch("custom_components.room_cal_ha.coordinator.RoomCalApiBackend") as MockBackend:
- instance = MockBackend.return_value
- instance.async_fetch = AsyncMock(side_effect=BackendConnectError("timeout"))
- instance.async_fetch_status = AsyncMock(return_value=degraded)
- instance.async_fetch_services = AsyncMock(return_value={})
- await coord.async_refresh()
- assert coord.data.current.guest_name == "Alice"
- assert coord.data.current_last_refresh is None
- assert coord.data.api_status.status == "error"
- async def test_api_auth_error_triggers_reauth(hass: HomeAssistant):
- entry = _make_entry(hass, {CONF_SYNC_MODE: SYNC_MODE_API, CONF_API_BASE_URL: "https://api.example.com"})
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- with (
- patch("custom_components.room_cal_ha.coordinator.RoomCalApiBackend") as MockBackend,
- patch.object(entry, "async_start_reauth") as mock_reauth,
- ):
- instance = MockBackend.return_value
- instance.async_fetch = AsyncMock(side_effect=BackendAuthError)
- instance.async_fetch_status = AsyncMock(return_value=ApiStatusData(status="auth_error"))
- instance.async_fetch_services = AsyncMock(return_value={})
- await coord.async_refresh()
- mock_reauth.assert_called_once_with(hass)
- # ---------------------------------------------------------------------------
- # Thin updates
- # ---------------------------------------------------------------------------
- async def test_thin_update_overlays_fields(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_CURRENT, SAMPLE)
- await coordinator.async_refresh()
- await coordinator.async_apply_update(STORE_CURRENT, {"wifi_ssid": "GuestNet", "door_code": "9999"})
- assert coordinator.data.current.wifi_ssid == "GuestNet"
- assert coordinator.data.current.door_code == "9999"
- assert coordinator.data.current.guest_name == "Alice"
- assert coordinator.data.current.wifi_key == "stone-angel-market"
- async def test_thin_update_updates_last_refresh(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_CURRENT, SAMPLE)
- await coordinator.async_refresh()
- assert coordinator.data.current_last_refresh is None
- await coordinator.async_apply_update(STORE_CURRENT, {"door_code": "1234"})
- assert coordinator.data.current_last_refresh is not None
- # ---------------------------------------------------------------------------
- # Store persistence
- # ---------------------------------------------------------------------------
- async def test_store_persists_across_reload(hass: HomeAssistant, mock_entry):
- mock_entry.add_to_hass(hass)
- coord = RoomCalCoordinator(hass, mock_entry)
- await coord.async_setup()
- await coord.store.async_set(STORE_CURRENT, SAMPLE)
- coord2 = RoomCalCoordinator(hass, mock_entry)
- await coord2.async_setup()
- loaded = coord2.store.get(STORE_CURRENT)
- assert loaded is not None
- assert loaded.guest_name == "Alice"
- assert loaded.door_code == "0472"
- # ---------------------------------------------------------------------------
- # _reservation_window_state
- # ---------------------------------------------------------------------------
- DATED = ReservationData(
- check_in_date=date(2026, 7, 1),
- check_in_time=time(14, 0),
- check_out_date=date(2026, 7, 5),
- check_out_time=time(12, 0),
- )
- def _state_at(coordinator, res, dt_str):
- """Freeze dt_util.now() to a naive local datetime and call _reservation_window_state."""
- frozen = MagicMock()
- frozen.replace.return_value = datetime.fromisoformat(dt_str) # naive, no TZ
- with patch("custom_components.room_cal_ha.coordinator.dt_util.now", return_value=frozen):
- return coordinator._reservation_window_state(res)
- async def test_window_state_active(coordinator):
- assert _state_at(coordinator, DATED, "2026-07-03T10:00:00") == "active"
- async def test_window_state_pending(coordinator):
- assert _state_at(coordinator, DATED, "2026-06-30T10:00:00") == "pending"
- async def test_window_state_elapsed(coordinator):
- assert _state_at(coordinator, DATED, "2026-07-10T12:00:00") == "elapsed"
- async def test_window_state_at_check_in_is_active(coordinator):
- assert _state_at(coordinator, DATED, "2026-07-01T14:00:00") == "active"
- async def test_window_state_at_check_out_is_elapsed(coordinator):
- assert _state_at(coordinator, DATED, "2026-07-05T12:00:00") == "elapsed"
- async def test_window_state_no_dates_returns_none(coordinator):
- assert coordinator._reservation_window_state(ReservationData()) is None
- # ---------------------------------------------------------------------------
- # Check-in WiFi key behavior
- # ---------------------------------------------------------------------------
- _WIFI_NEXT = ReservationData(
- guest_name="Bob",
- check_in_date=date(2026, 7, 1),
- check_in_time=time(14, 0),
- check_out_date=date(2026, 7, 5),
- check_out_time=time(12, 0),
- wifi_ssid="GuestNet",
- wifi_key="securepass",
- )
- async def test_check_in_calls_wifi_key(hass: HomeAssistant, coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_NEXT, _WIFI_NEXT)
- mock_switch = MagicMock()
- mock_switch.async_turn_on = AsyncMock()
- coordinator._guest_mode_switch = mock_switch
- with patch("custom_components.room_cal_ha.wifi.async_set_wifi_key", new_callable=AsyncMock) as mock_wifi:
- await coordinator._async_do_check_in()
- mock_wifi.assert_awaited_once_with(hass, "GuestNet", "securepass")
- mock_switch.async_turn_on.assert_awaited_once()
- async def test_check_in_skips_wifi_when_ssid_none(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_NEXT, ReservationData(wifi_ssid=None, wifi_key="securepass"))
- coordinator._guest_mode_switch = None
- with patch("custom_components.room_cal_ha.wifi.async_set_wifi_key", new_callable=AsyncMock) as mock_wifi:
- await coordinator._async_do_check_in()
- mock_wifi.assert_not_awaited()
- async def test_check_in_skips_wifi_when_key_none(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_NEXT, ReservationData(wifi_ssid="GuestNet", wifi_key=None))
- coordinator._guest_mode_switch = None
- with patch("custom_components.room_cal_ha.wifi.async_set_wifi_key", new_callable=AsyncMock) as mock_wifi:
- await coordinator._async_do_check_in()
- mock_wifi.assert_not_awaited()
- async def test_check_in_wifi_error_still_activates_guest_mode(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_NEXT, _WIFI_NEXT)
- mock_switch = MagicMock()
- mock_switch.async_turn_on = AsyncMock()
- coordinator._guest_mode_switch = mock_switch
- with patch("custom_components.room_cal_ha.wifi.async_set_wifi_key", side_effect=HomeAssistantError("hub down")):
- await coordinator._async_do_check_in()
- mock_switch.async_turn_on.assert_awaited_once()
- # ---------------------------------------------------------------------------
- # async_start_override / async_end_override
- # ---------------------------------------------------------------------------
- _OVERRIDE_RES = ReservationData(guest_name="Override Guest", door_code="1111")
- _WIFI_OVERRIDE = ReservationData(guest_name="WiFi Guest", wifi_ssid="GuestNet", wifi_key="secret")
- async def test_start_override_sets_current_in_data(coordinator: RoomCalCoordinator):
- coordinator._guest_mode_switch = None
- await coordinator.async_start_override(OverrideData(current=_OVERRIDE_RES))
- assert coordinator.data.current.guest_name == "Override Guest"
- async def test_start_override_sets_override_active_flag(coordinator: RoomCalCoordinator):
- coordinator._guest_mode_switch = None
- await coordinator.async_start_override(OverrideData(current=_OVERRIDE_RES))
- assert coordinator.data.override_active is True
- async def test_start_override_cancels_mode_sync(coordinator: RoomCalCoordinator):
- cancel1, cancel2 = MagicMock(), MagicMock()
- coordinator._mode_sync_handles = [cancel1, cancel2]
- coordinator._guest_mode_switch = None
- await coordinator.async_start_override(OverrideData(current=_OVERRIDE_RES))
- cancel1.assert_called_once()
- cancel2.assert_called_once()
- assert coordinator._mode_sync_handles == []
- async def test_start_override_turns_on_guest_mode(coordinator: RoomCalCoordinator):
- mock_switch = MagicMock()
- mock_switch.async_turn_on = AsyncMock()
- coordinator._guest_mode_switch = mock_switch
- await coordinator.async_start_override(OverrideData(current=_OVERRIDE_RES))
- mock_switch.async_turn_on.assert_awaited_once()
- async def test_start_override_calls_wifi_when_credentials_present(hass: HomeAssistant, coordinator: RoomCalCoordinator):
- coordinator._guest_mode_switch = MagicMock()
- coordinator._guest_mode_switch.async_turn_on = AsyncMock()
- with patch("custom_components.room_cal_ha.wifi.async_set_wifi_key", new_callable=AsyncMock) as mock_wifi:
- await coordinator.async_start_override(OverrideData(current=_WIFI_OVERRIDE))
- mock_wifi.assert_awaited_once_with(hass, "GuestNet", "secret")
- async def test_start_override_skips_wifi_when_ssid_missing(coordinator: RoomCalCoordinator):
- coordinator._guest_mode_switch = None
- no_ssid = ReservationData(wifi_ssid=None, wifi_key="secret")
- with patch("custom_components.room_cal_ha.wifi.async_set_wifi_key", new_callable=AsyncMock) as mock_wifi:
- await coordinator.async_start_override(OverrideData(current=no_ssid))
- mock_wifi.assert_not_awaited()
- async def test_start_override_uses_store_next_when_override_next_is_none(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_NEXT, SAMPLE)
- coordinator._guest_mode_switch = None
- await coordinator.async_start_override(OverrideData(current=_OVERRIDE_RES))
- assert coordinator.data.next.guest_name == "Alice"
- async def test_start_override_uses_override_next_when_provided(coordinator: RoomCalCoordinator):
- override_next = ReservationData(guest_name="Override Next")
- coordinator._guest_mode_switch = None
- await coordinator.async_start_override(OverrideData(current=_OVERRIDE_RES, next=override_next))
- assert coordinator.data.next.guest_name == "Override Next"
- async def test_end_override_clears_override_active(coordinator: RoomCalCoordinator):
- coordinator._guest_mode_switch = None
- coordinator._override = OverrideData(current=_OVERRIDE_RES)
- await coordinator.async_end_override()
- assert coordinator.data.override_active is False
- async def test_end_override_restores_store_current(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_CURRENT, SAMPLE)
- coordinator._guest_mode_switch = None
- coordinator._override = OverrideData(current=_OVERRIDE_RES)
- await coordinator.async_end_override()
- assert coordinator.data.current.guest_name == "Alice"
- # ---------------------------------------------------------------------------
- # Thin updates during override
- # ---------------------------------------------------------------------------
- async def test_thin_update_current_during_override_updates_override_not_store(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_CURRENT, SAMPLE)
- await coordinator.async_refresh()
- coordinator._override = OverrideData(current=_OVERRIDE_RES)
- await coordinator.async_apply_update(STORE_CURRENT, {"door_code": "9999"})
- assert coordinator.data.current.door_code == "9999"
- # Store is unchanged
- assert coordinator.store.get(STORE_CURRENT).door_code == "0472"
- async def test_thin_update_current_during_override_data_still_shows_override_active(coordinator: RoomCalCoordinator):
- await coordinator.async_refresh()
- coordinator._override = OverrideData(current=_OVERRIDE_RES)
- await coordinator.async_apply_update(STORE_CURRENT, {"door_code": "9999"})
- assert coordinator.data.override_active is True
- async def test_thin_update_next_during_override_falls_through_to_store(coordinator: RoomCalCoordinator):
- coordinator._override = OverrideData(current=_OVERRIDE_RES) # override.next is None
- await coordinator.async_refresh()
- await coordinator.async_apply_update(STORE_NEXT, {"guest_name": "Next Guest"})
- assert coordinator.store.get(STORE_NEXT).guest_name == "Next Guest"
- assert coordinator.data.override_active is True
- assert coordinator.data.current.guest_name == "Override Guest"
- # ---------------------------------------------------------------------------
- # Backend fetch during override preserves override display
- # ---------------------------------------------------------------------------
- async def test_backend_fetch_during_override_updates_store_but_preserves_override(hass: HomeAssistant):
- entry = _make_entry(hass, {CONF_SYNC_MODE: SYNC_MODE_CALDAV, CONF_CALDAV_URL: "https://cal.example.com", CONF_CALENDAR_NAME: "Room"})
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- coord._override = OverrideData(current=_OVERRIDE_RES)
- backend_data = ReservationData(guest_name="Backend Alice")
- with patch("custom_components.room_cal_ha.coordinator.CalDavBackend") as MockBackend:
- MockBackend.return_value.async_fetch = AsyncMock(return_value=(backend_data, None))
- await coord.async_refresh()
- # Store was written by the backend
- assert coord.store.get(STORE_CURRENT).guest_name == "Backend Alice"
- # But displayed data still shows the override
- assert coord.data.current.guest_name == "Override Guest"
- assert coord.data.override_active is True
- # ---------------------------------------------------------------------------
- # async_reconcile_from_store (shared helper)
- # ---------------------------------------------------------------------------
- _ACTIVE = ReservationData(
- guest_name="Active Guest",
- check_in_date=date(2026, 6, 20),
- check_in_time=time(14, 0),
- check_out_date=date(2026, 6, 25),
- check_out_time=time(12, 0),
- )
- _ELAPSED = ReservationData(
- guest_name="Past Guest",
- check_in_date=date(2026, 6, 1),
- check_in_time=time(14, 0),
- check_out_date=date(2026, 6, 5),
- check_out_time=time(12, 0),
- )
- _FUTURE = ReservationData(
- guest_name="Future Guest",
- check_in_date=date(2026, 7, 1),
- check_in_time=time(14, 0),
- check_out_date=date(2026, 7, 5),
- check_out_time=time(12, 0),
- )
- # Freeze time at 2026-06-22 12:00 local (naive)
- _RECONCILE_NOW = MagicMock()
- _RECONCILE_NOW.replace.return_value = datetime(2026, 6, 22, 12, 0, 0)
- async def test_reconcile_active_current_turns_on_guest_mode(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_CURRENT, _ACTIVE)
- mock_switch = MagicMock()
- mock_switch.async_turn_on = AsyncMock()
- mock_switch.async_turn_off = AsyncMock()
- coordinator._guest_mode_switch = mock_switch
- with (
- patch.object(coordinator, "_is_mode_sync_on", return_value=True),
- patch("custom_components.room_cal_ha.coordinator.dt_util.now", return_value=_RECONCILE_NOW),
- ):
- await coordinator.async_reconcile_from_store()
- coordinator.cancel_mode_sync()
- mock_switch.async_turn_on.assert_awaited_once()
- mock_switch.async_turn_off.assert_not_awaited()
- async def test_reconcile_elapsed_current_clears_store_and_turns_off_guest_mode(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_CURRENT, _ELAPSED)
- mock_switch = MagicMock()
- mock_switch.async_turn_on = AsyncMock()
- mock_switch.async_turn_off = AsyncMock()
- coordinator._guest_mode_switch = mock_switch
- with (
- patch.object(coordinator, "_is_mode_sync_on", return_value=True),
- patch("custom_components.room_cal_ha.coordinator.dt_util.now", return_value=_RECONCILE_NOW),
- ):
- await coordinator.async_reconcile_from_store()
- assert coordinator.store.get(STORE_CURRENT) is None
- mock_switch.async_turn_off.assert_awaited_once()
- async def test_reconcile_promotes_next_when_check_in_elapsed(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_NEXT, _ACTIVE)
- with (
- patch.object(coordinator, "_is_mode_sync_on", return_value=True),
- patch("custom_components.room_cal_ha.coordinator.dt_util.now", return_value=_RECONCILE_NOW),
- ):
- await coordinator.async_reconcile_from_store()
- coordinator.cancel_mode_sync()
- assert coordinator.store.get(STORE_CURRENT).guest_name == "Active Guest"
- async def test_reconcile_does_not_touch_guest_mode_when_mode_sync_off(coordinator: RoomCalCoordinator):
- await coordinator.store.async_set(STORE_CURRENT, _ACTIVE)
- mock_switch = MagicMock()
- mock_switch.async_turn_on = AsyncMock()
- mock_switch.async_turn_off = AsyncMock()
- coordinator._guest_mode_switch = mock_switch
- with (
- patch.object(coordinator, "_is_mode_sync_on", return_value=False),
- patch("custom_components.room_cal_ha.coordinator.dt_util.now", return_value=_RECONCILE_NOW),
- ):
- await coordinator.async_reconcile_from_store()
- mock_switch.async_turn_on.assert_not_awaited()
- mock_switch.async_turn_off.assert_not_awaited()
|