401 lines
14 KiB
Python
401 lines
14 KiB
Python
"""The tests for the webdav calendar component."""
|
|
# pylint: disable=protected-access
|
|
import datetime
|
|
import logging
|
|
import unittest
|
|
from unittest.mock import (patch, Mock, MagicMock)
|
|
|
|
import homeassistant.components.calendar as calendar_base
|
|
import homeassistant.components.calendar.caldav as caldav
|
|
from caldav.objects import Event
|
|
from homeassistant.const import CONF_PLATFORM, STATE_OFF, STATE_ON
|
|
from homeassistant.util import dt
|
|
from tests.common import get_test_home_assistant
|
|
|
|
TEST_PLATFORM = {calendar_base.DOMAIN: {CONF_PLATFORM: 'test'}}
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
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
|
|
"""
|
|
|
|
]
|
|
|
|
|
|
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))
|
|
|
|
|
|
def _mocked_dav_client(*args, **kwargs):
|
|
"""Mock requests.get invocations."""
|
|
calendars = [
|
|
_mock_calendar("First"),
|
|
_mock_calendar("Second")
|
|
]
|
|
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
|
|
|
|
|
|
class TestComponentsWebDavCalendar(unittest.TestCase):
|
|
"""Test the WebDav calendar."""
|
|
|
|
hass = None # HomeAssistant
|
|
|
|
# pylint: disable=invalid-name
|
|
def setUp(self):
|
|
"""Set up things to be run when tests are started."""
|
|
self.hass = get_test_home_assistant()
|
|
self.hass.http = Mock()
|
|
self.calendar = _mock_calendar("Private")
|
|
|
|
# pylint: disable=invalid-name
|
|
def tearDown(self):
|
|
"""Stop everything that was started."""
|
|
self.hass.stop()
|
|
|
|
@patch('caldav.DAVClient', side_effect=_mocked_dav_client)
|
|
def test_setup_component(self, req_mock):
|
|
"""Test setup component with calendars."""
|
|
def _add_device(devices):
|
|
assert len(devices) == 2
|
|
assert devices[0].name == "First"
|
|
assert devices[0].dev_id == "First"
|
|
assert not devices[0].data.include_all_day
|
|
assert devices[1].name == "Second"
|
|
assert devices[1].dev_id == "Second"
|
|
assert not devices[1].data.include_all_day
|
|
|
|
caldav.setup_platform(self.hass,
|
|
{
|
|
"url": "http://test.local",
|
|
"custom_calendars": []
|
|
},
|
|
_add_device)
|
|
|
|
@patch('caldav.DAVClient', side_effect=_mocked_dav_client)
|
|
def test_setup_component_with_no_calendar_matching(self, req_mock):
|
|
"""Test setup component with wrong calendar."""
|
|
def _add_device(devices):
|
|
assert not devices
|
|
|
|
caldav.setup_platform(self.hass,
|
|
{
|
|
"url": "http://test.local",
|
|
"calendars": ["none"],
|
|
"custom_calendars": []
|
|
},
|
|
_add_device)
|
|
|
|
@patch('caldav.DAVClient', side_effect=_mocked_dav_client)
|
|
def test_setup_component_with_a_calendar_match(self, req_mock):
|
|
"""Test setup component with right calendar."""
|
|
def _add_device(devices):
|
|
assert len(devices) == 1
|
|
assert devices[0].name == "Second"
|
|
|
|
caldav.setup_platform(self.hass,
|
|
{
|
|
"url": "http://test.local",
|
|
"calendars": ["Second"],
|
|
"custom_calendars": []
|
|
},
|
|
_add_device)
|
|
|
|
@patch('caldav.DAVClient', side_effect=_mocked_dav_client)
|
|
def test_setup_component_with_one_custom_calendar(self, req_mock):
|
|
"""Test setup component with custom calendars."""
|
|
def _add_device(devices):
|
|
assert len(devices) == 1
|
|
assert devices[0].name == "HomeOffice"
|
|
assert devices[0].dev_id == "Second HomeOffice"
|
|
assert devices[0].data.include_all_day
|
|
|
|
caldav.setup_platform(self.hass,
|
|
{
|
|
"url": "http://test.local",
|
|
"custom_calendars": [
|
|
{
|
|
"name": "HomeOffice",
|
|
"calendar": "Second",
|
|
"filter": "HomeOffice"
|
|
}]
|
|
},
|
|
_add_device)
|
|
|
|
@patch('homeassistant.util.dt.now', return_value=_local_datetime(17, 45))
|
|
def test_ongoing_event(self, mock_now):
|
|
"""Test that the ongoing event is returned."""
|
|
cal = caldav.WebDavCalendarEventDevice(self.hass,
|
|
DEVICE_DATA,
|
|
self.calendar)
|
|
|
|
assert cal.name == DEVICE_DATA["name"]
|
|
assert cal.state == STATE_ON
|
|
assert cal.device_state_attributes == {
|
|
"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",
|
|
}
|
|
|
|
@patch('homeassistant.util.dt.now', return_value=_local_datetime(17, 30))
|
|
def test_just_ended_event(self, mock_now):
|
|
"""Test that the next ongoing event is returned."""
|
|
cal = caldav.WebDavCalendarEventDevice(self.hass,
|
|
DEVICE_DATA,
|
|
self.calendar)
|
|
|
|
assert cal.name == DEVICE_DATA["name"]
|
|
assert cal.state == STATE_ON
|
|
assert cal.device_state_attributes == {
|
|
"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",
|
|
}
|
|
|
|
@patch('homeassistant.util.dt.now', return_value=_local_datetime(17, 00))
|
|
def test_ongoing_event_different_tz(self, mock_now):
|
|
"""Test that the ongoing event with another timezone is returned."""
|
|
cal = caldav.WebDavCalendarEventDevice(self.hass,
|
|
DEVICE_DATA,
|
|
self.calendar)
|
|
|
|
assert cal.name == DEVICE_DATA["name"]
|
|
assert cal.state == STATE_ON
|
|
assert cal.device_state_attributes == {
|
|
"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",
|
|
}
|
|
|
|
@patch('homeassistant.util.dt.now', return_value=_local_datetime(8, 30))
|
|
def test_ongoing_event_with_offset(self, mock_now):
|
|
"""Test that the offset is taken into account."""
|
|
cal = caldav.WebDavCalendarEventDevice(self.hass,
|
|
DEVICE_DATA,
|
|
self.calendar)
|
|
|
|
assert cal.state == STATE_OFF
|
|
assert cal.device_state_attributes == {
|
|
"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",
|
|
}
|
|
|
|
@patch('homeassistant.util.dt.now', return_value=_local_datetime(12, 00))
|
|
def test_matching_filter(self, mock_now):
|
|
"""Test that the matching event is returned."""
|
|
cal = caldav.WebDavCalendarEventDevice(self.hass,
|
|
DEVICE_DATA,
|
|
self.calendar,
|
|
False,
|
|
"This is a normal event")
|
|
|
|
assert cal.state == STATE_OFF
|
|
assert not cal.offset_reached()
|
|
assert cal.device_state_attributes == {
|
|
"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",
|
|
}
|
|
|
|
@patch('homeassistant.util.dt.now', return_value=_local_datetime(12, 00))
|
|
def test_matching_filter_real_regexp(self, mock_now):
|
|
"""Test that the event matching the regexp is returned."""
|
|
cal = caldav.WebDavCalendarEventDevice(self.hass,
|
|
DEVICE_DATA,
|
|
self.calendar,
|
|
False,
|
|
"^This.*event")
|
|
|
|
assert cal.state == STATE_OFF
|
|
assert not cal.offset_reached()
|
|
assert cal.device_state_attributes == {
|
|
"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",
|
|
}
|
|
|
|
@patch('homeassistant.util.dt.now', return_value=_local_datetime(20, 00))
|
|
def test_filter_matching_past_event(self, mock_now):
|
|
"""Test that the matching past event is not returned."""
|
|
cal = caldav.WebDavCalendarEventDevice(self.hass,
|
|
DEVICE_DATA,
|
|
self.calendar,
|
|
False,
|
|
"This is a normal event")
|
|
|
|
assert cal.data.event is None
|
|
|
|
@patch('homeassistant.util.dt.now', return_value=_local_datetime(12, 00))
|
|
def test_no_result_with_filtering(self, mock_now):
|
|
"""Test that nothing is returned since nothing matches."""
|
|
cal = caldav.WebDavCalendarEventDevice(self.hass,
|
|
DEVICE_DATA,
|
|
self.calendar,
|
|
False,
|
|
"This is a non-existing event")
|
|
|
|
assert cal.data.event is None
|
|
|
|
@patch('homeassistant.util.dt.now', return_value=_local_datetime(17, 30))
|
|
def test_all_day_event_returned(self, mock_now):
|
|
"""Test that the event lasting the whole day is returned."""
|
|
cal = caldav.WebDavCalendarEventDevice(self.hass,
|
|
DEVICE_DATA,
|
|
self.calendar,
|
|
True)
|
|
|
|
assert cal.name == DEVICE_DATA["name"]
|
|
assert cal.state == STATE_ON
|
|
assert cal.device_state_attributes == {
|
|
"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",
|
|
}
|