From abb1328a6739d48cf3ee2b8b39cda7c2892876a2 Mon Sep 17 00:00:00 2001
From: Allen Porter <allen@thebends.org>
Date: Sun, 12 Nov 2023 10:44:26 -0800
Subject: [PATCH] Fix for Google Calendar API returning invalid RRULE:DATE
 rules (#103870)

---
 homeassistant/components/google/calendar.py |  9 +++-
 tests/components/google/test_calendar.py    | 48 +++++++++++++++++++++
 2 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py
index bd0fe18912e..3e34a7234a4 100644
--- a/homeassistant/components/google/calendar.py
+++ b/homeassistant/components/google/calendar.py
@@ -521,8 +521,13 @@ class GoogleCalendarEntity(
 def _get_calendar_event(event: Event) -> CalendarEvent:
     """Return a CalendarEvent from an API event."""
     rrule: str | None = None
-    if len(event.recurrence) == 1:
-        rrule = event.recurrence[0].lstrip(RRULE_PREFIX)
+    # Home Assistant expects a single RRULE: and all other rule types are unsupported or ignored
+    if (
+        len(event.recurrence) == 1
+        and (raw_rule := event.recurrence[0])
+        and raw_rule.startswith(RRULE_PREFIX)
+    ):
+        rrule = raw_rule.removeprefix(RRULE_PREFIX)
     return CalendarEvent(
         uid=event.ical_uuid,
         recurrence_id=event.id if event.recurring_event_id else None,
diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py
index 3617456c9e6..a70cd8aee9f 100644
--- a/tests/components/google/test_calendar.py
+++ b/tests/components/google/test_calendar.py
@@ -1299,3 +1299,51 @@ async def test_event_differs_timezone(
         "description": event["description"],
         "supported_features": 3,
     }
+
+
+async def test_invalid_rrule_fix(
+    hass: HomeAssistant,
+    hass_client: ClientSessionGenerator,
+    mock_events_list_items,
+    component_setup,
+) -> None:
+    """Test that an invalid RRULE returned from Google Calendar API is handled correctly end to end."""
+    week_from_today = dt_util.now().date() + datetime.timedelta(days=7)
+    end_event = week_from_today + datetime.timedelta(days=1)
+    event = {
+        **TEST_EVENT,
+        "start": {"date": week_from_today.isoformat()},
+        "end": {"date": end_event.isoformat()},
+        "recurrence": [
+            "RRULE:DATE;TZID=Europe/Warsaw:20230818T020000,20230915T020000,20231013T020000,20231110T010000,20231208T010000",
+        ],
+    }
+    mock_events_list_items([event])
+
+    assert await component_setup()
+
+    state = hass.states.get(TEST_ENTITY)
+    assert state.name == TEST_ENTITY_NAME
+    assert state.state == STATE_OFF
+
+    # Pick a date range that contains two instances of the event
+    web_client = await hass_client()
+    response = await web_client.get(
+        get_events_url(TEST_ENTITY, "2023-08-10T00:00:00Z", "2023-09-20T00:00:00Z")
+    )
+    assert response.status == HTTPStatus.OK
+    events = await response.json()
+
+    # Both instances are returned, however the RDATE rule is ignored by Home
+    # Assistant so they are just treateded as flattened events.
+    assert len(events) == 2
+
+    event = events[0]
+    assert event["uid"] == "cydrevtfuybguinhomj@google.com"
+    assert event["recurrence_id"] == "_c8rinwq863h45qnucyoi43ny8_20230818"
+    assert event["rrule"] is None
+
+    event = events[1]
+    assert event["uid"] == "cydrevtfuybguinhomj@google.com"
+    assert event["recurrence_id"] == "_c8rinwq863h45qnucyoi43ny8_20230915"
+    assert event["rrule"] is None
-- 
GitLab