core/tests/components/broadlink/test_device.py

358 lines
13 KiB
Python

"""Tests for Broadlink devices."""
from unittest.mock import patch
import broadlink.exceptions as blke
from homeassistant.components.broadlink.const import DOMAIN
from homeassistant.components.broadlink.device import get_domains
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_FRIENDLY_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import get_device
DEVICE_FACTORY = "homeassistant.components.broadlink.device.blk.gendevice"
async def test_device_setup(hass: HomeAssistant) -> None:
"""Test a successful setup."""
device = get_device("Office")
with (
patch.object(hass.config_entries, "async_forward_entry_setups") as mock_forward,
patch.object(hass.config_entries.flow, "async_init") as mock_init,
):
mock_setup = await device.setup_entry(hass)
assert mock_setup.entry.state is ConfigEntryState.LOADED
assert mock_setup.api.auth.call_count == 1
assert mock_setup.api.get_fwversion.call_count == 1
assert mock_setup.factory.call_count == 1
forward_entries = set(mock_forward.mock_calls[0][1][1])
domains = get_domains(mock_setup.api.type)
assert mock_forward.call_count == 1
assert forward_entries == domains
assert mock_init.call_count == 0
async def test_device_setup_authentication_error(hass: HomeAssistant) -> None:
"""Test we handle an authentication error."""
device = get_device("Living Room")
mock_api = device.get_mock_api()
mock_api.auth.side_effect = blke.AuthenticationError()
with (
patch.object(hass.config_entries, "async_forward_entry_setups") as mock_forward,
patch.object(hass.config_entries.flow, "async_init") as mock_init,
):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.SETUP_ERROR
assert mock_setup.api.auth.call_count == 1
assert mock_forward.call_count == 0
assert mock_init.call_count == 1
assert mock_init.mock_calls[0][2]["context"]["source"] == "reauth"
assert mock_init.mock_calls[0][2]["data"] == {
"name": device.name,
**device.get_entry_data(),
}
async def test_device_setup_network_timeout(hass: HomeAssistant) -> None:
"""Test we handle a network timeout."""
device = get_device("Office")
mock_api = device.get_mock_api()
mock_api.auth.side_effect = blke.NetworkTimeoutError()
with (
patch.object(hass.config_entries, "async_forward_entry_setups") as mock_forward,
patch.object(hass.config_entries.flow, "async_init") as mock_init,
):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.SETUP_RETRY
assert mock_setup.api.auth.call_count == 1
assert mock_forward.call_count == 0
assert mock_init.call_count == 0
async def test_device_setup_os_error(hass: HomeAssistant) -> None:
"""Test we handle an OS error."""
device = get_device("Office")
mock_api = device.get_mock_api()
mock_api.auth.side_effect = OSError()
with (
patch.object(hass.config_entries, "async_forward_entry_setups") as mock_forward,
patch.object(hass.config_entries.flow, "async_init") as mock_init,
):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.SETUP_RETRY
assert mock_setup.api.auth.call_count == 1
assert mock_forward.call_count == 0
assert mock_init.call_count == 0
async def test_device_setup_broadlink_exception(hass: HomeAssistant) -> None:
"""Test we handle a Broadlink exception."""
device = get_device("Office")
mock_api = device.get_mock_api()
mock_api.auth.side_effect = blke.BroadlinkException()
with (
patch.object(hass.config_entries, "async_forward_entry_setups") as mock_forward,
patch.object(hass.config_entries.flow, "async_init") as mock_init,
):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.SETUP_ERROR
assert mock_setup.api.auth.call_count == 1
assert mock_forward.call_count == 0
assert mock_init.call_count == 0
async def test_device_setup_update_network_timeout(hass: HomeAssistant) -> None:
"""Test we handle a network timeout in the update step."""
device = get_device("Office")
mock_api = device.get_mock_api()
mock_api.check_sensors.side_effect = blke.NetworkTimeoutError()
with (
patch.object(hass.config_entries, "async_forward_entry_setups") as mock_forward,
patch.object(hass.config_entries.flow, "async_init") as mock_init,
):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.SETUP_RETRY
assert mock_setup.api.auth.call_count == 1
assert mock_setup.api.check_sensors.call_count == 1
assert mock_forward.call_count == 0
assert mock_init.call_count == 0
async def test_device_setup_update_authorization_error(hass: HomeAssistant) -> None:
"""Test we handle an authorization error in the update step."""
device = get_device("Office")
mock_api = device.get_mock_api()
mock_api.check_sensors.side_effect = (
blke.AuthorizationError(),
{"temperature": 30},
)
with (
patch.object(hass.config_entries, "async_forward_entry_setups") as mock_forward,
patch.object(hass.config_entries.flow, "async_init") as mock_init,
):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.LOADED
assert mock_setup.api.auth.call_count == 2
assert mock_setup.api.check_sensors.call_count == 2
forward_entries = set(mock_forward.mock_calls[0][1][1])
domains = get_domains(mock_api.type)
assert mock_forward.call_count == 1
assert forward_entries == domains
assert mock_init.call_count == 0
async def test_device_setup_update_authentication_error(hass: HomeAssistant) -> None:
"""Test we handle an authentication error in the update step."""
device = get_device("Garage")
mock_api = device.get_mock_api()
mock_api.check_sensors.side_effect = blke.AuthorizationError()
mock_api.auth.side_effect = (None, blke.AuthenticationError())
with (
patch.object(hass.config_entries, "async_forward_entry_setups") as mock_forward,
patch.object(hass.config_entries.flow, "async_init") as mock_init,
):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.SETUP_RETRY
assert mock_setup.api.auth.call_count == 2
assert mock_setup.api.check_sensors.call_count == 1
assert mock_forward.call_count == 0
assert mock_init.call_count == 1
assert mock_init.mock_calls[0][2]["context"]["source"] == "reauth"
assert mock_init.mock_calls[0][2]["data"] == {
"name": device.name,
**device.get_entry_data(),
}
async def test_device_setup_update_broadlink_exception(hass: HomeAssistant) -> None:
"""Test we handle a Broadlink exception in the update step."""
device = get_device("Garage")
mock_api = device.get_mock_api()
mock_api.check_sensors.side_effect = blke.BroadlinkException()
with (
patch.object(hass.config_entries, "async_forward_entry_setups") as mock_forward,
patch.object(hass.config_entries.flow, "async_init") as mock_init,
):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.SETUP_RETRY
assert mock_setup.api.auth.call_count == 1
assert mock_setup.api.check_sensors.call_count == 1
assert mock_forward.call_count == 0
assert mock_init.call_count == 0
async def test_device_setup_get_fwversion_broadlink_exception(
hass: HomeAssistant,
) -> None:
"""Test we load the device even if we cannot read the firmware version."""
device = get_device("Office")
mock_api = device.get_mock_api()
mock_api.get_fwversion.side_effect = blke.BroadlinkException()
with patch.object(
hass.config_entries, "async_forward_entry_setups"
) as mock_forward:
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.LOADED
forward_entries = set(mock_forward.mock_calls[0][1][1])
domains = get_domains(mock_setup.api.type)
assert mock_forward.call_count == 1
assert forward_entries == domains
async def test_device_setup_get_fwversion_os_error(hass: HomeAssistant) -> None:
"""Test we load the device even if we cannot read the firmware version."""
device = get_device("Office")
mock_api = device.get_mock_api()
mock_api.get_fwversion.side_effect = OSError()
with patch.object(
hass.config_entries, "async_forward_entry_setups"
) as mock_forward:
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
assert mock_setup.entry.state is ConfigEntryState.LOADED
forward_entries = set(mock_forward.mock_calls[0][1][1])
domains = get_domains(mock_setup.api.type)
assert mock_forward.call_count == 1
assert forward_entries == domains
async def test_device_setup_registry(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test we register the device and the entries correctly."""
device = get_device("Office")
mock_setup = await device.setup_entry(hass)
await hass.async_block_till_done()
assert len(device_registry.devices) == 1
device_entry = device_registry.async_get_device(
identifiers={(DOMAIN, mock_setup.entry.unique_id)}
)
assert device_entry.identifiers == {(DOMAIN, device.mac)}
assert device_entry.name == device.name
assert device_entry.model == device.model
assert device_entry.manufacturer == device.manufacturer
assert device_entry.sw_version == device.fwversion
for entry in er.async_entries_for_device(entity_registry, device_entry.id):
assert (
hass.states.get(entry.entity_id)
.attributes[ATTR_FRIENDLY_NAME]
.startswith(device.name)
)
async def test_device_unload_works(hass: HomeAssistant) -> None:
"""Test we unload the device."""
device = get_device("Office")
with patch.object(hass.config_entries, "async_forward_entry_setups"):
mock_setup = await device.setup_entry(hass)
with patch.object(
hass.config_entries, "async_forward_entry_unload", return_value=True
) as mock_forward:
await hass.config_entries.async_unload(mock_setup.entry.entry_id)
assert mock_setup.entry.state is ConfigEntryState.NOT_LOADED
forward_entries = {c[1][1] for c in mock_forward.mock_calls}
domains = get_domains(mock_setup.api.type)
assert mock_forward.call_count == len(domains)
assert forward_entries == domains
async def test_device_unload_authentication_error(hass: HomeAssistant) -> None:
"""Test we unload a device that failed the authentication step."""
device = get_device("Living Room")
mock_api = device.get_mock_api()
mock_api.auth.side_effect = blke.AuthenticationError()
with (
patch.object(hass.config_entries, "async_forward_entry_setups"),
patch.object(hass.config_entries.flow, "async_init"),
):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
with patch.object(
hass.config_entries, "async_forward_entry_unload", return_value=True
) as mock_forward:
await hass.config_entries.async_unload(mock_setup.entry.entry_id)
assert mock_setup.entry.state is ConfigEntryState.NOT_LOADED
assert mock_forward.call_count == 0
async def test_device_unload_update_failed(hass: HomeAssistant) -> None:
"""Test we unload a device that failed the update step."""
device = get_device("Office")
mock_api = device.get_mock_api()
mock_api.check_sensors.side_effect = blke.NetworkTimeoutError()
with patch.object(hass.config_entries, "async_forward_entry_setups"):
mock_setup = await device.setup_entry(hass, mock_api=mock_api)
with patch.object(
hass.config_entries, "async_forward_entry_unload", return_value=True
) as mock_forward:
await hass.config_entries.async_unload(mock_setup.entry.entry_id)
assert mock_setup.entry.state is ConfigEntryState.NOT_LOADED
assert mock_forward.call_count == 0
async def test_device_update_listener(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test we update device and entity registry when the entry is renamed."""
device = get_device("Office")
mock_setup = await device.setup_entry(hass)
await hass.async_block_till_done()
with patch(DEVICE_FACTORY, return_value=mock_setup.api):
hass.config_entries.async_update_entry(mock_setup.entry, title="New Name")
await hass.async_block_till_done()
device_entry = device_registry.async_get_device(
identifiers={(DOMAIN, mock_setup.entry.unique_id)}
)
assert device_entry.name == "New Name"
for entry in er.async_entries_for_device(entity_registry, device_entry.id):
assert (
hass.states.get(entry.entity_id)
.attributes[ATTR_FRIENDLY_NAME]
.startswith("New Name")
)