Cleanup nest config flow tests to use common setup fixtures (#72878)

* Cleanup nest config flow tests to use common setup

* Remove some conditionals in test setup
This commit is contained in:
Allen Porter 2022-06-02 21:42:52 -07:00 committed by GitHub
parent 5b31414225
commit 3a8a816584
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 171 additions and 237 deletions

View File

@ -25,10 +25,15 @@ PlatformSetup = Callable[[], Awaitable[None]]
_T = TypeVar("_T") _T = TypeVar("_T")
YieldFixture = Generator[_T, None, None] YieldFixture = Generator[_T, None, None]
WEB_AUTH_DOMAIN = DOMAIN
APP_AUTH_DOMAIN = f"{DOMAIN}.installed"
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" CLOUD_PROJECT_ID = "cloud-id-9876"
SUBSCRIBER_ID = "projects/cloud-id-9876/subscriptions/subscriber-id-9876"
CONFIG = { CONFIG = {
"nest": { "nest": {
@ -79,10 +84,12 @@ TEST_CONFIG_YAML_ONLY = NestTestConfig(
config=CONFIG, config=CONFIG,
config_entry_data={ config_entry_data={
"sdm": {}, "sdm": {},
"auth_implementation": "nest",
"token": create_token_entry(), "token": create_token_entry(),
}, },
) )
TEST_CONFIGFLOW_YAML_ONLY = NestTestConfig(
config=TEST_CONFIG_YAML_ONLY.config, config_entry_data=None
)
# Exercises mode where subscriber id is created in the config flow, but # Exercises mode where subscriber id is created in the config flow, but
# all authentication is defined in configuration.yaml # all authentication is defined in configuration.yaml
@ -96,11 +103,14 @@ TEST_CONFIG_HYBRID = NestTestConfig(
}, },
config_entry_data={ config_entry_data={
"sdm": {}, "sdm": {},
"auth_implementation": "nest",
"token": create_token_entry(), "token": create_token_entry(),
"cloud_project_id": CLOUD_PROJECT_ID,
"subscriber_id": SUBSCRIBER_ID, "subscriber_id": SUBSCRIBER_ID,
}, },
) )
TEST_CONFIGFLOW_HYBRID = NestTestConfig(
TEST_CONFIG_HYBRID.config, config_entry_data=None
)
TEST_CONFIG_LEGACY = NestTestConfig( TEST_CONFIG_LEGACY = NestTestConfig(
config={ config={

View File

@ -5,7 +5,7 @@ from collections.abc import Generator
import copy import copy
import shutil import shutil
from typing import Any from typing import Any
from unittest.mock import patch from unittest.mock import AsyncMock, patch
import uuid import uuid
import aiohttp import aiohttp
@ -24,6 +24,7 @@ from .common import (
SUBSCRIBER_ID, SUBSCRIBER_ID,
TEST_CONFIG_HYBRID, TEST_CONFIG_HYBRID,
TEST_CONFIG_YAML_ONLY, TEST_CONFIG_YAML_ONLY,
WEB_AUTH_DOMAIN,
CreateDevice, CreateDevice,
FakeSubscriber, FakeSubscriber,
NestTestConfig, NestTestConfig,
@ -114,6 +115,17 @@ def subscriber() -> YieldFixture[FakeSubscriber]:
yield subscriber yield subscriber
@pytest.fixture
def mock_subscriber() -> YieldFixture[AsyncMock]:
"""Fixture for injecting errors into the subscriber."""
mock_subscriber = AsyncMock(FakeSubscriber)
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber",
return_value=mock_subscriber,
):
yield mock_subscriber
@pytest.fixture @pytest.fixture
async def device_manager(subscriber: FakeSubscriber) -> DeviceManager: async def device_manager(subscriber: FakeSubscriber) -> DeviceManager:
"""Set up the DeviceManager.""" """Set up the DeviceManager."""
@ -170,6 +182,12 @@ def subscriber_id() -> str:
return SUBSCRIBER_ID return SUBSCRIBER_ID
@pytest.fixture
def auth_implementation() -> str | None:
"""Fixture to let tests override the auth implementation in the config entry."""
return WEB_AUTH_DOMAIN
@pytest.fixture( @pytest.fixture(
params=[TEST_CONFIG_YAML_ONLY, TEST_CONFIG_HYBRID], params=[TEST_CONFIG_YAML_ONLY, TEST_CONFIG_HYBRID],
ids=["yaml-config-only", "hybrid-config"], ids=["yaml-config-only", "hybrid-config"],
@ -195,7 +213,9 @@ def config(
@pytest.fixture @pytest.fixture
def config_entry( def config_entry(
subscriber_id: str | None, nest_test_config: NestTestConfig subscriber_id: str | None,
auth_implementation: str | None,
nest_test_config: NestTestConfig,
) -> MockConfigEntry | None: ) -> MockConfigEntry | None:
"""Fixture that sets up the ConfigEntry for the test.""" """Fixture that sets up the ConfigEntry for the test."""
if nest_test_config.config_entry_data is None: if nest_test_config.config_entry_data is None:
@ -206,6 +226,7 @@ def config_entry(
data[CONF_SUBSCRIBER_ID] = subscriber_id data[CONF_SUBSCRIBER_ID] = subscriber_id
else: else:
del data[CONF_SUBSCRIBER_ID] del data[CONF_SUBSCRIBER_ID]
data["auth_implementation"] = auth_implementation
return MockConfigEntry(domain=DOMAIN, data=data) return MockConfigEntry(domain=DOMAIN, data=data)

View File

@ -1,7 +1,6 @@
"""Test the Google Nest Device Access config flow.""" """Test the Google Nest Device Access config flow."""
import copy from unittest.mock import patch
from unittest.mock import AsyncMock, patch
from google_nest_sdm.exceptions import ( from google_nest_sdm.exceptions import (
AuthException, AuthException,
@ -11,35 +10,27 @@ from google_nest_sdm.exceptions import (
from google_nest_sdm.structure import Structure from google_nest_sdm.structure import Structure
import pytest import pytest
from homeassistant import config_entries, setup from homeassistant import config_entries
from homeassistant.components import dhcp from homeassistant.components import dhcp
from homeassistant.components.nest.const import DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN from homeassistant.components.nest.const import DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers import config_entry_oauth2_flow
from .common import FakeSubscriber, MockConfigEntry from .common import (
APP_AUTH_DOMAIN,
CLIENT_ID,
CLOUD_PROJECT_ID,
FAKE_TOKEN,
PROJECT_ID,
SUBSCRIBER_ID,
TEST_CONFIG_HYBRID,
TEST_CONFIG_YAML_ONLY,
TEST_CONFIGFLOW_HYBRID,
TEST_CONFIGFLOW_YAML_ONLY,
WEB_AUTH_DOMAIN,
MockConfigEntry,
)
CLIENT_ID = "1234"
CLIENT_SECRET = "5678"
PROJECT_ID = "project-id-4321"
SUBSCRIBER_ID = "projects/cloud-id-9876/subscriptions/subscriber-id-9876"
CLOUD_PROJECT_ID = "cloud-id-9876"
CONFIG = {
DOMAIN: {
"project_id": PROJECT_ID,
"subscriber_id": SUBSCRIBER_ID,
CONF_CLIENT_ID: CLIENT_ID,
CONF_CLIENT_SECRET: CLIENT_SECRET,
},
"http": {"base_url": "https://example.com"},
}
ORIG_AUTH_DOMAIN = DOMAIN
WEB_AUTH_DOMAIN = DOMAIN
APP_AUTH_DOMAIN = f"{DOMAIN}.installed"
WEB_REDIRECT_URL = "https://example.com/auth/external/callback" WEB_REDIRECT_URL = "https://example.com/auth/external/callback"
APP_REDIRECT_URL = "urn:ietf:wg:oauth:2.0:oob" APP_REDIRECT_URL = "urn:ietf:wg:oauth:2.0:oob"
@ -49,30 +40,6 @@ FAKE_DHCP_DATA = dhcp.DhcpServiceInfo(
) )
@pytest.fixture
def subscriber() -> FakeSubscriber:
"""Create FakeSubscriber."""
return FakeSubscriber()
def get_config_entry(hass):
"""Return a single config entry."""
entries = hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
return entries[0]
def create_config_entry(hass: HomeAssistant, data: dict) -> ConfigEntry:
"""Create the ConfigEntry."""
entry = MockConfigEntry(
domain=DOMAIN,
data=data,
unique_id=DOMAIN,
)
entry.add_to_hass(hass)
return entry
class OAuthFixture: class OAuthFixture:
"""Simulate the oauth flow used by the config flow.""" """Simulate the oauth flow used by the config flow."""
@ -196,7 +163,9 @@ class OAuthFixture:
def get_config_entry(self) -> ConfigEntry: def get_config_entry(self) -> ConfigEntry:
"""Get the config entry.""" """Get the config entry."""
return get_config_entry(self.hass) entries = self.hass.config_entries.async_entries(DOMAIN)
assert len(entries) == 1
return entries[0]
@pytest.fixture @pytest.fixture
@ -205,16 +174,10 @@ async def oauth(hass, hass_client_no_auth, aioclient_mock, current_request_with_
return OAuthFixture(hass, hass_client_no_auth, aioclient_mock) return OAuthFixture(hass, hass_client_no_auth, aioclient_mock)
async def async_setup_configflow(hass): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_YAML_ONLY])
"""Set up component so the pubsub subscriber is managed by config flow.""" async def test_web_full_flow(hass, oauth, setup_platform):
config = copy.deepcopy(CONFIG)
del config[DOMAIN]["subscriber_id"] # Create in config flow instead
return await setup.async_setup_component(hass, DOMAIN, config)
async def test_web_full_flow(hass, oauth):
"""Check full flow.""" """Check full flow."""
assert await setup.async_setup_component(hass, DOMAIN, CONFIG) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -238,29 +201,15 @@ async def test_web_full_flow(hass, oauth):
assert "subscriber_id" not in entry.data assert "subscriber_id" not in entry.data
async def test_web_reauth(hass, oauth): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIG_YAML_ONLY])
async def test_web_reauth(hass, oauth, setup_platform, config_entry):
"""Test Nest reauthentication.""" """Test Nest reauthentication."""
assert await setup.async_setup_component(hass, DOMAIN, CONFIG) await setup_platform()
old_entry = create_config_entry( assert config_entry.data["token"].get("access_token") == FAKE_TOKEN
hass,
{
"auth_implementation": WEB_AUTH_DOMAIN,
"token": {
# Verify this is replaced at end of the test
"access_token": "some-revoked-token",
},
"sdm": {},
},
)
entry = get_config_entry(hass) result = await oauth.async_reauth(config_entry.data)
assert entry.data["token"] == {
"access_token": "some-revoked-token",
}
result = await oauth.async_reauth(old_entry.data)
await oauth.async_oauth_web_flow(result) await oauth.async_oauth_web_flow(result)
entry = await oauth.async_finish_setup(result) entry = await oauth.async_finish_setup(result)
@ -277,11 +226,9 @@ async def test_web_reauth(hass, oauth):
assert "subscriber_id" not in entry.data # not updated assert "subscriber_id" not in entry.data # not updated
async def test_single_config_entry(hass): async def test_single_config_entry(hass, setup_platform):
"""Test that only a single config entry is allowed.""" """Test that only a single config entry is allowed."""
create_config_entry(hass, {"auth_implementation": WEB_AUTH_DOMAIN, "sdm": {}}) await setup_platform()
assert await setup.async_setup_component(hass, DOMAIN, CONFIG)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -290,18 +237,13 @@ async def test_single_config_entry(hass):
assert result["reason"] == "single_instance_allowed" assert result["reason"] == "single_instance_allowed"
async def test_unexpected_existing_config_entries(hass, oauth): async def test_unexpected_existing_config_entries(hass, oauth, setup_platform):
"""Test Nest reauthentication with multiple existing config entries.""" """Test Nest reauthentication with multiple existing config entries."""
# Note that this case will not happen in the future since only a single # Note that this case will not happen in the future since only a single
# instance is now allowed, but this may have been allowed in the past. # instance is now allowed, but this may have been allowed in the past.
# On reauth, only one entry is kept and the others are deleted. # On reauth, only one entry is kept and the others are deleted.
assert await setup.async_setup_component(hass, DOMAIN, CONFIG) await setup_platform()
old_entry = MockConfigEntry(
domain=DOMAIN, data={"auth_implementation": WEB_AUTH_DOMAIN, "sdm": {}}
)
old_entry.add_to_hass(hass)
old_entry = MockConfigEntry( old_entry = MockConfigEntry(
domain=DOMAIN, data={"auth_implementation": WEB_AUTH_DOMAIN, "sdm": {}} domain=DOMAIN, data={"auth_implementation": WEB_AUTH_DOMAIN, "sdm": {}}
@ -333,9 +275,9 @@ async def test_unexpected_existing_config_entries(hass, oauth):
assert "subscriber_id" not in entry.data # not updated assert "subscriber_id" not in entry.data # not updated
async def test_reauth_missing_config_entry(hass): async def test_reauth_missing_config_entry(hass, setup_platform):
"""Test the reauth flow invoked missing existing data.""" """Test the reauth flow invoked missing existing data."""
assert await setup.async_setup_component(hass, DOMAIN, CONFIG) await setup_platform()
# Invoke the reauth flow with no existing data # Invoke the reauth flow with no existing data
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -345,9 +287,10 @@ async def test_reauth_missing_config_entry(hass):
assert result["reason"] == "missing_configuration" assert result["reason"] == "missing_configuration"
async def test_app_full_flow(hass, oauth): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_YAML_ONLY])
async def test_app_full_flow(hass, oauth, setup_platform):
"""Check full flow.""" """Check full flow."""
assert await setup.async_setup_component(hass, DOMAIN, CONFIG) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -370,24 +313,15 @@ async def test_app_full_flow(hass, oauth):
assert "subscriber_id" not in entry.data assert "subscriber_id" not in entry.data
async def test_app_reauth(hass, oauth): @pytest.mark.parametrize(
"nest_test_config,auth_implementation", [(TEST_CONFIG_YAML_ONLY, APP_AUTH_DOMAIN)]
)
async def test_app_reauth(hass, oauth, setup_platform, config_entry):
"""Test Nest reauthentication for Installed App Auth.""" """Test Nest reauthentication for Installed App Auth."""
assert await setup.async_setup_component(hass, DOMAIN, CONFIG) await setup_platform()
old_entry = create_config_entry( result = await oauth.async_reauth(config_entry.data)
hass,
{
"auth_implementation": APP_AUTH_DOMAIN,
"token": {
# Verify this is replaced at end of the test
"access_token": "some-revoked-token",
},
"sdm": {},
},
)
result = await oauth.async_reauth(old_entry.data)
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
# Verify existing tokens are replaced # Verify existing tokens are replaced
@ -404,9 +338,10 @@ async def test_app_reauth(hass, oauth):
assert "subscriber_id" not in entry.data # not updated assert "subscriber_id" not in entry.data # not updated
async def test_pubsub_subscription(hass, oauth, subscriber): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_pubsub_subscription(hass, oauth, subscriber, setup_platform):
"""Check flow that creates a pub/sub subscription.""" """Check flow that creates a pub/sub subscription."""
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -414,16 +349,11 @@ async def test_pubsub_subscription(hass, oauth, subscriber):
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN) result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber",
return_value=subscriber,
):
result = await oauth.async_configure(result, {"code": "1234"}) result = await oauth.async_configure(result, {"code": "1234"})
await oauth.async_pubsub_flow(result) await oauth.async_pubsub_flow(result)
entry = await oauth.async_finish_setup( entry = await oauth.async_finish_setup(
result, {"cloud_project_id": CLOUD_PROJECT_ID} result, {"cloud_project_id": CLOUD_PROJECT_ID}
) )
await hass.async_block_till_done()
assert entry.title == "OAuth for Apps" assert entry.title == "OAuth for Apps"
assert "token" in entry.data assert "token" in entry.data
@ -439,9 +369,12 @@ async def test_pubsub_subscription(hass, oauth, subscriber):
assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID
async def test_pubsub_subscription_strip_whitespace(hass, oauth, subscriber): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_pubsub_subscription_strip_whitespace(
hass, oauth, subscriber, setup_platform
):
"""Check that project id has whitespace stripped on entry.""" """Check that project id has whitespace stripped on entry."""
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -449,16 +382,11 @@ async def test_pubsub_subscription_strip_whitespace(hass, oauth, subscriber):
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN) result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber",
return_value=subscriber,
):
result = await oauth.async_configure(result, {"code": "1234"}) result = await oauth.async_configure(result, {"code": "1234"})
await oauth.async_pubsub_flow(result) await oauth.async_pubsub_flow(result)
entry = await oauth.async_finish_setup( entry = await oauth.async_finish_setup(
result, {"cloud_project_id": " " + CLOUD_PROJECT_ID + " "} result, {"cloud_project_id": " " + CLOUD_PROJECT_ID + " "}
) )
await hass.async_block_till_done()
assert entry.title == "OAuth for Apps" assert entry.title == "OAuth for Apps"
assert "token" in entry.data assert "token" in entry.data
@ -474,9 +402,12 @@ async def test_pubsub_subscription_strip_whitespace(hass, oauth, subscriber):
assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID
async def test_pubsub_subscription_auth_failure(hass, oauth): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_pubsub_subscription_auth_failure(
hass, oauth, setup_platform, mock_subscriber
):
"""Check flow that creates a pub/sub subscription.""" """Check flow that creates a pub/sub subscription."""
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -484,23 +415,22 @@ async def test_pubsub_subscription_auth_failure(hass, oauth):
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN) result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
result = await oauth.async_configure(result, {"code": "1234"}) result = await oauth.async_configure(result, {"code": "1234"})
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber.create_subscription", mock_subscriber.create_subscription.side_effect = AuthException()
side_effect=AuthException(),
):
await oauth.async_pubsub_flow(result) await oauth.async_pubsub_flow(result)
result = await oauth.async_configure( result = await oauth.async_configure(result, {"cloud_project_id": CLOUD_PROJECT_ID})
result, {"cloud_project_id": CLOUD_PROJECT_ID}
)
await hass.async_block_till_done()
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "invalid_access_token" assert result["reason"] == "invalid_access_token"
async def test_pubsub_subscription_failure(hass, oauth): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_pubsub_subscription_failure(
hass, oauth, setup_platform, mock_subscriber
):
"""Check flow that creates a pub/sub subscription.""" """Check flow that creates a pub/sub subscription."""
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -509,14 +439,10 @@ async def test_pubsub_subscription_failure(hass, oauth):
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
result = await oauth.async_configure(result, {"code": "1234"}) result = await oauth.async_configure(result, {"code": "1234"})
await oauth.async_pubsub_flow(result) await oauth.async_pubsub_flow(result)
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber.create_subscription", mock_subscriber.create_subscription.side_effect = SubscriberException()
side_effect=SubscriberException(),
): result = await oauth.async_configure(result, {"cloud_project_id": CLOUD_PROJECT_ID})
result = await oauth.async_configure(
result, {"cloud_project_id": CLOUD_PROJECT_ID}
)
await hass.async_block_till_done()
assert result["type"] == "form" assert result["type"] == "form"
assert "errors" in result assert "errors" in result
@ -524,9 +450,12 @@ async def test_pubsub_subscription_failure(hass, oauth):
assert result["errors"]["cloud_project_id"] == "subscriber_error" assert result["errors"]["cloud_project_id"] == "subscriber_error"
async def test_pubsub_subscription_configuration_failure(hass, oauth): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_pubsub_subscription_configuration_failure(
hass, oauth, setup_platform, mock_subscriber
):
"""Check flow that creates a pub/sub subscription.""" """Check flow that creates a pub/sub subscription."""
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -535,14 +464,9 @@ async def test_pubsub_subscription_configuration_failure(hass, oauth):
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
result = await oauth.async_configure(result, {"code": "1234"}) result = await oauth.async_configure(result, {"code": "1234"})
await oauth.async_pubsub_flow(result) await oauth.async_pubsub_flow(result)
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber.create_subscription", mock_subscriber.create_subscription.side_effect = ConfigurationException()
side_effect=ConfigurationException(), result = await oauth.async_configure(result, {"cloud_project_id": CLOUD_PROJECT_ID})
):
result = await oauth.async_configure(
result, {"cloud_project_id": CLOUD_PROJECT_ID}
)
await hass.async_block_till_done()
assert result["type"] == "form" assert result["type"] == "form"
assert "errors" in result assert "errors" in result
@ -550,9 +474,10 @@ async def test_pubsub_subscription_configuration_failure(hass, oauth):
assert result["errors"]["cloud_project_id"] == "bad_project_id" assert result["errors"]["cloud_project_id"] == "bad_project_id"
async def test_pubsub_with_wrong_project_id(hass, oauth): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_pubsub_with_wrong_project_id(hass, oauth, setup_platform):
"""Test a possible common misconfiguration mixing up project ids.""" """Test a possible common misconfiguration mixing up project ids."""
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -572,23 +497,16 @@ async def test_pubsub_with_wrong_project_id(hass, oauth):
assert result["errors"]["cloud_project_id"] == "wrong_project_id" assert result["errors"]["cloud_project_id"] == "wrong_project_id"
async def test_pubsub_subscriber_config_entry_reauth(hass, oauth, subscriber): @pytest.mark.parametrize(
"""Test the pubsub subscriber id is preserved during reauth.""" "nest_test_config,auth_implementation", [(TEST_CONFIG_HYBRID, APP_AUTH_DOMAIN)]
assert await async_setup_configflow(hass)
old_entry = create_config_entry(
hass,
{
"auth_implementation": APP_AUTH_DOMAIN,
"subscriber_id": SUBSCRIBER_ID,
"cloud_project_id": CLOUD_PROJECT_ID,
"token": {
"access_token": "some-revoked-token",
},
"sdm": {},
},
) )
result = await oauth.async_reauth(old_entry.data) async def test_pubsub_subscriber_config_entry_reauth(
hass, oauth, setup_platform, subscriber, config_entry
):
"""Test the pubsub subscriber id is preserved during reauth."""
await setup_platform()
result = await oauth.async_reauth(config_entry.data)
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
# Entering an updated access token refreshs the config entry. # Entering an updated access token refreshs the config entry.
@ -606,7 +524,8 @@ async def test_pubsub_subscriber_config_entry_reauth(hass, oauth, subscriber):
assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID
async def test_config_entry_title_from_home(hass, oauth, subscriber): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_config_entry_title_from_home(hass, oauth, setup_platform, subscriber):
"""Test that the Google Home name is used for the config entry title.""" """Test that the Google Home name is used for the config entry title."""
device_manager = await subscriber.async_get_device_manager() device_manager = await subscriber.async_get_device_manager()
@ -623,7 +542,7 @@ async def test_config_entry_title_from_home(hass, oauth, subscriber):
) )
) )
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -631,16 +550,11 @@ async def test_config_entry_title_from_home(hass, oauth, subscriber):
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN) result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber",
return_value=subscriber,
):
result = await oauth.async_configure(result, {"code": "1234"}) result = await oauth.async_configure(result, {"code": "1234"})
await oauth.async_pubsub_flow(result) await oauth.async_pubsub_flow(result)
entry = await oauth.async_finish_setup( entry = await oauth.async_finish_setup(
result, {"cloud_project_id": CLOUD_PROJECT_ID} result, {"cloud_project_id": CLOUD_PROJECT_ID}
) )
await hass.async_block_till_done()
assert entry.title == "Example Home" assert entry.title == "Example Home"
assert "token" in entry.data assert "token" in entry.data
@ -648,7 +562,10 @@ async def test_config_entry_title_from_home(hass, oauth, subscriber):
assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID
async def test_config_entry_title_multiple_homes(hass, oauth, subscriber): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_config_entry_title_multiple_homes(
hass, oauth, setup_platform, subscriber
):
"""Test handling of multiple Google Homes authorized.""" """Test handling of multiple Google Homes authorized."""
device_manager = await subscriber.async_get_device_manager() device_manager = await subscriber.async_get_device_manager()
@ -677,7 +594,7 @@ async def test_config_entry_title_multiple_homes(hass, oauth, subscriber):
) )
) )
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -685,23 +602,18 @@ async def test_config_entry_title_multiple_homes(hass, oauth, subscriber):
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN) result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber",
return_value=subscriber,
):
result = await oauth.async_configure(result, {"code": "1234"}) result = await oauth.async_configure(result, {"code": "1234"})
await oauth.async_pubsub_flow(result) await oauth.async_pubsub_flow(result)
entry = await oauth.async_finish_setup( entry = await oauth.async_finish_setup(
result, {"cloud_project_id": CLOUD_PROJECT_ID} result, {"cloud_project_id": CLOUD_PROJECT_ID}
) )
await hass.async_block_till_done()
assert entry.title == "Example Home #1, Example Home #2" assert entry.title == "Example Home #1, Example Home #2"
async def test_title_failure_fallback(hass, oauth): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_title_failure_fallback(hass, oauth, setup_platform, mock_subscriber):
"""Test exception handling when determining the structure names.""" """Test exception handling when determining the structure names."""
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -709,19 +621,13 @@ async def test_title_failure_fallback(hass, oauth):
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN) result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
mock_subscriber = AsyncMock(FakeSubscriber)
mock_subscriber.async_get_device_manager.side_effect = AuthException() mock_subscriber.async_get_device_manager.side_effect = AuthException()
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber",
return_value=mock_subscriber,
):
result = await oauth.async_configure(result, {"code": "1234"}) result = await oauth.async_configure(result, {"code": "1234"})
await oauth.async_pubsub_flow(result) await oauth.async_pubsub_flow(result)
entry = await oauth.async_finish_setup( entry = await oauth.async_finish_setup(
result, {"cloud_project_id": CLOUD_PROJECT_ID} result, {"cloud_project_id": CLOUD_PROJECT_ID}
) )
await hass.async_block_till_done()
assert entry.title == "OAuth for Apps" assert entry.title == "OAuth for Apps"
assert "token" in entry.data assert "token" in entry.data
@ -729,7 +635,8 @@ async def test_title_failure_fallback(hass, oauth):
assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID
async def test_structure_missing_trait(hass, oauth, subscriber): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_HYBRID])
async def test_structure_missing_trait(hass, oauth, setup_platform, subscriber):
"""Test handling the case where a structure has no name set.""" """Test handling the case where a structure has no name set."""
device_manager = await subscriber.async_get_device_manager() device_manager = await subscriber.async_get_device_manager()
@ -743,7 +650,7 @@ async def test_structure_missing_trait(hass, oauth, subscriber):
) )
) )
assert await async_setup_configflow(hass) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -751,16 +658,11 @@ async def test_structure_missing_trait(hass, oauth, subscriber):
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN) result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
await oauth.async_oauth_app_flow(result) await oauth.async_oauth_app_flow(result)
with patch(
"homeassistant.components.nest.api.GoogleNestSubscriber",
return_value=subscriber,
):
result = await oauth.async_configure(result, {"code": "1234"}) result = await oauth.async_configure(result, {"code": "1234"})
await oauth.async_pubsub_flow(result) await oauth.async_pubsub_flow(result)
entry = await oauth.async_finish_setup( entry = await oauth.async_finish_setup(
result, {"cloud_project_id": CLOUD_PROJECT_ID} result, {"cloud_project_id": CLOUD_PROJECT_ID}
) )
await hass.async_block_till_done()
# Fallback to default name # Fallback to default name
assert entry.title == "OAuth for Apps" assert entry.title == "OAuth for Apps"
@ -778,9 +680,10 @@ async def test_dhcp_discovery_without_config(hass, oauth):
assert result["reason"] == "missing_configuration" assert result["reason"] == "missing_configuration"
async def test_dhcp_discovery(hass, oauth): @pytest.mark.parametrize("nest_test_config", [TEST_CONFIGFLOW_YAML_ONLY])
async def test_dhcp_discovery(hass, oauth, setup_platform):
"""Discover via dhcp when config is present.""" """Discover via dhcp when config is present."""
assert await setup.async_setup_component(hass, DOMAIN, CONFIG) await setup_platform()
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,