mirror of https://github.com/home-assistant/core
352 lines
12 KiB
Python
352 lines
12 KiB
Python
"""Tests for the Bond module."""
|
|
|
|
from unittest.mock import MagicMock, Mock
|
|
|
|
from aiohttp import ClientConnectionError, ClientResponseError
|
|
from bond_async import DeviceType
|
|
import pytest
|
|
|
|
from homeassistant.components.bond import DOMAIN, BondData
|
|
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN
|
|
from homeassistant.config_entries import ConfigEntryState
|
|
from homeassistant.const import ATTR_ASSUMED_STATE, CONF_ACCESS_TOKEN, CONF_HOST
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from .common import (
|
|
ceiling_fan,
|
|
patch_bond_bridge,
|
|
patch_bond_device,
|
|
patch_bond_device_ids,
|
|
patch_bond_device_properties,
|
|
patch_bond_device_state,
|
|
patch_bond_version,
|
|
patch_setup_entry,
|
|
patch_start_bpup,
|
|
setup_bond_entity,
|
|
setup_platform,
|
|
)
|
|
|
|
from tests.common import MockConfigEntry
|
|
from tests.typing import WebSocketGenerator
|
|
|
|
|
|
async def test_async_setup_no_domain_config(hass: HomeAssistant) -> None:
|
|
"""Test setup without configuration is noop."""
|
|
result = await async_setup_component(hass, DOMAIN, {})
|
|
|
|
assert result is True
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"exc",
|
|
[
|
|
ClientConnectionError,
|
|
ClientResponseError(MagicMock(), MagicMock(), status=404),
|
|
TimeoutError,
|
|
OSError,
|
|
],
|
|
)
|
|
async def test_async_setup_raises_entry_not_ready(
|
|
hass: HomeAssistant, exc: Exception
|
|
) -> None:
|
|
"""Test that it throws ConfigEntryNotReady when exception occurs during setup."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data={CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
|
|
)
|
|
config_entry.add_to_hass(hass)
|
|
|
|
with patch_bond_version(side_effect=exc):
|
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
|
assert config_entry.state is ConfigEntryState.SETUP_RETRY
|
|
|
|
|
|
async def test_async_setup_raises_fails_if_auth_fails(hass: HomeAssistant) -> None:
|
|
"""Test that setup fails if auth fails during setup."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data={CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
|
|
)
|
|
config_entry.add_to_hass(hass)
|
|
|
|
with patch_bond_version(
|
|
side_effect=ClientResponseError(MagicMock(), MagicMock(), status=401)
|
|
):
|
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
|
assert config_entry.state is ConfigEntryState.SETUP_ERROR
|
|
|
|
|
|
async def test_async_setup_entry_sets_up_hub_and_supported_domains(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
) -> None:
|
|
"""Test that configuring entry sets up cover domain."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data={CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
|
|
)
|
|
|
|
with (
|
|
patch_bond_bridge(),
|
|
patch_bond_version(
|
|
return_value={
|
|
"bondid": "ZXXX12345",
|
|
"target": "test-model",
|
|
"fw_ver": "test-version",
|
|
"mcu_ver": "test-hw-version",
|
|
}
|
|
),
|
|
patch_setup_entry("cover") as mock_cover_async_setup_entry,
|
|
patch_setup_entry("fan") as mock_fan_async_setup_entry,
|
|
patch_setup_entry("light") as mock_light_async_setup_entry,
|
|
patch_setup_entry("switch") as mock_switch_async_setup_entry,
|
|
):
|
|
result = await setup_bond_entity(hass, config_entry, patch_device_ids=True)
|
|
assert result is True
|
|
await hass.async_block_till_done()
|
|
|
|
assert isinstance(config_entry.runtime_data, BondData)
|
|
assert config_entry.state is ConfigEntryState.LOADED
|
|
assert config_entry.unique_id == "ZXXX12345"
|
|
|
|
# verify hub device is registered correctly
|
|
hub = device_registry.async_get_device(identifiers={(DOMAIN, "ZXXX12345")})
|
|
assert hub.name == "bond-name"
|
|
assert hub.manufacturer == "Olibra"
|
|
assert hub.model == "test-model"
|
|
assert hub.sw_version == "test-version"
|
|
assert hub.hw_version == "test-hw-version"
|
|
assert hub.configuration_url == "http://some host"
|
|
|
|
# verify supported domains are setup
|
|
assert len(mock_cover_async_setup_entry.mock_calls) == 1
|
|
assert len(mock_fan_async_setup_entry.mock_calls) == 1
|
|
assert len(mock_light_async_setup_entry.mock_calls) == 1
|
|
assert len(mock_switch_async_setup_entry.mock_calls) == 1
|
|
|
|
|
|
async def test_unload_config_entry(hass: HomeAssistant) -> None:
|
|
"""Test that configuration entry supports unloading."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data={CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
|
|
)
|
|
|
|
result = await setup_bond_entity(
|
|
hass,
|
|
config_entry,
|
|
patch_version=True,
|
|
patch_device_ids=True,
|
|
patch_platforms=True,
|
|
patch_bridge=True,
|
|
)
|
|
assert result is True
|
|
await hass.async_block_till_done()
|
|
|
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
|
|
|
|
|
async def test_old_identifiers_are_removed(
|
|
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
|
) -> None:
|
|
"""Test we remove the old non-unique identifiers."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data={CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
|
|
)
|
|
config_entry.add_to_hass(hass)
|
|
|
|
old_identifers = (DOMAIN, "device_id")
|
|
new_identifiers = (DOMAIN, "ZXXX12345", "device_id")
|
|
device_registry.async_get_or_create(
|
|
config_entry_id=config_entry.entry_id,
|
|
identifiers={old_identifers},
|
|
manufacturer="any",
|
|
name="old",
|
|
)
|
|
|
|
with (
|
|
patch_bond_bridge(),
|
|
patch_bond_version(
|
|
return_value={
|
|
"bondid": "ZXXX12345",
|
|
"target": "test-model",
|
|
"fw_ver": "test-version",
|
|
}
|
|
),
|
|
patch_start_bpup(),
|
|
patch_bond_device_ids(return_value=["bond-device-id", "device_id"]),
|
|
patch_bond_device(
|
|
return_value={
|
|
"name": "test1",
|
|
"type": DeviceType.GENERIC_DEVICE,
|
|
}
|
|
),
|
|
patch_bond_device_properties(return_value={}),
|
|
patch_bond_device_state(return_value={}),
|
|
):
|
|
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
|
await hass.async_block_till_done()
|
|
|
|
assert config_entry.state is ConfigEntryState.LOADED
|
|
assert config_entry.unique_id == "ZXXX12345"
|
|
|
|
# verify the device info is cleaned up
|
|
assert device_registry.async_get_device(identifiers={old_identifers}) is None
|
|
assert device_registry.async_get_device(identifiers={new_identifiers}) is not None
|
|
|
|
|
|
async def test_smart_by_bond_device_suggested_area(
|
|
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
|
) -> None:
|
|
"""Test we can setup a smart by bond device and get the suggested area."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data={CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
|
|
)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
|
|
with (
|
|
patch_bond_bridge(side_effect=ClientResponseError(Mock(), Mock(), status=404)),
|
|
patch_bond_version(
|
|
return_value={
|
|
"bondid": "KXXX12345",
|
|
"target": "test-model",
|
|
"fw_ver": "test-version",
|
|
}
|
|
),
|
|
patch_start_bpup(),
|
|
patch_bond_device_ids(return_value=["bond-device-id", "device_id"]),
|
|
patch_bond_device(
|
|
return_value={
|
|
"name": "test1",
|
|
"type": DeviceType.GENERIC_DEVICE,
|
|
"location": "Den",
|
|
}
|
|
),
|
|
patch_bond_device_properties(return_value={}),
|
|
patch_bond_device_state(return_value={}),
|
|
):
|
|
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
|
await hass.async_block_till_done()
|
|
|
|
assert config_entry.state is ConfigEntryState.LOADED
|
|
assert config_entry.unique_id == "KXXX12345"
|
|
|
|
device = device_registry.async_get_device(identifiers={(DOMAIN, "KXXX12345")})
|
|
assert device is not None
|
|
assert device.suggested_area == "Den"
|
|
|
|
|
|
async def test_bridge_device_suggested_area(
|
|
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
|
) -> None:
|
|
"""Test we can setup a bridge bond device and get the suggested area."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data={CONF_HOST: "some host", CONF_ACCESS_TOKEN: "test-token"},
|
|
)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
|
|
with (
|
|
patch_bond_bridge(
|
|
return_value={
|
|
"name": "Office Bridge",
|
|
"location": "Office",
|
|
}
|
|
),
|
|
patch_bond_version(
|
|
return_value={
|
|
"bondid": "ZXXX12345",
|
|
"target": "test-model",
|
|
"fw_ver": "test-version",
|
|
}
|
|
),
|
|
patch_start_bpup(),
|
|
patch_bond_device_ids(return_value=["bond-device-id", "device_id"]),
|
|
patch_bond_device(
|
|
return_value={
|
|
"name": "test1",
|
|
"type": DeviceType.GENERIC_DEVICE,
|
|
"location": "Bathroom",
|
|
}
|
|
),
|
|
patch_bond_device_properties(return_value={}),
|
|
patch_bond_device_state(return_value={}),
|
|
):
|
|
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
|
await hass.async_block_till_done()
|
|
|
|
assert config_entry.state is ConfigEntryState.LOADED
|
|
assert config_entry.unique_id == "ZXXX12345"
|
|
|
|
device = device_registry.async_get_device(identifiers={(DOMAIN, "ZXXX12345")})
|
|
assert device is not None
|
|
assert device.suggested_area == "Office"
|
|
|
|
|
|
async def test_device_remove_devices(
|
|
hass: HomeAssistant,
|
|
hass_ws_client: WebSocketGenerator,
|
|
entity_registry: er.EntityRegistry,
|
|
device_registry: dr.DeviceRegistry,
|
|
) -> None:
|
|
"""Test we can only remove a device that no longer exists."""
|
|
assert await async_setup_component(hass, "config", {})
|
|
|
|
config_entry = await setup_platform(
|
|
hass,
|
|
FAN_DOMAIN,
|
|
ceiling_fan("name-1"),
|
|
bond_version={"bondid": "test-hub-id"},
|
|
bond_device_id="test-device-id",
|
|
)
|
|
|
|
entity = entity_registry.entities["fan.name_1"]
|
|
assert entity.unique_id == "test-hub-id_test-device-id"
|
|
|
|
device_entry = device_registry.async_get(entity.device_id)
|
|
client = await hass_ws_client(hass)
|
|
response = await client.remove_device(device_entry.id, config_entry.entry_id)
|
|
assert not response["success"]
|
|
|
|
dead_device_entry = device_registry.async_get_or_create(
|
|
config_entry_id=config_entry.entry_id,
|
|
identifiers={(DOMAIN, "test-hub-id", "remove-device-id")},
|
|
)
|
|
response = await client.remove_device(dead_device_entry.id, config_entry.entry_id)
|
|
assert response["success"]
|
|
|
|
dead_device_entry = device_registry.async_get_or_create(
|
|
config_entry_id=config_entry.entry_id,
|
|
identifiers={(DOMAIN, "wrong-hub-id", "test-device-id")},
|
|
)
|
|
response = await client.remove_device(dead_device_entry.id, config_entry.entry_id)
|
|
assert response["success"]
|
|
|
|
hub_device_entry = device_registry.async_get_or_create(
|
|
config_entry_id=config_entry.entry_id,
|
|
identifiers={(DOMAIN, "test-hub-id")},
|
|
)
|
|
response = await client.remove_device(hub_device_entry.id, config_entry.entry_id)
|
|
assert not response["success"]
|
|
|
|
|
|
async def test_smart_by_bond_v3_firmware(hass: HomeAssistant) -> None:
|
|
"""Test we can detect smart by bond with the v3 firmware."""
|
|
await setup_platform(
|
|
hass,
|
|
FAN_DOMAIN,
|
|
ceiling_fan("name-1"),
|
|
bond_version={"bondid": "KXXXX12345", "target": "breck-northstar"},
|
|
bond_device_id="test-device-id",
|
|
)
|
|
assert ATTR_ASSUMED_STATE not in hass.states.get("fan.name_1").attributes
|