mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Refactor nest test_init_sdm tests to use fixtures with varied config types (#64736)
This commit is contained in:
parent
15532c38d7
commit
cdf7742c3e
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user