core/tests/components/matter/test_adapter.py

206 lines
7.0 KiB
Python

"""Test the adapter."""
from __future__ import annotations
from unittest.mock import MagicMock
from matter_server.common.models import EventType
import pytest
from homeassistant.components.matter.adapter import get_clean_name
from homeassistant.components.matter.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from .common import create_node_from_fixture
from tests.common import MockConfigEntry
@pytest.mark.usefixtures("matter_node")
@pytest.mark.parametrize(
("node_fixture", "name"),
[
("onoff_light", "Mock OnOff Light"),
("onoff_light_alt_name", "Mock OnOff Light"),
("onoff_light_no_name", "Mock Light"),
],
)
async def test_device_registry_single_node_device(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
name: str,
) -> None:
"""Test bridge devices are set up correctly with via_device."""
entry = device_registry.async_get_device(
identifiers={
(DOMAIN, "deviceid_00000000000004D2-0000000000000001-MatterNodeDevice")
}
)
assert entry is not None
# test serial id present as additional identifier
assert (DOMAIN, "serial_12345678") in entry.identifiers
assert entry.name == name
assert entry.manufacturer == "Nabu Casa"
assert entry.model == "Mock Light"
assert entry.model_id == "32768"
assert entry.hw_version == "v1.0"
assert entry.sw_version == "v1.0"
assert entry.serial_number == "12345678"
@pytest.mark.usefixtures("matter_node")
@pytest.mark.parametrize("node_fixture", ["on_off_plugin_unit"])
async def test_device_registry_single_node_device_alt(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test additional device with different attribute values."""
entry = device_registry.async_get_device(
identifiers={
(DOMAIN, "deviceid_00000000000004D2-0000000000000001-MatterNodeDevice")
}
)
assert entry is not None
# test name is derived from productName (because nodeLabel is absent)
assert entry.name == "Mock OnOffPluginUnit"
# test serial id NOT present as additional identifier
assert (DOMAIN, "serial_TEST_SN") not in entry.identifiers
assert entry.serial_number is None
@pytest.mark.usefixtures("matter_node")
@pytest.mark.skip("Waiting for a new test fixture")
@pytest.mark.parametrize("node_fixture", ["fake_bridge_two_light"])
async def test_device_registry_bridge(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test bridge devices are set up correctly with via_device."""
# Validate bridge
bridge_entry = device_registry.async_get_device(
identifiers={(DOMAIN, "mock-hub-id")}
)
assert bridge_entry is not None
assert bridge_entry.name == "My Mock Bridge"
assert bridge_entry.manufacturer == "Mock Vendor"
assert bridge_entry.model == "Mock Bridge"
assert bridge_entry.hw_version == "TEST_VERSION"
assert bridge_entry.sw_version == "123.4.5"
# Device 1
device1_entry = device_registry.async_get_device(
identifiers={(DOMAIN, "mock-id-kitchen-ceiling")}
)
assert device1_entry is not None
assert device1_entry.via_device_id == bridge_entry.id
assert device1_entry.name == "Kitchen Ceiling"
assert device1_entry.manufacturer == "Mock Vendor"
assert device1_entry.model == "Mock Light"
assert device1_entry.hw_version is None
assert device1_entry.sw_version == "67.8.9"
# Device 2
device2_entry = device_registry.async_get_device(
identifiers={(DOMAIN, "mock-id-living-room-ceiling")}
)
assert device2_entry is not None
assert device2_entry.via_device_id == bridge_entry.id
assert device2_entry.name == "Living Room Ceiling"
assert device2_entry.manufacturer == "Mock Vendor"
assert device2_entry.model == "Mock Light"
assert device2_entry.hw_version is None
assert device2_entry.sw_version == "1.49.1"
@pytest.mark.usefixtures("integration")
async def test_node_added_subscription(
hass: HomeAssistant,
matter_client: MagicMock,
) -> None:
"""Test subscription to new devices work."""
assert matter_client.subscribe_events.call_count == 5
assert (
matter_client.subscribe_events.call_args.kwargs["event_filter"]
== EventType.NODE_UPDATED
)
node_added_callback = matter_client.subscribe_events.call_args.kwargs["callback"]
node = create_node_from_fixture("onoff_light")
entity_state = hass.states.get("light.mock_onoff_light")
assert not entity_state
node_added_callback(EventType.NODE_ADDED, node)
await hass.async_block_till_done()
entity_state = hass.states.get("light.mock_onoff_light")
assert entity_state
@pytest.mark.usefixtures("matter_node")
@pytest.mark.parametrize("node_fixture", ["air_purifier"])
async def test_device_registry_single_node_composed_device(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test that a composed device within a standalone node only creates one HA device entry."""
assert len(device_registry.devices) == 1
@pytest.mark.usefixtures("matter_node")
@pytest.mark.parametrize("node_fixture", ["multi_endpoint_light"])
async def test_multi_endpoint_name(hass: HomeAssistant) -> None:
"""Test that the entity name gets postfixed if the device has multiple primary endpoints."""
entity_state = hass.states.get("light.inovelli_light_1")
assert entity_state
assert entity_state.name == "Inovelli Light (1)"
entity_state = hass.states.get("light.inovelli_light_6")
assert entity_state
assert entity_state.name == "Inovelli Light (6)"
async def test_get_clean_name() -> None:
"""Test get_clean_name helper.
Test device names that are assigned to `null`
or have a trailing null char with spaces.
"""
assert get_clean_name(None) is None
assert get_clean_name("\x00") is None
assert get_clean_name(" \x00") is None
assert get_clean_name("") is None
assert get_clean_name("Mock device") == "Mock device"
assert get_clean_name("Mock device \x00") == "Mock device"
async def test_bad_node_not_crash_integration(
hass: HomeAssistant,
matter_client: MagicMock,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test that a bad node does not crash the integration."""
good_node = create_node_from_fixture("onoff_light")
bad_node = create_node_from_fixture("onoff_light")
del bad_node.endpoints[0].node
matter_client.get_nodes.return_value = [good_node, bad_node]
config_entry = MockConfigEntry(
domain="matter", data={"url": "http://mock-matter-server-url"}
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert matter_client.get_nodes.call_count == 1
assert hass.states.get("light.mock_onoff_light") is not None
assert len(hass.states.async_all("light")) == 1
assert "Error setting up node" in caplog.text