core/tests/components/unifi/test_hub.py

181 lines
5.7 KiB
Python
Raw Normal View History

"""Test UniFi Network."""
from collections.abc import Callable
from http import HTTPStatus
from types import MappingProxyType
from typing import Any
from unittest.mock import patch
import aiounifi
import pytest
from homeassistant.components.unifi.const import DOMAIN as UNIFI_DOMAIN
from homeassistant.components.unifi.errors import AuthenticationRequired, CannotConnect
from homeassistant.components.unifi.hub import get_unifi_api
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.const import CONF_HOST, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
import homeassistant.util.dt as dt_util
from tests.test_util.aiohttp import AiohttpClientMocker
async def test_hub_setup(
device_registry: dr.DeviceRegistry,
config_entry_factory: Callable[[], ConfigEntry],
) -> None:
"""Successful setup."""
with patch(
"homeassistant.config_entries.ConfigEntries.async_forward_entry_setups",
return_value=True,
) as forward_entry_setup:
config_entry = await config_entry_factory()
assert len(forward_entry_setup.mock_calls) == 1
assert forward_entry_setup.mock_calls[0][1] == (
config_entry,
[
Platform.BUTTON,
Platform.DEVICE_TRACKER,
Platform.IMAGE,
Platform.SENSOR,
Platform.SWITCH,
Platform.UPDATE,
],
)
device_entry = device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
identifiers={(UNIFI_DOMAIN, config_entry.unique_id)},
)
assert device_entry.sw_version == "7.4.162"
async def test_reset_after_successful_setup(
hass: HomeAssistant, config_entry_setup: ConfigEntry
) -> None:
"""Calling reset when the entry has been setup."""
assert config_entry_setup.state is ConfigEntryState.LOADED
assert await hass.config_entries.async_unload(config_entry_setup.entry_id)
assert config_entry_setup.state is ConfigEntryState.NOT_LOADED
async def test_reset_fails(
hass: HomeAssistant, config_entry_setup: ConfigEntry
) -> None:
"""Calling reset when the entry has been setup can return false."""
assert config_entry_setup.state is ConfigEntryState.LOADED
with patch(
"homeassistant.config_entries.ConfigEntries.async_forward_entry_unload",
return_value=False,
):
assert not await hass.config_entries.async_unload(config_entry_setup.entry_id)
assert config_entry_setup.state is ConfigEntryState.LOADED
async def test_connection_state_signalling(
hass: HomeAssistant,
mock_device_registry,
websocket_mock,
config_entry_factory: Callable[[], ConfigEntry],
client_payload: list[dict[str, Any]],
) -> None:
"""Verify connection statesignalling and connection state are working."""
client_payload.append(
{
"hostname": "client",
"ip": "10.0.0.1",
"is_wired": True,
"last_seen": dt_util.as_timestamp(dt_util.utcnow()),
"mac": "00:00:00:00:00:01",
}
)
await config_entry_factory()
# Controller is connected
assert hass.states.get("device_tracker.client").state == "home"
await websocket_mock.disconnect()
# Controller is disconnected
assert hass.states.get("device_tracker.client").state == "unavailable"
await websocket_mock.reconnect()
# Controller is once again connected
assert hass.states.get("device_tracker.client").state == "home"
async def test_reconnect_mechanism(
aioclient_mock: AiohttpClientMocker, websocket_mock, config_entry_setup: ConfigEntry
) -> None:
"""Verify reconnect prints only on first reconnection try."""
aioclient_mock.clear_requests()
aioclient_mock.get(
f"https://{config_entry_setup.data[CONF_HOST]}:1234/",
status=HTTPStatus.BAD_GATEWAY,
)
await websocket_mock.disconnect()
assert aioclient_mock.call_count == 0
await websocket_mock.reconnect(fail=True)
assert aioclient_mock.call_count == 1
await websocket_mock.reconnect(fail=True)
assert aioclient_mock.call_count == 2
@pytest.mark.parametrize(
"exception",
[
TimeoutError,
aiounifi.BadGateway,
aiounifi.ServiceUnavailable,
aiounifi.AiounifiException,
],
)
@pytest.mark.usefixtures("config_entry_setup")
async def test_reconnect_mechanism_exceptions(websocket_mock, exception) -> None:
"""Verify async_reconnect calls expected methods."""
with (
patch("aiounifi.Controller.login", side_effect=exception),
patch(
"homeassistant.components.unifi.hub.hub.UnifiWebsocket.reconnect"
) as mock_reconnect,
):
await websocket_mock.disconnect()
await websocket_mock.reconnect()
mock_reconnect.assert_called_once()
@pytest.mark.parametrize(
("side_effect", "raised_exception"),
[
(TimeoutError, CannotConnect),
(aiounifi.BadGateway, CannotConnect),
(aiounifi.Forbidden, CannotConnect),
(aiounifi.ServiceUnavailable, CannotConnect),
(aiounifi.RequestError, CannotConnect),
(aiounifi.ResponseError, CannotConnect),
(aiounifi.Unauthorized, AuthenticationRequired),
(aiounifi.LoginRequired, AuthenticationRequired),
(aiounifi.AiounifiException, AuthenticationRequired),
],
)
async def test_get_unifi_api_fails_to_connect(
hass: HomeAssistant,
side_effect,
raised_exception,
config_entry_data: MappingProxyType[str, Any],
) -> None:
"""Check that get_unifi_api can handle UniFi Network being unavailable."""
with (
patch("aiounifi.Controller.login", side_effect=side_effect),
pytest.raises(raised_exception),
2023-08-26 23:27:45 +00:00
):
await get_unifi_api(hass, config_entry_data)