Stop updating google_calendars.yaml if it does not already exist (#72340)

* Stop updating google_calendars.yaml if it does not already exist

* Add additional test coverage to make CI pass

* Add test for no updates to google_calendar.yaml

* Add parameter to test for expecting write calls

* Missing call argument

* Remove conditional and replace with inline assert
pull/72485/head
Allen Porter 2022-05-24 23:59:27 -07:00 committed by GitHub
parent 9591d5366e
commit 71bc650ac7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 15 deletions

View File

@ -300,6 +300,7 @@ async def async_setup_services(
calendars = await hass.async_add_executor_job(
load_config, hass.config.path(YAML_DEVICES)
)
calendars_file_lock = asyncio.Lock()
async def _found_calendar(calendar_item: Calendar) -> None:
calendar = get_calendar_info(
@ -307,15 +308,19 @@ async def async_setup_services(
calendar_item.dict(exclude_unset=True),
)
calendar_id = calendar_item.id
# Populate the yaml file with all discovered calendars
if calendar_id not in calendars:
calendars[calendar_id] = calendar
await hass.async_add_executor_job(
update_config, hass.config.path(YAML_DEVICES), calendar
)
else:
# Prefer entity/name information from yaml, overriding api
calendar = calendars[calendar_id]
# If the google_calendars.yaml file already exists, populate it for
# backwards compatibility, but otherwise do not create it if it does
# not exist.
if calendars:
if calendar_id not in calendars:
calendars[calendar_id] = calendar
async with calendars_file_lock:
await hass.async_add_executor_job(
update_config, hass.config.path(YAML_DEVICES), calendar
)
else:
# Prefer entity/name information from yaml, overriding api
calendar = calendars[calendar_id]
async_dispatcher_send(hass, DISCOVER_CALENDAR, calendar)
created_calendars = set()

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from collections.abc import Awaitable, Callable
import datetime
from typing import Any, Generator, TypeVar
from unittest.mock import mock_open, patch
from unittest.mock import Mock, mock_open, patch
from aiohttp.client_exceptions import ClientError
from gcal_sync.auth import API_BASE_URL
@ -104,11 +104,11 @@ def calendars_config(calendars_config_entity: dict[str, Any]) -> list[dict[str,
def mock_calendars_yaml(
hass: HomeAssistant,
calendars_config: list[dict[str, Any]],
) -> None:
) -> Generator[Mock, None, None]:
"""Fixture that prepares the google_calendars.yaml mocks."""
mocked_open_function = mock_open(read_data=yaml.dump(calendars_config))
with patch("homeassistant.components.google.open", mocked_open_function):
yield
yield mocked_open_function
class FakeStorage:
@ -170,10 +170,17 @@ def config_entry_token_expiry(token_expiry: datetime.datetime) -> float:
return token_expiry.timestamp()
@pytest.fixture
def config_entry_options() -> dict[str, Any] | None:
"""Fixture to set initial config entry options."""
return None
@pytest.fixture
def config_entry(
token_scopes: list[str],
config_entry_token_expiry: float,
config_entry_options: dict[str, Any] | None,
) -> MockConfigEntry:
"""Fixture to create a config entry for the integration."""
return MockConfigEntry(
@ -188,6 +195,7 @@ def config_entry(
"expires_at": config_entry_token_expiry,
},
},
options=config_entry_options,
)

View File

@ -572,14 +572,16 @@ async def test_opaque_event(
assert (len(events) > 0) == expect_visible_event
@pytest.mark.parametrize("mock_test_setup", [None])
async def test_scan_calendar_error(
hass,
component_setup,
test_api_calendar,
mock_calendars_list,
config_entry,
):
"""Test that the calendar update handles a server error."""
config_entry.add_to_hass(hass)
mock_calendars_list({}, exc=ClientError())
assert await component_setup()

View File

@ -6,7 +6,7 @@ import datetime
import http
import time
from typing import Any
from unittest.mock import patch
from unittest.mock import Mock, patch
import pytest
@ -19,6 +19,7 @@ from homeassistant.components.google import (
SERVICE_ADD_EVENT,
SERVICE_SCAN_CALENDARS,
)
from homeassistant.components.google.const import CONF_CALENDAR_ACCESS
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import STATE_OFF
from homeassistant.core import HomeAssistant, State
@ -229,7 +230,10 @@ async def test_found_calendar_from_api(
assert not hass.states.get(TEST_YAML_ENTITY)
@pytest.mark.parametrize("calendars_config,google_config", [([], {})])
@pytest.mark.parametrize(
"calendars_config,google_config,config_entry_options",
[([], {}, {CONF_CALENDAR_ACCESS: "read_write"})],
)
async def test_load_application_credentials(
hass: HomeAssistant,
component_setup: ComponentSetup,
@ -604,3 +608,48 @@ async def test_expired_token_requires_reauth(
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
assert flows[0]["step_id"] == "reauth_confirm"
@pytest.mark.parametrize(
"calendars_config,expect_write_calls",
[
(
[
{
"cal_id": "ignored",
"entities": {"device_id": "existing", "name": "existing"},
}
],
True,
),
([], False),
],
ids=["has_yaml", "no_yaml"],
)
async def test_calendar_yaml_update(
hass: HomeAssistant,
component_setup: ComponentSetup,
mock_calendars_yaml: Mock,
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:
"""Test updating the yaml file with a new calendar."""
mock_calendars_list({"items": [test_api_calendar]})
mock_events_list({})
assert await component_setup()
mock_calendars_yaml().read.assert_called()
mock_calendars_yaml().write.called is expect_write_calls
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)