diff --git a/tests/test_caldav_client.py b/tests/test_caldav_client.py index 3b952cb..426419d 100644 --- a/tests/test_caldav_client.py +++ b/tests/test_caldav_client.py @@ -5,8 +5,9 @@ from unittest.mock import MagicMock import pytest from caldav import DAVClient, Principal +from icalendar import Calendar, Event -from src.caldav_client import _process_exdate, connect_to_caldav, get_calendar +from src.caldav_client import _process_exdate, connect_to_caldav, fetch_events, get_calendar class MockDatetime: @@ -266,3 +267,242 @@ def test_process_exdate_empty_dates_list(): result = _process_exdate(mock_dt) assert result == [] + + +def create_mock_event( # noqa PLR0913 + uid, + summary, + start_time, + end_time, + last_modified=None, + description=None, + location=None, + rrule=None, + exdate=None, + recurrence_id=None, +): + """Create a mock CalDAV event.""" + event = Event() + event.add("uid", uid) + event.add("summary", summary) + event.add("dtstart", start_time) + event.add("dtend", end_time) + + if last_modified: + event.add("last-modified", last_modified) + if description: + event.add("description", description) + if location: + event.add("location", location) + if rrule: + event.add("rrule", rrule) + if exdate: + event.add("exdate", exdate) + if recurrence_id: + event.add("recurrence-id", recurrence_id) + + cal = Calendar() + cal.add_component(event) + + mock_event = MagicMock() + mock_event.data = cal.to_ical() + return mock_event + + +def test_fetch_events_single_event(): + """Test fetching a single simple event.""" + start_time = datetime(2024, 1, 1, 10, 0, tzinfo=timezone.utc) + end_time = datetime(2024, 1, 1, 11, 0, tzinfo=timezone.utc) + last_modified = datetime(2024, 1, 1, 9, 0, tzinfo=timezone.utc) + + mock_event = create_mock_event( + uid="test-event-1", + summary="Test Event", + start_time=start_time, + end_time=end_time, + last_modified=last_modified, + description="Test Description", + location="Test Location", + ) + + mock_calendar = MagicMock() + mock_calendar.name = "Test Calendar" + mock_calendar.events.return_value = [mock_event] + + result = fetch_events(mock_calendar) + + assert len(result) == 1 + event = result["test-event-1"] + assert event["uid"] == "test-event-1" + assert event["summary"] == "Test Event" + assert event["description"] == "Test Description" + assert event["location"] == "Test Location" + assert event["start"] == start_time.isoformat() + assert event["end"] == end_time.isoformat() + assert event["last_modified"] == last_modified.isoformat() + assert event["rrule"] is None + assert event["exdate"] is None + assert event["recurrence_id"] is None + + +def test_fetch_events_recurring_event(): + """Test fetching a recurring event with exceptions.""" + start_time = datetime(2024, 1, 1, 10, 0, tzinfo=timezone.utc) + end_time = datetime(2024, 1, 1, 11, 0, tzinfo=timezone.utc) + + rrule = { + "FREQ": ["WEEKLY"], + "COUNT": [4], + "BYDAY": ["MO", "WE", "FR"], + } + + exdate = datetime(2024, 1, 8, 10, 0, tzinfo=timezone.utc) + + mock_event = create_mock_event( + uid="recurring-event-1", + summary="Recurring Meeting", + start_time=start_time, + end_time=end_time, + rrule=rrule, + exdate=exdate, + ) + + mock_calendar = MagicMock() + mock_calendar.name = "Test Calendar" + mock_calendar.events.return_value = [mock_event] + + result = fetch_events(mock_calendar) + + assert len(result) == 1 + event = result["recurring-event-1"] + assert event["uid"] == "recurring-event-1" + assert event["rrule"] == rrule + assert event["exdate"] == [exdate.isoformat()] + + +def test_fetch_events_recurring_instance(): + """Test fetching a specific instance of a recurring event.""" + start_time = datetime(2024, 1, 1, 10, 0, tzinfo=timezone.utc) + end_time = datetime(2024, 1, 1, 11, 0, tzinfo=timezone.utc) + recurrence_id = datetime(2024, 1, 8, 10, 0, tzinfo=timezone.utc) + + mock_event = create_mock_event( + uid="recurring-event-1", + summary="Modified Instance", + start_time=start_time, + end_time=end_time, + recurrence_id=recurrence_id, + ) + + mock_calendar = MagicMock() + mock_calendar.name = "Test Calendar" + mock_calendar.events.return_value = [mock_event] + + result = fetch_events(mock_calendar) + + assert len(result) == 1 + event_key = "recurring-event-1-2024-01-08T10:00:00+00:00" + assert event_key in result + assert result[event_key]["recurrence_id"] == recurrence_id.isoformat() + + +def test_fetch_events_multiple_events(): + """Test fetching multiple events with different properties.""" + events = [ + create_mock_event( + uid="event-1", + summary="Regular Event", + start_time=datetime(2024, 1, 1, 10, 0, tzinfo=timezone.utc), + end_time=datetime(2024, 1, 1, 11, 0, tzinfo=timezone.utc), + ), + create_mock_event( + uid="event-2", + summary="Recurring Event", + start_time=datetime(2024, 1, 2, 14, 0, tzinfo=timezone.utc), + end_time=datetime(2024, 1, 2, 15, 0, tzinfo=timezone.utc), + rrule={"FREQ": ["DAILY"], "COUNT": [3]}, + ), + ] + + mock_calendar = MagicMock() + mock_calendar.name = "Test Calendar" + mock_calendar.events.return_value = events + + result = fetch_events(mock_calendar) + + assert len(result) == 2 # noqa PLR2004 + assert "event-1" in result + assert "event-2" in result + assert result["event-2"]["rrule"] == {"FREQ": ["DAILY"], "COUNT": [3]} + + +def test_fetch_events_empty_calendar(): + """Test fetching events from an empty calendar.""" + mock_calendar = MagicMock() + mock_calendar.name = "Empty Calendar" + mock_calendar.events.return_value = [] + + result = fetch_events(mock_calendar) + + assert isinstance(result, dict) + assert len(result) == 0 + + +def test_fetch_events_error_handling(): + """Test handling of errors when fetching events.""" + mock_calendar = MagicMock() + mock_calendar.name = "Test Calendar" + mock_calendar.events.side_effect = Exception("Failed to fetch events") + + with pytest.raises(Exception, match="Failed to fetch events"): + fetch_events(mock_calendar) + + +def test_fetch_events_malformed_event(): + """Test handling of malformed event data.""" + event1 = Event() + event2 = Event() + event2.add("uid", "good-event") + + cal1 = Calendar() + cal1.add_component(event1) + mock_event1 = MagicMock() + mock_event1.data = cal1.to_ical() + + cal2 = Calendar() + cal2.add_component(event2) + mock_event2 = MagicMock() + mock_event2.data = cal2.to_ical() + + mock_calendar = MagicMock() + mock_calendar.name = "Test Calendar" + mock_calendar.events.return_value = [mock_event1, mock_event2] + + result = fetch_events(mock_calendar) + + assert "good-event" in result + assert result["good-event"]["start"] is None + assert result["good-event"]["end"] is None + + +def test_fetch_events_with_empty_fields(): + """Test handling of events with empty optional fields.""" + mock_event = create_mock_event( + uid="event-1", + summary="Event With Empty Fields", + start_time=datetime(2024, 1, 1, 10, 0, tzinfo=timezone.utc), + end_time=datetime(2024, 1, 1, 11, 0, tzinfo=timezone.utc), + description="", + location="", + ) + + mock_calendar = MagicMock() + mock_calendar.name = "Test Calendar" + mock_calendar.events.return_value = [mock_event] + + result = fetch_events(mock_calendar) + + assert len(result) == 1 + event = result["event-1"] + assert event["description"] == "" + assert event["location"] == ""