Simplify google calendar authentication setup (#67314)
Simplify google calendar authentication to combine some of the cases together, and reduce unecessarily checks. Make the tests share common authentication setup and reduce use of mocks by introducing a fake for holding on to credentials. This makes future refactoring simpler, so we don't have to care as much about the interactions with the credentials storage.
This commit is contained in:
parent
a151d3f9a0
commit
0e74184e4f
4 changed files with 77 additions and 59 deletions
|
@ -5,7 +5,6 @@ from collections.abc import Mapping
|
|||
from datetime import datetime, timedelta, timezone
|
||||
from enum import Enum
|
||||
import logging
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
from googleapiclient import discovery as google_discovery
|
||||
|
@ -163,7 +162,10 @@ ADD_EVENT_SERVICE_SCHEMA = vol.Schema(
|
|||
|
||||
|
||||
def do_authentication(
|
||||
hass: HomeAssistant, hass_config: ConfigType, config: ConfigType
|
||||
hass: HomeAssistant,
|
||||
hass_config: ConfigType,
|
||||
config: ConfigType,
|
||||
storage: Storage,
|
||||
) -> bool:
|
||||
"""Notify user of actions and authenticate.
|
||||
|
||||
|
@ -226,7 +228,6 @@ def do_authentication(
|
|||
# not ready yet, call again
|
||||
return
|
||||
|
||||
storage = Storage(hass.config.path(TOKEN_FILE))
|
||||
storage.put(credentials)
|
||||
do_setup(hass, hass_config, config)
|
||||
assert listener
|
||||
|
@ -256,14 +257,14 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
# component is set up by tts platform
|
||||
return True
|
||||
|
||||
token_file = hass.config.path(TOKEN_FILE)
|
||||
if not os.path.isfile(token_file):
|
||||
_LOGGER.debug("Token file does not exist, authenticating for first time")
|
||||
do_authentication(hass, config, conf)
|
||||
else:
|
||||
if not check_correct_scopes(hass, token_file, conf):
|
||||
_LOGGER.debug("Existing scopes are not sufficient, re-authenticating")
|
||||
do_authentication(hass, config, conf)
|
||||
storage = Storage(hass.config.path(TOKEN_FILE))
|
||||
creds = storage.get()
|
||||
if (
|
||||
not creds
|
||||
or not creds.scopes
|
||||
or conf[CONF_CALENDAR_ACCESS].scope not in creds.scopes
|
||||
):
|
||||
do_authentication(hass, config, conf, storage)
|
||||
else:
|
||||
do_setup(hass, config, conf)
|
||||
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
"""Test configuration and mocks for the google integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
import datetime
|
||||
from typing import Any, Generator, TypeVar
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from oauth2client.client import Credentials, OAuth2Credentials
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.google import GoogleCalendarService
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
ApiResult = Callable[[dict[str, Any]], None]
|
||||
T = TypeVar("T")
|
||||
|
@ -36,6 +42,62 @@ def test_calendar():
|
|||
return TEST_CALENDAR
|
||||
|
||||
|
||||
class FakeStorage:
|
||||
"""A fake storage object for persiting creds."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize FakeStorage."""
|
||||
self._creds: Credentials | None = None
|
||||
|
||||
def get(self) -> Credentials | None:
|
||||
"""Get credentials from storage."""
|
||||
return self._creds
|
||||
|
||||
def put(self, creds: Credentials) -> None:
|
||||
"""Put credentials in storage."""
|
||||
self._creds = creds
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def token_scopes() -> list[str]:
|
||||
"""Fixture for scopes used during test."""
|
||||
return ["https://www.googleapis.com/auth/calendar"]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def creds(token_scopes: list[str]) -> OAuth2Credentials:
|
||||
"""Fixture that defines creds used in the test."""
|
||||
token_expiry = utcnow() + datetime.timedelta(days=7)
|
||||
return OAuth2Credentials(
|
||||
access_token="ACCESS_TOKEN",
|
||||
client_id="client-id",
|
||||
client_secret="client-secret",
|
||||
refresh_token="REFRESH_TOKEN",
|
||||
token_expiry=token_expiry,
|
||||
token_uri="http://example.com",
|
||||
user_agent="n/a",
|
||||
scopes=token_scopes,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def storage() -> YieldFixture[FakeStorage]:
|
||||
"""Fixture to populate an existing token file for read on startup."""
|
||||
storage = FakeStorage()
|
||||
with patch("homeassistant.components.google.Storage", return_value=storage):
|
||||
yield storage
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mock_token_read(
|
||||
hass: HomeAssistant,
|
||||
creds: OAuth2Credentials,
|
||||
storage: FakeStorage,
|
||||
) -> None:
|
||||
"""Fixture to populate an existing token file for read on startup."""
|
||||
storage.put(creds)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_next_event():
|
||||
"""Mock the google calendar data."""
|
||||
|
|
|
@ -21,7 +21,6 @@ from homeassistant.components.google import (
|
|||
CONF_TRACK,
|
||||
DEVICE_SCHEMA,
|
||||
SERVICE_SCAN_CALENDARS,
|
||||
do_setup,
|
||||
)
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.helpers.template import DATE_STR_FORMAT
|
||||
|
@ -86,21 +85,18 @@ def get_calendar_info(calendar):
|
|||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_google_setup(hass, test_calendar):
|
||||
def mock_google_setup(hass, test_calendar, mock_token_read):
|
||||
"""Mock the google set up functions."""
|
||||
hass.loop.run_until_complete(async_setup_component(hass, "group", {"group": {}}))
|
||||
calendar = get_calendar_info(test_calendar)
|
||||
calendars = {calendar[CONF_CAL_ID]: calendar}
|
||||
patch_google_auth = patch(
|
||||
"homeassistant.components.google.do_authentication", side_effect=do_setup
|
||||
)
|
||||
patch_google_load = patch(
|
||||
"homeassistant.components.google.load_config", return_value=calendars
|
||||
)
|
||||
patch_google_services = patch("homeassistant.components.google.setup_services")
|
||||
async_mock_service(hass, "google", SERVICE_SCAN_CALENDARS)
|
||||
|
||||
with patch_google_auth, patch_google_load, patch_google_services:
|
||||
with patch_google_load, patch_google_services:
|
||||
yield
|
||||
|
||||
|
||||
|
|
|
@ -53,28 +53,6 @@ async def mock_code_flow(
|
|||
yield mock_flow
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def token_scopes() -> list[str]:
|
||||
"""Fixture for scopes used during test."""
|
||||
return ["https://www.googleapis.com/auth/calendar"]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def creds(token_scopes: list[str]) -> OAuth2Credentials:
|
||||
"""Fixture that defines creds used in the test."""
|
||||
token_expiry = utcnow() + datetime.timedelta(days=7)
|
||||
return OAuth2Credentials(
|
||||
access_token="ACCESS_TOKEN",
|
||||
client_id="client-id",
|
||||
client_secret="client-secret",
|
||||
refresh_token="REFRESH_TOKEN",
|
||||
token_expiry=token_expiry,
|
||||
token_uri="http://example.com",
|
||||
user_agent="n/a",
|
||||
scopes=token_scopes,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mock_exchange(creds: OAuth2Credentials) -> YieldFixture[Mock]:
|
||||
"""Fixture for mocking out the exchange for credentials."""
|
||||
|
@ -84,25 +62,6 @@ async def mock_exchange(creds: OAuth2Credentials) -> YieldFixture[Mock]:
|
|||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def mock_token_write(hass: HomeAssistant) -> None:
|
||||
"""Fixture to avoid writing token files to disk."""
|
||||
with patch(
|
||||
"homeassistant.components.google.os.path.isfile", return_value=True
|
||||
), patch("homeassistant.components.google.Storage.put"):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mock_token_read(
|
||||
hass: HomeAssistant,
|
||||
creds: OAuth2Credentials,
|
||||
) -> None:
|
||||
"""Fixture to populate an existing token file."""
|
||||
with patch("homeassistant.components.google.Storage.get", return_value=creds):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def calendars_config() -> list[dict[str, Any]]:
|
||||
"""Fixture for tests to override default calendar configuration."""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue