import os import sys from datetime import datetime, date from unittest.mock import MagicMock, patch sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) import main class _Val: def __init__(self, v): self.dt = v def _fake_component(summary, dtstart_value, dtend_value, description="", uid="uid-1"): comp = {} comp["summary"] = summary comp["description"] = description comp["uid"] = uid comp["dtstart"] = _Val(dtstart_value) comp["dtend"] = _Val(dtend_value) def get(key, default=None): return comp.get(key, default) fake = MagicMock() fake.get = get return fake class _MutableComponent(dict): def get(self, key, default=None): return super().get(key, default) def test_list_upcoming_events_maps_timed_and_all_day_events(): timed = MagicMock() timed.icalendar_component = _fake_component( "THL FM LS", datetime(2026, 9, 14, 19, 0, 0), datetime(2026, 9, 14, 20, 0, 0) ) all_day = MagicMock() all_day.icalendar_component = _fake_component( "Volksfestwache", date(2026, 9, 19), date(2026, 9, 20) ) fake_calendar = MagicMock() fake_calendar.search.return_value = [all_day, timed] # unsorted on purpose with patch.object(main, "_caldav_calendar", return_value=fake_calendar): events = main._list_upcoming_events_sync(14, calendar="feuerwehr") assert [e["summary"] for e in events] == ["THL FM LS", "Volksfestwache"] assert events[0]["all_day"] is False assert events[1]["all_day"] is True assert events[0]["uid"] == "uid-1" assert events[0]["calendar"] == "feuerwehr" def test_list_upcoming_events_merges_both_calendars_when_none_given(): personal_event = MagicMock() personal_event.icalendar_component = _fake_component( "Zahnarzt", datetime(2026, 9, 14, 9, 0, 0), datetime(2026, 9, 14, 9, 30, 0), uid="uid-p" ) ffw_event = MagicMock() ffw_event.icalendar_component = _fake_component( "Einsatz", datetime(2026, 9, 14, 10, 0, 0), datetime(2026, 9, 14, 11, 0, 0), uid="uid-f" ) def fake_caldav_calendar(calendar=main.DEFAULT_CALENDAR): cal = MagicMock() cal.search.return_value = [personal_event] if calendar == "personal" else [ffw_event] return cal with patch.object(main, "_caldav_calendar", side_effect=fake_caldav_calendar): events = main._list_upcoming_events_sync(14) assert [e["uid"] for e in events] == ["uid-p", "uid-f"] assert [e["calendar"] for e in events] == ["personal", "feuerwehr"] def test_create_event_calls_save_event_with_parsed_dates(): fake_calendar = MagicMock() with patch.object(main, "_caldav_calendar", return_value=fake_calendar): result = main._create_event_sync( "JARVIS Testtermin", "2026-09-20T10:00:00", "2026-09-20T11:00:00", "desc" ) fake_calendar.save_event.assert_called_once() _, kwargs = fake_calendar.save_event.call_args assert kwargs["summary"] == "JARVIS Testtermin" assert kwargs["dtstart"] == datetime(2026, 9, 20, 10, 0, 0, tzinfo=main.CALENDAR_TIMEZONE) assert kwargs["dtstart"].tzinfo is not None assert result["summary"] == "JARVIS Testtermin" assert result["calendar"] == "personal" def test_create_event_passes_calendar_argument_through(): fake_calendar = MagicMock() with patch.object(main, "_caldav_calendar", return_value=fake_calendar) as mock_get_calendar: result = main._create_event_sync( "Einsatz", "2026-09-20T10:00:00", "2026-09-20T11:00:00", calendar="feuerwehr" ) mock_get_calendar.assert_called_once_with("feuerwehr") assert result["calendar"] == "feuerwehr" def test_update_event_moves_and_renames_by_uid(): comp = _MutableComponent() comp["dtstart"] = _Val(datetime(2026, 9, 27, 10, 0, 0)) comp["dtend"] = _Val(datetime(2026, 9, 27, 11, 0, 0)) comp["summary"] = "Old" comp["description"] = "old desc" fake_event = MagicMock() fake_event.icalendar_component = comp fake_calendar = MagicMock() fake_calendar.event_by_uid.return_value = fake_event with patch.object(main, "_caldav_calendar", return_value=fake_calendar): result = main._update_event_sync("uid-123", start="2026-09-28T14:00:00", summary="New") fake_calendar.event_by_uid.assert_called_once_with("uid-123") fake_event.save.assert_called_once() assert comp["dtstart"].dt == datetime(2026, 9, 28, 14, 0, 0, tzinfo=main.CALENDAR_TIMEZONE) assert comp["dtend"].dt == datetime(2026, 9, 27, 11, 0, 0) # untouched - not passed assert comp["summary"] == "New" assert comp["description"] == "old desc" # untouched - not passed assert result["uid"] == "uid-123" def test_update_event_passes_calendar_argument_through(): comp = _MutableComponent() comp["dtstart"] = _Val(datetime(2026, 9, 27, 10, 0, 0)) comp["dtend"] = _Val(datetime(2026, 9, 27, 11, 0, 0)) comp["summary"] = "Old" comp["description"] = "old desc" fake_event = MagicMock() fake_event.icalendar_component = comp fake_calendar = MagicMock() fake_calendar.event_by_uid.return_value = fake_event with patch.object(main, "_caldav_calendar", return_value=fake_calendar) as mock_get_calendar: main._update_event_sync("uid-123", summary="New", calendar="feuerwehr") mock_get_calendar.assert_called_once_with("feuerwehr") def test_delete_event_by_uid(): fake_event = MagicMock() fake_calendar = MagicMock() fake_calendar.event_by_uid.return_value = fake_event with patch.object(main, "_caldav_calendar", return_value=fake_calendar): result = main._delete_event_sync("uid-456") fake_calendar.event_by_uid.assert_called_once_with("uid-456") fake_event.delete.assert_called_once() assert result == {"deleted": True, "uid": "uid-456"} def test_delete_event_passes_calendar_argument_through(): fake_event = MagicMock() fake_calendar = MagicMock() fake_calendar.event_by_uid.return_value = fake_event with patch.object(main, "_caldav_calendar", return_value=fake_calendar) as mock_get_calendar: main._delete_event_sync("uid-456", calendar="feuerwehr") mock_get_calendar.assert_called_once_with("feuerwehr")