Remove google calendar configuration.yaml deprecated in 2022.6 (#77814)

pull/77862/head
Allen Porter 2022-09-05 12:12:38 -07:00 committed by GitHub
parent 6644f62ad2
commit 8280b8422c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 449 deletions

View File

@ -10,20 +10,12 @@ import aiohttp
from gcal_sync.api import GoogleCalendarService
from gcal_sync.exceptions import ApiException, AuthException
from gcal_sync.model import DateOrDatetime, Event
from oauth2client.file import Storage
import voluptuous as vol
from voluptuous.error import Error as VoluptuousError
import yaml
from homeassistant import config_entries
from homeassistant.components.application_credentials import (
ClientCredential,
async_import_client_credential,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_CLIENT_ID,
CONF_CLIENT_SECRET,
CONF_DEVICE_ID,
CONF_ENTITIES,
CONF_NAME,
@ -40,15 +32,10 @@ from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType
from .api import ApiAuthImpl, get_feature_access
from .const import (
CONF_CALENDAR_ACCESS,
DATA_CONFIG,
DATA_SERVICE,
DEVICE_AUTH_IMPL,
DOMAIN,
EVENT_DESCRIPTION,
EVENT_END_DATE,
@ -79,37 +66,15 @@ DEFAULT_CONF_OFFSET = "!!"
EVENT_CALENDAR_ID = "calendar_id"
NOTIFICATION_ID = "google_calendar_notification"
NOTIFICATION_TITLE = "Google Calendar Setup"
GROUP_NAME_ALL_CALENDARS = "Google Calendar Sensors"
SERVICE_ADD_EVENT = "add_event"
YAML_DEVICES = f"{DOMAIN}_calendars.yaml"
TOKEN_FILE = f".{DOMAIN}.token"
PLATFORMS = [Platform.CALENDAR]
CONFIG_SCHEMA = vol.Schema(
vol.All(
cv.deprecated(DOMAIN),
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
vol.Optional(CONF_TRACK_NEW, default=True): cv.boolean,
vol.Optional(CONF_CALENDAR_ACCESS, default="read_write"): cv.enum(
FeatureAccess
),
}
)
},
),
extra=vol.ALLOW_EXTRA,
)
CONFIG_SCHEMA = vol.Schema(cv.removed(DOMAIN), extra=vol.ALLOW_EXTRA)
_SINGLE_CALSEARCH_CONFIG = vol.All(
cv.deprecated(CONF_MAX_RESULTS),
@ -171,65 +136,6 @@ ADD_EVENT_SERVICE_SCHEMA = vol.All(
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Google component."""
if DOMAIN not in config:
return True
conf = config.get(DOMAIN, {})
hass.data[DOMAIN] = {DATA_CONFIG: conf}
if CONF_CLIENT_ID in conf and CONF_CLIENT_SECRET in conf:
await async_import_client_credential(
hass,
DOMAIN,
ClientCredential(
conf[CONF_CLIENT_ID],
conf[CONF_CLIENT_SECRET],
),
DEVICE_AUTH_IMPL,
)
# Import credentials from the old token file into the new way as
# a ConfigEntry managed by home assistant.
storage = Storage(hass.config.path(TOKEN_FILE))
creds = await hass.async_add_executor_job(storage.get)
if creds and get_feature_access(hass).scope in creds.scopes:
_LOGGER.debug("Importing configuration entry with credentials")
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={
"creds": creds,
},
)
)
async_create_issue(
hass,
DOMAIN,
"deprecated_yaml",
breaks_in_ha_version="2022.9.0", # Warning first added in 2022.6.0
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_yaml",
)
if conf.get(CONF_TRACK_NEW) is False:
# The track_new as False would previously result in new entries
# in google_calendars.yaml with track set to False which is
# handled at calendar entity creation time.
async_create_issue(
hass,
DOMAIN,
"removed_track_new_yaml",
breaks_in_ha_version="2022.6.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="removed_track_new_yaml",
)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Google from a config entry."""
hass.data.setdefault(DOMAIN, {})

View File

@ -41,15 +41,5 @@
},
"application_credentials": {
"description": "Follow the [instructions]({more_info_url}) for [OAuth consent screen]({oauth_consent_url}) to give Home Assistant access to your Google Calendar. You also need to create Application Credentials linked to your Calendar:\n1. Go to [Credentials]({oauth_creds_url}) and click **Create Credentials**.\n1. From the drop-down list select **OAuth client ID**.\n1. Select **TV and Limited Input devices** for the Application Type.\n\n"
},
"issues": {
"deprecated_yaml": {
"title": "The Google Calendar YAML configuration is being removed",
"description": "Configuring the Google Calendar in configuration.yaml is being removed in Home Assistant 2022.9.\n\nYour existing OAuth Application Credentials and access settings have been imported into the UI automatically. Remove the YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
},
"removed_track_new_yaml": {
"title": "Google Calendar entity tracking has changed",
"description": "You have disabled entity tracking for Google Calendar in configuration.yaml, which is no longer supported. You must manually change the integration System Options in the UI to disable newly discovered entities going forward. Remove the track_new setting from configuration.yaml and restart Home Assistant to fix this issue."
}
}
}

View File

@ -9,12 +9,15 @@ from unittest.mock import Mock, mock_open, patch
from aiohttp.client_exceptions import ClientError
from gcal_sync.auth import API_BASE_URL
from oauth2client.client import Credentials, OAuth2Credentials
from oauth2client.client import OAuth2Credentials
import pytest
import yaml
from homeassistant.components.google import CONF_TRACK_NEW, DOMAIN
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.components.application_credentials import (
ClientCredential,
async_import_client_credential,
)
from homeassistant.components.google import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
@ -115,22 +118,6 @@ def mock_calendars_yaml(
yield mocked_open_function
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
def token_scopes() -> list[str]:
"""Fixture for scopes used during test."""
@ -163,14 +150,6 @@ def creds(
)
@pytest.fixture(autouse=True)
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
def config_entry_token_expiry(token_expiry: datetime.datetime) -> float:
"""Fixture for token expiration value stored in the config entry."""
@ -214,16 +193,6 @@ def config_entry(
)
@pytest.fixture
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_events_list(
aioclient_mock: AiohttpClientMocker,
@ -327,33 +296,17 @@ def set_time_zone(hass):
@pytest.fixture
def google_config_track_new() -> None:
"""Fixture for tests to set the 'track_new' configuration.yaml setting."""
return None
@pytest.fixture
def google_config(google_config_track_new: bool | None) -> dict[str, Any]:
"""Fixture for overriding component config."""
google_config = {CONF_CLIENT_ID: CLIENT_ID, CONF_CLIENT_SECRET: CLIENT_SECRET}
if google_config_track_new is not None:
google_config[CONF_TRACK_NEW] = google_config_track_new
return google_config
@pytest.fixture
def config(google_config: dict[str, Any]) -> dict[str, Any]:
"""Fixture for overriding component config."""
return {DOMAIN: google_config} if google_config else {}
@pytest.fixture
def component_setup(hass: HomeAssistant, config: dict[str, Any]) -> ComponentSetup:
def component_setup(
hass: HomeAssistant, config_entry: MockConfigEntry
) -> ComponentSetup:
"""Fixture for setting up the integration."""
async def _setup_func() -> bool:
result = await async_setup_component(hass, DOMAIN, config)
await hass.async_block_till_done()
return result
assert await async_setup_component(hass, "application_credentials", {})
await async_import_client_credential(
hass, DOMAIN, ClientCredential("client-id", "client-secret"), "device_auth"
)
config_entry.add_to_hass(hass)
return await hass.config_entries.async_setup(config_entry.entry_id)
return _setup_func

View File

@ -4,11 +4,9 @@ from __future__ import annotations
from collections.abc import Callable
import datetime
from typing import Any
from unittest.mock import Mock, patch
from aiohttp.client_exceptions import ClientError
from freezegun.api import FrozenDateTimeFactory
from oauth2client.client import (
DeviceFlowInfo,
FlowExchangeError,
@ -26,15 +24,10 @@ from homeassistant.components.google.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
from .conftest import (
CLIENT_ID,
CLIENT_SECRET,
EMAIL_ADDRESS,
ComponentSetup,
YieldFixture,
)
from .conftest import CLIENT_ID, CLIENT_SECRET, EMAIL_ADDRESS, YieldFixture
from tests.common import MockConfigEntry, async_fire_time_changed
@ -48,6 +41,12 @@ async def request_setup(current_request_with_host) -> None:
return
@pytest.fixture(autouse=True)
async def setup_app_creds(hass: HomeAssistant) -> None:
"""Fixture to setup application credentials component."""
await async_setup_component(hass, "application_credentials", {})
@pytest.fixture
async def code_expiration_delta() -> datetime.timedelta:
"""Fixture for code expiration time, defaulting to the future."""
@ -117,74 +116,12 @@ async def fire_alarm(hass, point_in_time):
await hass.async_block_till_done()
async def test_full_flow_yaml_creds(
hass: HomeAssistant,
mock_code_flow: Mock,
mock_exchange: Mock,
component_setup: ComponentSetup,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test successful creds setup."""
assert await component_setup()
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result.get("type") == "progress"
assert result.get("step_id") == "auth"
assert "description_placeholders" in result
assert "url" in result["description_placeholders"]
with patch(
"homeassistant.components.google.async_setup_entry", return_value=True
) as mock_setup:
# Run one tick to invoke the credential exchange check
now = utcnow()
await fire_alarm(hass, now + CODE_CHECK_ALARM_TIMEDELTA)
result = await hass.config_entries.flow.async_configure(
flow_id=result["flow_id"]
)
assert result.get("type") == "create_entry"
assert result.get("title") == EMAIL_ADDRESS
assert "data" in result
data = result["data"]
assert "token" in data
assert 0 < data["token"]["expires_in"] <= 60 * 60
assert (
datetime.datetime.now().timestamp()
<= data["token"]["expires_at"]
< (datetime.datetime.now() + datetime.timedelta(days=8)).timestamp()
)
data["token"].pop("expires_at")
data["token"].pop("expires_in")
assert data == {
"auth_implementation": "device_auth",
"token": {
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"scope": "https://www.googleapis.com/auth/calendar",
"token_type": "Bearer",
},
}
assert result.get("options") == {"calendar_access": "read_write"}
assert len(mock_setup.mock_calls) == 1
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
@pytest.mark.parametrize("google_config", [None])
async def test_full_flow_application_creds(
hass: HomeAssistant,
mock_code_flow: Mock,
mock_exchange: Mock,
config: dict[str, Any],
component_setup: ComponentSetup,
) -> None:
"""Test successful creds setup."""
assert await component_setup()
await async_import_client_credential(
hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "imported-cred"
)
@ -240,10 +177,11 @@ async def test_full_flow_application_creds(
async def test_code_error(
hass: HomeAssistant,
mock_code_flow: Mock,
component_setup: ComponentSetup,
) -> None:
"""Test server error setting up the oauth flow."""
assert await component_setup()
await async_import_client_credential(
hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "imported-cred"
)
with patch(
"homeassistant.components.google.api.OAuth2WebServerFlow.step1_get_device_and_user_codes",
@ -259,10 +197,11 @@ async def test_code_error(
async def test_timeout_error(
hass: HomeAssistant,
mock_code_flow: Mock,
component_setup: ComponentSetup,
) -> None:
"""Test timeout error setting up the oauth flow."""
assert await component_setup()
await async_import_client_credential(
hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "imported-cred"
)
with patch(
"homeassistant.components.google.api.OAuth2WebServerFlow.step1_get_device_and_user_codes",
@ -279,10 +218,11 @@ async def test_timeout_error(
async def test_expired_after_exchange(
hass: HomeAssistant,
mock_code_flow: Mock,
component_setup: ComponentSetup,
) -> None:
"""Test credential exchange expires."""
assert await component_setup()
await async_import_client_credential(
hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "imported-cred"
)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -310,10 +250,11 @@ async def test_exchange_error(
hass: HomeAssistant,
mock_code_flow: Mock,
mock_exchange: Mock,
component_setup: ComponentSetup,
) -> None:
"""Test an error while exchanging the code for credentials."""
assert await component_setup()
await async_import_client_credential(
hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "device_auth"
)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -371,17 +312,13 @@ async def test_exchange_error(
assert len(entries) == 1
@pytest.mark.parametrize("google_config", [None])
async def test_duplicate_config_entries(
hass: HomeAssistant,
mock_code_flow: Mock,
mock_exchange: Mock,
config: dict[str, Any],
config_entry: MockConfigEntry,
component_setup: ComponentSetup,
) -> None:
"""Test that the same account cannot be setup twice."""
assert await component_setup()
await async_import_client_credential(
hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "imported-cred"
)
@ -416,19 +353,14 @@ async def test_duplicate_config_entries(
assert result.get("reason") == "already_configured"
@pytest.mark.parametrize(
"google_config,primary_calendar_email", [(None, "another-email@example.com")]
)
@pytest.mark.parametrize("primary_calendar_email", ["another-email@example.com"])
async def test_multiple_config_entries(
hass: HomeAssistant,
mock_code_flow: Mock,
mock_exchange: Mock,
config: dict[str, Any],
config_entry: MockConfigEntry,
component_setup: ComponentSetup,
) -> None:
"""Test that multiple config entries can be set at once."""
assert await component_setup()
await async_import_client_credential(
hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "imported-cred"
)
@ -483,21 +415,6 @@ async def test_missing_configuration(
assert result.get("reason") == "missing_credentials"
@pytest.mark.parametrize("google_config", [None])
async def test_missing_configuration_yaml_empty(
hass: HomeAssistant,
component_setup: ComponentSetup,
) -> None:
"""Test setup with an empty yaml configuration and no credentials."""
assert await component_setup()
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result.get("type") == "abort"
assert result.get("reason") == "missing_credentials"
async def test_wrong_configuration(
hass: HomeAssistant,
) -> None:
@ -524,36 +441,10 @@ async def test_wrong_configuration(
assert result.get("reason") == "oauth_error"
async def test_import_config_entry_from_existing_token(
hass: HomeAssistant,
mock_token_read: None,
component_setup: ComponentSetup,
) -> None:
"""Test setup with an existing token file."""
assert await component_setup()
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
data = entries[0].data
assert "token" in data
data["token"].pop("expires_at")
data["token"].pop("expires_in")
assert data == {
"auth_implementation": "device_auth",
"token": {
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"scope": "https://www.googleapis.com/auth/calendar",
"token_type": "Bearer",
},
}
async def test_reauth_flow(
hass: HomeAssistant,
mock_code_flow: Mock,
mock_exchange: Mock,
component_setup: ComponentSetup,
) -> None:
"""Test can't configure when config entry already exists."""
config_entry = MockConfigEntry(
@ -564,12 +455,13 @@ async def test_reauth_flow(
},
)
config_entry.add_to_hass(hass)
await async_import_client_credential(
hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "device_auth"
)
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
assert await component_setup()
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
@ -628,10 +520,11 @@ async def test_calendar_lookup_failure(
hass: HomeAssistant,
mock_code_flow: Mock,
mock_exchange: Mock,
component_setup: ComponentSetup,
) -> None:
"""Test successful config flow and title fetch fails gracefully."""
assert await component_setup()
await async_import_client_credential(
hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), "device_auth"
)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -656,7 +549,6 @@ async def test_calendar_lookup_failure(
async def test_options_flow_triggers_reauth(
hass: HomeAssistant,
component_setup: ComponentSetup,
config_entry: MockConfigEntry,
) -> None:
"""Test load and unload of a ConfigEntry."""
@ -665,7 +557,7 @@ async def test_options_flow_triggers_reauth(
with patch(
"homeassistant.components.google.async_setup_entry", return_value=True
) as mock_setup:
await component_setup()
await hass.config_entries.async_setup(config_entry.entry_id)
mock_setup.assert_called_once()
assert config_entry.state is ConfigEntryState.LOADED
@ -689,7 +581,6 @@ async def test_options_flow_triggers_reauth(
async def test_options_flow_no_changes(
hass: HomeAssistant,
component_setup: ComponentSetup,
config_entry: MockConfigEntry,
) -> None:
"""Test load and unload of a ConfigEntry."""
@ -698,7 +589,7 @@ async def test_options_flow_no_changes(
with patch(
"homeassistant.components.google.async_setup_entry", return_value=True
) as mock_setup:
await component_setup()
await hass.config_entries.async_setup(config_entry.entry_id)
mock_setup.assert_called_once()
assert config_entry.state is ConfigEntryState.LOADED

View File

@ -12,10 +12,6 @@ from aiohttp.client_exceptions import ClientError
import pytest
import voluptuous as vol
from homeassistant.components.application_credentials import (
ClientCredential,
async_import_client_credential,
)
from homeassistant.components.google import DOMAIN, SERVICE_ADD_EVENT
from homeassistant.components.google.calendar import SERVICE_CREATE_EVENT
from homeassistant.components.google.const import CONF_CALENDAR_ACCESS
@ -23,7 +19,6 @@ from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_OFF
from homeassistant.core import HomeAssistant, State
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
from .conftest import (
@ -32,7 +27,6 @@ from .conftest import (
TEST_API_ENTITY,
TEST_API_ENTITY_NAME,
TEST_YAML_ENTITY,
TEST_YAML_ENTITY_NAME,
ApiResult,
ComponentSetup,
)
@ -59,15 +53,6 @@ def assert_state(actual: State | None, expected: State | None) -> None:
assert actual.attributes == expected.attributes
@pytest.fixture
def setup_config_entry(
hass: HomeAssistant, config_entry: MockConfigEntry
) -> MockConfigEntry:
"""Fixture to initialize the config entry."""
config_entry.add_to_hass(hass)
return config_entry
@pytest.fixture(
params=[
(
@ -110,7 +95,6 @@ def add_event_call_service(
async def test_unload_entry(
hass: HomeAssistant,
component_setup: ComponentSetup,
setup_config_entry: MockConfigEntry,
) -> None:
"""Test load and unload of a ConfigEntry."""
await component_setup()
@ -134,8 +118,7 @@ async def test_existing_token_missing_scope(
config_entry: MockConfigEntry,
) -> None:
"""Test setup where existing token does not have sufficient scopes."""
config_entry.add_to_hass(hass)
assert await component_setup()
await component_setup()
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
@ -154,8 +137,7 @@ async def test_config_entry_scope_reauth(
config_entry: MockConfigEntry,
) -> None:
"""Test setup where the config entry options requires reauth to match the scope."""
config_entry.add_to_hass(hass)
assert await component_setup()
await component_setup()
assert config_entry.state is ConfigEntryState.SETUP_ERROR
@ -170,7 +152,6 @@ async def test_calendar_yaml_missing_required_fields(
component_setup: ComponentSetup,
calendars_config: list[dict[str, Any]],
mock_calendars_yaml: None,
setup_config_entry: MockConfigEntry,
) -> None:
"""Test setup with a missing schema fields, ignores the error and continues."""
assert await component_setup()
@ -187,7 +168,6 @@ async def test_invalid_calendar_yaml(
mock_calendars_list: ApiResult,
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
setup_config_entry: MockConfigEntry,
) -> None:
"""Test setup with missing entity id fields fails to load the platform."""
mock_calendars_list({"items": [test_api_calendar]})
@ -210,7 +190,6 @@ async def test_calendar_yaml_error(
mock_calendars_list: ApiResult,
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
setup_config_entry: MockConfigEntry,
) -> None:
"""Test setup with yaml file not found."""
mock_calendars_list({"items": [test_api_calendar]})
@ -229,7 +208,6 @@ async def test_init_calendar(
mock_calendars_list: ApiResult,
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
setup_config_entry: MockConfigEntry,
) -> None:
"""Test finding a calendar from the API."""
@ -246,37 +224,6 @@ async def test_init_calendar(
assert not hass.states.get(TEST_YAML_ENTITY)
@pytest.mark.parametrize(
"google_config,config_entry_options",
[({}, {CONF_CALENDAR_ACCESS: "read_write"})],
)
async def test_load_application_credentials(
hass: HomeAssistant,
component_setup: ComponentSetup,
mock_calendars_list: ApiResult,
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
setup_config_entry: MockConfigEntry,
) -> None:
"""Test loading an application credentials and a config entry."""
assert await async_setup_component(hass, "application_credentials", {})
await async_import_client_credential(
hass, DOMAIN, ClientCredential("client-id", "client-secret"), "device_auth"
)
mock_calendars_list({"items": [test_api_calendar]})
mock_events_list({})
assert await component_setup()
state = hass.states.get(TEST_API_ENTITY)
assert state
assert state.name == TEST_API_ENTITY_NAME
assert state.state == STATE_OFF
# No yaml config loaded that overwrites the entity name
assert not hass.states.get(TEST_YAML_ENTITY)
async def test_multiple_config_entries(
hass: HomeAssistant,
component_setup: ComponentSetup,
@ -330,71 +277,6 @@ async def test_multiple_config_entries(
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "Example calendar 2"
@pytest.mark.parametrize(
"calendars_config_track,expected_state,google_config_track_new",
[
(
True,
State(
TEST_YAML_ENTITY,
STATE_OFF,
attributes={
"offset_reached": False,
"friendly_name": TEST_YAML_ENTITY_NAME,
},
),
None,
),
(
True,
State(
TEST_YAML_ENTITY,
STATE_OFF,
attributes={
"offset_reached": False,
"friendly_name": TEST_YAML_ENTITY_NAME,
},
),
True,
),
(
True,
State(
TEST_YAML_ENTITY,
STATE_OFF,
attributes={
"offset_reached": False,
"friendly_name": TEST_YAML_ENTITY_NAME,
},
),
False, # Has no effect
),
(False, None, None),
(False, None, True),
(False, None, False),
],
)
async def test_calendar_config_track_new(
hass: HomeAssistant,
component_setup: ComponentSetup,
mock_calendars_yaml: None,
mock_calendars_list: ApiResult,
mock_events_list: ApiResult,
test_api_calendar: dict[str, Any],
calendars_config_track: bool,
expected_state: State,
setup_config_entry: MockConfigEntry,
) -> None:
"""Test calendar config that overrides whether or not a calendar is tracked."""
mock_calendars_list({"items": [test_api_calendar]})
mock_events_list({})
assert await component_setup()
state = hass.states.get(TEST_YAML_ENTITY)
assert_state(state, expected_state)
@pytest.mark.parametrize(
"date_fields,expected_error,error_match",
[
@ -519,7 +401,6 @@ async def test_add_event_invalid_params(
mock_calendars_list: ApiResult,
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
setup_config_entry: MockConfigEntry,
add_event_call_service: Callable[dict[str, Any], Awaitable[None]],
date_fields: dict[str, Any],
expected_error: type[Exception],
@ -561,7 +442,6 @@ async def test_add_event_date_in_x(
date_fields: dict[str, Any],
start_timedelta: datetime.timedelta,
end_timedelta: datetime.timedelta,
setup_config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker,
add_event_call_service: Callable[dict[str, Any], Awaitable[None]],
) -> None:
@ -597,7 +477,6 @@ async def test_add_event_date(
test_api_calendar: dict[str, Any],
mock_insert_event: Callable[[str, dict[str, Any]], None],
mock_events_list: ApiResult,
setup_config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker,
add_event_call_service: Callable[dict[str, Any], Awaitable[None]],
) -> None:
@ -638,7 +517,6 @@ async def test_add_event_date_time(
mock_insert_event: Callable[[str, dict[str, Any]], None],
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
setup_config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker,
add_event_call_service: Callable[dict[str, Any], Awaitable[None]],
) -> None:
@ -685,7 +563,6 @@ async def test_add_event_failure(
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
mock_insert_event: Callable[[..., dict[str, Any]], None],
setup_config_entry: MockConfigEntry,
add_event_call_service: Callable[dict[str, Any], Awaitable[None]],
) -> None:
"""Test service calls with incorrect fields."""
@ -711,7 +588,6 @@ async def test_add_event_failure(
async def test_invalid_token_expiry_in_config_entry(
hass: HomeAssistant,
component_setup: ComponentSetup,
setup_config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker,
) -> None:
"""Exercise case in issue #69623 with invalid token expiration persisted."""
@ -743,7 +619,6 @@ async def test_invalid_token_expiry_in_config_entry(
async def test_expired_token_refresh_internal_error(
hass: HomeAssistant,
component_setup: ComponentSetup,
setup_config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker,
) -> None:
"""Generic errors on reauth are treated as a retryable setup error."""
@ -753,7 +628,7 @@ async def test_expired_token_refresh_internal_error(
status=http.HTTPStatus.INTERNAL_SERVER_ERROR,
)
assert await component_setup()
await component_setup()
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
@ -767,7 +642,6 @@ async def test_expired_token_refresh_internal_error(
async def test_expired_token_requires_reauth(
hass: HomeAssistant,
component_setup: ComponentSetup,
setup_config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker,
) -> None:
"""Test case where reauth is required for token that cannot be refreshed."""
@ -777,7 +651,7 @@ async def test_expired_token_requires_reauth(
status=http.HTTPStatus.BAD_REQUEST,
)
assert await component_setup()
await component_setup()
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
@ -811,7 +685,6 @@ async def test_calendar_yaml_update(
mock_calendars_list: ApiResult,
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
setup_config_entry: MockConfigEntry,
calendars_config: dict[str, Any],
expect_write_calls: bool,
) -> None:
@ -836,7 +709,6 @@ async def test_calendar_yaml_update(
async def test_update_will_reload(
hass: HomeAssistant,
component_setup: ComponentSetup,
setup_config_entry: Any,
mock_calendars_list: ApiResult,
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
@ -887,12 +759,12 @@ async def test_assign_unique_id(
test_api_calendar: dict[str, Any],
mock_events_list: ApiResult,
mock_calendar_get: Callable[[...], None],
setup_config_entry: MockConfigEntry,
config_entry: MockConfigEntry,
) -> None:
"""Test an existing config is updated to have unique id if it does not exist."""
assert setup_config_entry.state is ConfigEntryState.NOT_LOADED
assert setup_config_entry.unique_id is None
assert config_entry.state is ConfigEntryState.NOT_LOADED
assert config_entry.unique_id is None
mock_calendar_get(
"primary",
@ -903,8 +775,8 @@ async def test_assign_unique_id(
mock_events_list({})
assert await component_setup()
assert setup_config_entry.state is ConfigEntryState.LOADED
assert setup_config_entry.unique_id == EMAIL_ADDRESS
assert config_entry.state is ConfigEntryState.LOADED
assert config_entry.unique_id == EMAIL_ADDRESS
@pytest.mark.parametrize(
@ -923,16 +795,16 @@ async def test_assign_unique_id_failure(
component_setup: ComponentSetup,
mock_calendars_list: ApiResult,
test_api_calendar: dict[str, Any],
config_entry: MockConfigEntry,
mock_events_list: ApiResult,
mock_calendar_get: Callable[[...], None],
setup_config_entry: MockConfigEntry,
request_status: http.HTTPStatus,
config_entry_status: ConfigEntryState,
) -> None:
"""Test lookup failures during unique id assignment are handled gracefully."""
assert setup_config_entry.state is ConfigEntryState.NOT_LOADED
assert setup_config_entry.unique_id is None
assert config_entry.state is ConfigEntryState.NOT_LOADED
assert config_entry.unique_id is None
mock_calendar_get(
"primary",
@ -942,7 +814,7 @@ async def test_assign_unique_id_failure(
mock_calendars_list({"items": [test_api_calendar]})
mock_events_list({})
assert await component_setup()
await component_setup()
assert setup_config_entry.state is config_entry_status
assert setup_config_entry.unique_id is None
assert config_entry.state is config_entry_status
assert config_entry.unique_id is None