core/tests/components/caldav/test_calendar.py

428 lines
13 KiB
Python
Raw Normal View History

"""The tests for the webdav calendar component."""
import datetime
2019-05-02 07:46:12 +00:00
from unittest.mock import MagicMock, Mock
2019-05-02 07:46:12 +00:00
from asynctest import patch
from caldav.objects import Event
2019-05-02 07:46:12 +00:00
import pytest
2019-05-02 07:46:12 +00:00
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.setup import async_setup_component
from homeassistant.util import dt
2019-05-02 07:46:12 +00:00
# pylint: disable=redefined-outer-name
2019-07-31 19:25:30 +00:00
DEVICE_DATA = {"name": "Private Calendar", "device_id": "Private Calendar"}
EVENTS = [
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//E-Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:1
DTSTAMP:20171125T000000Z
DTSTART:20171127T170000Z
DTEND:20171127T180000Z
SUMMARY:This is a normal event
LOCATION:Hamburg
DESCRIPTION:Surprisingly rainy
END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Global Dynamics.//CalDAV Client//EN
BEGIN:VEVENT
UID:2
DTSTAMP:20171125T000000Z
DTSTART:20171127T100000Z
DTEND:20171127T110000Z
SUMMARY:This is an offset event !!-02:00
LOCATION:Hamburg
DESCRIPTION:Surprisingly shiny
END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Global Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:3
DTSTAMP:20171125T000000Z
DTSTART:20171127
DTEND:20171128
SUMMARY:This is an all day event
LOCATION:Hamburg
DESCRIPTION:What a beautiful day
END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Global Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:4
DTSTAMP:20171125T000000Z
DTSTART:20171127
SUMMARY:This is an event without dtend or duration
LOCATION:Hamburg
DESCRIPTION:What an endless day
END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Global Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:5
DTSTAMP:20171125T000000Z
DTSTART:20171127
DURATION:PT1H
SUMMARY:This is an event with duration
LOCATION:Hamburg
DESCRIPTION:What a day
END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Global Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:6
DTSTAMP:20171125T000000Z
DTSTART:20171127T100000Z
DURATION:PT1H
SUMMARY:This is an event with duration
LOCATION:Hamburg
DESCRIPTION:What a day
END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Global Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:7
DTSTART;TZID=America/Los_Angeles:20171127T083000
DTSTAMP:20180301T020053Z
DTEND;TZID=America/Los_Angeles:20171127T093000
SUMMARY:Enjoy the sun
LOCATION:San Francisco
DESCRIPTION:Sunny day
END:VEVENT
END:VCALENDAR
2019-07-31 19:25:30 +00:00
""",
]
2019-05-02 07:46:12 +00:00
CALDAV_CONFIG = {
"platform": "caldav",
"url": "http://test.local",
"custom_calendars": [],
}
@pytest.fixture(autouse=True)
def mock_http(hass):
"""Mock the http component."""
hass.http = Mock()
@pytest.fixture
def mock_dav_client():
"""Mock the dav client."""
patch_dav_client = patch(
2019-07-31 19:25:30 +00:00
"caldav.DAVClient", return_value=_mocked_dav_client("First", "Second")
)
2019-05-02 07:46:12 +00:00
with patch_dav_client as dav_client:
yield dav_client
2019-07-31 19:25:30 +00:00
@pytest.fixture(name="calendar")
2019-05-02 07:46:12 +00:00
def mock_private_cal():
"""Mock a private calendar."""
_calendar = _mock_calendar("Private")
calendars = [_calendar]
client = _mocked_dav_client(calendars=calendars)
2019-07-31 19:25:30 +00:00
patch_dav_client = patch("caldav.DAVClient", return_value=client)
2019-05-02 07:46:12 +00:00
with patch_dav_client:
yield _calendar
def _local_datetime(hours, minutes):
"""Build a datetime object for testing in the correct timezone."""
return dt.as_local(datetime.datetime(2017, 11, 27, hours, minutes, 0))
2019-05-02 07:46:12 +00:00
def _mocked_dav_client(*names, calendars=None):
"""Mock requests.get invocations."""
2019-05-02 07:46:12 +00:00
if calendars is None:
calendars = [_mock_calendar(name) for name in names]
principal = Mock()
principal.calendars = MagicMock(return_value=calendars)
client = Mock()
client.principal = MagicMock(return_value=principal)
return client
def _mock_calendar(name):
events = []
for idx, event in enumerate(EVENTS):
events.append(Event(None, "%d.ics" % idx, event, None, str(idx)))
calendar = Mock()
calendar.date_search = MagicMock(return_value=events)
calendar.name = name
return calendar
2019-05-02 07:46:12 +00:00
async def test_setup_component(hass, mock_dav_client):
"""Test setup component with calendars."""
2019-07-31 19:25:30 +00:00
assert await async_setup_component(hass, "calendar", {"calendar": CALDAV_CONFIG})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.first")
2019-05-02 07:46:12 +00:00
assert state.name == "First"
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.second")
2019-05-02 07:46:12 +00:00
assert state.name == "Second"
2019-07-31 19:25:30 +00:00
async def test_setup_component_with_no_calendar_matching(hass, mock_dav_client):
2019-05-02 07:46:12 +00:00
"""Test setup component with wrong calendar."""
config = dict(CALDAV_CONFIG)
2019-07-31 19:25:30 +00:00
config["calendars"] = ["none"]
2019-05-02 07:46:12 +00:00
2019-07-31 19:25:30 +00:00
assert await async_setup_component(hass, "calendar", {"calendar": config})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
all_calendar_states = hass.states.async_entity_ids("calendar")
2019-05-02 07:46:12 +00:00
assert not all_calendar_states
async def test_setup_component_with_a_calendar_match(hass, mock_dav_client):
"""Test setup component with right calendar."""
config = dict(CALDAV_CONFIG)
2019-07-31 19:25:30 +00:00
config["calendars"] = ["Second"]
2019-05-02 07:46:12 +00:00
2019-07-31 19:25:30 +00:00
assert await async_setup_component(hass, "calendar", {"calendar": config})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
all_calendar_states = hass.states.async_entity_ids("calendar")
2019-05-02 07:46:12 +00:00
assert len(all_calendar_states) == 1
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.second")
assert state.name == "Second"
2019-05-02 07:46:12 +00:00
async def test_setup_component_with_one_custom_calendar(hass, mock_dav_client):
"""Test setup component with custom calendars."""
config = dict(CALDAV_CONFIG)
2019-07-31 19:25:30 +00:00
config["custom_calendars"] = [
{"name": "HomeOffice", "calendar": "Second", "search": "HomeOffice"}
]
2019-05-02 07:46:12 +00:00
2019-07-31 19:25:30 +00:00
assert await async_setup_component(hass, "calendar", {"calendar": config})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
all_calendar_states = hass.states.async_entity_ids("calendar")
2019-05-02 07:46:12 +00:00
assert len(all_calendar_states) == 1
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.second_homeoffice")
assert state.name == "HomeOffice"
2019-05-02 07:46:12 +00:00
2019-07-31 19:25:30 +00:00
@patch("homeassistant.util.dt.now", return_value=_local_datetime(17, 45))
2019-05-02 07:46:12 +00:00
async def test_ongoing_event(mock_now, hass, calendar):
"""Test that the ongoing event is returned."""
2019-07-31 19:25:30 +00:00
assert await async_setup_component(hass, "calendar", {"calendar": CALDAV_CONFIG})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.private")
2019-05-02 07:46:12 +00:00
assert state.name == calendar.name
assert state.state == STATE_ON
assert dict(state.attributes) == {
"friendly_name": "Private",
"message": "This is a normal event",
"all_day": False,
2019-07-31 19:25:30 +00:00
"offset_reached": False,
2019-05-02 07:46:12 +00:00
"start_time": "2017-11-27 17:00:00",
"end_time": "2017-11-27 18:00:00",
"location": "Hamburg",
"description": "Surprisingly rainy",
}
2019-07-31 19:25:30 +00:00
@patch("homeassistant.util.dt.now", return_value=_local_datetime(17, 30))
2019-05-02 07:46:12 +00:00
async def test_just_ended_event(mock_now, hass, calendar):
"""Test that the next ongoing event is returned."""
2019-07-31 19:25:30 +00:00
assert await async_setup_component(hass, "calendar", {"calendar": CALDAV_CONFIG})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.private")
2019-05-02 07:46:12 +00:00
assert state.name == calendar.name
assert state.state == STATE_ON
assert dict(state.attributes) == {
"friendly_name": "Private",
"message": "This is a normal event",
"all_day": False,
"offset_reached": False,
"start_time": "2017-11-27 17:00:00",
"end_time": "2017-11-27 18:00:00",
"location": "Hamburg",
"description": "Surprisingly rainy",
}
2019-07-31 19:25:30 +00:00
@patch("homeassistant.util.dt.now", return_value=_local_datetime(17, 00))
2019-05-02 07:46:12 +00:00
async def test_ongoing_event_different_tz(mock_now, hass, calendar):
"""Test that the ongoing event with another timezone is returned."""
2019-07-31 19:25:30 +00:00
assert await async_setup_component(hass, "calendar", {"calendar": CALDAV_CONFIG})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.private")
2019-05-02 07:46:12 +00:00
assert state.name == calendar.name
assert state.state == STATE_ON
assert dict(state.attributes) == {
"friendly_name": "Private",
"message": "Enjoy the sun",
"all_day": False,
"offset_reached": False,
"start_time": "2017-11-27 16:30:00",
"description": "Sunny day",
"end_time": "2017-11-27 17:30:00",
"location": "San Francisco",
}
2019-07-31 19:25:30 +00:00
@patch("homeassistant.util.dt.now", return_value=_local_datetime(8, 30))
2019-05-02 07:46:12 +00:00
async def test_ongoing_event_with_offset(mock_now, hass, calendar):
"""Test that the offset is taken into account."""
2019-07-31 19:25:30 +00:00
assert await async_setup_component(hass, "calendar", {"calendar": CALDAV_CONFIG})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.private")
2019-05-02 07:46:12 +00:00
assert state.name == calendar.name
assert state.state == STATE_OFF
assert dict(state.attributes) == {
"friendly_name": "Private",
"message": "This is an offset event",
"all_day": False,
"offset_reached": True,
"start_time": "2017-11-27 10:00:00",
"end_time": "2017-11-27 11:00:00",
"location": "Hamburg",
"description": "Surprisingly shiny",
}
2019-07-31 19:25:30 +00:00
@patch("homeassistant.util.dt.now", return_value=_local_datetime(12, 00))
2019-05-02 07:46:12 +00:00
async def test_matching_filter(mock_now, hass, calendar):
"""Test that the matching event is returned."""
config = dict(CALDAV_CONFIG)
2019-07-31 19:25:30 +00:00
config["custom_calendars"] = [
{"name": "Private", "calendar": "Private", "search": "This is a normal event"}
]
assert await async_setup_component(hass, "calendar", {"calendar": config})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.private_private")
2019-05-02 07:46:12 +00:00
assert state.name == calendar.name
assert state.state == STATE_OFF
assert dict(state.attributes) == {
"friendly_name": "Private",
"message": "This is a normal event",
"all_day": False,
"offset_reached": False,
"start_time": "2017-11-27 17:00:00",
"end_time": "2017-11-27 18:00:00",
"location": "Hamburg",
"description": "Surprisingly rainy",
}
2019-07-31 19:25:30 +00:00
@patch("homeassistant.util.dt.now", return_value=_local_datetime(12, 00))
2019-05-02 07:46:12 +00:00
async def test_matching_filter_real_regexp(mock_now, hass, calendar):
"""Test that the event matching the regexp is returned."""
config = dict(CALDAV_CONFIG)
2019-07-31 19:25:30 +00:00
config["custom_calendars"] = [
{"name": "Private", "calendar": "Private", "search": r".*rainy"}
]
assert await async_setup_component(hass, "calendar", {"calendar": config})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.private_private")
2019-05-02 07:46:12 +00:00
assert state.name == calendar.name
assert state.state == STATE_OFF
assert dict(state.attributes) == {
"friendly_name": "Private",
"message": "This is a normal event",
"all_day": False,
"offset_reached": False,
"start_time": "2017-11-27 17:00:00",
"end_time": "2017-11-27 18:00:00",
"location": "Hamburg",
"description": "Surprisingly rainy",
}
2019-07-31 19:25:30 +00:00
@patch("homeassistant.util.dt.now", return_value=_local_datetime(20, 00))
2019-05-02 07:46:12 +00:00
async def test_filter_matching_past_event(mock_now, hass, calendar):
"""Test that the matching past event is not returned."""
config = dict(CALDAV_CONFIG)
2019-07-31 19:25:30 +00:00
config["custom_calendars"] = [
{"name": "Private", "calendar": "Private", "search": "This is a normal event"}
]
assert await async_setup_component(hass, "calendar", {"calendar": config})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.private_private")
2019-05-02 07:46:12 +00:00
assert state.name == calendar.name
2019-07-31 19:25:30 +00:00
assert state.state == "off"
2019-05-02 07:46:12 +00:00
2019-07-31 19:25:30 +00:00
@patch("homeassistant.util.dt.now", return_value=_local_datetime(12, 00))
2019-05-02 07:46:12 +00:00
async def test_no_result_with_filtering(mock_now, hass, calendar):
"""Test that nothing is returned since nothing matches."""
config = dict(CALDAV_CONFIG)
2019-07-31 19:25:30 +00:00
config["custom_calendars"] = [
{
"name": "Private",
"calendar": "Private",
"search": "This is a non-existing event",
}
]
assert await async_setup_component(hass, "calendar", {"calendar": config})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.private_private")
2019-05-02 07:46:12 +00:00
assert state.name == calendar.name
2019-07-31 19:25:30 +00:00
assert state.state == "off"
2019-05-02 07:46:12 +00:00
2019-07-31 19:25:30 +00:00
@patch("homeassistant.util.dt.now", return_value=_local_datetime(17, 30))
2019-05-02 07:46:12 +00:00
async def test_all_day_event_returned(mock_now, hass, calendar):
"""Test that the event lasting the whole day is returned."""
config = dict(CALDAV_CONFIG)
2019-07-31 19:25:30 +00:00
config["custom_calendars"] = [
{"name": "Private", "calendar": "Private", "search": ".*"}
]
assert await async_setup_component(hass, "calendar", {"calendar": config})
2019-05-02 07:46:12 +00:00
await hass.async_block_till_done()
2019-07-31 19:25:30 +00:00
state = hass.states.get("calendar.private_private")
2019-05-02 07:46:12 +00:00
assert state.name == calendar.name
assert state.state == STATE_ON
assert dict(state.attributes) == {
"friendly_name": "Private",
"message": "This is an all day event",
"all_day": True,
"offset_reached": False,
"start_time": "2017-11-27 00:00:00",
"end_time": "2017-11-28 00:00:00",
"location": "Hamburg",
"description": "What a beautiful day",
}