"""Configure pytest for Litter-Robot tests.""" from typing import Any from unittest.mock import AsyncMock, MagicMock, patch from pylitterbot import ( Account, FeederRobot, LitterRobot3, LitterRobot4, LitterRobot5, Pet, Robot, ) from pylitterbot.exceptions import InvalidCommandException from pylitterbot.robot.litterrobot4 import HopperStatus import pytest from homeassistant.core import HomeAssistant from .common import ACCOUNT_USER_ID, CONFIG, DOMAIN from tests.common import MockConfigEntry, load_json_object_fixture ROBOT_DATA = load_json_object_fixture("litter_robot_3_data.json", DOMAIN) ROBOT_4_DATA = load_json_object_fixture("litter_robot_4_data.json", DOMAIN) ROBOT_5_DATA = load_json_object_fixture("litter_robot_5_data.json", DOMAIN) ROBOT_5_PRO_DATA = load_json_object_fixture("litter_robot_5_pro_data.json", DOMAIN) FEEDER_ROBOT_DATA = load_json_object_fixture("feeder_robot_data.json", DOMAIN) PET_DATA = load_json_object_fixture("pet_data.json", DOMAIN) def create_mock_robot( robot_data: dict | None, account: Account, *, v4: bool, v5: bool, v5_pro: bool, feeder: bool, side_effect: Any | None = None, ) -> Robot: """Create a mock Litter-Robot device.""" if not robot_data: robot_data = {} if v5 or v5_pro: data = ROBOT_5_PRO_DATA if v5_pro else ROBOT_5_DATA robot = LitterRobot5(data={**data, **robot_data}, account=account) robot.reset = AsyncMock(side_effect=side_effect) robot.change_filter = AsyncMock(side_effect=side_effect) robot.set_night_light_brightness = AsyncMock(side_effect=side_effect) robot.set_night_light_mode = AsyncMock(side_effect=side_effect) robot.set_panel_brightness = AsyncMock(side_effect=side_effect) elif v4: robot = LitterRobot4(data={**ROBOT_4_DATA, **robot_data}, account=account) elif feeder: robot = FeederRobot(data={**FEEDER_ROBOT_DATA, **robot_data}, account=account) robot.set_gravity_mode = AsyncMock(side_effect=side_effect) else: robot = LitterRobot3(data={**ROBOT_DATA, **robot_data}, account=account) robot.start_cleaning = AsyncMock(side_effect=side_effect) robot.set_power_status = AsyncMock(side_effect=side_effect) robot.reset_waste_drawer = AsyncMock(side_effect=side_effect) robot.set_sleep_mode = AsyncMock(side_effect=side_effect) robot.set_night_light = AsyncMock(side_effect=side_effect) robot.set_panel_lockout = AsyncMock(side_effect=side_effect) robot.set_wait_time = AsyncMock(side_effect=side_effect) robot.refresh = AsyncMock(side_effect=side_effect) return robot def create_mock_pet( pet_data: dict | None, account: Account, side_effect: Any | None = None, ) -> Pet: """Create a mock Pet.""" if not pet_data: pet_data = {} pet = Pet(data={**PET_DATA, **pet_data}, session=account.session) pet.fetch_weight_history = AsyncMock(side_effect=side_effect) return pet def create_mock_account( robot_data: dict | None = None, side_effect: Any | None = None, skip_robots: bool = False, v4: bool = False, v5: bool = False, v5_pro: bool = False, feeder: bool = False, pet: bool = False, ) -> MagicMock: """Create a mock Litter-Robot account.""" account = MagicMock(spec=Account) account.connect = AsyncMock() account.load_robots = AsyncMock() account.user_id = ACCOUNT_USER_ID account.robots = ( [] if skip_robots else [ create_mock_robot( robot_data, account, v4=v4, v5=v5, v5_pro=v5_pro, feeder=feeder, side_effect=side_effect, ) ] ) account.get_robots = lambda robot_class: [ robot for robot in account.robots if isinstance(robot, robot_class) ] account.pets = [create_mock_pet(PET_DATA, account, side_effect)] if pet else [] return account @pytest.fixture def mock_account() -> MagicMock: """Mock a Litter-Robot account.""" return create_mock_account() @pytest.fixture def mock_account_with_litterrobot_4() -> MagicMock: """Mock account with Litter-Robot 4.""" return create_mock_account(v4=True) @pytest.fixture def mock_account_with_litterrobot_5() -> MagicMock: """Mock account with Litter-Robot 5.""" return create_mock_account(v5=True) @pytest.fixture def mock_account_with_litterrobot_5_pro() -> MagicMock: """Mock account with Litter-Robot 5 Pro.""" return create_mock_account(v5_pro=True) @pytest.fixture def mock_account_with_litterhopper() -> MagicMock: """Mock account with LitterHopper attached to Litter-Robot 4.""" return create_mock_account( robot_data={"hopperStatus": HopperStatus.ENABLED, "isHopperRemoved": False}, v4=True, ) @pytest.fixture def mock_account_with_feederrobot() -> MagicMock: """Mock account with Feeder-Robot.""" return create_mock_account(feeder=True) @pytest.fixture def mock_account_with_pet() -> MagicMock: """Mock account with Feeder-Robot.""" return create_mock_account(pet=True) @pytest.fixture def mock_account_with_no_robots() -> MagicMock: """Mock a Litter-Robot account.""" return create_mock_account(skip_robots=True) @pytest.fixture def mock_account_with_sleeping_robot() -> MagicMock: """Mock a Litter-Robot account with a sleeping robot.""" return create_mock_account({"sleepModeActive": "102:00:00"}) @pytest.fixture def mock_account_with_sleep_disabled_robot() -> MagicMock: """Mock a Litter-Robot account with a robot that has sleep mode disabled.""" return create_mock_account({"sleepModeActive": "0"}) @pytest.fixture def mock_account_with_error() -> MagicMock: """Mock a Litter-Robot account with error.""" return create_mock_account({"unitStatus": "BR"}) @pytest.fixture def mock_account_with_side_effects() -> MagicMock: """Mock a Litter-Robot account with side effects.""" return create_mock_account( side_effect=InvalidCommandException("Invalid command: oops") ) async def setup_integration( hass: HomeAssistant, mock_account: MagicMock, platform_domain: str | None = None ) -> MockConfigEntry: """Load a Litter-Robot platform with the provided coordinator.""" entry = MockConfigEntry( domain=DOMAIN, data=CONFIG[DOMAIN], unique_id=ACCOUNT_USER_ID, version=1, minor_version=2, ) entry.add_to_hass(hass) with patch( "homeassistant.components.litterrobot.coordinator.Account", return_value=mock_account, ): await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() return entry