core/tests/components/fritz/test_button.py

255 lines
8.2 KiB
Python

"""Tests for Fritz!Tools button platform."""
from copy import deepcopy
from datetime import timedelta
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.components.fritz.const import DOMAIN, MeshRoles
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.util.dt import utcnow
from .const import (
MOCK_HOST_ATTRIBUTES_DATA,
MOCK_MESH_DATA,
MOCK_NEW_DEVICE_NODE,
MOCK_USER_DATA,
)
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_button_setup(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
fc_class_mock,
fh_class_mock,
snapshot: SnapshotAssertion,
) -> None:
"""Test setup of Fritz!Tools buttons."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
with patch("homeassistant.components.fritz.PLATFORMS", [Platform.BUTTON]):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
@pytest.mark.parametrize(
("entity_id", "wrapper_method"),
[
("button.mock_title_firmware_update", "async_trigger_firmware_update"),
("button.mock_title_restart", "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,
) -> None:
"""Test Fritz!Tools buttons."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
button = hass.states.get(entity_id)
assert button
assert button.state == STATE_UNKNOWN
with patch(
f"homeassistant.components.fritz.coordinator.AvmWrapper.{wrapper_method}"
) as mock_press_action:
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)
mock_press_action.assert_called_once()
button = hass.states.get(entity_id)
assert button.state != STATE_UNKNOWN
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_wol_button(
hass: HomeAssistant,
fc_class_mock,
fh_class_mock,
) -> None:
"""Test Fritz!Tools wake on LAN button."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
button = hass.states.get("button.printer_wake_on_lan")
assert button
assert button.state == STATE_UNKNOWN
with patch(
"homeassistant.components.fritz.coordinator.AvmWrapper.async_wake_on_lan"
) as mock_press_action:
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: "button.printer_wake_on_lan"},
blocking=True,
)
mock_press_action.assert_called_once_with("AA:BB:CC:00:11:22")
button = hass.states.get("button.printer_wake_on_lan")
assert button.state != STATE_UNKNOWN
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_wol_button_new_device(
hass: HomeAssistant,
fc_class_mock,
fh_class_mock,
) -> None:
"""Test WoL button is created for new device at runtime."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
mesh_data = deepcopy(MOCK_MESH_DATA)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
assert hass.states.get("button.printer_wake_on_lan")
assert not hass.states.get("button.server_wake_on_lan")
mesh_data["nodes"].append(MOCK_NEW_DEVICE_NODE)
fh_class_mock.get_mesh_topology.return_value = mesh_data
async_fire_time_changed(hass, utcnow() + timedelta(seconds=60))
await hass.async_block_till_done(wait_background_tasks=True)
assert hass.states.get("button.printer_wake_on_lan")
assert hass.states.get("button.server_wake_on_lan")
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_wol_button_absent_for_mesh_slave(
hass: HomeAssistant,
fc_class_mock,
fh_class_mock,
) -> None:
"""Test WoL button not created if interviewed box is in slave mode."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
slave_mesh_data = deepcopy(MOCK_MESH_DATA)
slave_mesh_data["nodes"][0]["mesh_role"] = MeshRoles.SLAVE
fh_class_mock.get_mesh_topology.return_value = slave_mesh_data
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
button = hass.states.get("button.printer_wake_on_lan")
assert button is None
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_wol_button_absent_for_non_lan_device(
hass: HomeAssistant,
fc_class_mock,
fh_class_mock,
) -> None:
"""Test WoL button not created if interviewed device is not connected via LAN."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
printer_wifi_data = deepcopy(MOCK_MESH_DATA)
# initialization logic uses the connection type of the `node_interface_1_uid` pair of the printer
# ni-230 is wifi interface of fritzbox
printer_node_interface = printer_wifi_data["nodes"][1]["node_interfaces"][0]
printer_node_interface["type"] = "WLAN"
printer_node_interface["node_links"][0]["node_interface_1_uid"] = "ni-230"
fh_class_mock.get_mesh_topology.return_value = printer_wifi_data
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
button = hass.states.get("button.printer_wake_on_lan")
assert button is None
async def test_cleanup_button(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
fc_class_mock,
fh_class_mock,
) -> None:
"""Test cleanup of orphan devices."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.LOADED
# check if tracked device is registered properly
device = device_registry.async_get_device(
connections={("mac", "aa:bb:cc:00:11:22")}
)
assert device
entities = [
entity
for entity in er.async_entries_for_config_entry(entity_registry, entry.entry_id)
if entity.unique_id.startswith("AA:BB:CC:00:11:22")
]
assert entities
assert len(entities) == 3
# removed tracked device and trigger cleanup
host_attributes = deepcopy(MOCK_HOST_ATTRIBUTES_DATA)
host_attributes.pop(0)
fh_class_mock.get_hosts_attributes.return_value = host_attributes
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
{ATTR_ENTITY_ID: "button.mock_title_cleanup"},
blocking=True,
)
await hass.async_block_till_done(wait_background_tasks=True)
# check if orphan tracked device is removed
device = device_registry.async_get_device(
connections={("mac", "aa:bb:cc:00:11:22")}
)
assert not device
entities = [
entity
for entity in er.async_entries_for_config_entry(entity_registry, entry.entry_id)
if entity.unique_id.startswith("AA:BB:CC:00:11:22")
]
assert not entities