core/tests/components/unifi/test_controller.py

299 lines
10 KiB
Python

"""Test UniFi Controller."""
from collections import deque
from copy import deepcopy
from datetime import timedelta
import aiounifi
from asynctest import Mock, patch
import pytest
from homeassistant.components import unifi
from homeassistant.components.unifi.const import (
CONF_CONTROLLER,
CONF_SITE_ID,
UNIFI_WIRELESS_CLIENTS,
)
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
CONTROLLER_HOST = {
"hostname": "controller_host",
"ip": "1.2.3.4",
"is_wired": True,
"last_seen": 1562600145,
"mac": "10:00:00:00:00:01",
"name": "Controller host",
"oui": "Producer",
"sw_mac": "00:00:00:00:01:01",
"sw_port": 1,
"wired-rx_bytes": 1234000000,
"wired-tx_bytes": 5678000000,
}
CONTROLLER_DATA = {
CONF_HOST: "1.2.3.4",
CONF_USERNAME: "username",
CONF_PASSWORD: "password",
CONF_PORT: 1234,
CONF_SITE_ID: "site_id",
CONF_VERIFY_SSL: False,
}
ENTRY_CONFIG = {CONF_CONTROLLER: CONTROLLER_DATA}
ENTRY_OPTIONS = {}
CONFIGURATION = []
SITES = {"Site name": {"desc": "Site name", "name": "site_id", "role": "admin"}}
async def setup_unifi_integration(
hass,
config=ENTRY_CONFIG,
options=ENTRY_OPTIONS,
sites=SITES,
clients_response=None,
devices_response=None,
clients_all_response=None,
known_wireless_clients=None,
controllers=None,
):
"""Create the UniFi controller."""
configuration = {}
if controllers:
configuration = {unifi.DOMAIN: {unifi.CONF_CONTROLLERS: controllers}}
assert await async_setup_component(hass, unifi.DOMAIN, configuration)
config_entry = MockConfigEntry(
domain=unifi.DOMAIN,
data=deepcopy(config),
options=deepcopy(options),
entry_id=1,
)
config_entry.add_to_hass(hass)
if known_wireless_clients:
hass.data[UNIFI_WIRELESS_CLIENTS].update_data(
known_wireless_clients, config_entry
)
mock_client_responses = deque()
if clients_response:
mock_client_responses.append(clients_response)
mock_device_responses = deque()
if devices_response:
mock_device_responses.append(devices_response)
mock_client_all_responses = deque()
if clients_all_response:
mock_client_all_responses.append(clients_all_response)
mock_requests = []
async def mock_request(self, method, path, json=None):
mock_requests.append({"method": method, "path": path, "json": json})
if path == "s/{site}/stat/sta" and mock_client_responses:
return mock_client_responses.popleft()
if path == "s/{site}/stat/device" and mock_device_responses:
return mock_device_responses.popleft()
if path == "s/{site}/rest/user" and mock_client_all_responses:
return mock_client_all_responses.popleft()
return {}
# "aiounifi.Controller.start_websocket", return_value=True
with patch("aiounifi.Controller.login", return_value=True), patch(
"aiounifi.Controller.sites", return_value=sites
), patch("aiounifi.Controller.request", new=mock_request), patch.object(
aiounifi.websocket.WSClient, "start", return_value=True
):
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
controller_id = unifi.get_controller_id_from_config_entry(config_entry)
if controller_id not in hass.data[unifi.DOMAIN]:
return None
controller = hass.data[unifi.DOMAIN][controller_id]
controller.mock_client_responses = mock_client_responses
controller.mock_device_responses = mock_device_responses
controller.mock_client_all_responses = mock_client_all_responses
controller.mock_requests = mock_requests
return controller
async def test_controller_setup(hass):
"""Successful setup."""
with patch(
"homeassistant.config_entries.ConfigEntries.async_forward_entry_setup",
return_value=True,
) as forward_entry_setup:
controller = await setup_unifi_integration(hass)
entry = controller.config_entry
assert len(forward_entry_setup.mock_calls) == len(
unifi.controller.SUPPORTED_PLATFORMS
)
assert forward_entry_setup.mock_calls[0][1] == (entry, "device_tracker")
assert forward_entry_setup.mock_calls[1][1] == (entry, "sensor")
assert forward_entry_setup.mock_calls[2][1] == (entry, "switch")
assert controller.host == CONTROLLER_DATA[CONF_HOST]
assert controller.site == CONTROLLER_DATA[CONF_SITE_ID]
assert controller.site_name in SITES
assert controller.site_role == SITES[controller.site_name]["role"]
assert (
controller.option_allow_bandwidth_sensors
== unifi.const.DEFAULT_ALLOW_BANDWIDTH_SENSORS
)
assert controller.option_block_clients == unifi.const.DEFAULT_BLOCK_CLIENTS
assert controller.option_track_clients == unifi.const.DEFAULT_TRACK_CLIENTS
assert controller.option_track_devices == unifi.const.DEFAULT_TRACK_DEVICES
assert (
controller.option_track_wired_clients == unifi.const.DEFAULT_TRACK_WIRED_CLIENTS
)
assert controller.option_detection_time == timedelta(
seconds=unifi.const.DEFAULT_DETECTION_TIME
)
assert controller.option_ssid_filter == unifi.const.DEFAULT_SSID_FILTER
assert controller.mac is None
assert controller.signal_update == "unifi-update-1.2.3.4-site_id"
assert controller.signal_options_update == "unifi-options-1.2.3.4-site_id"
async def test_controller_mac(hass):
"""Test that it is possible to identify controller mac."""
controller = await setup_unifi_integration(hass, clients_response=[CONTROLLER_HOST])
assert controller.mac == "10:00:00:00:00:01"
async def test_controller_import_config(hass):
"""Test that import configuration.yaml instructions work."""
controllers = [
{
CONF_HOST: "1.2.3.4",
CONF_SITE_ID: "Site name",
unifi.CONF_BLOCK_CLIENT: ["random mac"],
unifi.CONF_DONT_TRACK_CLIENTS: True,
unifi.CONF_DONT_TRACK_DEVICES: True,
unifi.CONF_DONT_TRACK_WIRED_CLIENTS: True,
unifi.CONF_DETECTION_TIME: 150,
unifi.CONF_SSID_FILTER: ["SSID"],
}
]
controller = await setup_unifi_integration(hass, controllers=controllers)
assert controller.option_allow_bandwidth_sensors is False
assert controller.option_block_clients == ["random mac"]
assert controller.option_track_clients is False
assert controller.option_track_devices is False
assert controller.option_track_wired_clients is False
assert controller.option_detection_time == timedelta(seconds=150)
assert controller.option_ssid_filter == ["SSID"]
async def test_controller_not_accessible(hass):
"""Retry to login gets scheduled when connection fails."""
with patch.object(
unifi.controller, "get_controller", side_effect=unifi.errors.CannotConnect
):
await setup_unifi_integration(hass)
assert hass.data[unifi.DOMAIN] == {}
async def test_controller_unknown_error(hass):
"""Unknown errors are handled."""
with patch.object(unifi.controller, "get_controller", side_effect=Exception):
await setup_unifi_integration(hass)
assert hass.data[unifi.DOMAIN] == {}
async def test_reset_after_successful_setup(hass):
"""Calling reset when the entry has been setup."""
controller = await setup_unifi_integration(hass)
assert len(controller.listeners) == 5
result = await controller.async_reset()
await hass.async_block_till_done()
assert result is True
assert len(controller.listeners) == 0
async def test_wireless_client_event_calls_update_wireless_devices(hass):
"""Call update_wireless_devices method when receiving wireless client event."""
controller = await setup_unifi_integration(hass)
with patch(
"homeassistant.components.unifi.controller.UniFiController.update_wireless_clients",
return_value=None,
) as wireless_clients_mock:
controller.api.websocket._data = {
"meta": {"rc": "ok", "message": "events"},
"data": [
{
"datetime": "2020-01-20T19:37:04Z",
"key": aiounifi.events.WIRELESS_CLIENT_CONNECTED,
"msg": "User[11:22:33:44:55:66] has connected to WLAN",
"time": 1579549024893,
}
],
}
controller.api.session_handler("data")
assert wireless_clients_mock.assert_called_once
async def test_get_controller(hass):
"""Successful call."""
with patch("aiounifi.Controller.login", return_value=Mock()):
assert await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
async def test_get_controller_verify_ssl_false(hass):
"""Successful call with verify ssl set to false."""
controller_data = dict(CONTROLLER_DATA)
controller_data[CONF_VERIFY_SSL] = False
with patch("aiounifi.Controller.login", return_value=Mock()):
assert await unifi.controller.get_controller(hass, **controller_data)
async def test_get_controller_login_failed(hass):
"""Check that get_controller can handle a failed login."""
with patch(
"aiounifi.Controller.login", side_effect=aiounifi.Unauthorized
), pytest.raises(unifi.errors.AuthenticationRequired):
await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
async def test_get_controller_controller_unavailable(hass):
"""Check that get_controller can handle controller being unavailable."""
with patch(
"aiounifi.Controller.login", side_effect=aiounifi.RequestError
), pytest.raises(unifi.errors.CannotConnect):
await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
async def test_get_controller_unknown_error(hass):
"""Check that get_controller can handle unknown errors."""
with patch(
"aiounifi.Controller.login", side_effect=aiounifi.AiounifiException
), pytest.raises(unifi.errors.AuthenticationRequired):
await unifi.controller.get_controller(hass, **CONTROLLER_DATA)