From 6e5bc0da94b56e18a0e16267fd70354ff1e600fa Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 25 Jun 2024 17:07:50 +0200 Subject: [PATCH] Improve type hints in cloud tests (#120420) --- tests/components/cloud/__init__.py | 7 ++- tests/components/cloud/conftest.py | 16 +++---- tests/components/cloud/test_account_link.py | 10 ++++- tests/components/cloud/test_alexa_config.py | 41 ++++++++++------- tests/components/cloud/test_client.py | 19 +++++--- tests/components/cloud/test_google_config.py | 47 +++++++++++++------- tests/components/cloud/test_init.py | 12 +++-- 7 files changed, 96 insertions(+), 56 deletions(-) diff --git a/tests/components/cloud/__init__.py b/tests/components/cloud/__init__.py index f1ce24e576f..18f8cd4d311 100644 --- a/tests/components/cloud/__init__.py +++ b/tests/components/cloud/__init__.py @@ -1,5 +1,6 @@ """Tests for the cloud component.""" +from typing import Any from unittest.mock import AsyncMock, patch from homeassistant.components.cloud.const import ( @@ -14,7 +15,9 @@ from homeassistant.components.cloud.const import ( from homeassistant.components.cloud.prefs import ( ALEXA_SETTINGS_VERSION, GOOGLE_SETTINGS_VERSION, + CloudPreferences, ) +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component PIPELINE_DATA = { @@ -66,7 +69,7 @@ PIPELINE_DATA = { } -async def mock_cloud(hass, config=None): +async def mock_cloud(hass: HomeAssistant, config: dict[str, Any] | None = None) -> None: """Mock cloud.""" # The homeassistant integration is needed by cloud. It's not in it's requirements # because it's always setup by bootstrap. Set it up manually in tests. @@ -78,7 +81,7 @@ async def mock_cloud(hass, config=None): await cloud_inst.initialize() -def mock_cloud_prefs(hass, prefs): +def mock_cloud_prefs(hass: HomeAssistant, prefs: dict[str, Any]) -> CloudPreferences: """Fixture for cloud component.""" prefs_to_set = { PREF_ALEXA_SETTINGS_VERSION: ALEXA_SETTINGS_VERSION, diff --git a/tests/components/cloud/conftest.py b/tests/components/cloud/conftest.py index a7abb932124..c7d0702ea88 100644 --- a/tests/components/cloud/conftest.py +++ b/tests/components/cloud/conftest.py @@ -15,7 +15,7 @@ from hass_nabucasa.remote import RemoteUI from hass_nabucasa.voice import Voice import jwt import pytest -from typing_extensions import AsyncGenerator +from typing_extensions import AsyncGenerator, Generator from homeassistant.components.cloud.client import CloudClient from homeassistant.components.cloud.const import DATA_CLOUD @@ -199,21 +199,21 @@ def tts_mutagen_mock_fixture_autouse(tts_mutagen_mock: MagicMock) -> None: @pytest.fixture(autouse=True) -def mock_user_data(): +def mock_user_data() -> Generator[MagicMock]: """Mock os module.""" with patch("hass_nabucasa.Cloud._write_user_info") as writer: yield writer @pytest.fixture -def mock_cloud_fixture(hass): +def mock_cloud_fixture(hass: HomeAssistant) -> CloudPreferences: """Fixture for cloud component.""" hass.loop.run_until_complete(mock_cloud(hass)) return mock_cloud_prefs(hass, {}) @pytest.fixture -async def cloud_prefs(hass): +async def cloud_prefs(hass: HomeAssistant) -> CloudPreferences: """Fixture for cloud preferences.""" cloud_prefs = CloudPreferences(hass) await cloud_prefs.async_initialize() @@ -221,13 +221,13 @@ async def cloud_prefs(hass): @pytest.fixture -async def mock_cloud_setup(hass): +async def mock_cloud_setup(hass: HomeAssistant) -> None: """Set up the cloud.""" await mock_cloud(hass) @pytest.fixture -def mock_cloud_login(hass, mock_cloud_setup): +def mock_cloud_login(hass: HomeAssistant, mock_cloud_setup: None) -> Generator[None]: """Mock cloud is logged in.""" hass.data[DATA_CLOUD].id_token = jwt.encode( { @@ -242,7 +242,7 @@ def mock_cloud_login(hass, mock_cloud_setup): @pytest.fixture(name="mock_auth") -def mock_auth_fixture(): +def mock_auth_fixture() -> Generator[None]: """Mock check token.""" with ( patch("hass_nabucasa.auth.CognitoAuth.async_check_token"), @@ -252,7 +252,7 @@ def mock_auth_fixture(): @pytest.fixture -def mock_expired_cloud_login(hass, mock_cloud_setup): +def mock_expired_cloud_login(hass: HomeAssistant, mock_cloud_setup: None) -> None: """Mock cloud is logged in.""" hass.data[DATA_CLOUD].id_token = jwt.encode( { diff --git a/tests/components/cloud/test_account_link.py b/tests/components/cloud/test_account_link.py index 7a85531904a..acaff7db76c 100644 --- a/tests/components/cloud/test_account_link.py +++ b/tests/components/cloud/test_account_link.py @@ -6,6 +6,7 @@ from time import time from unittest.mock import AsyncMock, Mock, patch import pytest +from typing_extensions import Generator from homeassistant import config_entries from homeassistant.components.cloud import account_link @@ -21,7 +22,9 @@ TEST_DOMAIN = "oauth2_test" @pytest.fixture -def flow_handler(hass): +def flow_handler( + hass: HomeAssistant, +) -> Generator[type[config_entry_oauth2_flow.AbstractOAuth2FlowHandler]]: """Return a registered config flow.""" mock_platform(hass, f"{TEST_DOMAIN}.config_flow") @@ -180,7 +183,10 @@ async def test_get_services_error(hass: HomeAssistant) -> None: @pytest.mark.usefixtures("current_request_with_host") -async def test_implementation(hass: HomeAssistant, flow_handler) -> None: +async def test_implementation( + hass: HomeAssistant, + flow_handler: type[config_entry_oauth2_flow.AbstractOAuth2FlowHandler], +) -> None: """Test Cloud OAuth2 implementation.""" hass.data[DATA_CLOUD] = None diff --git a/tests/components/cloud/test_alexa_config.py b/tests/components/cloud/test_alexa_config.py index e4ad425d4d4..3b4868b56ac 100644 --- a/tests/components/cloud/test_alexa_config.py +++ b/tests/components/cloud/test_alexa_config.py @@ -34,7 +34,7 @@ from tests.test_util.aiohttp import AiohttpClientMocker @pytest.fixture -def cloud_stub(): +def cloud_stub() -> Mock: """Stub the cloud.""" return Mock(is_logged_in=True, subscription_expired=False) @@ -51,7 +51,10 @@ def expose_entity(hass: HomeAssistant, entity_id: str, should_expose: bool) -> N async def test_alexa_config_expose_entity_prefs( - hass: HomeAssistant, cloud_prefs, cloud_stub, entity_registry: er.EntityRegistry + hass: HomeAssistant, + cloud_prefs: CloudPreferences, + cloud_stub: Mock, + entity_registry: er.EntityRegistry, ) -> None: """Test Alexa config should expose using prefs.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -133,7 +136,7 @@ async def test_alexa_config_expose_entity_prefs( async def test_alexa_config_report_state( - hass: HomeAssistant, cloud_prefs, cloud_stub + hass: HomeAssistant, cloud_prefs: CloudPreferences, cloud_stub: Mock ) -> None: """Test Alexa config should expose using prefs.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -168,7 +171,9 @@ async def test_alexa_config_report_state( async def test_alexa_config_invalidate_token( - hass: HomeAssistant, cloud_prefs, aioclient_mock: AiohttpClientMocker + hass: HomeAssistant, + cloud_prefs: CloudPreferences, + aioclient_mock: AiohttpClientMocker, ) -> None: """Test Alexa config should expose using prefs.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -218,11 +223,11 @@ async def test_alexa_config_invalidate_token( ) async def test_alexa_config_fail_refresh_token( hass: HomeAssistant, - cloud_prefs, + cloud_prefs: CloudPreferences, aioclient_mock: AiohttpClientMocker, entity_registry: er.EntityRegistry, - reject_reason, - expected_exception, + reject_reason: str, + expected_exception: type[Exception], ) -> None: """Test Alexa config failing to refresh token.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -342,7 +347,10 @@ def patch_sync_helper(): async def test_alexa_update_expose_trigger_sync( - hass: HomeAssistant, entity_registry: er.EntityRegistry, cloud_prefs, cloud_stub + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + cloud_prefs: CloudPreferences, + cloud_stub: Mock, ) -> None: """Test Alexa config responds to updating exposed entities.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -415,11 +423,11 @@ async def test_alexa_update_expose_trigger_sync( ] +@pytest.mark.usefixtures("mock_cloud_login") async def test_alexa_entity_registry_sync( hass: HomeAssistant, entity_registry: er.EntityRegistry, - mock_cloud_login, - cloud_prefs, + cloud_prefs: CloudPreferences, ) -> None: """Test Alexa config responds to entity registry.""" # Enable exposing new entities to Alexa @@ -475,7 +483,7 @@ async def test_alexa_entity_registry_sync( async def test_alexa_update_report_state( - hass: HomeAssistant, cloud_prefs, cloud_stub + hass: HomeAssistant, cloud_prefs: CloudPreferences, cloud_stub: Mock ) -> None: """Test Alexa config responds to reporting state.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -502,8 +510,9 @@ async def test_alexa_update_report_state( assert len(mock_sync.mock_calls) == 1 +@pytest.mark.usefixtures("mock_expired_cloud_login") def test_enabled_requires_valid_sub( - hass: HomeAssistant, mock_expired_cloud_login, cloud_prefs + hass: HomeAssistant, cloud_prefs: CloudPreferences ) -> None: """Test that alexa config enabled requires a valid Cloud sub.""" assert cloud_prefs.alexa_enabled @@ -518,7 +527,7 @@ def test_enabled_requires_valid_sub( async def test_alexa_handle_logout( - hass: HomeAssistant, cloud_prefs, cloud_stub + hass: HomeAssistant, cloud_prefs: CloudPreferences, cloud_stub: Mock ) -> None: """Test Alexa config responds to logging out.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -561,7 +570,7 @@ async def test_alexa_handle_logout( async def test_alexa_config_migrate_expose_entity_prefs( hass: HomeAssistant, cloud_prefs: CloudPreferences, - cloud_stub, + cloud_stub: Mock, entity_registry: er.EntityRegistry, alexa_settings_version: int, ) -> None: @@ -755,7 +764,7 @@ async def test_alexa_config_migrate_expose_entity_prefs_v2_exposed( async def test_alexa_config_migrate_expose_entity_prefs_default_none( hass: HomeAssistant, cloud_prefs: CloudPreferences, - cloud_stub, + cloud_stub: Mock, entity_registry: er.EntityRegistry, ) -> None: """Test migrating Alexa entity config.""" @@ -793,7 +802,7 @@ async def test_alexa_config_migrate_expose_entity_prefs_default_none( async def test_alexa_config_migrate_expose_entity_prefs_default( hass: HomeAssistant, cloud_prefs: CloudPreferences, - cloud_stub, + cloud_stub: Mock, entity_registry: er.EntityRegistry, ) -> None: """Test migrating Alexa entity config.""" diff --git a/tests/components/cloud/test_client.py b/tests/components/cloud/test_client.py index 62af4e88857..005efd990fb 100644 --- a/tests/components/cloud/test_client.py +++ b/tests/components/cloud/test_client.py @@ -1,6 +1,7 @@ """Test the cloud.iot module.""" from datetime import timedelta +from typing import Any from unittest.mock import AsyncMock, MagicMock, Mock, PropertyMock, patch import aiohttp @@ -20,6 +21,7 @@ from homeassistant.components.cloud.const import ( PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE, ) +from homeassistant.components.cloud.prefs import CloudPreferences from homeassistant.components.homeassistant.exposed_entities import ( DATA_EXPOSED_ENTITIES, async_expose_entity, @@ -37,7 +39,7 @@ from tests.components.alexa import test_smart_home as test_alexa @pytest.fixture -def mock_cloud_inst(): +def mock_cloud_inst() -> MagicMock: """Mock cloud class.""" return MagicMock(subscription_expired=False) @@ -81,7 +83,9 @@ async def test_handler_alexa(hass: HomeAssistant) -> None: assert device["manufacturerName"] == "Home Assistant" -async def test_handler_alexa_disabled(hass: HomeAssistant, mock_cloud_fixture) -> None: +async def test_handler_alexa_disabled( + hass: HomeAssistant, mock_cloud_fixture: CloudPreferences +) -> None: """Test handler Alexa when user has disabled it.""" mock_cloud_fixture._prefs[PREF_ENABLE_ALEXA] = False cloud = hass.data[DATA_CLOUD] @@ -154,7 +158,10 @@ async def test_handler_google_actions(hass: HomeAssistant) -> None: ], ) async def test_handler_google_actions_disabled( - hass: HomeAssistant, mock_cloud_fixture, intent, response_payload + hass: HomeAssistant, + mock_cloud_fixture: CloudPreferences, + intent: str, + response_payload: dict[str, Any], ) -> None: """Test handler Google Actions when user has disabled it.""" mock_cloud_fixture._prefs[PREF_ENABLE_GOOGLE] = False @@ -253,11 +260,10 @@ async def test_webhook_msg( assert '{"nonexisting": "payload"}' in caplog.text +@pytest.mark.usefixtures("mock_cloud_setup", "mock_cloud_login") async def test_google_config_expose_entity( hass: HomeAssistant, entity_registry: er.EntityRegistry, - mock_cloud_setup, - mock_cloud_login, ) -> None: """Test Google config exposing entity method uses latest config.""" @@ -281,11 +287,10 @@ async def test_google_config_expose_entity( assert not gconf.should_expose(state) +@pytest.mark.usefixtures("mock_cloud_setup", "mock_cloud_login") async def test_google_config_should_2fa( hass: HomeAssistant, entity_registry: er.EntityRegistry, - mock_cloud_setup, - mock_cloud_login, ) -> None: """Test Google config disabling 2FA method uses latest config.""" diff --git a/tests/components/cloud/test_google_config.py b/tests/components/cloud/test_google_config.py index 40d3f6ef2c5..b152309b24a 100644 --- a/tests/components/cloud/test_google_config.py +++ b/tests/components/cloud/test_google_config.py @@ -36,7 +36,7 @@ from tests.common import async_fire_time_changed @pytest.fixture -def mock_conf(hass, cloud_prefs): +def mock_conf(hass: HomeAssistant, cloud_prefs: CloudPreferences) -> CloudGoogleConfig: """Mock Google conf.""" return CloudGoogleConfig( hass, @@ -59,7 +59,7 @@ def expose_entity(hass: HomeAssistant, entity_id: str, should_expose: bool) -> N async def test_google_update_report_state( - mock_conf, hass: HomeAssistant, cloud_prefs + mock_conf: CloudGoogleConfig, hass: HomeAssistant, cloud_prefs: CloudPreferences ) -> None: """Test Google config responds to updating preference.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -83,7 +83,7 @@ async def test_google_update_report_state( async def test_google_update_report_state_subscription_expired( - mock_conf, hass: HomeAssistant, cloud_prefs + mock_conf: CloudGoogleConfig, hass: HomeAssistant, cloud_prefs: CloudPreferences ) -> None: """Test Google config not reporting state when subscription has expired.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -106,7 +106,9 @@ async def test_google_update_report_state_subscription_expired( assert len(mock_report_state.mock_calls) == 0 -async def test_sync_entities(mock_conf, hass: HomeAssistant, cloud_prefs) -> None: +async def test_sync_entities( + mock_conf: CloudGoogleConfig, hass: HomeAssistant, cloud_prefs: CloudPreferences +) -> None: """Test sync devices.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -129,7 +131,9 @@ async def test_sync_entities(mock_conf, hass: HomeAssistant, cloud_prefs) -> Non async def test_google_update_expose_trigger_sync( - hass: HomeAssistant, entity_registry: er.EntityRegistry, cloud_prefs + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + cloud_prefs: CloudPreferences, ) -> None: """Test Google config responds to updating exposed entities.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -185,11 +189,11 @@ async def test_google_update_expose_trigger_sync( assert len(mock_sync.mock_calls) == 1 +@pytest.mark.usefixtures("mock_cloud_login") async def test_google_entity_registry_sync( hass: HomeAssistant, entity_registry: er.EntityRegistry, - mock_cloud_login, - cloud_prefs, + cloud_prefs: CloudPreferences, ) -> None: """Test Google config responds to entity registry.""" @@ -257,11 +261,11 @@ async def test_google_entity_registry_sync( assert len(mock_sync.mock_calls) == 3 +@pytest.mark.usefixtures("mock_cloud_login") async def test_google_device_registry_sync( hass: HomeAssistant, entity_registry: er.EntityRegistry, - mock_cloud_login, - cloud_prefs, + cloud_prefs: CloudPreferences, ) -> None: """Test Google config responds to device registry.""" config = CloudGoogleConfig( @@ -329,8 +333,9 @@ async def test_google_device_registry_sync( assert len(mock_sync.mock_calls) == 1 +@pytest.mark.usefixtures("mock_cloud_login") async def test_sync_google_when_started( - hass: HomeAssistant, mock_cloud_login, cloud_prefs + hass: HomeAssistant, cloud_prefs: CloudPreferences ) -> None: """Test Google config syncs on init.""" config = CloudGoogleConfig( @@ -342,8 +347,9 @@ async def test_sync_google_when_started( assert len(mock_sync.mock_calls) == 1 +@pytest.mark.usefixtures("mock_cloud_login") async def test_sync_google_on_home_assistant_start( - hass: HomeAssistant, mock_cloud_login, cloud_prefs + hass: HomeAssistant, cloud_prefs: CloudPreferences ) -> None: """Test Google config syncs when home assistant started.""" config = CloudGoogleConfig( @@ -361,7 +367,10 @@ async def test_sync_google_on_home_assistant_start( async def test_google_config_expose_entity_prefs( - hass: HomeAssistant, mock_conf, cloud_prefs, entity_registry: er.EntityRegistry + hass: HomeAssistant, + mock_conf: CloudGoogleConfig, + cloud_prefs: CloudPreferences, + entity_registry: er.EntityRegistry, ) -> None: """Test Google config should expose using prefs.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -437,8 +446,9 @@ async def test_google_config_expose_entity_prefs( assert not mock_conf.should_expose(state_not_exposed) +@pytest.mark.usefixtures("mock_expired_cloud_login") def test_enabled_requires_valid_sub( - hass: HomeAssistant, mock_expired_cloud_login, cloud_prefs + hass: HomeAssistant, cloud_prefs: CloudPreferences ) -> None: """Test that google config enabled requires a valid Cloud sub.""" assert cloud_prefs.google_enabled @@ -453,7 +463,7 @@ def test_enabled_requires_valid_sub( async def test_setup_google_assistant( - hass: HomeAssistant, mock_conf, cloud_prefs + hass: HomeAssistant, mock_conf: CloudGoogleConfig, cloud_prefs: CloudPreferences ) -> None: """Test that we set up the google_assistant integration if enabled in cloud.""" assert await async_setup_component(hass, "homeassistant", {}) @@ -472,8 +482,9 @@ async def test_setup_google_assistant( assert "google_assistant" in hass.config.components +@pytest.mark.usefixtures("mock_cloud_login") async def test_google_handle_logout( - hass: HomeAssistant, cloud_prefs, mock_cloud_login + hass: HomeAssistant, cloud_prefs: CloudPreferences ) -> None: """Test Google config responds to logging out.""" gconf = CloudGoogleConfig( @@ -853,8 +864,9 @@ async def test_google_config_migrate_expose_entity_prefs_default( } +@pytest.mark.usefixtures("mock_cloud_login") async def test_google_config_get_agent_user_id( - hass: HomeAssistant, mock_cloud_login, cloud_prefs + hass: HomeAssistant, cloud_prefs: CloudPreferences ) -> None: """Test overridden get_agent_user_id_from_webhook method.""" config = CloudGoogleConfig( @@ -867,8 +879,9 @@ async def test_google_config_get_agent_user_id( assert config.get_agent_user_id_from_webhook("other_id") != config.agent_user_id +@pytest.mark.usefixtures("mock_cloud_login") async def test_google_config_get_agent_users( - hass: HomeAssistant, mock_cloud_login, cloud_prefs + hass: HomeAssistant, cloud_prefs: CloudPreferences ) -> None: """Test overridden async_get_agent_users method.""" username_mock = PropertyMock(return_value="blah") diff --git a/tests/components/cloud/test_init.py b/tests/components/cloud/test_init.py index d201b45b670..ad123cded84 100644 --- a/tests/components/cloud/test_init.py +++ b/tests/components/cloud/test_init.py @@ -67,8 +67,9 @@ async def test_constructor_loads_info_from_config(hass: HomeAssistant) -> None: assert cl.remotestate_server == "test-remotestate-server" +@pytest.mark.usefixtures("mock_cloud_fixture") async def test_remote_services( - hass: HomeAssistant, mock_cloud_fixture, hass_read_only_user: MockUser + hass: HomeAssistant, hass_read_only_user: MockUser ) -> None: """Setup cloud component and test services.""" cloud = hass.data[DATA_CLOUD] @@ -114,7 +115,8 @@ async def test_remote_services( assert mock_disconnect.called is False -async def test_shutdown_event(hass: HomeAssistant, mock_cloud_fixture) -> None: +@pytest.mark.usefixtures("mock_cloud_fixture") +async def test_shutdown_event(hass: HomeAssistant) -> None: """Test if the cloud will stop on shutdown event.""" with patch("hass_nabucasa.Cloud.stop") as mock_stop: hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) @@ -149,7 +151,8 @@ async def test_setup_existing_cloud_user( assert hass_storage[STORAGE_KEY]["data"]["cloud_user"] == user.id -async def test_on_connect(hass: HomeAssistant, mock_cloud_fixture) -> None: +@pytest.mark.usefixtures("mock_cloud_fixture") +async def test_on_connect(hass: HomeAssistant) -> None: """Test cloud on connect triggers.""" cl = hass.data[DATA_CLOUD] @@ -206,7 +209,8 @@ async def test_on_connect(hass: HomeAssistant, mock_cloud_fixture) -> None: assert cloud_states[-1] == CloudConnectionState.CLOUD_DISCONNECTED -async def test_remote_ui_url(hass: HomeAssistant, mock_cloud_fixture) -> None: +@pytest.mark.usefixtures("mock_cloud_fixture") +async def test_remote_ui_url(hass: HomeAssistant) -> None: """Test getting remote ui url.""" cl = hass.data[DATA_CLOUD]