Improve tests of Fritz!Tools (part1) (#66972)

This commit is contained in:
Michael 2022-02-23 01:35:22 +01:00 committed by GitHub
parent 1658d530e1
commit b8590fde40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1077 additions and 109 deletions

View File

@ -394,13 +394,10 @@ omit =
homeassistant/components/freebox/router.py homeassistant/components/freebox/router.py
homeassistant/components/freebox/sensor.py homeassistant/components/freebox/sensor.py
homeassistant/components/freebox/switch.py homeassistant/components/freebox/switch.py
homeassistant/components/fritz/__init__.py
homeassistant/components/fritz/binary_sensor.py homeassistant/components/fritz/binary_sensor.py
homeassistant/components/fritz/button.py
homeassistant/components/fritz/common.py homeassistant/components/fritz/common.py
homeassistant/components/fritz/const.py homeassistant/components/fritz/const.py
homeassistant/components/fritz/device_tracker.py homeassistant/components/fritz/device_tracker.py
homeassistant/components/fritz/diagnostics.py
homeassistant/components/fritz/sensor.py homeassistant/components/fritz/sensor.py
homeassistant/components/fritz/services.py homeassistant/components/fritz/services.py
homeassistant/components/fritz/switch.py homeassistant/components/fritz/switch.py

View File

@ -1,115 +1,86 @@
"""Common stuff for AVM Fritz!Box tests.""" """Common stuff for AVM Fritz!Box tests."""
from unittest import mock import logging
from unittest.mock import patch from unittest.mock import patch
from fritzconnection.core.processor import Service
from fritzconnection.lib.fritzhosts import FritzHosts
import pytest import pytest
from .const import MOCK_FB_SERVICES, MOCK_MESH_DATA, MOCK_MODELNAME
@pytest.fixture() LOGGER = logging.getLogger(__name__)
def fc_class_mock():
"""Fixture that sets up a mocked FritzConnection class."""
with patch("fritzconnection.FritzConnection", autospec=True) as result: class FritzServiceMock(Service):
result.return_value = FritzConnectionMock() """Service mocking."""
yield result
def __init__(self, serviceId: str, actions: dict) -> None:
"""Init Service mock."""
super().__init__()
self._actions = actions
self.serviceId = serviceId
class FritzConnectionMock: # pylint: disable=too-few-public-methods class FritzConnectionMock: # pylint: disable=too-few-public-methods
"""FritzConnection mocking.""" """FritzConnection mocking."""
FRITZBOX_DATA = { def __init__(self, services):
("WANIPConn:1", "GetStatusInfo"): {
"NewConnectionStatus": "Connected",
"NewUptime": 35307,
},
("WANIPConnection:1", "GetStatusInfo"): {},
("WANCommonIFC:1", "GetCommonLinkProperties"): {
"NewLayer1DownstreamMaxBitRate": 10087000,
"NewLayer1UpstreamMaxBitRate": 2105000,
"NewPhysicalLinkStatus": "Up",
},
("WANCommonIFC:1", "GetAddonInfos"): {
"NewByteSendRate": 3438,
"NewByteReceiveRate": 67649,
"NewTotalBytesSent": 1712232562,
"NewTotalBytesReceived": 5221019883,
},
("LANEthernetInterfaceConfig:1", "GetStatistics"): {
"NewBytesSent": 23004321,
"NewBytesReceived": 12045,
},
("DeviceInfo:1", "GetInfo"): {
"NewSerialNumber": "abcdefgh",
"NewName": "TheName",
"NewModelName": "FRITZ!Box 7490",
},
}
FRITZBOX_DATA_INDEXED = {
("X_AVM-DE_Homeauto:1", "GetGenericDeviceInfos"): [
{
"NewSwitchIsValid": "VALID",
"NewMultimeterIsValid": "VALID",
"NewTemperatureIsValid": "VALID",
"NewDeviceId": 16,
"NewAIN": "08761 0114116",
"NewDeviceName": "FRITZ!DECT 200 #1",
"NewTemperatureOffset": "0",
"NewSwitchLock": "0",
"NewProductName": "FRITZ!DECT 200",
"NewPresent": "CONNECTED",
"NewMultimeterPower": 1673,
"NewHkrComfortTemperature": "0",
"NewSwitchMode": "AUTO",
"NewManufacturer": "AVM",
"NewMultimeterIsEnabled": "ENABLED",
"NewHkrIsTemperature": "0",
"NewFunctionBitMask": 2944,
"NewTemperatureIsEnabled": "ENABLED",
"NewSwitchState": "ON",
"NewSwitchIsEnabled": "ENABLED",
"NewFirmwareVersion": "03.87",
"NewHkrSetVentilStatus": "CLOSED",
"NewMultimeterEnergy": 5182,
"NewHkrComfortVentilStatus": "CLOSED",
"NewHkrReduceTemperature": "0",
"NewHkrReduceVentilStatus": "CLOSED",
"NewHkrIsEnabled": "DISABLED",
"NewHkrSetTemperature": "0",
"NewTemperatureCelsius": "225",
"NewHkrIsValid": "INVALID",
},
{},
],
("Hosts1", "GetGenericHostEntry"): [
{
"NewSerialNumber": 1234,
"NewName": "TheName",
"NewModelName": "FRITZ!Box 7490",
},
{},
],
}
MODELNAME = "FRITZ!Box 7490"
def __init__(self):
"""Inint Mocking class.""" """Inint Mocking class."""
self.modelname = self.MODELNAME self.modelname = MOCK_MODELNAME
self.call_action = mock.Mock(side_effect=self._side_effect_call_action) self.call_action = self._call_action
type(self).action_names = mock.PropertyMock( self._services = services
side_effect=self._side_effect_action_names
)
self.services = { self.services = {
srv: None srv: FritzServiceMock(serviceId=srv, actions=actions)
for srv, _ in list(self.FRITZBOX_DATA) + list(self.FRITZBOX_DATA_INDEXED) for srv, actions in services.items()
} }
LOGGER.debug("-" * 80)
LOGGER.debug("FritzConnectionMock - services: %s", self.services)
def _call_action(self, service: str, action: str, **kwargs):
LOGGER.debug(
"_call_action service: %s, action: %s, **kwargs: %s",
service,
action,
{**kwargs},
)
if ":" in service:
service, number = service.split(":", 1)
service = service + number
elif not service[-1].isnumeric():
service = service + "1"
def _side_effect_call_action(self, service, action, **kwargs):
if kwargs: if kwargs:
index = next(iter(kwargs.values()))
return self.FRITZBOX_DATA_INDEXED[(service, action)][index]
return self.FRITZBOX_DATA[(service, action)] if (index := kwargs.get("NewIndex")) is None:
index = next(iter(kwargs.values()))
def _side_effect_action_names(self): return self._services[service][action][index]
return list(self.FRITZBOX_DATA) + list(self.FRITZBOX_DATA_INDEXED) return self._services[service][action]
class FritzHostMock(FritzHosts):
"""FritzHosts mocking."""
def get_mesh_topology(self, raw=False):
"""Retrurn mocked mesh data."""
return MOCK_MESH_DATA
@pytest.fixture()
def fc_class_mock():
"""Fixture that sets up a mocked FritzConnection class."""
with patch(
"homeassistant.components.fritz.common.FritzConnection", autospec=True
) as result:
result.return_value = FritzConnectionMock(MOCK_FB_SERVICES)
yield result
@pytest.fixture()
def fh_class_mock():
"""Fixture that sets up a mocked FritzHosts class."""
with patch(
"homeassistant.components.fritz.common.FritzHosts",
new=FritzHostMock,
) as result:
yield result

View File

@ -26,9 +26,758 @@ MOCK_CONFIG = {
} }
} }
MOCK_HOST = "fake_host" MOCK_HOST = "fake_host"
MOCK_IP = "192.168.178.1" MOCK_IPS = {"fritz.box": "192.168.178.1", "printer": "192.168.178.2"}
MOCK_MODELNAME = "FRITZ!Box 7530 AX"
MOCK_FIRMWARE = "256.07.29"
MOCK_SERIAL_NUMBER = "fake_serial_number" MOCK_SERIAL_NUMBER = "fake_serial_number"
MOCK_FIRMWARE_INFO = [True, "1.1.1"] MOCK_FIRMWARE_INFO = [True, "1.1.1"]
MOCK_MESH_SSID = "TestSSID"
MOCK_MESH_MASTER_MAC = "1C:ED:6F:12:34:11"
MOCK_MESH_MASTER_WIFI1_MAC = "1C:ED:6F:12:34:12"
MOCK_MESH_SLAVE_MAC = "1C:ED:6F:12:34:21"
MOCK_MESH_SLAVE_WIFI1_MAC = "1C:ED:6F:12:34:22"
MOCK_FB_SERVICES: dict[str, dict] = {
"DeviceInfo1": {
"GetInfo": {
"NewSerialNumber": MOCK_MESH_MASTER_MAC,
"NewName": "TheName",
"NewModelName": MOCK_MODELNAME,
"NewSoftwareVersion": MOCK_FIRMWARE,
"NewUpTime": 2518179,
},
},
"Hosts1": {
"GetGenericHostEntry": [
{
"NewIPAddress": MOCK_IPS["fritz.box"],
"NewAddressSource": "Static",
"NewLeaseTimeRemaining": 0,
"NewMACAddress": MOCK_MESH_MASTER_MAC,
"NewInterfaceType": "",
"NewActive": True,
"NewHostName": "fritz.box",
},
{
"NewIPAddress": MOCK_IPS["printer"],
"NewAddressSource": "DHCP",
"NewLeaseTimeRemaining": 0,
"NewMACAddress": "AA:BB:CC:00:11:22",
"NewInterfaceType": "Ethernet",
"NewActive": True,
"NewHostName": "printer",
},
],
"X_AVM-DE_GetMeshListPath": {},
},
"LANEthernetInterfaceConfig1": {
"GetStatistics": {
"NewBytesSent": 23004321,
"NewBytesReceived": 12045,
},
},
"Layer3Forwarding1": {
"GetDefaultConnectionService": {
"NewDefaultConnectionService": "1.WANPPPConnection.1"
}
},
"UserInterface1": {
"GetInfo": {},
},
"WANCommonIFC1": {
"GetCommonLinkProperties": {
"NewLayer1DownstreamMaxBitRate": 10087000,
"NewLayer1UpstreamMaxBitRate": 2105000,
"NewPhysicalLinkStatus": "Up",
},
"GetAddonInfos": {
"NewByteSendRate": 3438,
"NewByteReceiveRate": 67649,
"NewTotalBytesSent": 1712232562,
"NewTotalBytesReceived": 5221019883,
"NewX_AVM_DE_TotalBytesSent64": 1712232562,
"NeWX_AVM_DE_TotalBytesReceived64": 5221019883,
},
"GetTotalBytesSent": {"NewTotalBytesSent": 1712232562},
"GetTotalBytesReceived": {"NewTotalBytesReceived": 5221019883},
},
"WANCommonInterfaceConfig1": {
"GetCommonLinkProperties": {
"NewWANAccessType": "DSL",
"NewLayer1UpstreamMaxBitRate": 51805000,
"NewLayer1DownstreamMaxBitRate": 318557000,
"NewPhysicalLinkStatus": "Up",
}
},
"WANDSLInterfaceConfig1": {
"GetInfo": {
"NewEnable": True,
"NewStatus": "Up",
"NewDataPath": "Interleaved",
"NewUpstreamCurrRate": 46720,
"NewDownstreamCurrRate": 292030,
"NewUpstreamMaxRate": 51348,
"NewDownstreamMaxRate": 315978,
"NewUpstreamNoiseMargin": 90,
"NewDownstreamNoiseMargin": 80,
"NewUpstreamAttenuation": 70,
"NewDownstreamAttenuation": 120,
"NewATURVendor": "41564d00",
"NewATURCountry": "0400",
"NewUpstreamPower": 500,
"NewDownstreamPower": 500,
}
},
"WANIPConn1": {
"GetStatusInfo": {
"NewConnectionStatus": "Connected",
"NewUptime": 35307,
},
"GetExternalIPAddress": {"NewExternalIPAddress": "1.2.3.4"},
},
"WANPPPConnection1": {
"GetInfo": {
"NewEnable": True,
"NewConnectionStatus": "Connected",
"NewUptime": 57199,
"NewUpstreamMaxBitRate": 46531924,
"NewDownstreamMaxBitRate": 43430530,
"NewExternalIPAddress": "1.2.3.4",
},
"GetPortMappingNumberOfEntries": {},
},
"X_AVM-DE_Homeauto1": {
"GetGenericDeviceInfos": [
{
"NewSwitchIsValid": "VALID",
"NewMultimeterIsValid": "VALID",
"NewTemperatureIsValid": "VALID",
"NewDeviceId": 16,
"NewAIN": "08761 0114116",
"NewDeviceName": "FRITZ!DECT 200 #1",
"NewTemperatureOffset": "0",
"NewSwitchLock": "0",
"NewProductName": "FRITZ!DECT 200",
"NewPresent": "CONNECTED",
"NewMultimeterPower": 1673,
"NewHkrComfortTemperature": "0",
"NewSwitchMode": "AUTO",
"NewManufacturer": "AVM",
"NewMultimeterIsEnabled": "ENABLED",
"NewHkrIsTemperature": "0",
"NewFunctionBitMask": 2944,
"NewTemperatureIsEnabled": "ENABLED",
"NewSwitchState": "ON",
"NewSwitchIsEnabled": "ENABLED",
"NewFirmwareVersion": "03.87",
"NewHkrSetVentilStatus": "CLOSED",
"NewMultimeterEnergy": 5182,
"NewHkrComfortVentilStatus": "CLOSED",
"NewHkrReduceTemperature": "0",
"NewHkrReduceVentilStatus": "CLOSED",
"NewHkrIsEnabled": "DISABLED",
"NewHkrSetTemperature": "0",
"NewTemperatureCelsius": "225",
"NewHkrIsValid": "INVALID",
},
{},
],
},
"X_AVM-DE_HostFilter1": {
"GetWANAccessByIP": {
MOCK_IPS["printer"]: {"NewDisallow": False, "NewWANAccess": "granted"}
}
},
}
MOCK_MESH_DATA = {
"schema_version": "1.9",
"nodes": [
{
"uid": "n-1",
"device_name": "fritz.box",
"device_model": "FRITZ!Box 7530 AX",
"device_manufacturer": "AVM",
"device_firmware_version": "256.07.29",
"device_mac_address": MOCK_MESH_MASTER_MAC,
"is_meshed": True,
"mesh_role": "master",
"meshd_version": "3.13",
"node_interfaces": [
{
"uid": "ni-5",
"name": "LANBridge",
"type": "LAN",
"mac_address": MOCK_MESH_MASTER_MAC,
"blocking_state": "NOT_BLOCKED",
"node_links": [],
},
{
"uid": "ni-30",
"name": "LAN:2",
"type": "LAN",
"mac_address": MOCK_MESH_MASTER_MAC,
"blocking_state": "NOT_BLOCKED",
"node_links": [],
},
{
"uid": "ni-32",
"name": "LAN:3",
"type": "LAN",
"mac_address": MOCK_MESH_MASTER_MAC,
"blocking_state": "NOT_BLOCKED",
"node_links": [],
},
{
"uid": "ni-31",
"name": "LAN:1",
"type": "LAN",
"mac_address": MOCK_MESH_MASTER_MAC,
"blocking_state": "NOT_BLOCKED",
"node_links": [
{
"uid": "nl-78",
"type": "LAN",
"state": "CONNECTED",
"last_connected": 1642872967,
"node_1_uid": "n-1",
"node_2_uid": "n-76",
"node_interface_1_uid": "ni-31",
"node_interface_2_uid": "ni-77",
"max_data_rate_rx": 1000000,
"max_data_rate_tx": 1000000,
"cur_data_rate_rx": 0,
"cur_data_rate_tx": 0,
"cur_availability_rx": 99,
"cur_availability_tx": 99,
}
],
},
{
"uid": "ni-33",
"name": "LAN:4",
"type": "LAN",
"mac_address": MOCK_MESH_MASTER_MAC,
"blocking_state": "NOT_BLOCKED",
"node_links": [],
},
{
"uid": "ni-230",
"name": "AP:2G:0",
"type": "WLAN",
"mac_address": MOCK_MESH_MASTER_WIFI1_MAC,
"blocking_state": "UNKNOWN",
"node_links": [
{
"uid": "nl-219",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618820,
"node_1_uid": "n-1",
"node_2_uid": "n-89",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-90",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 54000,
"cur_data_rate_tx": 65000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 51,
"tx_rsni": 255,
"rx_rcpi": -38,
"tx_rcpi": 255,
},
{
"uid": "nl-168",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1645162418,
"node_1_uid": "n-1",
"node_2_uid": "n-118",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-119",
"max_data_rate_rx": 144400,
"max_data_rate_tx": 144400,
"cur_data_rate_rx": 144400,
"cur_data_rate_tx": 130000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 37,
"tx_rsni": 255,
"rx_rcpi": -52,
"tx_rcpi": 255,
},
{
"uid": "nl-185",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1645273363,
"node_1_uid": "n-1",
"node_2_uid": "n-100",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-99",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 1000,
"cur_data_rate_tx": 1000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 35,
"tx_rsni": 255,
"rx_rcpi": -54,
"tx_rcpi": 255,
},
{
"uid": "nl-166",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618912,
"node_1_uid": "n-1",
"node_2_uid": "n-16",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-15",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 54000,
"cur_data_rate_tx": 65000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 41,
"tx_rsni": 255,
"rx_rcpi": -48,
"tx_rcpi": 255,
},
{
"uid": "nl-239",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618828,
"node_1_uid": "n-1",
"node_2_uid": "n-59",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-58",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 54000,
"cur_data_rate_tx": 65000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 43,
"tx_rsni": 255,
"rx_rcpi": -46,
"tx_rcpi": 255,
},
{
"uid": "nl-173",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1645331764,
"node_1_uid": "n-1",
"node_2_uid": "n-137",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-138",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 72200,
"cur_data_rate_tx": 65000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 38,
"tx_rsni": 255,
"rx_rcpi": -51,
"tx_rcpi": 255,
},
{
"uid": "nl-217",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618833,
"node_1_uid": "n-1",
"node_2_uid": "n-128",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-127",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 54000,
"cur_data_rate_tx": 72200,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 41,
"tx_rsni": 255,
"rx_rcpi": -48,
"tx_rcpi": 255,
},
{
"uid": "nl-198",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618820,
"node_1_uid": "n-1",
"node_2_uid": "n-105",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-106",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 48000,
"cur_data_rate_tx": 58500,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 28,
"tx_rsni": 255,
"rx_rcpi": -61,
"tx_rcpi": 255,
},
{
"uid": "nl-213",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618820,
"node_1_uid": "n-1",
"node_2_uid": "n-111",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-112",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 48000,
"cur_data_rate_tx": 1000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 44,
"tx_rsni": 255,
"rx_rcpi": -45,
"tx_rcpi": 255,
},
{
"uid": "nl-224",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618831,
"node_1_uid": "n-1",
"node_2_uid": "n-197",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-196",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 48000,
"cur_data_rate_tx": 1000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 51,
"tx_rsni": 255,
"rx_rcpi": -38,
"tx_rcpi": 255,
},
{
"uid": "nl-182",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618822,
"node_1_uid": "n-1",
"node_2_uid": "n-56",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-55",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 54000,
"cur_data_rate_tx": 72200,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 34,
"tx_rsni": 255,
"rx_rcpi": -55,
"tx_rcpi": 255,
},
{
"uid": "nl-205",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618820,
"node_1_uid": "n-1",
"node_2_uid": "n-109",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-108",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 54000,
"cur_data_rate_tx": 1000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 43,
"tx_rsni": 255,
"rx_rcpi": -46,
"tx_rcpi": 255,
},
{
"uid": "nl-240",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618827,
"node_1_uid": "n-1",
"node_2_uid": "n-95",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-96",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 48000,
"cur_data_rate_tx": 58500,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 25,
"tx_rsni": 255,
"rx_rcpi": -64,
"tx_rcpi": 255,
},
{
"uid": "nl-146",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1642872967,
"node_1_uid": "n-1",
"node_2_uid": "n-167",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-134",
"max_data_rate_rx": 144400,
"max_data_rate_tx": 144400,
"cur_data_rate_rx": 144400,
"cur_data_rate_tx": 130000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 48,
"tx_rsni": 255,
"rx_rcpi": -41,
"tx_rcpi": 255,
},
{
"uid": "nl-232",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1644618829,
"node_1_uid": "n-1",
"node_2_uid": "n-18",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-17",
"max_data_rate_rx": 72200,
"max_data_rate_tx": 72200,
"cur_data_rate_rx": 48000,
"cur_data_rate_tx": 21700,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 22,
"tx_rsni": 255,
"rx_rcpi": -67,
"tx_rcpi": 255,
},
],
"ssid": MOCK_MESH_SSID,
"opmode": "AP",
"security": "WPA2_WPA3_MIXED",
"supported_streams_tx": [
["20 MHz", 2],
["40 MHz", 0],
["80 MHz", 0],
["160 MHz", 0],
["80+80 MHz", 0],
],
"supported_streams_rx": [
["20 MHz", 2],
["40 MHz", 0],
["80 MHz", 0],
["160 MHz", 0],
["80+80 MHz", 0],
],
"current_channel": 13,
"phymodes": ["g", "n", "ax"],
"channel_utilization": 0,
"anpi": -91,
"steering_enabled": True,
"11k_friendly": True,
"11v_friendly": True,
"legacy_friendly": True,
"rrm_compliant": False,
"channel_list": [
{"channel": 1},
{"channel": 2},
{"channel": 3},
{"channel": 4},
{"channel": 5},
{"channel": 6},
{"channel": 7},
{"channel": 8},
{"channel": 9},
{"channel": 10},
{"channel": 11},
{"channel": 12},
{"channel": 13},
],
},
],
},
{
"uid": "n-76",
"device_name": "printer",
"device_model": "",
"device_manufacturer": "",
"device_firmware_version": "",
"device_mac_address": "AA:BB:CC:00:11:22",
"is_meshed": False,
"mesh_role": "unknown",
"meshd_version": "0.0",
"node_interfaces": [
{
"uid": "ni-77",
"name": "eth0",
"type": "LAN",
"mac_address": "AA:BB:CC:00:11:22",
"blocking_state": "UNKNOWN",
"node_links": [
{
"uid": "nl-78",
"type": "LAN",
"state": "CONNECTED",
"last_connected": 1642872967,
"node_1_uid": "n-1",
"node_2_uid": "n-76",
"node_interface_1_uid": "ni-31",
"node_interface_2_uid": "ni-77",
"max_data_rate_rx": 1000000,
"max_data_rate_tx": 1000000,
"cur_data_rate_rx": 0,
"cur_data_rate_tx": 0,
"cur_availability_rx": 99,
"cur_availability_tx": 99,
}
],
}
],
},
{
"uid": "n-167",
"device_name": "fritz-repeater",
"device_model": "FRITZ!Box 7490",
"device_manufacturer": "AVM",
"device_firmware_version": "113.07.29",
"device_mac_address": MOCK_MESH_SLAVE_MAC,
"is_meshed": True,
"mesh_role": "slave",
"meshd_version": "3.13",
"node_interfaces": [
{
"uid": "ni-140",
"name": "LAN:3",
"type": "LAN",
"mac_address": MOCK_MESH_SLAVE_MAC,
"blocking_state": "UNKNOWN",
"node_links": [],
},
{
"uid": "ni-139",
"name": "LAN:4",
"type": "LAN",
"mac_address": MOCK_MESH_SLAVE_MAC,
"blocking_state": "UNKNOWN",
"node_links": [],
},
{
"uid": "ni-141",
"name": "LAN:2",
"type": "LAN",
"mac_address": MOCK_MESH_SLAVE_MAC,
"blocking_state": "UNKNOWN",
"node_links": [],
},
{
"uid": "ni-134",
"name": "UPLINK:2G:0",
"type": "WLAN",
"mac_address": MOCK_MESH_SLAVE_WIFI1_MAC,
"blocking_state": "UNKNOWN",
"node_links": [
{
"uid": "nl-146",
"type": "WLAN",
"state": "CONNECTED",
"last_connected": 1642872967,
"node_1_uid": "n-1",
"node_2_uid": "n-167",
"node_interface_1_uid": "ni-230",
"node_interface_2_uid": "ni-134",
"max_data_rate_rx": 144400,
"max_data_rate_tx": 144400,
"cur_data_rate_rx": 144400,
"cur_data_rate_tx": 130000,
"cur_availability_rx": 100,
"cur_availability_tx": 100,
"rx_rsni": 48,
"tx_rsni": 255,
"rx_rcpi": -41,
"tx_rcpi": 255,
}
],
"ssid": "",
"opmode": "WDS_REPEATER",
"security": "WPA3PSK",
"supported_streams_tx": [
["20 MHz", 3],
["40 MHz", 3],
["80 MHz", 0],
["160 MHz", 0],
["80+80 MHz", 0],
],
"supported_streams_rx": [
["20 MHz", 3],
["40 MHz", 3],
["80 MHz", 0],
["160 MHz", 0],
["80+80 MHz", 0],
],
"current_channel": 13,
"phymodes": ["b", "g", "n"],
"channel_utilization": 0,
"anpi": 255,
"steering_enabled": True,
"11k_friendly": False,
"11v_friendly": True,
"legacy_friendly": True,
"rrm_compliant": False,
"channel_list": [
{"channel": 1},
{"channel": 2},
{"channel": 3},
{"channel": 4},
{"channel": 5},
{"channel": 6},
{"channel": 7},
{"channel": 8},
{"channel": 9},
{"channel": 10},
{"channel": 11},
{"channel": 12},
{"channel": 13},
],
"client_position": "unknown",
},
{
"uid": "ni-143",
"name": "LANBridge",
"type": "LAN",
"mac_address": MOCK_MESH_SLAVE_MAC,
"blocking_state": "UNKNOWN",
"node_links": [],
},
{
"uid": "ni-142",
"name": "LAN:1",
"type": "LAN",
"mac_address": MOCK_MESH_SLAVE_MAC,
"blocking_state": "UNKNOWN",
"node_links": [],
},
],
},
],
}
MOCK_USER_DATA = MOCK_CONFIG[DOMAIN][CONF_DEVICES][0] MOCK_USER_DATA = MOCK_CONFIG[DOMAIN][CONF_DEVICES][0]
MOCK_DEVICE_INFO = { MOCK_DEVICE_INFO = {
@ -38,7 +787,7 @@ MOCK_DEVICE_INFO = {
MOCK_SSDP_DATA = ssdp.SsdpServiceInfo( MOCK_SSDP_DATA = ssdp.SsdpServiceInfo(
ssdp_usn="mock_usn", ssdp_usn="mock_usn",
ssdp_st="mock_st", ssdp_st="mock_st",
ssdp_location=f"https://{MOCK_IP}:12345/test", ssdp_location=f"https://{MOCK_IPS['fritz.box']}:12345/test",
upnp={ upnp={
ATTR_UPNP_FRIENDLY_NAME: "fake_name", ATTR_UPNP_FRIENDLY_NAME: "fake_name",
ATTR_UPNP_UDN: "uuid:only-a-test", ATTR_UPNP_UDN: "uuid:only-a-test",

View File

@ -0,0 +1,75 @@
"""Tests for Shelly button platform."""
from unittest.mock import patch
import pytest
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.components.fritz.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .const import MOCK_USER_DATA
from tests.common import MockConfigEntry
async def test_button_setup(hass: HomeAssistant, fc_class_mock, fh_class_mock):
"""Test setup of Fritz!Tools buttons."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.LOADED
buttons = hass.states.async_all(BUTTON_DOMAIN)
assert len(buttons) == 4
for button in buttons:
assert button.state == STATE_UNKNOWN
@pytest.mark.parametrize(
"entity_id, wrapper_method",
[
("button.mock_title_firmware_update", "async_trigger_firmware_update"),
("button.mock_title_reboot", "async_trigger_reboot"),
("button.mock_title_reconnect", "async_trigger_reconnect"),
("button.mock_title_cleanup", "async_trigger_cleanup"),
],
)
async def test_buttons(
hass: HomeAssistant,
entity_id: str,
wrapper_method: str,
fc_class_mock,
fh_class_mock,
):
"""Test Fritz!Tools buttons."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.LOADED
button = hass.states.get(entity_id)
assert button
assert button.state == STATE_UNKNOWN
with patch(
f"homeassistant.components.fritz.common.AvmWrapper.{wrapper_method}"
) as mock_press_action:
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)
await hass.async_block_till_done()
mock_press_action.assert_called_once()
button = hass.states.get(entity_id)
assert button.state != STATE_UNKNOWN

View File

@ -26,7 +26,7 @@ from homeassistant.data_entry_flow import (
from .const import ( from .const import (
MOCK_FIRMWARE_INFO, MOCK_FIRMWARE_INFO,
MOCK_IP, MOCK_IPS,
MOCK_REQUEST, MOCK_REQUEST,
MOCK_SSDP_DATA, MOCK_SSDP_DATA,
MOCK_USER_DATA, MOCK_USER_DATA,
@ -51,7 +51,7 @@ async def test_user(hass: HomeAssistant, fc_class_mock, mock_get_source_ip):
"requests.post" "requests.post"
) as mock_request_post, patch( ) as mock_request_post, patch(
"homeassistant.components.fritz.config_flow.socket.gethostbyname", "homeassistant.components.fritz.config_flow.socket.gethostbyname",
return_value=MOCK_IP, return_value=MOCK_IPS["fritz.box"],
): ):
mock_request_get.return_value.status_code = 200 mock_request_get.return_value.status_code = 200
@ -102,7 +102,7 @@ async def test_user_already_configured(
"requests.post" "requests.post"
) as mock_request_post, patch( ) as mock_request_post, patch(
"homeassistant.components.fritz.config_flow.socket.gethostbyname", "homeassistant.components.fritz.config_flow.socket.gethostbyname",
return_value=MOCK_IP, return_value=MOCK_IPS["fritz.box"],
): ):
mock_request_get.return_value.status_code = 200 mock_request_get.return_value.status_code = 200
@ -295,7 +295,7 @@ async def test_ssdp_already_configured(
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch("homeassistant.components.fritz.common.FritzStatus"), patch(
"homeassistant.components.fritz.config_flow.socket.gethostbyname", "homeassistant.components.fritz.config_flow.socket.gethostbyname",
return_value=MOCK_IP, return_value=MOCK_IPS["fritz.box"],
): ):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -322,7 +322,7 @@ async def test_ssdp_already_configured_host(
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch("homeassistant.components.fritz.common.FritzStatus"), patch(
"homeassistant.components.fritz.config_flow.socket.gethostbyname", "homeassistant.components.fritz.config_flow.socket.gethostbyname",
return_value=MOCK_IP, return_value=MOCK_IPS["fritz.box"],
): ):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -349,7 +349,7 @@ async def test_ssdp_already_configured_host_uuid(
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch("homeassistant.components.fritz.common.FritzStatus"), patch(
"homeassistant.components.fritz.config_flow.socket.gethostbyname", "homeassistant.components.fritz.config_flow.socket.gethostbyname",
return_value=MOCK_IP, return_value=MOCK_IPS["fritz.box"],
): ):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
@ -420,7 +420,7 @@ async def test_ssdp(hass: HomeAssistant, fc_class_mock, mock_get_source_ip):
) )
assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert result["type"] == RESULT_TYPE_CREATE_ENTRY
assert result["data"][CONF_HOST] == MOCK_IP assert result["data"][CONF_HOST] == MOCK_IPS["fritz.box"]
assert result["data"][CONF_PASSWORD] == "fake_pass" assert result["data"][CONF_PASSWORD] == "fake_pass"
assert result["data"][CONF_USERNAME] == "fake_user" assert result["data"][CONF_USERNAME] == "fake_user"

View File

@ -0,0 +1,80 @@
"""Tests for the AVM Fritz!Box integration."""
from __future__ import annotations
from aiohttp import ClientSession
from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.fritz.common import AvmWrapper
from homeassistant.components.fritz.const import DOMAIN
from homeassistant.components.fritz.diagnostics import TO_REDACT
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .const import MOCK_USER_DATA
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
async def test_entry_diagnostics(
hass: HomeAssistant, hass_client: ClientSession, fc_class_mock, fh_class_mock
):
"""Test config entry diagnostics."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.LOADED
entry_dict = entry.as_dict()
for key in TO_REDACT:
entry_dict["data"][key] = REDACTED
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
avm_wrapper: AvmWrapper = hass.data[DOMAIN][entry.entry_id]
assert result == {
"entry": entry_dict,
"device_info": {
"client_devices": [
{
"connected_to": device.connected_to,
"connection_type": device.connection_type,
"hostname": device.hostname,
"is_connected": device.is_connected,
"last_activity": device.last_activity.isoformat(),
"wan_access": device.wan_access,
}
for _, device in avm_wrapper.devices.items()
],
"connection_type": "WANPPPConnection",
"current_firmware": "256.07.29",
"discovered_services": [
"DeviceInfo1",
"Hosts1",
"LANEthernetInterfaceConfig1",
"Layer3Forwarding1",
"UserInterface1",
"WANCommonIFC1",
"WANCommonInterfaceConfig1",
"WANDSLInterfaceConfig1",
"WANIPConn1",
"WANPPPConnection1",
"X_AVM-DE_Homeauto1",
"X_AVM-DE_HostFilter1",
],
"is_router": True,
"last_exception": None,
"last_update success": True,
"latest_firmware": None,
"mesh_role": "master",
"model": "FRITZ!Box 7530 AX",
"update_available": False,
"wan_link_properties": {
"NewLayer1DownstreamMaxBitRate": 318557000,
"NewLayer1UpstreamMaxBitRate": 51805000,
"NewPhysicalLinkStatus": "Up",
"NewWANAccessType": "DSL",
},
},
}

View File

@ -0,0 +1,96 @@
"""Tests for AVM Fritz!Box."""
from unittest.mock import patch
from fritzconnection.core.exceptions import FritzSecurityError
import pytest
from homeassistant.components.device_tracker.const import (
CONF_CONSIDER_HOME,
DEFAULT_CONSIDER_HOME,
)
from homeassistant.components.fritz.const import DOMAIN, FRITZ_EXCEPTIONS
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .const import MOCK_USER_DATA
from tests.common import MockConfigEntry
async def test_setup(hass: HomeAssistant, fc_class_mock, fh_class_mock):
"""Test setup and unload of Fritz!Tools."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.LOADED
await hass.config_entries.async_unload(entry.entry_id)
assert entry.state == ConfigEntryState.NOT_LOADED
async def test_options_reload(hass: HomeAssistant, fc_class_mock, fh_class_mock):
"""Test reload of Fritz!Tools, when options changed."""
entry = MockConfigEntry(
domain=DOMAIN,
data=MOCK_USER_DATA,
options={CONF_CONSIDER_HOME: DEFAULT_CONSIDER_HOME.total_seconds()},
)
entry.add_to_hass(hass)
with patch(
"homeassistant.config_entries.ConfigEntries.async_reload",
return_value=None,
) as mock_reload:
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.LOADED
result = await hass.config_entries.options.async_init(entry.entry_id)
await hass.async_block_till_done()
await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={CONF_CONSIDER_HOME: 60},
)
await hass.async_block_till_done()
mock_reload.assert_called_once()
async def test_setup_auth_fail(hass: HomeAssistant):
"""Test starting a flow by user with an already configured device."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.fritz.common.FritzConnection",
side_effect=FritzSecurityError,
):
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.SETUP_ERROR
@pytest.mark.parametrize(
"error",
FRITZ_EXCEPTIONS,
)
async def test_setup_fail(hass: HomeAssistant, error):
"""Test starting a flow by user with an already configured device."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.fritz.common.FritzConnection",
side_effect=error,
):
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.SETUP_RETRY