* Moved climate components with tests into platform dirs. * Updated tests from climate component. * Moved binary_sensor components with tests into platform dirs. * Updated tests from binary_sensor component. * Moved calendar components with tests into platform dirs. * Updated tests from calendar component. * Moved camera components with tests into platform dirs. * Updated tests from camera component. * Moved cover components with tests into platform dirs. * Updated tests from cover component. * Moved device_tracker components with tests into platform dirs. * Updated tests from device_tracker component. * Moved fan components with tests into platform dirs. * Updated tests from fan component. * Moved geo_location components with tests into platform dirs. * Updated tests from geo_location component. * Moved image_processing components with tests into platform dirs. * Updated tests from image_processing component. * Moved light components with tests into platform dirs. * Updated tests from light component. * Moved lock components with tests into platform dirs. * Moved media_player components with tests into platform dirs. * Updated tests from media_player component. * Moved scene components with tests into platform dirs. * Moved sensor components with tests into platform dirs. * Updated tests from sensor component. * Moved switch components with tests into platform dirs. * Updated tests from sensor component. * Moved vacuum components with tests into platform dirs. * Updated tests from vacuum component. * Moved weather components with tests into platform dirs. * Fixed __init__.py files * Fixes for stuff moved as part of this branch. * Fix stuff needed to merge with balloob's branch. * Formatting issues. * Missing __init__.py files. * Fix-ups * Fixup * Regenerated requirements. * Linting errors fixed. * Fixed more broken tests. * Missing init files. * Fix broken tests. * More broken tests * There seems to be a thread race condition. I suspect the logger stuff is running in another thread, which means waiting until the aio loop is done is missing the log messages. Used sleep instead because that allows the logger thread to run. I think the api_streams sensor might not be thread safe. * Disabled tests, will remove sensor in #22147 * Updated coverage and codeowners.
400 lines
14 KiB
Python
400 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.caldav.calendar 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",
|
|
}
|