"""Unit tests for _parse_event in the CalDAV backend.""" import json from datetime import date, time from unittest.mock import MagicMock import pytest from homeassistant.core import HomeAssistant from custom_components.room_cal_ha.backends.caldav import _parse_event from custom_components.room_cal_ha.models import ReservationData # --------------------------------------------------------------------------- # iCal helpers # --------------------------------------------------------------------------- _X_DATA = { "guest_name": "Alice Smith", "wifi_key": "secret-key", "door_code": "4567", } def _make_event(summary: str, dtstart: str, dtend: str, x_data=None, all_day=False): """Build a mock caldav.Event with the given iCal fields.""" if all_day: start_line = f"DTSTART;VALUE=DATE:{dtstart}" end_line = f"DTEND;VALUE=DATE:{dtend}" else: start_line = f"DTSTART:{dtstart}Z" end_line = f"DTEND:{dtend}Z" lines = [ "BEGIN:VCALENDAR", "VERSION:2.0", "BEGIN:VEVENT", f"SUMMARY:{summary}", start_line, end_line, ] if x_data is not None: lines.append(f"X-ROOMCAL-DATA:{json.dumps(x_data)}") lines += ["END:VEVENT", "END:VCALENDAR", ""] event = MagicMock() event.data = "\r\n".join(lines) return event # --------------------------------------------------------------------------- # Valid event # --------------------------------------------------------------------------- def test_parse_valid_event_returns_reservation_data(hass: HomeAssistant): event = _make_event("Guest: Alice", "20260701T140000", "20260705T120000", _X_DATA) result = _parse_event(event) assert isinstance(result, ReservationData) assert result.guest_name == "Alice Smith" assert result.wifi_key == "secret-key" assert result.door_code == "4567" def test_parse_valid_event_extracts_dates(hass: HomeAssistant): event = _make_event("Guest: Alice", "20260701T140000", "20260705T120000", _X_DATA) result = _parse_event(event) assert result is not None assert result.check_in_date == date(2026, 7, 1) assert result.check_out_date == date(2026, 7, 5) def test_parse_valid_event_extracts_times(hass: HomeAssistant): from zoneinfo import ZoneInfo from homeassistant.util import dt as dt_util dt_util.set_default_time_zone(ZoneInfo("UTC")) event = _make_event("Guest: Alice", "20260701T140000", "20260705T120000", _X_DATA) result = _parse_event(event) assert result is not None assert result.check_in_time == time(14, 0) assert result.check_out_time == time(12, 0) # --------------------------------------------------------------------------- # Filtering — events that should return None # --------------------------------------------------------------------------- def test_parse_wrong_title_prefix_returns_none(hass: HomeAssistant): event = _make_event("Blocked", "20260701T140000", "20260705T120000", _X_DATA) assert _parse_event(event) is None def test_parse_all_day_event_returns_none(hass: HomeAssistant): event = _make_event("Guest: Alice", "20260701", "20260705", _X_DATA, all_day=True) assert _parse_event(event) is None def test_parse_missing_x_data_returns_none(hass: HomeAssistant): event = _make_event("Guest: Alice", "20260701T140000", "20260705T120000", x_data=None) assert _parse_event(event) is None def test_parse_invalid_json_in_x_data_returns_none(hass: HomeAssistant): event = _make_event("Guest: Alice", "20260701T140000", "20260705T120000") # Manually inject bad JSON into the iCal event.data = event.data.replace("END:VEVENT", "X-ROOMCAL-DATA:not-valid-json\r\nEND:VEVENT") assert _parse_event(event) is None def test_parse_garbage_ical_returns_none(hass: HomeAssistant): event = MagicMock() event.data = "this is not ical at all" # Should not raise — catches all exceptions result = _parse_event(event) assert result is None # --------------------------------------------------------------------------- # Partial data # --------------------------------------------------------------------------- def test_parse_event_missing_dtend_still_returns_result(hass: HomeAssistant): """DTEND is optional — event without it should still parse (checkout fields will be None).""" lines = [ "BEGIN:VCALENDAR", "VERSION:2.0", "BEGIN:VEVENT", "SUMMARY:Guest: Alice", "DTSTART:20260701T140000Z", f"X-ROOMCAL-DATA:{json.dumps(_X_DATA)}", "END:VEVENT", "END:VCALENDAR", "", ] event = MagicMock() event.data = "\r\n".join(lines) result = _parse_event(event) assert result is not None assert result.check_in_date == date(2026, 7, 1) assert result.check_out_date is None assert result.check_out_time is None def test_parse_event_x_data_missing_optional_fields(hass: HomeAssistant): """X-ROOMCAL-DATA may omit wifi_key / door_code — those become None.""" sparse = {"guest_name": "Bob"} event = _make_event("Guest: Bob", "20260801T140000", "20260803T120000", sparse) result = _parse_event(event) assert result is not None assert result.guest_name == "Bob" assert result.wifi_key is None assert result.door_code is None