| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- from datetime import date, time, datetime, timezone
- from unittest.mock import AsyncMock, patch
- import pytest
- from homeassistant.core import HomeAssistant
- from custom_components.room_cal_ha.const import (
- CONF_CALDAV_ENABLED,
- CONF_CALDAV_URL,
- CONF_CALENDAR_NAME,
- DOMAIN,
- STORE_CURRENT,
- STORE_NEXT,
- )
- from custom_components.room_cal_ha.coordinator import RoomCalCoordinator
- from custom_components.room_cal_ha.models import ReservationData
- from pytest_homeassistant_custom_component.common import MockConfigEntry
- 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",
- )
- CALDAV_OPTIONS = {
- CONF_CALDAV_ENABLED: True,
- CONF_CALDAV_URL: "https://cal.example.com",
- CONF_CALENDAR_NAME: "Room",
- }
- _ENTRY_DATA = {
- "room_state_name": "Room State",
- "current_name": "Current",
- "next_name": "Next",
- }
- def _caldav_entry(hass):
- entry = MockConfigEntry(domain=DOMAIN, data=_ENTRY_DATA, options=CALDAV_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
- async def test_caldav_disabled_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
- async def test_caldav_fetch_writes_store_and_timestamps(hass: HomeAssistant):
- entry = _caldav_entry(hass)
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- with patch.object(coord, "_fetch_caldav", 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 = _caldav_entry(hass)
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- await coord.store.async_set(STORE_CURRENT, SAMPLE)
- with patch.object(coord, "_fetch_caldav", 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_failure_preserves_store(hass: HomeAssistant):
- entry = _caldav_entry(hass)
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- await coord.store.async_set(STORE_CURRENT, SAMPLE)
- with patch.object(coord, "_fetch_caldav", side_effect=Exception("network error")):
- 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):
- import caldav.lib.error as caldav_error
- entry = _caldav_entry(hass)
- coord = RoomCalCoordinator(hass, entry)
- await coord.async_setup()
- with (
- patch.object(coord, "_fetch_caldav", side_effect=caldav_error.AuthorizationError),
- patch.object(entry, "async_start_reauth") as mock_reauth,
- ):
- await coord.async_refresh()
- mock_reauth.assert_called_once_with(hass)
- 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"
- # Untouched fields preserved
- 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
- 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)
- # New coordinator instance simulates HA restart
- 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"
|