Files
core/tests/components/harbor/conftest.py
T

127 lines
4.1 KiB
Python

"""Common fixtures for the Harbor tests."""
from collections.abc import Awaitable, Callable, Generator
from types import SimpleNamespace
from typing import Any
from unittest.mock import AsyncMock, patch
import pytest
from homeassistant.components.harbor.const import (
CONF_CERT_PEM,
CONF_KEY_PEM,
CONF_SERIAL,
DOMAIN,
)
from homeassistant.const import CONF_IP_ADDRESS
from tests.common import MockConfigEntry
SERIAL = "1234567890"
CERT_PEM = "-----BEGIN CERTIFICATE-----\nMIIBdummy\n-----END CERTIFICATE-----"
KEY_PEM = "-----BEGIN PRIVATE KEY-----\nMIIBdummy\n-----END PRIVATE KEY-----"
HEARTBEAT_TOPIC = f"cameras/{SERIAL}/events/heartbeat"
LIVEKIT_TOPIC = f"cameras/{SERIAL}/events/local_livekit_heartbeat"
SETTINGS_TOPIC = f"cameras/{SERIAL}/responses/get-settings"
SETTINGS_PAYLOAD: dict[str, Any] = {
"settings": {
"preference_stream_paused": False,
"preference_video_flip": True,
"preference_video_has_clock_display": False,
},
}
HEARTBEAT_PAYLOAD: dict[str, Any] = {
"temperature": 98.6,
"os_version": "1.2.3",
"settings": {"preference_display_name": "Nursery"},
}
LIVEKIT_PAYLOAD: dict[str, Any] = {
"bitrate": 1234.5,
"network_bars": 3,
"stream_quality": "GOOD",
"viewers_by_identity_full": {
"viewer-1": {"identity": "alice"},
"viewer-2": {"identity": "bob"},
},
"os_version": "1.2.3",
"app_version": "4.5.6",
}
def connection_callback(
mock_mqtt_client: AsyncMock,
) -> Callable[[bool], Awaitable[None]]:
"""Return the on_connection_change callback the integration registered."""
return mock_mqtt_client.call_args.kwargs["on_connection_change"]
async def emit_message(
mock_mqtt_client: AsyncMock, topic: str, payload: dict[str, Any]
) -> None:
"""Deliver an MQTT message through the handler the integration registered."""
await mock_mqtt_client.call_args.kwargs["message_handler"](topic, payload)
async def set_connected(mock_mqtt_client: AsyncMock, connected: bool) -> None:
"""Drive the MQTT connection state the integration observes."""
await connection_callback(mock_mqtt_client)(connected)
@pytest.fixture(autouse=True)
def mock_connect_timeout() -> Generator[None]:
"""Patch the connect timeout so unreachable-camera tests run quickly."""
with patch("homeassistant.components.harbor.coordinator.CONNECT_TIMEOUT", 0):
yield
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.harbor.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def mock_mqtt_client() -> Generator[AsyncMock]:
"""Mock the Harbor MQTT client, reporting a successful connection on start."""
with patch(
"homeassistant.components.harbor.coordinator.HarborMQTTClient",
autospec=True,
) as mock_client:
async def _start() -> None:
await set_connected(mock_client, True)
# Setup waits for the first device message too; simulate the
# initial-commands response landing right after connect, the
# same way a real camera answers before any explicit test
# message. Empty so it doesn't set values tests don't expect.
await mock_client.call_args.kwargs["message_handler"](HEARTBEAT_TOPIC, {})
mock_client.return_value.start.side_effect = _start
# The config flow probes get-settings for the camera's friendly name;
# default to an unnamed camera so the title falls back to the serial.
mock_client.return_value.get_settings.return_value = SimpleNamespace(
settings=SimpleNamespace(preference_display_name=None)
)
yield mock_client
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return a mock Harbor config entry."""
return MockConfigEntry(
domain=DOMAIN,
unique_id=SERIAL,
title=f"Camera {SERIAL}",
data={
CONF_SERIAL: SERIAL,
CONF_CERT_PEM: CERT_PEM,
CONF_KEY_PEM: KEY_PEM,
CONF_IP_ADDRESS: "192.168.1.10",
},
)