"""Test fixtures for mqtt component.""" import asyncio from random import getrandbits from typing import Any from unittest.mock import patch import pytest from typing_extensions import AsyncGenerator, Generator from homeassistant.components import mqtt from homeassistant.components.mqtt.models import MessageCallbackType, ReceiveMessage from homeassistant.components.mqtt.util import EnsureJobAfterCooldown from homeassistant.const import EVENT_HOMEASSISTANT_STARTED from homeassistant.core import HomeAssistant, callback from tests.common import MockConfigEntry from tests.typing import MqttMockPahoClient ENTRY_DEFAULT_BIRTH_MESSAGE = { mqtt.CONF_BROKER: "mock-broker", mqtt.CONF_BIRTH_MESSAGE: { mqtt.ATTR_TOPIC: "homeassistant/status", mqtt.ATTR_PAYLOAD: "online", mqtt.ATTR_QOS: 0, mqtt.ATTR_RETAIN: False, }, } @pytest.fixture(autouse=True) def patch_hass_config(mock_hass_config: None) -> None: """Patch configuration.yaml.""" @pytest.fixture def temp_dir_prefix() -> str: """Set an alternate temp dir prefix.""" return "test" @pytest.fixture def mock_temp_dir(temp_dir_prefix: str) -> Generator[str]: """Mock the certificate temp directory.""" with patch( # Patch temp dir name to avoid tests fail running in parallel "homeassistant.components.mqtt.util.TEMP_DIR_NAME", f"home-assistant-mqtt-{temp_dir_prefix}-{getrandbits(10):03x}", ) as mocked_temp_dir: yield mocked_temp_dir @pytest.fixture def mock_debouncer(hass: HomeAssistant) -> Generator[asyncio.Event]: """Mock EnsureJobAfterCooldown. Returns an asyncio.Event that allows to await the debouncer task to be finished. """ task_done = asyncio.Event() class MockDeboncer(EnsureJobAfterCooldown): """Mock the MQTT client (un)subscribe debouncer.""" async def _async_job(self) -> None: """Execute after a cooldown period.""" await super()._async_job() task_done.set() # We mock the import of EnsureJobAfterCooldown in client.py with patch( "homeassistant.components.mqtt.client.EnsureJobAfterCooldown", MockDeboncer ): yield task_done @pytest.fixture async def setup_with_birth_msg_client_mock( hass: HomeAssistant, mqtt_config_entry_data: dict[str, Any] | None, mqtt_client_mock: MqttMockPahoClient, ) -> AsyncGenerator[MqttMockPahoClient]: """Test sending birth message.""" birth = asyncio.Event() with ( patch("homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN", 0.0), patch("homeassistant.components.mqtt.client.DISCOVERY_COOLDOWN", 0.0), patch("homeassistant.components.mqtt.client.SUBSCRIBE_COOLDOWN", 0.0), ): entry = MockConfigEntry( domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"} ) entry.add_to_hass(hass) hass.config.components.add(mqtt.DOMAIN) assert await hass.config_entries.async_setup(entry.entry_id) hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) @callback def wait_birth(msg: ReceiveMessage) -> None: """Handle birth message.""" birth.set() await mqtt.async_subscribe(hass, "homeassistant/status", wait_birth) await hass.async_block_till_done() await birth.wait() yield mqtt_client_mock @pytest.fixture def recorded_calls() -> list[ReceiveMessage]: """Fixture to hold recorded calls.""" return [] @pytest.fixture def record_calls(recorded_calls: list[ReceiveMessage]) -> MessageCallbackType: """Fixture to record calls.""" @callback def record_calls(msg: ReceiveMessage) -> None: """Record calls.""" recorded_calls.append(msg) return record_calls