Refactor nest test_init_sdm tests to use fixtures with varied config types (#64736)

This commit is contained in:
Allen Porter 2022-01-22 20:56:03 -08:00 committed by GitHub
parent 15532c38d7
commit cdf7742c3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 373 additions and 188 deletions

View File

@ -1,7 +1,9 @@
"""Common libraries for test setup.""" """Common libraries for test setup."""
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from dataclasses import dataclass
import time import time
from typing import Any, Generator, TypeVar
from unittest.mock import patch from unittest.mock import patch
from google_nest_sdm.device_manager import DeviceManager from google_nest_sdm.device_manager import DeviceManager
@ -15,17 +17,22 @@ from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
# Typing helpers
PlatformSetup = Callable[[], Awaitable[None]]
T = TypeVar("T")
YieldFixture = Generator[T, None, None]
PROJECT_ID = "some-project-id" PROJECT_ID = "some-project-id"
CLIENT_ID = "some-client-id" CLIENT_ID = "some-client-id"
CLIENT_SECRET = "some-client-secret" CLIENT_SECRET = "some-client-secret"
SUBSCRIBER_ID = "projects/example/subscriptions/subscriber-id-9876"
CONFIG = { CONFIG = {
"nest": { "nest": {
"client_id": CLIENT_ID, "client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET, "client_secret": CLIENT_SECRET,
# Required fields for using SDM API
"project_id": PROJECT_ID, "project_id": PROJECT_ID,
"subscriber_id": "projects/example/subscriptions/subscriber-id-9876", "subscriber_id": SUBSCRIBER_ID,
}, },
} }
@ -33,24 +40,64 @@ FAKE_TOKEN = "some-token"
FAKE_REFRESH_TOKEN = "some-refresh-token" FAKE_REFRESH_TOKEN = "some-refresh-token"
def create_config_entry(hass, token_expiration_time=None) -> MockConfigEntry: def create_token_entry(token_expiration_time=None):
"""Create a ConfigEntry and add it to Home Assistant.""" """Create OAuth 'token' data for a ConfigEntry."""
if token_expiration_time is None: if token_expiration_time is None:
token_expiration_time = time.time() + 86400 token_expiration_time = time.time() + 86400
return {
"access_token": FAKE_TOKEN,
"refresh_token": FAKE_REFRESH_TOKEN,
"scope": " ".join(SDM_SCOPES),
"token_type": "Bearer",
"expires_at": token_expiration_time,
}
def create_config_entry(token_expiration_time=None) -> MockConfigEntry:
"""Create a ConfigEntry and add it to Home Assistant."""
config_entry_data = { config_entry_data = {
"sdm": {}, # Indicates new SDM API, not legacy API "sdm": {}, # Indicates new SDM API, not legacy API
"auth_implementation": "nest", "auth_implementation": "nest",
"token": { "token": create_token_entry(token_expiration_time),
"access_token": FAKE_TOKEN,
"refresh_token": FAKE_REFRESH_TOKEN,
"scope": " ".join(SDM_SCOPES),
"token_type": "Bearer",
"expires_at": token_expiration_time,
},
} }
config_entry = MockConfigEntry(domain=DOMAIN, data=config_entry_data) return MockConfigEntry(domain=DOMAIN, data=config_entry_data)
config_entry.add_to_hass(hass)
return config_entry
@dataclass
class NestTestConfig:
"""Holder for integration configuration."""
config: dict[str, Any]
config_entry_data: dict[str, Any]
# Exercises mode where all configuration is in configuration.yaml
TEST_CONFIG_YAML_ONLY = NestTestConfig(
config=CONFIG,
config_entry_data={
"sdm": {},
"auth_implementation": "nest",
"token": create_token_entry(),
},
)
# Exercises mode where subscriber id is created in the config flow, but
# all authentication is defined in configuration.yaml
TEST_CONFIG_HYBRID = NestTestConfig(
config={
"nest": {
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"project_id": PROJECT_ID,
},
},
config_entry_data={
"sdm": {},
"auth_implementation": "nest",
"token": create_token_entry(),
"subscriber_id": SUBSCRIBER_ID,
},
)
class FakeSubscriber(GoogleNestSubscriber): class FakeSubscriber(GoogleNestSubscriber):
@ -100,7 +147,7 @@ async def async_setup_sdm_platform(
): ):
"""Set up the platform and prerequisites.""" """Set up the platform and prerequisites."""
if with_config: if with_config:
create_config_entry(hass) create_config_entry().add_to_hass(hass)
subscriber = FakeSubscriber() subscriber = FakeSubscriber()
device_manager = await subscriber.async_get_device_manager() device_manager = await subscriber.async_get_device_manager()
if devices: if devices:

View File

@ -1,13 +1,34 @@
"""Common libraries for test setup.""" """Common libraries for test setup."""
from __future__ import annotations
import copy
import shutil import shutil
from typing import Any
from unittest.mock import patch from unittest.mock import patch
import uuid import uuid
import aiohttp import aiohttp
from google_nest_sdm.auth import AbstractAuth from google_nest_sdm.auth import AbstractAuth
from google_nest_sdm.device_manager import DeviceManager
import pytest import pytest
from homeassistant.components.nest import DOMAIN
from homeassistant.components.nest.const import CONF_SUBSCRIBER_ID
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .common import (
SUBSCRIBER_ID,
TEST_CONFIG_HYBRID,
TEST_CONFIG_YAML_ONLY,
FakeSubscriber,
NestTestConfig,
PlatformSetup,
YieldFixture,
)
from tests.common import MockConfigEntry
class FakeAuth(AbstractAuth): class FakeAuth(AbstractAuth):
"""A fake implementation of the auth class that records requests. """A fake implementation of the auth class that records requests.
@ -76,3 +97,100 @@ def cleanup_media_storage(hass):
with patch("homeassistant.components.nest.media_source.MEDIA_PATH", new=tmp_path): with patch("homeassistant.components.nest.media_source.MEDIA_PATH", new=tmp_path):
yield yield
shutil.rmtree(hass.config.path(tmp_path), ignore_errors=True) shutil.rmtree(hass.config.path(tmp_path), ignore_errors=True)
@pytest.fixture
def subscriber() -> YieldFixture[FakeSubscriber]:
"""Set up the FakeSusbcriber."""
subscriber = FakeSubscriber()
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber",
return_value=subscriber,
):
yield subscriber
@pytest.fixture
async def device_manager(subscriber: FakeSubscriber) -> DeviceManager:
"""Set up the DeviceManager."""
return await subscriber.async_get_device_manager()
@pytest.fixture
def platforms() -> list[str]:
"""Fixture to specify platforms to test."""
return []
@pytest.fixture
def subscriber_id() -> str:
"""Fixture to let tests override subscriber id regardless of configuration type used."""
return SUBSCRIBER_ID
@pytest.fixture(
params=[TEST_CONFIG_YAML_ONLY, TEST_CONFIG_HYBRID],
ids=["yaml-config-only", "hybrid-config"],
)
def nest_test_config(request) -> NestTestConfig:
"""Fixture that sets up the configuration used for the test."""
return request.param
@pytest.fixture
def config(
subscriber_id: str | None, nest_test_config: NestTestConfig
) -> dict[str, Any]:
"""Fixture that sets up the configuration.yaml for the test."""
config = copy.deepcopy(nest_test_config.config)
if CONF_SUBSCRIBER_ID in config.get(DOMAIN, {}):
if subscriber_id:
config[DOMAIN][CONF_SUBSCRIBER_ID] = subscriber_id
else:
del config[DOMAIN][CONF_SUBSCRIBER_ID]
return config
@pytest.fixture
def config_entry(
subscriber_id: str | None, nest_test_config: NestTestConfig
) -> MockConfigEntry | None:
"""Fixture that sets up the ConfigEntry for the test."""
if nest_test_config.config_entry_data is None:
return None
data = copy.deepcopy(nest_test_config.config_entry_data)
if CONF_SUBSCRIBER_ID in data:
if subscriber_id:
data[CONF_SUBSCRIBER_ID] = subscriber_id
else:
del data[CONF_SUBSCRIBER_ID]
return MockConfigEntry(domain=DOMAIN, data=data)
@pytest.fixture
async def setup_base_platform(
hass: HomeAssistant,
platforms: list[str],
config: dict[str, Any],
config_entry: MockConfigEntry | None,
) -> YieldFixture[PlatformSetup]:
"""Fixture to setup the integration platform."""
if config_entry:
config_entry.add_to_hass(hass)
with patch(
"homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation"
), patch("homeassistant.components.nest.PLATFORMS", platforms):
async def _setup_func() -> bool:
assert await async_setup_component(hass, DOMAIN, config)
await hass.async_block_till_done()
yield _setup_func
@pytest.fixture
async def setup_platform(
setup_base_platform: PlatformSetup, subscriber: FakeSubscriber
) -> PlatformSetup:
"""Fixture to setup the integration platform and subscriber."""
return setup_base_platform

View File

@ -39,7 +39,7 @@ async def test_auth(hass, aioclient_mock):
"""Exercise authentication library creates valid credentials.""" """Exercise authentication library creates valid credentials."""
expiration_time = time.time() + 86400 expiration_time = time.time() + 86400
create_config_entry(hass, expiration_time) create_config_entry(expiration_time).add_to_hass(hass)
# Prepare to capture credentials in API request. Empty payloads just mean # Prepare to capture credentials in API request. Empty payloads just mean
# no devices or structures are loaded. # no devices or structures are loaded.
@ -88,7 +88,7 @@ async def test_auth_expired_token(hass, aioclient_mock):
"""Verify behavior of an expired token.""" """Verify behavior of an expired token."""
expiration_time = time.time() - 86400 expiration_time = time.time() - 86400
create_config_entry(hass, expiration_time) create_config_entry(expiration_time).add_to_hass(hass)
# Prepare a token refresh response # Prepare a token refresh response
aioclient_mock.post( aioclient_mock.post(

View File

@ -70,7 +70,8 @@ async def test_entry_diagnostics(hass, hass_client):
async def test_setup_susbcriber_failure(hass, hass_client): async def test_setup_susbcriber_failure(hass, hass_client):
"""Test configuration error.""" """Test configuration error."""
config_entry = create_config_entry(hass) config_entry = create_config_entry()
config_entry.add_to_hass(hass)
with patch( with patch(
"homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation" "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation"
), patch( ), patch(

View File

@ -3,10 +3,14 @@ Test for setup methods for the SDM API.
The tests fake out the subscriber/devicemanager and simulate setup behavior The tests fake out the subscriber/devicemanager and simulate setup behavior
and failure modes. and failure modes.
By default all tests use test fixtures that run in each possible configuration
mode (e.g. yaml, ConfigEntry, etc) however some tests override and just run in
relevant modes.
""" """
import copy
import logging import logging
from typing import Any
from unittest.mock import patch from unittest.mock import patch
from google_nest_sdm.exceptions import ( from google_nest_sdm.exceptions import (
@ -15,44 +19,75 @@ from google_nest_sdm.exceptions import (
ConfigurationException, ConfigurationException,
SubscriberException, SubscriberException,
) )
import pytest
from homeassistant.components.nest import DOMAIN from homeassistant.components.nest import DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.setup import async_setup_component
from .common import CONFIG, async_setup_sdm_platform, create_config_entry from .common import (
TEST_CONFIG_HYBRID,
TEST_CONFIG_YAML_ONLY,
FakeSubscriber,
NestTestConfig,
YieldFixture,
)
PLATFORM = "sensor" PLATFORM = "sensor"
async def test_setup_success(hass, caplog): @pytest.fixture
"""Test successful setup.""" def platforms() -> list[str]:
"""Fixture to setup the platforms to test."""
return ["sensor"]
@pytest.fixture
def error_caplog(caplog):
"""Fixture to capture nest init error messages."""
with caplog.at_level(logging.ERROR, logger="homeassistant.components.nest"): with caplog.at_level(logging.ERROR, logger="homeassistant.components.nest"):
await async_setup_sdm_platform(hass, PLATFORM) yield caplog
assert not caplog.records
@pytest.fixture
def warning_caplog(caplog):
"""Fixture to capture nest init warning messages."""
with caplog.at_level(logging.WARNING, logger="homeassistant.components.nest"):
yield caplog
@pytest.fixture
def subscriber_side_effect() -> None:
"""Fixture to inject failures into FakeSubscriber start."""
return None
@pytest.fixture
def failing_subscriber(subscriber_side_effect: Any) -> YieldFixture[FakeSubscriber]:
"""Fixture overriding default subscriber behavior to allow failure injection."""
subscriber = FakeSubscriber()
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber.start_async",
side_effect=subscriber_side_effect,
):
yield subscriber
async def test_setup_success(hass, error_caplog, setup_platform):
"""Test successful setup."""
await setup_platform()
assert not error_caplog.records
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
assert entries[0].state is ConfigEntryState.LOADED assert entries[0].state is ConfigEntryState.LOADED
async def async_setup_sdm(hass, config=CONFIG, with_config=True): @pytest.mark.parametrize("subscriber_id", [("invalid-subscriber-format")])
"""Prepare test setup.""" async def test_setup_configuration_failure(
if with_config: hass, caplog, subscriber_id, setup_base_platform
create_config_entry(hass) ):
with patch(
"homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation"
):
return await async_setup_component(hass, DOMAIN, config)
async def test_setup_configuration_failure(hass, caplog):
"""Test configuration error.""" """Test configuration error."""
config = copy.deepcopy(CONFIG) await setup_base_platform()
config[DOMAIN]["subscriber_id"] = "invalid-subscriber-format"
result = await async_setup_sdm(hass, config)
assert result
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
@ -63,49 +98,43 @@ async def test_setup_configuration_failure(hass, caplog):
assert "Subscription misconfigured. Expected subscriber_id" in caplog.text assert "Subscription misconfigured. Expected subscriber_id" in caplog.text
async def test_setup_susbcriber_failure(hass, caplog): @pytest.mark.parametrize("subscriber_side_effect", [SubscriberException()])
async def test_setup_susbcriber_failure(
hass, error_caplog, failing_subscriber, setup_base_platform
):
"""Test configuration error.""" """Test configuration error."""
with patch( await setup_base_platform()
"homeassistant.components.nest.api.GoogleNestSubscriber.start_async", assert "Subscriber error:" in error_caplog.text
side_effect=SubscriberException(),
), caplog.at_level(logging.ERROR, logger="homeassistant.components.nest"):
result = await async_setup_sdm(hass)
assert result
assert "Subscriber error:" in caplog.text
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
assert entries[0].state is ConfigEntryState.SETUP_RETRY assert entries[0].state is ConfigEntryState.SETUP_RETRY
async def test_setup_device_manager_failure(hass, caplog): async def test_setup_device_manager_failure(hass, error_caplog, setup_base_platform):
"""Test configuration error.""" """Test device manager api failure."""
with patch( with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber.start_async" "homeassistant.components.nest.api.GoogleNestSubscriber.start_async"
), patch( ), patch(
"homeassistant.components.nest.api.GoogleNestSubscriber.async_get_device_manager", "homeassistant.components.nest.api.GoogleNestSubscriber.async_get_device_manager",
side_effect=ApiException(), side_effect=ApiException(),
), caplog.at_level(
logging.ERROR, logger="homeassistant.components.nest"
): ):
result = await async_setup_sdm(hass) await setup_base_platform()
assert result
assert len(caplog.messages) == 1 assert len(error_caplog.messages) == 1
assert "Device manager error:" in caplog.text assert "Device manager error:" in error_caplog.text
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
assert entries[0].state is ConfigEntryState.SETUP_RETRY assert entries[0].state is ConfigEntryState.SETUP_RETRY
async def test_subscriber_auth_failure(hass, caplog): @pytest.mark.parametrize("subscriber_side_effect", [AuthException()])
"""Test configuration error.""" async def test_subscriber_auth_failure(
with patch( hass, caplog, setup_base_platform, failing_subscriber
"homeassistant.components.nest.api.GoogleNestSubscriber.start_async", ):
side_effect=AuthException(), """Test subscriber throws an authentication error."""
): await setup_base_platform()
result = await async_setup_sdm(hass, CONFIG)
assert result
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
@ -116,70 +145,45 @@ async def test_subscriber_auth_failure(hass, caplog):
assert flows[0]["step_id"] == "reauth_confirm" assert flows[0]["step_id"] == "reauth_confirm"
async def test_setup_missing_subscriber_id(hass, caplog): @pytest.mark.parametrize("subscriber_id", [(None)])
"""Test missing susbcriber id from config and config entry.""" async def test_setup_missing_subscriber_id(hass, warning_caplog, setup_base_platform):
config = copy.deepcopy(CONFIG) """Test missing susbcriber id from configuration."""
del config[DOMAIN]["subscriber_id"] await setup_base_platform()
assert "Configuration option" in warning_caplog.text
with caplog.at_level(logging.WARNING, logger="homeassistant.components.nest"):
result = await async_setup_sdm(hass, config)
assert result
assert "Configuration option" in caplog.text
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
assert entries[0].state is ConfigEntryState.SETUP_ERROR assert entries[0].state is ConfigEntryState.SETUP_ERROR
async def test_setup_subscriber_id_config_entry(hass, caplog): @pytest.mark.parametrize("subscriber_side_effect", [(ConfigurationException())])
"""Test successful setup with subscriber id in ConfigEntry.""" async def test_subscriber_configuration_failure(
config = copy.deepcopy(CONFIG) hass, error_caplog, setup_base_platform, failing_subscriber
subscriber_id = config[DOMAIN]["subscriber_id"] ):
del config[DOMAIN]["subscriber_id"]
config_entry = create_config_entry(hass)
data = {**config_entry.data}
data["subscriber_id"] = subscriber_id
hass.config_entries.async_update_entry(config_entry, data=data)
with caplog.at_level(logging.ERROR, logger="homeassistant.components.nest"):
await async_setup_sdm_platform(hass, PLATFORM, with_config=False)
assert not caplog.records
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
assert entries[0].state is ConfigEntryState.LOADED
async def test_subscriber_configuration_failure(hass, caplog):
"""Test configuration error.""" """Test configuration error."""
with patch( await setup_base_platform()
"homeassistant.components.nest.api.GoogleNestSubscriber.start_async", assert "Configuration error: " in error_caplog.text
side_effect=ConfigurationException(),
), caplog.at_level(logging.ERROR, logger="homeassistant.components.nest"):
result = await async_setup_sdm(hass, CONFIG)
assert result
assert "Configuration error: " in caplog.text
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
assert entries[0].state is ConfigEntryState.SETUP_ERROR assert entries[0].state is ConfigEntryState.SETUP_ERROR
async def test_empty_config(hass, caplog): @pytest.mark.parametrize(
"""Test successful setup.""" "nest_test_config", [NestTestConfig(config={}, config_entry_data=None)]
with caplog.at_level(logging.ERROR, logger="homeassistant.components.nest"): )
result = await async_setup_component(hass, DOMAIN, {}) async def test_empty_config(hass, error_caplog, config, setup_platform):
assert result """Test setup is a no-op with not config."""
assert not caplog.records await setup_platform()
assert not error_caplog.records
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 0 assert len(entries) == 0
async def test_unload_entry(hass, caplog): async def test_unload_entry(hass, setup_platform):
"""Test successful unload of a ConfigEntry.""" """Test successful unload of a ConfigEntry."""
await async_setup_sdm_platform(hass, PLATFORM) await setup_platform()
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
@ -190,33 +194,27 @@ async def test_unload_entry(hass, caplog):
assert entry.state == ConfigEntryState.NOT_LOADED assert entry.state == ConfigEntryState.NOT_LOADED
async def test_remove_entry(hass, caplog): @pytest.mark.parametrize(
"nest_test_config,delete_called",
[
(
TEST_CONFIG_YAML_ONLY,
False,
), # User manually created subscriber, preserve on remove
(
TEST_CONFIG_HYBRID,
True,
), # Integration created subscriber, garbage collect on remove
],
ids=["yaml-config-only", "hybrid-config"],
)
async def test_remove_entry(hass, nest_test_config, setup_base_platform, delete_called):
"""Test successful unload of a ConfigEntry.""" """Test successful unload of a ConfigEntry."""
await async_setup_sdm_platform(hass, PLATFORM) with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber",
entries = hass.config_entries.async_entries(DOMAIN) return_value=FakeSubscriber(),
assert len(entries) == 1 ):
entry = entries[0] await setup_base_platform()
assert entry.state is ConfigEntryState.LOADED
assert await hass.config_entries.async_remove(entry.entry_id)
entries = hass.config_entries.async_entries(DOMAIN)
assert not entries
async def test_remove_entry_deletes_subscriber(hass, caplog):
"""Test ConfigEntry unload deletes a subscription."""
config = copy.deepcopy(CONFIG)
subscriber_id = config[DOMAIN]["subscriber_id"]
del config[DOMAIN]["subscriber_id"]
config_entry = create_config_entry(hass)
data = {**config_entry.data}
data["subscriber_id"] = subscriber_id
hass.config_entries.async_update_entry(config_entry, data=data)
await async_setup_sdm_platform(hass, PLATFORM, with_config=False)
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
@ -224,27 +222,29 @@ async def test_remove_entry_deletes_subscriber(hass, caplog):
assert entry.state is ConfigEntryState.LOADED assert entry.state is ConfigEntryState.LOADED
with patch( with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber.subscriber_id"
), patch(
"homeassistant.components.nest.api.GoogleNestSubscriber.delete_subscription", "homeassistant.components.nest.api.GoogleNestSubscriber.delete_subscription",
) as delete: ) as delete:
assert await hass.config_entries.async_remove(entry.entry_id) assert await hass.config_entries.async_remove(entry.entry_id)
assert delete.called assert delete.called == delete_called
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert not entries assert not entries
async def test_remove_entry_delete_subscriber_failure(hass, caplog): @pytest.mark.parametrize(
"nest_test_config", [TEST_CONFIG_HYBRID], ids=["hyrbid-config"]
)
async def test_remove_entry_delete_subscriber_failure(
hass, nest_test_config, setup_base_platform
):
"""Test a failure when deleting the subscription.""" """Test a failure when deleting the subscription."""
config = copy.deepcopy(CONFIG) with patch(
subscriber_id = config[DOMAIN]["subscriber_id"] "homeassistant.components.nest.api.GoogleNestSubscriber",
del config[DOMAIN]["subscriber_id"] return_value=FakeSubscriber(),
):
config_entry = create_config_entry(hass) await setup_base_platform()
data = {**config_entry.data}
data["subscriber_id"] = subscriber_id
hass.config_entries.async_update_entry(config_entry, data=data)
await async_setup_sdm_platform(hass, PLATFORM, with_config=False)
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1 assert len(entries) == 1
@ -254,8 +254,9 @@ async def test_remove_entry_delete_subscriber_failure(hass, caplog):
with patch( with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber.delete_subscription", "homeassistant.components.nest.api.GoogleNestSubscriber.delete_subscription",
side_effect=SubscriberException(), side_effect=SubscriberException(),
): ) as delete:
assert await hass.config_entries.async_remove(entry.entry_id) assert await hass.config_entries.async_remove(entry.entry_id)
assert delete.called
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
assert not entries assert not entries

View File

@ -1043,7 +1043,8 @@ async def test_media_store_persistence(hass, auth, hass_client, event_store):
# Fetch media for events when published # Fetch media for events when published
subscriber.cache_policy.fetch = True subscriber.cache_policy.fetch = True
config_entry = create_config_entry(hass) config_entry = create_config_entry()
config_entry.add_to_hass(hass)
with patch( with patch(
"homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation" "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation"

View File

@ -6,7 +6,9 @@ pubsub subscriber.
""" """
from google_nest_sdm.device import Device from google_nest_sdm.device import Device
from google_nest_sdm.device_manager import DeviceManager
from google_nest_sdm.event import EventMessage from google_nest_sdm.event import EventMessage
import pytest
from homeassistant.components.sensor import ATTR_STATE_CLASS, STATE_CLASS_MEASUREMENT from homeassistant.components.sensor import ATTR_STATE_CLASS, STATE_CLASS_MEASUREMENT
from homeassistant.const import ( from homeassistant.const import (
@ -17,24 +19,26 @@ from homeassistant.const import (
PERCENTAGE, PERCENTAGE,
TEMP_CELSIUS, TEMP_CELSIUS,
) )
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from .common import async_setup_sdm_platform from .common import FakeSubscriber, PlatformSetup
PLATFORM = "sensor"
THERMOSTAT_TYPE = "sdm.devices.types.THERMOSTAT" THERMOSTAT_TYPE = "sdm.devices.types.THERMOSTAT"
async def async_setup_sensor(hass, devices={}, structures={}): @pytest.fixture
"""Set up the platform and prerequisites.""" def platforms() -> list[str]:
return await async_setup_sdm_platform(hass, PLATFORM, devices, structures) """Fixture to setup the platforms to test."""
return ["sensor"]
async def test_thermostat_device(hass): async def test_thermostat_device(
hass: HomeAssistant, device_manager: DeviceManager, setup_platform: PlatformSetup
):
"""Test a thermostat with temperature and humidity sensors.""" """Test a thermostat with temperature and humidity sensors."""
devices = { device_manager.add_device(
"some-device-id": Device.MakeDevice( Device.MakeDevice(
{ {
"name": "some-device-id", "name": "some-device-id",
"type": THERMOSTAT_TYPE, "type": THERMOSTAT_TYPE,
@ -52,8 +56,8 @@ async def test_thermostat_device(hass):
}, },
auth=None, auth=None,
) )
} )
await async_setup_sensor(hass, devices) await setup_platform()
temperature = hass.states.get("sensor.my_sensor_temperature") temperature = hass.states.get("sensor.my_sensor_temperature")
assert temperature is not None assert temperature is not None
@ -87,9 +91,9 @@ async def test_thermostat_device(hass):
assert device.identifiers == {("nest", "some-device-id")} assert device.identifiers == {("nest", "some-device-id")}
async def test_no_devices(hass): async def test_no_devices(hass: HomeAssistant, setup_platform: PlatformSetup):
"""Test no devices returned by the api.""" """Test no devices returned by the api."""
await async_setup_sensor(hass) await setup_platform()
temperature = hass.states.get("sensor.my_sensor_temperature") temperature = hass.states.get("sensor.my_sensor_temperature")
assert temperature is None assert temperature is None
@ -98,10 +102,12 @@ async def test_no_devices(hass):
assert humidity is None assert humidity is None
async def test_device_no_sensor_traits(hass): async def test_device_no_sensor_traits(
hass: HomeAssistant, device_manager: DeviceManager, setup_platform: PlatformSetup
) -> None:
"""Test a device with applicable sensor traits.""" """Test a device with applicable sensor traits."""
devices = { device_manager.add_device(
"some-device-id": Device.MakeDevice( Device.MakeDevice(
{ {
"name": "some-device-id", "name": "some-device-id",
"type": THERMOSTAT_TYPE, "type": THERMOSTAT_TYPE,
@ -109,8 +115,8 @@ async def test_device_no_sensor_traits(hass):
}, },
auth=None, auth=None,
) )
} )
await async_setup_sensor(hass, devices) await setup_platform()
temperature = hass.states.get("sensor.my_sensor_temperature") temperature = hass.states.get("sensor.my_sensor_temperature")
assert temperature is None assert temperature is None
@ -119,10 +125,12 @@ async def test_device_no_sensor_traits(hass):
assert humidity is None assert humidity is None
async def test_device_name_from_structure(hass): async def test_device_name_from_structure(
hass: HomeAssistant, device_manager: DeviceManager, setup_platform: PlatformSetup
) -> None:
"""Test a device without a custom name, inferring name from structure.""" """Test a device without a custom name, inferring name from structure."""
devices = { device_manager.add_device(
"some-device-id": Device.MakeDevice( Device.MakeDevice(
{ {
"name": "some-device-id", "name": "some-device-id",
"type": THERMOSTAT_TYPE, "type": THERMOSTAT_TYPE,
@ -137,18 +145,23 @@ async def test_device_name_from_structure(hass):
}, },
auth=None, auth=None,
) )
} )
await async_setup_sensor(hass, devices) await setup_platform()
temperature = hass.states.get("sensor.some_room_temperature") temperature = hass.states.get("sensor.some_room_temperature")
assert temperature is not None assert temperature is not None
assert temperature.state == "25.2" assert temperature.state == "25.2"
async def test_event_updates_sensor(hass): async def test_event_updates_sensor(
hass: HomeAssistant,
subscriber: FakeSubscriber,
device_manager: DeviceManager,
setup_platform: PlatformSetup,
) -> None:
"""Test a pubsub message received by subscriber to update temperature.""" """Test a pubsub message received by subscriber to update temperature."""
devices = { device_manager.add_device(
"some-device-id": Device.MakeDevice( Device.MakeDevice(
{ {
"name": "some-device-id", "name": "some-device-id",
"type": THERMOSTAT_TYPE, "type": THERMOSTAT_TYPE,
@ -163,8 +176,8 @@ async def test_event_updates_sensor(hass):
}, },
auth=None, auth=None,
) )
} )
subscriber = await async_setup_sensor(hass, devices) await setup_platform()
temperature = hass.states.get("sensor.my_sensor_temperature") temperature = hass.states.get("sensor.my_sensor_temperature")
assert temperature is not None assert temperature is not None
@ -194,10 +207,12 @@ async def test_event_updates_sensor(hass):
assert temperature.state == "26.2" assert temperature.state == "26.2"
async def test_device_with_unknown_type(hass): async def test_device_with_unknown_type(
hass: HomeAssistant, device_manager: DeviceManager, setup_platform: PlatformSetup
) -> None:
"""Test a device without a custom name, inferring name from structure.""" """Test a device without a custom name, inferring name from structure."""
devices = { device_manager.add_device(
"some-device-id": Device.MakeDevice( Device.MakeDevice(
{ {
"name": "some-device-id", "name": "some-device-id",
"type": "some-unknown-type", "type": "some-unknown-type",
@ -212,8 +227,8 @@ async def test_device_with_unknown_type(hass):
}, },
auth=None, auth=None,
) )
} )
await async_setup_sensor(hass, devices) await setup_platform()
temperature = hass.states.get("sensor.my_sensor_temperature") temperature = hass.states.get("sensor.my_sensor_temperature")
assert temperature is not None assert temperature is not None
@ -232,10 +247,12 @@ async def test_device_with_unknown_type(hass):
assert device.identifiers == {("nest", "some-device-id")} assert device.identifiers == {("nest", "some-device-id")}
async def test_temperature_rounding(hass): async def test_temperature_rounding(
hass: HomeAssistant, device_manager: DeviceManager, setup_platform: PlatformSetup
) -> None:
"""Test the rounding of overly precise temperatures.""" """Test the rounding of overly precise temperatures."""
devices = { device_manager.add_device(
"some-device-id": Device.MakeDevice( Device.MakeDevice(
{ {
"name": "some-device-id", "name": "some-device-id",
"type": THERMOSTAT_TYPE, "type": THERMOSTAT_TYPE,
@ -250,8 +267,8 @@ async def test_temperature_rounding(hass):
}, },
auth=None, auth=None,
) )
} )
await async_setup_sensor(hass, devices) await setup_platform()
temperature = hass.states.get("sensor.my_sensor_temperature") temperature = hass.states.get("sensor.my_sensor_temperature")
assert temperature.state == "25.2" assert temperature.state == "25.2"