Coerce previously persisted local calendars to have valid durations (#90970)

This commit is contained in:
Allen Porter 2023-04-06 13:41:38 -07:00 committed by GitHub
parent 87c22c3ad5
commit 3595e2fd5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 115 additions and 11 deletions

View File

@ -2,7 +2,7 @@
from __future__ import annotations
from datetime import datetime
from datetime import date, datetime, timedelta
import logging
from typing import Any
@ -186,14 +186,23 @@ def _parse_event(event: dict[str, Any]) -> Event:
def _get_calendar_event(event: Event) -> CalendarEvent:
"""Return a CalendarEvent from an API event."""
start: datetime | date
end: datetime | date
if isinstance(event.start, datetime) and isinstance(event.end, datetime):
start = dt_util.as_local(event.start)
end = dt_util.as_local(event.end)
if (end - start) <= timedelta(seconds=0):
end = start + timedelta(minutes=30)
else:
start = event.start
end = event.end
if (end - start) <= timedelta(days=0):
end = start + timedelta(days=1)
return CalendarEvent(
summary=event.summary,
start=dt_util.as_local(event.start)
if isinstance(event.start, datetime)
else event.start,
end=dt_util.as_local(event.end)
if isinstance(event.end, datetime)
else event.end,
start=start,
end=end,
description=event.description,
uid=event.uid,
rrule=event.rrule.as_rrule_str() if event.rrule else None,

View File

@ -26,10 +26,10 @@ TEST_ENTITY = "calendar.light_schedule"
class FakeStore(LocalCalendarStore):
"""Mock storage implementation."""
def __init__(self, hass: HomeAssistant, path: Path) -> None:
def __init__(self, hass: HomeAssistant, path: Path, ics_content: str) -> None:
"""Initialize FakeStore."""
super().__init__(hass, path)
self._content = ""
self._content = ics_content
def _load(self) -> str:
"""Read from calendar storage."""
@ -40,15 +40,21 @@ class FakeStore(LocalCalendarStore):
self._content = ics_content
@pytest.fixture(name="ics_content", autouse=True)
def mock_ics_content() -> str:
"""Fixture to allow tests to set initial ics content for the calendar store."""
return ""
@pytest.fixture(name="store", autouse=True)
def mock_store() -> Generator[None, None, None]:
def mock_store(ics_content: str) -> Generator[None, None, None]:
"""Test cleanup, remove any media storage persisted during the test."""
stores: dict[Path, FakeStore] = {}
def new_store(hass: HomeAssistant, path: Path) -> FakeStore:
if path not in stores:
stores[path] = FakeStore(hass, path)
stores[path] = FakeStore(hass, path, ics_content)
return stores[path]
with patch(

View File

@ -1,6 +1,7 @@
"""Tests for calendar platform of local calendar."""
import datetime
import textwrap
import pytest
@ -940,3 +941,91 @@ async def test_create_event_service(
"location": "Test Location",
}
]
@pytest.mark.parametrize(
"ics_content",
[
textwrap.dedent(
"""\
BEGIN:VCALENDAR
BEGIN:VEVENT
SUMMARY:Bastille Day Party
DTSTART:19970714
DTEND:19970714
END:VEVENT
END:VCALENDAR
"""
),
textwrap.dedent(
"""\
BEGIN:VCALENDAR
BEGIN:VEVENT
SUMMARY:Bastille Day Party
DTSTART:19970714
DTEND:19970710
END:VEVENT
END:VCALENDAR
"""
),
],
ids=["no_duration", "negative"],
)
async def test_invalid_all_day_event(
ws_client: ClientFixture,
setup_integration: None,
get_events: GetEventsFn,
) -> None:
"""Test all day events with invalid durations, which are coerced to be valid."""
events = await get_events("1997-07-14T00:00:00Z", "1997-07-16T00:00:00Z")
assert list(map(event_fields, events)) == [
{
"summary": "Bastille Day Party",
"start": {"date": "1997-07-14"},
"end": {"date": "1997-07-15"},
}
]
@pytest.mark.parametrize(
"ics_content",
[
textwrap.dedent(
"""\
BEGIN:VCALENDAR
BEGIN:VEVENT
SUMMARY:Bastille Day Party
DTSTART:19970714T110000
DTEND:19970714T110000
END:VEVENT
END:VCALENDAR
"""
),
textwrap.dedent(
"""\
BEGIN:VCALENDAR
BEGIN:VEVENT
SUMMARY:Bastille Day Party
DTSTART:19970714T110000
DTEND:19970710T100000
END:VEVENT
END:VCALENDAR
"""
),
],
ids=["no_duration", "negative"],
)
async def test_invalid_event_duration(
ws_client: ClientFixture,
setup_integration: None,
get_events: GetEventsFn,
) -> None:
"""Test events with invalid durations, which are coerced to be valid."""
events = await get_events("1997-07-14T00:00:00Z", "1997-07-16T00:00:00Z")
assert list(map(event_fields, events)) == [
{
"summary": "Bastille Day Party",
"start": {"dateTime": "1997-07-14T11:00:00-06:00"},
"end": {"dateTime": "1997-07-14T11:30:00-06:00"},
}
]