"""Common fixtures for the LetPot tests.""" from collections.abc import Callable, Generator from unittest.mock import AsyncMock, patch from letpot.models import ( DeviceFeature, LetPotDevice, LetPotDeviceInfo, LetPotDeviceStatus, ) import pytest from homeassistant.components.letpot.const import ( CONF_ACCESS_TOKEN_EXPIRES, CONF_REFRESH_TOKEN, CONF_REFRESH_TOKEN_EXPIRES, CONF_USER_ID, DOMAIN, ) from homeassistant.const import CONF_ACCESS_TOKEN, CONF_EMAIL from . import AUTHENTICATION, MAX_STATUS, SE_STATUS from tests.common import MockConfigEntry @pytest.fixture def device_type() -> str: """Return the device type to use for mock data.""" return "LPH63" def _mock_device_info(device_type: str) -> LetPotDeviceInfo: """Return mock device info for the given type.""" return LetPotDeviceInfo( model=device_type, model_name=f"LetPot {device_type}", model_code=device_type, features=_mock_device_features(device_type), ) def _mock_device_features(device_type: str) -> DeviceFeature: """Return mock device feature support for the given type.""" if device_type == "LPH31": return ( DeviceFeature.CATEGORY_HYDROPONIC_GARDEN | DeviceFeature.LIGHT_BRIGHTNESS_LOW_HIGH | DeviceFeature.PUMP_STATUS ) if device_type == "LPH62": return ( DeviceFeature.CATEGORY_HYDROPONIC_GARDEN | DeviceFeature.LIGHT_BRIGHTNESS_LEVELS | DeviceFeature.NUTRIENT_BUTTON | DeviceFeature.PUMP_AUTO | DeviceFeature.TEMPERATURE | DeviceFeature.TEMPERATURE_SET_UNIT | DeviceFeature.WATER_LEVEL ) if device_type == "LPH63": return ( DeviceFeature.CATEGORY_HYDROPONIC_GARDEN | DeviceFeature.LIGHT_BRIGHTNESS_LEVELS | DeviceFeature.NUTRIENT_BUTTON | DeviceFeature.PUMP_AUTO | DeviceFeature.PUMP_STATUS | DeviceFeature.TEMPERATURE | DeviceFeature.WATER_LEVEL ) raise ValueError(f"No mock data for device type {device_type}") def _mock_device_status(device_type: str) -> LetPotDeviceStatus: """Return mock device status for the given type.""" if device_type == "LPH31": return SE_STATUS if device_type in {"LPH62", "LPH63"}: return MAX_STATUS raise ValueError(f"No mock data for device type {device_type}") def _mock_light_brightness_levels(device_type: str) -> list[int]: """Return mock brightness levels for the given type.""" if device_type == "LPH31": return [500, 1000] if device_type in {"LPH62", "LPH63"}: return [125, 250, 375, 500, 625, 750, 875, 1000] raise ValueError(f"No mock data for device type {device_type}") @pytest.fixture def mock_setup_entry() -> Generator[AsyncMock]: """Override async_setup_entry.""" with patch( "homeassistant.components.letpot.async_setup_entry", return_value=True ) as mock_setup_entry: yield mock_setup_entry @pytest.fixture def mock_client(device_type: str) -> Generator[AsyncMock]: """Mock a LetPotClient.""" with ( patch( "homeassistant.components.letpot.LetPotClient", autospec=True, ) as mock_client, patch( "homeassistant.components.letpot.config_flow.LetPotClient", new=mock_client, ), ): client = mock_client.return_value client.login.return_value = AUTHENTICATION client.refresh_token.return_value = AUTHENTICATION client.get_devices.return_value = [ LetPotDevice( serial_number=f"{device_type}ABCD", name="Garden", device_type=device_type, is_online=True, is_remote=False, ) ] yield client @pytest.fixture def mock_device_client() -> Generator[AsyncMock]: """Mock a LetPotDeviceClient.""" with patch( "homeassistant.components.letpot.LetPotDeviceClient", autospec=True, ) as mock_device_client: device_client = mock_device_client.return_value subscribe_callbacks: dict[str, Callable] = {} def subscribe_side_effect(serial: str, callback: Callable) -> None: subscribe_callbacks[serial] = callback def request_status_side_effect(serial: str) -> None: # Deliver a status update to the subscriber, like the real client if (callback := subscribe_callbacks.get(serial)) is not None: callback(_mock_device_status(serial[:5])) def get_current_status_side_effect(serial: str) -> LetPotDeviceStatus: request_status_side_effect(serial) return _mock_device_status(serial[:5]) device_client.device_info.side_effect = lambda serial: _mock_device_info( serial[:5] ) device_client.get_light_brightness_levels.side_effect = ( lambda serial: _mock_light_brightness_levels(serial[:5]) ) device_client.get_current_status.side_effect = get_current_status_side_effect device_client.request_status_update.side_effect = request_status_side_effect device_client.subscribe.side_effect = subscribe_side_effect yield device_client @pytest.fixture def mock_config_entry() -> MockConfigEntry: """Mock a config entry.""" return MockConfigEntry( domain=DOMAIN, title=AUTHENTICATION.email, data={ CONF_ACCESS_TOKEN: AUTHENTICATION.access_token, CONF_ACCESS_TOKEN_EXPIRES: AUTHENTICATION.access_token_expires, CONF_REFRESH_TOKEN: AUTHENTICATION.refresh_token, CONF_REFRESH_TOKEN_EXPIRES: AUTHENTICATION.refresh_token_expires, CONF_USER_ID: AUTHENTICATION.user_id, CONF_EMAIL: AUTHENTICATION.email, }, unique_id=AUTHENTICATION.user_id, )