From 23277181ca52891dd661ae3ef4471da164332b17 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 22 Dec 2021 22:31:56 -0800 Subject: [PATCH] Increase test coverage for google calendar (#62648) * Increase test coverage for google calendar Update tests to exercise the API responses, getting test coverage to 97% for calendar.py ----------- coverage: platform linux, python 3.9.6-final-0 ----------- Name Stmts Miss Cover Missing --------------------------------------------------------------------------- homeassistant/components/google/__init__.py 193 84 56% 92, 163-228, 238, 244-247, 254-262, 274, 298-299, 305-347, 387-392, 416-430, 435-437 homeassistant/components/google/calendar.py 122 4 97% 41, 45, 51, 135 --------------------------------------------------------------------------- TOTAL 315 88 72% * Revert conftest changes * Update typing errors found on CI * Update python3.8 typing imports * Remove commented out code --- .coveragerc | 2 +- homeassistant/components/google/calendar.py | 4 +- tests/components/google/test_calendar.py | 173 +++++++++++++++++++- 3 files changed, 170 insertions(+), 9 deletions(-) diff --git a/.coveragerc b/.coveragerc index d5876b62b61..c5b0e6c0923 100644 --- a/.coveragerc +++ b/.coveragerc @@ -399,7 +399,7 @@ omit = homeassistant/components/glances/sensor.py homeassistant/components/gntp/notify.py homeassistant/components/goalfeed/* - homeassistant/components/google/* + homeassistant/components/google/__init__.py homeassistant/components/google_cloud/tts.py homeassistant/components/google_maps/device_tracker.py homeassistant/components/google_pubsub/__init__.py diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index 5c06e0fbb94..7381e37d2aa 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -121,8 +121,8 @@ class GoogleCalendarData: def _prepare_query(self): try: service = self.calendar_service.get() - except ServerNotFoundError: - _LOGGER.error("Unable to connect to Google") + except ServerNotFoundError as err: + _LOGGER.error("Unable to connect to Google: %s", err) return None, None params = dict(DEFAULT_GOOGLE_SEARCH_PARAMS) params["calendarId"] = self.calendar_id diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py index ad7b6b12001..3b5aa7365dc 100644 --- a/tests/components/google/test_calendar.py +++ b/tests/components/google/test_calendar.py @@ -1,5 +1,10 @@ """The tests for the google calendar platform.""" + +from __future__ import annotations + import copy +from http import HTTPStatus +from typing import Any, Callable from unittest.mock import Mock, patch import httplib2 @@ -11,10 +16,12 @@ from homeassistant.components.google import ( CONF_CLIENT_SECRET, CONF_DEVICE_ID, CONF_ENTITIES, + CONF_IGNORE_AVAILABILITY, CONF_NAME, CONF_TRACK, DEVICE_SCHEMA, SERVICE_SCAN_CALENDARS, + GoogleCalendarService, do_setup, ) from homeassistant.const import STATE_OFF, STATE_ON @@ -23,6 +30,8 @@ from homeassistant.setup import async_setup_component from homeassistant.util import slugify import homeassistant.util.dt as dt_util +from .conftest import TEST_CALENDAR + from tests.common import async_mock_service GOOGLE_CONFIG = {CONF_CLIENT_ID: "client_id", CONF_CLIENT_SECRET: "client_secret"} @@ -69,6 +78,7 @@ def get_calendar_info(calendar): CONF_TRACK: calendar["track"], CONF_NAME: calendar["summary"], CONF_DEVICE_ID: slugify(calendar["summary"]), + CONF_IGNORE_AVAILABILITY: calendar.get("ignore_availability", True), } ], } @@ -95,12 +105,6 @@ def mock_google_setup(hass, test_calendar): yield -@pytest.fixture(autouse=True) -def mock_http(hass): - """Mock the http component.""" - hass.http = Mock() - - @pytest.fixture(autouse=True) def set_time_zone(): """Set the time zone for the tests.""" @@ -314,3 +318,160 @@ async def test_update_error(hass, google_service): state = hass.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == "off" + + +async def test_calendars_api(hass, hass_client, google_service): + """Test the Rest API returns the calendar.""" + assert await async_setup_component(hass, "google", {"google": GOOGLE_CONFIG}) + await hass.async_block_till_done() + + client = await hass_client() + response = await client.get("/api/calendars") + assert response.status == HTTPStatus.OK + data = await response.json() + assert data == [ + { + "entity_id": TEST_ENTITY, + "name": TEST_ENTITY_NAME, + } + ] + + +async def test_http_event_api_failure(hass, hass_client, google_service): + """Test the Rest API response during a calendar failure.""" + google_service.return_value.get = Mock( + side_effect=httplib2.ServerNotFoundError("unit test") + ) + + assert await async_setup_component(hass, "google", {"google": GOOGLE_CONFIG}) + await hass.async_block_till_done() + + start = dt_util.now().isoformat() + end = (dt_util.now() + dt_util.dt.timedelta(minutes=60)).isoformat() + + client = await hass_client() + response = await client.get(f"/api/calendars/{TEST_ENTITY}?start={start}&end={end}") + assert response.status == HTTPStatus.OK + # A failure to talk to the server results in an empty list of events + events = await response.json() + assert events == [] + + +@pytest.fixture +def mock_events_list( + google_service: GoogleCalendarService, +) -> Callable[[dict[str, Any]], None]: + """Fixture to construct a fake event list API response.""" + + def _put_result(response: dict[str, Any]) -> None: + google_service.return_value.get.return_value.events.return_value.list.return_value.execute.return_value = ( + response + ) + return + + return _put_result + + +async def test_http_api_event(hass, hass_client, google_service, mock_events_list): + """Test querying the API and fetching events from the server.""" + now = dt_util.now() + + mock_events_list( + { + "items": [ + { + "summary": "Event title", + "start": {"dateTime": now.isoformat()}, + "end": { + "dateTime": (now + dt_util.dt.timedelta(minutes=5)).isoformat() + }, + } + ], + } + ) + assert await async_setup_component(hass, "google", {"google": GOOGLE_CONFIG}) + await hass.async_block_till_done() + + start = (now - dt_util.dt.timedelta(minutes=60)).isoformat() + end = (now + dt_util.dt.timedelta(minutes=60)).isoformat() + + client = await hass_client() + response = await client.get(f"/api/calendars/{TEST_ENTITY}?start={start}&end={end}") + assert response.status == HTTPStatus.OK + events = await response.json() + assert len(events) == 1 + assert "summary" in events[0] + assert events[0]["summary"] == "Event title" + + +def create_ignore_avail_calendar() -> dict[str, Any]: + """Create a calendar with ignore_availability set.""" + calendar = TEST_CALENDAR.copy() + calendar["ignore_availability"] = False + return calendar + + +@pytest.mark.parametrize("test_calendar", [create_ignore_avail_calendar()]) +async def test_opaque_event(hass, hass_client, google_service, mock_events_list): + """Test querying the API and fetching events from the server.""" + now = dt_util.now() + + mock_events_list( + { + "items": [ + { + "summary": "Event title", + "transparency": "opaque", + "start": {"dateTime": now.isoformat()}, + "end": { + "dateTime": (now + dt_util.dt.timedelta(minutes=5)).isoformat() + }, + } + ], + } + ) + assert await async_setup_component(hass, "google", {"google": GOOGLE_CONFIG}) + await hass.async_block_till_done() + + start = (now - dt_util.dt.timedelta(minutes=60)).isoformat() + end = (now + dt_util.dt.timedelta(minutes=60)).isoformat() + + client = await hass_client() + response = await client.get(f"/api/calendars/{TEST_ENTITY}?start={start}&end={end}") + assert response.status == HTTPStatus.OK + events = await response.json() + assert len(events) == 1 + assert "summary" in events[0] + assert events[0]["summary"] == "Event title" + + +@pytest.mark.parametrize("test_calendar", [create_ignore_avail_calendar()]) +async def test_transparent_event(hass, hass_client, google_service, mock_events_list): + """Test querying the API and fetching events from the server.""" + now = dt_util.now() + + mock_events_list( + { + "items": [ + { + "summary": "Event title", + "transparency": "transparent", + "start": {"dateTime": now.isoformat()}, + "end": { + "dateTime": (now + dt_util.dt.timedelta(minutes=5)).isoformat() + }, + } + ], + } + ) + assert await async_setup_component(hass, "google", {"google": GOOGLE_CONFIG}) + await hass.async_block_till_done() + + start = (now - dt_util.dt.timedelta(minutes=60)).isoformat() + end = (now + dt_util.dt.timedelta(minutes=60)).isoformat() + + client = await hass_client() + response = await client.get(f"/api/calendars/{TEST_ENTITY}?start={start}&end={end}") + assert response.status == HTTPStatus.OK + events = await response.json() + assert events == []