| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- """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
|