mirror of
https://github.com/home-assistant/core.git
synced 2026-08-06 13:26:29 +01:00
145 lines
4.8 KiB
Python
145 lines
4.8 KiB
Python
"""Test the Harbor integration setup and coordinator."""
|
|
|
|
from unittest.mock import AsyncMock
|
|
|
|
from syrupy.assertion import SnapshotAssertion
|
|
|
|
from homeassistant.components.harbor.const import DOMAIN
|
|
from homeassistant.config_entries import ConfigEntryState
|
|
from homeassistant.const import STATE_UNAVAILABLE
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import device_registry as dr
|
|
|
|
from . import setup_integration
|
|
from .conftest import (
|
|
HEARTBEAT_PAYLOAD,
|
|
HEARTBEAT_TOPIC,
|
|
SERIAL,
|
|
emit_message,
|
|
set_connected,
|
|
)
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
# The default test fixture reports no device data on connect, so the device
|
|
# keeps its placeholder name and the entity id derives from that.
|
|
_SENSOR = "sensor.harbor_camera_1234567890_temperature"
|
|
|
|
|
|
async def test_setup_and_unload(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_mqtt_client: AsyncMock,
|
|
) -> None:
|
|
"""Test a config entry loads, starts the client, and unloads cleanly."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert mock_config_entry.state is ConfigEntryState.LOADED
|
|
assert mock_mqtt_client.return_value.start.called
|
|
|
|
assert await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
|
|
assert mock_mqtt_client.return_value.stop.called
|
|
|
|
|
|
async def test_setup_uses_instance_scoped_mqtt_client_id(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_mqtt_client: AsyncMock,
|
|
) -> None:
|
|
"""Test setup uses an MQTT client id unique to this HA instance."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
client_id = mock_mqtt_client.call_args.kwargs["client_id"]
|
|
|
|
assert client_id.startswith(f"{DOMAIN}-")
|
|
assert client_id.endswith(f"-{SERIAL}")
|
|
assert client_id != f"{DOMAIN}-{SERIAL}"
|
|
|
|
|
|
async def test_setup_retry_when_unreachable(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_mqtt_client: AsyncMock,
|
|
) -> None:
|
|
"""Test setup is retried when the camera never connects."""
|
|
# Start the client without ever reporting a successful connection.
|
|
mock_mqtt_client.return_value.start.side_effect = None
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
|
assert mock_mqtt_client.return_value.stop.called
|
|
|
|
|
|
async def test_setup_retry_when_no_data_arrives(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_mqtt_client: AsyncMock,
|
|
) -> None:
|
|
"""Test setup is retried when the camera connects but never sends data."""
|
|
|
|
async def _start() -> None:
|
|
await set_connected(mock_mqtt_client, True)
|
|
|
|
mock_mqtt_client.return_value.start.side_effect = _start
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
|
assert mock_mqtt_client.return_value.stop.called
|
|
|
|
|
|
async def test_availability_follows_connection(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_mqtt_client: AsyncMock,
|
|
) -> None:
|
|
"""Test entity availability tracks the MQTT connection."""
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
# Setup waits for the first device message, so entities start available.
|
|
assert hass.states.get(_SENSOR).state != STATE_UNAVAILABLE
|
|
|
|
# A repeated connected signal is a no-op and keeps entities available.
|
|
await set_connected(mock_mqtt_client, True)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get(_SENSOR).state != STATE_UNAVAILABLE
|
|
|
|
# Losing the connection flips entities back to unavailable.
|
|
await set_connected(mock_mqtt_client, False)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get(_SENSOR).state == STATE_UNAVAILABLE
|
|
|
|
# Reconnecting restores availability without needing fresh device data.
|
|
await set_connected(mock_mqtt_client, True)
|
|
await hass.async_block_till_done()
|
|
assert hass.states.get(_SENSOR).state != STATE_UNAVAILABLE
|
|
|
|
|
|
async def test_device_registry(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_mqtt_client: AsyncMock,
|
|
snapshot: SnapshotAssertion,
|
|
) -> None:
|
|
"""Test the device adopts the name and firmware from the first message.
|
|
|
|
Setup waits for that first message before registering entities, so the
|
|
device is correct from the start instead of needing a later reload.
|
|
"""
|
|
|
|
async def _start() -> None:
|
|
await set_connected(mock_mqtt_client, True)
|
|
await emit_message(mock_mqtt_client, HEARTBEAT_TOPIC, HEARTBEAT_PAYLOAD)
|
|
|
|
mock_mqtt_client.return_value.start.side_effect = _start
|
|
|
|
await setup_integration(hass, mock_config_entry)
|
|
|
|
device = device_registry.async_get_device(identifiers={(DOMAIN, SERIAL)})
|
|
assert device == snapshot
|