"""Test fixtures for home_connect.""" from collections.abc import Awaitable, Callable, Generator import time from typing import Any from unittest.mock import MagicMock, Mock, PropertyMock, patch from homeconnect.api import HomeConnectAppliance, HomeConnectError import pytest from homeassistant.components.application_credentials import ( ClientCredential, async_import_client_credential, ) from homeassistant.components.home_connect import update_all_devices from homeassistant.components.home_connect.const import DOMAIN from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry, load_json_object_fixture MOCK_APPLIANCES_PROPERTIES = { x["name"]: x for x in load_json_object_fixture("home_connect/appliances.json")["data"][ "homeappliances" ] } CLIENT_ID = "1234" CLIENT_SECRET = "5678" FAKE_ACCESS_TOKEN = "some-access-token" FAKE_REFRESH_TOKEN = "some-refresh-token" FAKE_AUTH_IMPL = "conftest-imported-cred" SERVER_ACCESS_TOKEN = { "refresh_token": "server-refresh-token", "access_token": "server-access-token", "type": "Bearer", "expires_in": 60, } @pytest.fixture(name="token_expiration_time") def mock_token_expiration_time() -> float: """Fixture for expiration time of the config entry auth token.""" return time.time() + 86400 @pytest.fixture(name="token_entry") def mock_token_entry(token_expiration_time: float) -> dict[str, Any]: """Fixture for OAuth 'token' data for a ConfigEntry.""" return { "refresh_token": FAKE_REFRESH_TOKEN, "access_token": FAKE_ACCESS_TOKEN, "type": "Bearer", "expires_at": token_expiration_time, } @pytest.fixture(name="config_entry") def mock_config_entry(token_entry: dict[str, Any]) -> MockConfigEntry: """Fixture for a config entry.""" return MockConfigEntry( domain=DOMAIN, data={ "auth_implementation": FAKE_AUTH_IMPL, "token": token_entry, }, ) @pytest.fixture async def setup_credentials(hass: HomeAssistant) -> None: """Fixture to setup credentials.""" assert await async_setup_component(hass, "application_credentials", {}) await async_import_client_credential( hass, DOMAIN, ClientCredential(CLIENT_ID, CLIENT_SECRET), FAKE_AUTH_IMPL, ) @pytest.fixture def platforms() -> list[Platform]: """Fixture to specify platforms to test.""" return [] async def bypass_throttle(hass: HomeAssistant, config_entry: MockConfigEntry): """Add kwarg to disable throttle.""" await update_all_devices(hass, config_entry, no_throttle=True) @pytest.fixture(name="bypass_throttle") def mock_bypass_throttle(): """Fixture to bypass the throttle decorator in __init__.""" with patch( "homeassistant.components.home_connect.update_all_devices", side_effect=lambda x, y: bypass_throttle(x, y), ): yield @pytest.fixture(name="integration_setup") async def mock_integration_setup( hass: HomeAssistant, platforms: list[Platform], config_entry: MockConfigEntry, ) -> Callable[[], Awaitable[bool]]: """Fixture to set up the integration.""" config_entry.add_to_hass(hass) async def run() -> bool: with patch("homeassistant.components.home_connect.PLATFORMS", platforms): result = await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() return result return run @pytest.fixture(name="get_appliances") def mock_get_appliances() -> Generator[None, Any, None]: """Mock ConfigEntryAuth parent (HomeAssistantAPI) method.""" with patch( "homeassistant.components.home_connect.api.ConfigEntryAuth.get_appliances", ) as mock: yield mock @pytest.fixture(name="appliance") def mock_appliance(request) -> Mock: """Fixture to mock Appliance.""" app = "Washer" if hasattr(request, "param") and request.param: app = request.param mock = MagicMock( autospec=HomeConnectAppliance, **MOCK_APPLIANCES_PROPERTIES.get(app), ) mock.name = app type(mock).status = PropertyMock(return_value={}) mock.get.return_value = {} mock.get_programs_available.return_value = [] mock.get_status.return_value = {} mock.get_settings.return_value = {} return mock @pytest.fixture(name="problematic_appliance") def mock_problematic_appliance() -> Mock: """Fixture to mock a problematic Appliance.""" app = "Washer" mock = Mock( spec=HomeConnectAppliance, **MOCK_APPLIANCES_PROPERTIES.get(app), ) mock.name = app setattr(mock, "status", {}) mock.get_programs_active.side_effect = HomeConnectError mock.get_programs_available.side_effect = HomeConnectError mock.start_program.side_effect = HomeConnectError mock.stop_program.side_effect = HomeConnectError mock.get_status.side_effect = HomeConnectError mock.get_settings.side_effect = HomeConnectError mock.set_setting.side_effect = HomeConnectError return mock def get_all_appliances(): """Return a list of `HomeConnectAppliance` instances for all appliances.""" appliances = {} data = load_json_object_fixture("home_connect/appliances.json").get("data") programs_active = load_json_object_fixture("home_connect/programs-active.json") programs_available = load_json_object_fixture( "home_connect/programs-available.json" ) def listen_callback(mock, callback): callback["callback"](mock) for home_appliance in data["homeappliances"]: api_status = load_json_object_fixture("home_connect/status.json") api_settings = load_json_object_fixture("home_connect/settings.json") ha_id = home_appliance["haId"] ha_type = home_appliance["type"] appliance = MagicMock(spec=HomeConnectAppliance, **home_appliance) appliance.name = home_appliance["name"] appliance.listen_events.side_effect = ( lambda app=appliance, **x: listen_callback(app, x) ) appliance.get_programs_active.return_value = programs_active.get( ha_type, {} ).get("data", {}) appliance.get_programs_available.return_value = [ program["key"] for program in programs_available.get(ha_type, {}) .get("data", {}) .get("programs", []) ] appliance.get_status.return_value = HomeConnectAppliance.json2dict( api_status.get("data", {}).get("status", []) ) appliance.get_settings.return_value = HomeConnectAppliance.json2dict( api_settings.get(ha_type, {}).get("data", {}).get("settings", []) ) setattr(appliance, "status", {}) appliance.status.update(appliance.get_status.return_value) appliance.status.update(appliance.get_settings.return_value) appliance.set_setting.side_effect = ( lambda x, y, appliance=appliance: appliance.status.update({x: {"value": y}}) ) appliance.start_program.side_effect = ( lambda x, appliance=appliance: appliance.status.update( {"BSH.Common.Root.ActiveProgram": {"value": x}} ) ) appliance.stop_program.side_effect = ( lambda appliance=appliance: appliance.status.update( {"BSH.Common.Root.ActiveProgram": {}} ) ) appliances[ha_id] = appliance return list(appliances.values())