From cdf7742c3e6faebf025985d86da7763ad8c10fe4 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 22 Jan 2022 20:56:03 -0800 Subject: [PATCH] Refactor nest test_init_sdm tests to use fixtures with varied config types (#64736) --- tests/components/nest/common.py | 77 ++++-- tests/components/nest/conftest.py | 118 +++++++++ tests/components/nest/test_api.py | 4 +- tests/components/nest/test_diagnostics.py | 3 +- tests/components/nest/test_init_sdm.py | 263 +++++++++++---------- tests/components/nest/test_media_source.py | 3 +- tests/components/nest/test_sensor_sdm.py | 93 +++++--- 7 files changed, 373 insertions(+), 188 deletions(-) diff --git a/tests/components/nest/common.py b/tests/components/nest/common.py index f61c50f686a..e957cffe29d 100644 --- a/tests/components/nest/common.py +++ b/tests/components/nest/common.py @@ -1,7 +1,9 @@ """Common libraries for test setup.""" from collections.abc import Awaitable, Callable +from dataclasses import dataclass import time +from typing import Any, Generator, TypeVar from unittest.mock import patch from google_nest_sdm.device_manager import DeviceManager @@ -15,17 +17,22 @@ from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry +# Typing helpers +PlatformSetup = Callable[[], Awaitable[None]] +T = TypeVar("T") +YieldFixture = Generator[T, None, None] + PROJECT_ID = "some-project-id" CLIENT_ID = "some-client-id" CLIENT_SECRET = "some-client-secret" +SUBSCRIBER_ID = "projects/example/subscriptions/subscriber-id-9876" CONFIG = { "nest": { "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, - # Required fields for using SDM API "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" -def create_config_entry(hass, token_expiration_time=None) -> MockConfigEntry: - """Create a ConfigEntry and add it to Home Assistant.""" +def create_token_entry(token_expiration_time=None): + """Create OAuth 'token' data for a ConfigEntry.""" if token_expiration_time is None: 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 = { "sdm": {}, # Indicates new SDM API, not legacy API "auth_implementation": "nest", - "token": { - "access_token": FAKE_TOKEN, - "refresh_token": FAKE_REFRESH_TOKEN, - "scope": " ".join(SDM_SCOPES), - "token_type": "Bearer", - "expires_at": token_expiration_time, - }, + "token": create_token_entry(token_expiration_time), } - config_entry = MockConfigEntry(domain=DOMAIN, data=config_entry_data) - config_entry.add_to_hass(hass) - return config_entry + return MockConfigEntry(domain=DOMAIN, data=config_entry_data) + + +@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): @@ -100,7 +147,7 @@ async def async_setup_sdm_platform( ): """Set up the platform and prerequisites.""" if with_config: - create_config_entry(hass) + create_config_entry().add_to_hass(hass) subscriber = FakeSubscriber() device_manager = await subscriber.async_get_device_manager() if devices: diff --git a/tests/components/nest/conftest.py b/tests/components/nest/conftest.py index 22a89f57235..8391e93145c 100644 --- a/tests/components/nest/conftest.py +++ b/tests/components/nest/conftest.py @@ -1,13 +1,34 @@ """Common libraries for test setup.""" +from __future__ import annotations +import copy import shutil +from typing import Any from unittest.mock import patch import uuid import aiohttp from google_nest_sdm.auth import AbstractAuth +from google_nest_sdm.device_manager import DeviceManager 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): """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): yield 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 diff --git a/tests/components/nest/test_api.py b/tests/components/nest/test_api.py index 835edf0c3a2..894fda09a8f 100644 --- a/tests/components/nest/test_api.py +++ b/tests/components/nest/test_api.py @@ -39,7 +39,7 @@ async def test_auth(hass, aioclient_mock): """Exercise authentication library creates valid credentials.""" 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 # 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.""" 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 aioclient_mock.post( diff --git a/tests/components/nest/test_diagnostics.py b/tests/components/nest/test_diagnostics.py index 8d506dc37fe..09930c18501 100644 --- a/tests/components/nest/test_diagnostics.py +++ b/tests/components/nest/test_diagnostics.py @@ -70,7 +70,8 @@ async def test_entry_diagnostics(hass, hass_client): async def test_setup_susbcriber_failure(hass, hass_client): """Test configuration error.""" - config_entry = create_config_entry(hass) + config_entry = create_config_entry() + config_entry.add_to_hass(hass) with patch( "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation" ), patch( diff --git a/tests/components/nest/test_init_sdm.py b/tests/components/nest/test_init_sdm.py index 0ef1ca4e18d..381252c6f75 100644 --- a/tests/components/nest/test_init_sdm.py +++ b/tests/components/nest/test_init_sdm.py @@ -3,10 +3,14 @@ Test for setup methods for the SDM API. The tests fake out the subscriber/devicemanager and simulate setup behavior 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 +from typing import Any from unittest.mock import patch from google_nest_sdm.exceptions import ( @@ -15,44 +19,75 @@ from google_nest_sdm.exceptions import ( ConfigurationException, SubscriberException, ) +import pytest from homeassistant.components.nest import DOMAIN 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" -async def test_setup_success(hass, caplog): - """Test successful setup.""" +@pytest.fixture +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"): - await async_setup_sdm_platform(hass, PLATFORM) - assert not caplog.records + yield caplog + + +@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) assert len(entries) == 1 assert entries[0].state is ConfigEntryState.LOADED -async def async_setup_sdm(hass, config=CONFIG, with_config=True): - """Prepare test setup.""" - if with_config: - 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): +@pytest.mark.parametrize("subscriber_id", [("invalid-subscriber-format")]) +async def test_setup_configuration_failure( + hass, caplog, subscriber_id, setup_base_platform +): """Test configuration error.""" - config = copy.deepcopy(CONFIG) - config[DOMAIN]["subscriber_id"] = "invalid-subscriber-format" - - result = await async_setup_sdm(hass, config) - assert result + await setup_base_platform() entries = hass.config_entries.async_entries(DOMAIN) 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 -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.""" - with patch( - "homeassistant.components.nest.api.GoogleNestSubscriber.start_async", - 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 + await setup_base_platform() + assert "Subscriber error:" in error_caplog.text entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 assert entries[0].state is ConfigEntryState.SETUP_RETRY -async def test_setup_device_manager_failure(hass, caplog): - """Test configuration error.""" +async def test_setup_device_manager_failure(hass, error_caplog, setup_base_platform): + """Test device manager api failure.""" with patch( "homeassistant.components.nest.api.GoogleNestSubscriber.start_async" ), patch( "homeassistant.components.nest.api.GoogleNestSubscriber.async_get_device_manager", side_effect=ApiException(), - ), caplog.at_level( - logging.ERROR, logger="homeassistant.components.nest" ): - result = await async_setup_sdm(hass) - assert result - assert len(caplog.messages) == 1 - assert "Device manager error:" in caplog.text + await setup_base_platform() + + assert len(error_caplog.messages) == 1 + assert "Device manager error:" in error_caplog.text entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 assert entries[0].state is ConfigEntryState.SETUP_RETRY -async def test_subscriber_auth_failure(hass, caplog): - """Test configuration error.""" - with patch( - "homeassistant.components.nest.api.GoogleNestSubscriber.start_async", - side_effect=AuthException(), - ): - result = await async_setup_sdm(hass, CONFIG) - assert result +@pytest.mark.parametrize("subscriber_side_effect", [AuthException()]) +async def test_subscriber_auth_failure( + hass, caplog, setup_base_platform, failing_subscriber +): + """Test subscriber throws an authentication error.""" + await setup_base_platform() entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 @@ -116,70 +145,45 @@ async def test_subscriber_auth_failure(hass, caplog): assert flows[0]["step_id"] == "reauth_confirm" -async def test_setup_missing_subscriber_id(hass, caplog): - """Test missing susbcriber id from config and config entry.""" - config = copy.deepcopy(CONFIG) - del config[DOMAIN]["subscriber_id"] - - 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 +@pytest.mark.parametrize("subscriber_id", [(None)]) +async def test_setup_missing_subscriber_id(hass, warning_caplog, setup_base_platform): + """Test missing susbcriber id from configuration.""" + await setup_base_platform() + assert "Configuration option" in warning_caplog.text entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 assert entries[0].state is ConfigEntryState.SETUP_ERROR -async def test_setup_subscriber_id_config_entry(hass, caplog): - """Test successful setup with subscriber id in ConfigEntry.""" - 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) - - 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): +@pytest.mark.parametrize("subscriber_side_effect", [(ConfigurationException())]) +async def test_subscriber_configuration_failure( + hass, error_caplog, setup_base_platform, failing_subscriber +): """Test configuration error.""" - with patch( - "homeassistant.components.nest.api.GoogleNestSubscriber.start_async", - 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 + await setup_base_platform() + assert "Configuration error: " in error_caplog.text entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 assert entries[0].state is ConfigEntryState.SETUP_ERROR -async def test_empty_config(hass, caplog): - """Test successful setup.""" - with caplog.at_level(logging.ERROR, logger="homeassistant.components.nest"): - result = await async_setup_component(hass, DOMAIN, {}) - assert result - assert not caplog.records +@pytest.mark.parametrize( + "nest_test_config", [NestTestConfig(config={}, config_entry_data=None)] +) +async def test_empty_config(hass, error_caplog, config, setup_platform): + """Test setup is a no-op with not config.""" + await setup_platform() + assert not error_caplog.records entries = hass.config_entries.async_entries(DOMAIN) 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.""" - await async_setup_sdm_platform(hass, PLATFORM) + await setup_platform() entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 @@ -190,33 +194,27 @@ async def test_unload_entry(hass, caplog): 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.""" - await async_setup_sdm_platform(hass, PLATFORM) - - entries = hass.config_entries.async_entries(DOMAIN) - assert len(entries) == 1 - entry = entries[0] - 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) + with patch( + "homeassistant.components.nest.api.GoogleNestSubscriber", + return_value=FakeSubscriber(), + ): + await setup_base_platform() entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 @@ -224,27 +222,29 @@ async def test_remove_entry_deletes_subscriber(hass, caplog): assert entry.state is ConfigEntryState.LOADED with patch( + "homeassistant.components.nest.api.GoogleNestSubscriber.subscriber_id" + ), patch( "homeassistant.components.nest.api.GoogleNestSubscriber.delete_subscription", ) as delete: 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) 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.""" - 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) + with patch( + "homeassistant.components.nest.api.GoogleNestSubscriber", + return_value=FakeSubscriber(), + ): + await setup_base_platform() entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 @@ -254,8 +254,9 @@ async def test_remove_entry_delete_subscriber_failure(hass, caplog): with patch( "homeassistant.components.nest.api.GoogleNestSubscriber.delete_subscription", side_effect=SubscriberException(), - ): + ) as delete: assert await hass.config_entries.async_remove(entry.entry_id) + assert delete.called entries = hass.config_entries.async_entries(DOMAIN) assert not entries diff --git a/tests/components/nest/test_media_source.py b/tests/components/nest/test_media_source.py index 8f968638d1d..83b299d62fa 100644 --- a/tests/components/nest/test_media_source.py +++ b/tests/components/nest/test_media_source.py @@ -1043,7 +1043,8 @@ async def test_media_store_persistence(hass, auth, hass_client, event_store): # Fetch media for events when published subscriber.cache_policy.fetch = True - config_entry = create_config_entry(hass) + config_entry = create_config_entry() + config_entry.add_to_hass(hass) with patch( "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation" diff --git a/tests/components/nest/test_sensor_sdm.py b/tests/components/nest/test_sensor_sdm.py index 72b2ecfc529..3ef2bcb04da 100644 --- a/tests/components/nest/test_sensor_sdm.py +++ b/tests/components/nest/test_sensor_sdm.py @@ -6,7 +6,9 @@ pubsub subscriber. """ from google_nest_sdm.device import Device +from google_nest_sdm.device_manager import DeviceManager from google_nest_sdm.event import EventMessage +import pytest from homeassistant.components.sensor import ATTR_STATE_CLASS, STATE_CLASS_MEASUREMENT from homeassistant.const import ( @@ -17,24 +19,26 @@ from homeassistant.const import ( PERCENTAGE, TEMP_CELSIUS, ) +from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er -from .common import async_setup_sdm_platform - -PLATFORM = "sensor" +from .common import FakeSubscriber, PlatformSetup THERMOSTAT_TYPE = "sdm.devices.types.THERMOSTAT" -async def async_setup_sensor(hass, devices={}, structures={}): - """Set up the platform and prerequisites.""" - return await async_setup_sdm_platform(hass, PLATFORM, devices, structures) +@pytest.fixture +def platforms() -> list[str]: + """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.""" - devices = { - "some-device-id": Device.MakeDevice( + device_manager.add_device( + Device.MakeDevice( { "name": "some-device-id", "type": THERMOSTAT_TYPE, @@ -52,8 +56,8 @@ async def test_thermostat_device(hass): }, auth=None, ) - } - await async_setup_sensor(hass, devices) + ) + await setup_platform() temperature = hass.states.get("sensor.my_sensor_temperature") assert temperature is not None @@ -87,9 +91,9 @@ async def test_thermostat_device(hass): 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.""" - await async_setup_sensor(hass) + await setup_platform() temperature = hass.states.get("sensor.my_sensor_temperature") assert temperature is None @@ -98,10 +102,12 @@ async def test_no_devices(hass): 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.""" - devices = { - "some-device-id": Device.MakeDevice( + device_manager.add_device( + Device.MakeDevice( { "name": "some-device-id", "type": THERMOSTAT_TYPE, @@ -109,8 +115,8 @@ async def test_device_no_sensor_traits(hass): }, auth=None, ) - } - await async_setup_sensor(hass, devices) + ) + await setup_platform() temperature = hass.states.get("sensor.my_sensor_temperature") assert temperature is None @@ -119,10 +125,12 @@ async def test_device_no_sensor_traits(hass): 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.""" - devices = { - "some-device-id": Device.MakeDevice( + device_manager.add_device( + Device.MakeDevice( { "name": "some-device-id", "type": THERMOSTAT_TYPE, @@ -137,18 +145,23 @@ async def test_device_name_from_structure(hass): }, auth=None, ) - } - await async_setup_sensor(hass, devices) + ) + await setup_platform() temperature = hass.states.get("sensor.some_room_temperature") assert temperature is not None 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.""" - devices = { - "some-device-id": Device.MakeDevice( + device_manager.add_device( + Device.MakeDevice( { "name": "some-device-id", "type": THERMOSTAT_TYPE, @@ -163,8 +176,8 @@ async def test_event_updates_sensor(hass): }, auth=None, ) - } - subscriber = await async_setup_sensor(hass, devices) + ) + await setup_platform() temperature = hass.states.get("sensor.my_sensor_temperature") assert temperature is not None @@ -194,10 +207,12 @@ async def test_event_updates_sensor(hass): 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.""" - devices = { - "some-device-id": Device.MakeDevice( + device_manager.add_device( + Device.MakeDevice( { "name": "some-device-id", "type": "some-unknown-type", @@ -212,8 +227,8 @@ async def test_device_with_unknown_type(hass): }, auth=None, ) - } - await async_setup_sensor(hass, devices) + ) + await setup_platform() temperature = hass.states.get("sensor.my_sensor_temperature") assert temperature is not None @@ -232,10 +247,12 @@ async def test_device_with_unknown_type(hass): 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.""" - devices = { - "some-device-id": Device.MakeDevice( + device_manager.add_device( + Device.MakeDevice( { "name": "some-device-id", "type": THERMOSTAT_TYPE, @@ -250,8 +267,8 @@ async def test_temperature_rounding(hass): }, auth=None, ) - } - await async_setup_sensor(hass, devices) + ) + await setup_platform() temperature = hass.states.get("sensor.my_sensor_temperature") assert temperature.state == "25.2"