652 lines
21 KiB
Python
652 lines
21 KiB
Python
"""Tests for the calendar automation.
|
|
|
|
The tests create calendar based automations, set up a fake set of calendar
|
|
events, then advance time to exercise that the automation is called. The
|
|
tests use a fixture that mocks out events returned by the calendar entity,
|
|
and create events using a relative time offset and then advance the clock
|
|
forward exercising the triggers.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Generator
|
|
import datetime
|
|
import logging
|
|
import secrets
|
|
from typing import Any
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from homeassistant.components import calendar
|
|
import homeassistant.components.automation as automation
|
|
from homeassistant.components.calendar.trigger import EVENT_END, EVENT_START
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.setup import async_setup_component
|
|
import homeassistant.util.dt as dt_util
|
|
|
|
from tests.common import async_fire_time_changed, async_mock_service
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
CALENDAR_ENTITY_ID = "calendar.calendar_2"
|
|
CONFIG = {calendar.DOMAIN: {"platform": "demo"}}
|
|
|
|
TEST_AUTOMATION_ACTION = {
|
|
"service": "test.automation",
|
|
"data": {
|
|
"platform": "{{ trigger.platform }}",
|
|
"event": "{{ trigger.event }}",
|
|
"calendar_event": "{{ trigger.calendar_event }}",
|
|
},
|
|
}
|
|
|
|
# The trigger sets two alarms: One based on the next event and one
|
|
# to refresh the schedule. The test advances the time an arbitrary
|
|
# amount to trigger either type of event with a small jitter.
|
|
TEST_TIME_ADVANCE_INTERVAL = datetime.timedelta(minutes=1)
|
|
TEST_UPDATE_INTERVAL = datetime.timedelta(minutes=7)
|
|
|
|
|
|
class FakeSchedule:
|
|
"""Test fixture class for return events in a specific date range."""
|
|
|
|
def __init__(self, hass, freezer):
|
|
"""Initiailize FakeSchedule."""
|
|
self.hass = hass
|
|
self.freezer = freezer
|
|
# Map of event start time to event
|
|
self.events: list[calendar.CalendarEvent] = []
|
|
|
|
def create_event(
|
|
self,
|
|
start: datetime.timedelta,
|
|
end: datetime.timedelta,
|
|
summary: str | None = None,
|
|
description: str | None = None,
|
|
location: str | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Create a new fake event, used by tests."""
|
|
event = calendar.CalendarEvent(
|
|
start=start,
|
|
end=end,
|
|
summary=summary if summary else f"Event {secrets.token_hex(16)}",
|
|
description=description,
|
|
location=location,
|
|
)
|
|
self.events.append(event)
|
|
return event.as_dict()
|
|
|
|
async def async_get_events(
|
|
self,
|
|
hass: HomeAssistant,
|
|
start_date: datetime.datetime,
|
|
end_date: datetime.datetime,
|
|
) -> list[calendar.CalendarEvent]:
|
|
"""Get all events in a specific time frame, used by the demo calendar."""
|
|
assert start_date < end_date
|
|
values = []
|
|
local_start_date = dt_util.as_local(start_date)
|
|
local_end_date = dt_util.as_local(end_date)
|
|
for event in self.events:
|
|
if (
|
|
event.start_datetime_local < local_end_date
|
|
and local_start_date < event.end_datetime_local
|
|
):
|
|
values.append(event)
|
|
return values
|
|
|
|
async def fire_time(self, trigger_time: datetime.datetime) -> None:
|
|
"""Fire an alarm and wait."""
|
|
_LOGGER.debug(f"Firing alarm @ {trigger_time}")
|
|
self.freezer.move_to(trigger_time)
|
|
async_fire_time_changed(self.hass, trigger_time)
|
|
await self.hass.async_block_till_done()
|
|
|
|
async def fire_until(self, end: datetime.timedelta) -> None:
|
|
"""Simulate the passage of time by firing alarms until the time is reached."""
|
|
|
|
current_time = dt_util.as_utc(self.freezer())
|
|
if (end - current_time) > (TEST_UPDATE_INTERVAL * 2):
|
|
# Jump ahead to right before the target alarm them to remove
|
|
# unnecessary waiting, before advancing in smaller increments below.
|
|
# This leaves time for multiple update intervals to refresh the set
|
|
# of upcoming events
|
|
await self.fire_time(end - TEST_UPDATE_INTERVAL * 2)
|
|
|
|
while dt_util.utcnow() < end:
|
|
self.freezer.tick(TEST_TIME_ADVANCE_INTERVAL)
|
|
await self.fire_time(dt_util.utcnow())
|
|
|
|
|
|
@pytest.fixture
|
|
def set_time_zone(hass):
|
|
"""Set the time zone for the tests."""
|
|
# Set our timezone to CST/Regina so we can check calculations
|
|
# This keeps UTC-6 all year round
|
|
hass.config.set_time_zone("America/Regina")
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_schedule(hass, freezer):
|
|
"""Fixture that tests can use to make fake events."""
|
|
|
|
# Setup start time for all tests
|
|
freezer.move_to("2022-04-19 10:31:02+00:00")
|
|
|
|
schedule = FakeSchedule(hass, freezer)
|
|
with patch(
|
|
"homeassistant.components.demo.calendar.DemoCalendar.async_get_events",
|
|
new=schedule.async_get_events,
|
|
):
|
|
yield schedule
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
async def setup_calendar(hass: HomeAssistant, fake_schedule: FakeSchedule) -> None:
|
|
"""Initialize the demo calendar."""
|
|
assert await async_setup_component(hass, calendar.DOMAIN, CONFIG)
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
async def create_automation(hass: HomeAssistant, event_type: str, offset=None) -> None:
|
|
"""Register an automation."""
|
|
trigger_data = {
|
|
"platform": calendar.DOMAIN,
|
|
"entity_id": CALENDAR_ENTITY_ID,
|
|
"event": event_type,
|
|
}
|
|
if offset:
|
|
trigger_data["offset"] = offset
|
|
assert await async_setup_component(
|
|
hass,
|
|
automation.DOMAIN,
|
|
{
|
|
automation.DOMAIN: {
|
|
"trigger": trigger_data,
|
|
"action": TEST_AUTOMATION_ACTION,
|
|
"mode": "queued",
|
|
}
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
@pytest.fixture
|
|
def calls(hass: HomeAssistant) -> Callable[[], list]:
|
|
"""Fixture to return payload data for automation calls."""
|
|
service_calls = async_mock_service(hass, "test", "automation")
|
|
|
|
def get_trigger_data() -> list:
|
|
return [c.data for c in service_calls]
|
|
|
|
return get_trigger_data
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def mock_update_interval() -> Generator[None, None, None]:
|
|
"""Fixture to override the update interval for refreshing events."""
|
|
with patch(
|
|
"homeassistant.components.calendar.trigger.UPDATE_INTERVAL",
|
|
new=TEST_UPDATE_INTERVAL,
|
|
):
|
|
yield
|
|
|
|
|
|
async def test_event_start_trigger(hass, calls, fake_schedule):
|
|
"""Test the a calendar trigger based on start time."""
|
|
event_data = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_START)
|
|
assert len(calls()) == 0
|
|
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data,
|
|
}
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"offset_str, offset_delta",
|
|
[
|
|
("-01:00", datetime.timedelta(hours=-1)),
|
|
("+01:00", datetime.timedelta(hours=1)),
|
|
],
|
|
)
|
|
async def test_event_start_trigger_with_offset(
|
|
hass, calls, fake_schedule, offset_str, offset_delta
|
|
):
|
|
"""Test the a calendar trigger based on start time with an offset."""
|
|
event_data = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 12:30:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_START, offset=offset_str)
|
|
|
|
# No calls yet
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:55:00+00:00") + offset_delta,
|
|
)
|
|
assert len(calls()) == 0
|
|
|
|
# Event has started w/ offset
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 12:05:00+00:00") + offset_delta,
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data,
|
|
}
|
|
]
|
|
|
|
|
|
async def test_event_end_trigger(hass, calls, fake_schedule):
|
|
"""Test the a calendar trigger based on end time."""
|
|
event_data = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_END)
|
|
|
|
# Event started, nothing should fire yet
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:10:00+00:00")
|
|
)
|
|
assert len(calls()) == 0
|
|
|
|
# Event ends
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 12:10:00+00:00")
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_END,
|
|
"calendar_event": event_data,
|
|
}
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"offset_str, offset_delta",
|
|
[
|
|
("-01:00", datetime.timedelta(hours=-1)),
|
|
("+01:00", datetime.timedelta(hours=1)),
|
|
],
|
|
)
|
|
async def test_event_end_trigger_with_offset(
|
|
hass, calls, fake_schedule, offset_str, offset_delta
|
|
):
|
|
"""Test the a calendar trigger based on end time with an offset."""
|
|
event_data = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 12:30:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_END, offset=offset_str)
|
|
|
|
# No calls yet
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 12:05:00+00:00") + offset_delta,
|
|
)
|
|
assert len(calls()) == 0
|
|
|
|
# Event has started w/ offset
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 12:35:00+00:00") + offset_delta,
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_END,
|
|
"calendar_event": event_data,
|
|
}
|
|
]
|
|
|
|
|
|
async def test_calendar_trigger_with_no_events(hass, calls, fake_schedule):
|
|
"""Test a calendar trigger setup with no events."""
|
|
|
|
await create_automation(hass, EVENT_START)
|
|
await create_automation(hass, EVENT_END)
|
|
|
|
# No calls, at arbitrary times
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00")
|
|
)
|
|
assert len(calls()) == 0
|
|
|
|
|
|
async def test_multiple_start_events(hass, calls, fake_schedule):
|
|
"""Test that a trigger fires for multiple events."""
|
|
|
|
event_data1 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 10:45:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
)
|
|
event_data2 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_START)
|
|
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00")
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data1,
|
|
},
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data2,
|
|
},
|
|
]
|
|
|
|
|
|
async def test_multiple_end_events(hass, calls, fake_schedule):
|
|
"""Test that a trigger fires for multiple events."""
|
|
|
|
event_data1 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 10:45:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
)
|
|
event_data2 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_END)
|
|
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00")
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_END,
|
|
"calendar_event": event_data1,
|
|
},
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_END,
|
|
"calendar_event": event_data2,
|
|
},
|
|
]
|
|
|
|
|
|
async def test_multiple_events_sharing_start_time(hass, calls, fake_schedule):
|
|
"""Test that a trigger fires for every event sharing a start time."""
|
|
|
|
event_data1 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
|
|
)
|
|
event_data2 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_START)
|
|
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:35:00+00:00")
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data1,
|
|
},
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data2,
|
|
},
|
|
]
|
|
|
|
|
|
async def test_overlap_events(hass, calls, fake_schedule):
|
|
"""Test that a trigger fires for events that overlap."""
|
|
|
|
event_data1 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
|
|
)
|
|
event_data2 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:45:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_START)
|
|
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:20:00+00:00")
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data1,
|
|
},
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data2,
|
|
},
|
|
]
|
|
|
|
|
|
async def test_invalid_calendar_id(hass, caplog):
|
|
"""Test creating a trigger with an invalid calendar id."""
|
|
assert await async_setup_component(
|
|
hass,
|
|
automation.DOMAIN,
|
|
{
|
|
automation.DOMAIN: {
|
|
"action": TEST_AUTOMATION_ACTION,
|
|
"trigger": {
|
|
"platform": calendar.DOMAIN,
|
|
"entity_id": "invalid-calendar-id",
|
|
},
|
|
}
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert "Invalid config for [automation]" in caplog.text
|
|
|
|
|
|
async def test_legacy_entity_type(hass, caplog):
|
|
"""Test creating a trigger with an invalid calendar id."""
|
|
assert await async_setup_component(
|
|
hass,
|
|
automation.DOMAIN,
|
|
{
|
|
automation.DOMAIN: {
|
|
"action": TEST_AUTOMATION_ACTION,
|
|
"trigger": {
|
|
"platform": calendar.DOMAIN,
|
|
"entity_id": "calendar.calendar_3",
|
|
},
|
|
}
|
|
},
|
|
)
|
|
await hass.async_block_till_done()
|
|
assert "is not a calendar entity" in caplog.text
|
|
|
|
|
|
async def test_update_next_event(hass, calls, fake_schedule):
|
|
"""Test detection of a new event after initial trigger is setup."""
|
|
|
|
event_data1 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_START)
|
|
|
|
# No calls before event start
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 10:45:00+00:00")
|
|
)
|
|
assert len(calls()) == 0
|
|
|
|
# Create a new event between now and when the event fires
|
|
event_data2 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 10:55:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:05:00+00:00"),
|
|
)
|
|
|
|
# Advance past the end of the events
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00")
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data2,
|
|
},
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data1,
|
|
},
|
|
]
|
|
|
|
|
|
async def test_update_missed(hass, calls, fake_schedule):
|
|
"""Test that new events are missed if they arrive outside the update interval."""
|
|
|
|
event_data1 = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_START)
|
|
|
|
# Events are refreshed at t+TEST_UPDATE_INTERVAL minutes. A new event is
|
|
# added, but the next update happens after the event is already over.
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 10:38:00+00:00")
|
|
)
|
|
assert len(calls()) == 0
|
|
|
|
fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 10:40:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 10:55:00+00:00"),
|
|
)
|
|
|
|
# Only the first event is returned
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:05:00+00:00")
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data1,
|
|
},
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"create_data,fire_time,payload_data",
|
|
[
|
|
(
|
|
{
|
|
"start": datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
"end": datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
|
|
"summary": "Summary",
|
|
},
|
|
datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
|
|
{
|
|
"summary": "Summary",
|
|
"start": "2022-04-19T11:00:00+00:00",
|
|
"end": "2022-04-19T11:30:00+00:00",
|
|
"all_day": False,
|
|
},
|
|
),
|
|
(
|
|
{
|
|
"start": datetime.datetime.fromisoformat("2022-04-19 11:00:00+00:00"),
|
|
"end": datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
|
|
"summary": "Summary",
|
|
"description": "Description",
|
|
"location": "Location",
|
|
},
|
|
datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"),
|
|
{
|
|
"summary": "Summary",
|
|
"start": "2022-04-19T11:00:00+00:00",
|
|
"end": "2022-04-19T11:30:00+00:00",
|
|
"all_day": False,
|
|
"description": "Description",
|
|
"location": "Location",
|
|
},
|
|
),
|
|
(
|
|
{
|
|
"summary": "Summary",
|
|
"start": datetime.date.fromisoformat("2022-04-20"),
|
|
"end": datetime.date.fromisoformat("2022-04-21"),
|
|
},
|
|
datetime.datetime.fromisoformat("2022-04-20 00:00:01-06:00"),
|
|
{
|
|
"summary": "Summary",
|
|
"start": "2022-04-20",
|
|
"end": "2022-04-21",
|
|
"all_day": True,
|
|
},
|
|
),
|
|
],
|
|
ids=["basic", "more-fields", "all-day"],
|
|
)
|
|
async def test_event_payload(
|
|
hass, calls, fake_schedule, set_time_zone, create_data, fire_time, payload_data
|
|
):
|
|
"""Test the fields in the calendar event payload are set."""
|
|
fake_schedule.create_event(**create_data)
|
|
await create_automation(hass, EVENT_START)
|
|
assert len(calls()) == 0
|
|
|
|
await fake_schedule.fire_until(fire_time)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": payload_data,
|
|
}
|
|
]
|
|
|
|
|
|
async def test_trigger_timestamp_window_edge(hass, calls, fake_schedule, freezer):
|
|
"""Test that events in the edge of a scan are included."""
|
|
freezer.move_to("2022-04-19 11:00:00+00:00")
|
|
# Exactly at a TEST_UPDATE_INTERVAL boundary the start time,
|
|
# making this excluded from the first window.
|
|
event_data = fake_schedule.create_event(
|
|
start=datetime.datetime.fromisoformat("2022-04-19 11:14:00+00:00"),
|
|
end=datetime.datetime.fromisoformat("2022-04-19 11:30:00+00:00"),
|
|
)
|
|
await create_automation(hass, EVENT_START)
|
|
assert len(calls()) == 0
|
|
|
|
await fake_schedule.fire_until(
|
|
datetime.datetime.fromisoformat("2022-04-19 11:20:00+00:00")
|
|
)
|
|
assert calls() == [
|
|
{
|
|
"platform": "calendar",
|
|
"event": EVENT_START,
|
|
"calendar_event": event_data,
|
|
}
|
|
]
|