diff --git a/tests/components/deconz/test_services.py b/tests/components/deconz/test_services.py index de061fc4e8c..2ce3387de15 100644 --- a/tests/components/deconz/test_services.py +++ b/tests/components/deconz/test_services.py @@ -1,6 +1,7 @@ """deCONZ service tests.""" -from unittest.mock import patch +from collections.abc import Callable +from typing import Any import pytest import voluptuous as vol @@ -20,34 +21,29 @@ from homeassistant.components.deconz.services import ( SERVICE_REMOVE_ORPHANED_ENTRIES, ) from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er -from .test_gateway import ( - BRIDGEID, - DECONZ_WEB_REQUEST, - mock_deconz_put_request, - mock_deconz_request, - setup_deconz_integration, -) +from .test_gateway import BRIDGEID from tests.common import async_capture_events from tests.test_util.aiohttp import AiohttpClientMocker +@pytest.mark.usefixtures("config_entry_setup") async def test_configure_service_with_field( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker + hass: HomeAssistant, + mock_put_request: Callable[[str, str], AiohttpClientMocker], ) -> None: """Test that service invokes pydeconz with the correct path and data.""" - config_entry = await setup_deconz_integration(hass, aioclient_mock) - data = { SERVICE_FIELD: "/lights/2", CONF_BRIDGE_ID: BRIDGEID, SERVICE_DATA: {"on": True, "attr1": 10, "attr2": 20}, } - mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/2") + aioclient_mock = mock_put_request("/lights/2") await hass.services.async_call( DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data, blocking=True @@ -55,12 +51,10 @@ async def test_configure_service_with_field( assert aioclient_mock.mock_calls[1][2] == {"on": True, "attr1": 10, "attr2": 20} -async def test_configure_service_with_entity( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker -) -> None: - """Test that service invokes pydeconz with the correct path and data.""" - data = { - "lights": { +@pytest.mark.parametrize( + "light_payload", + [ + { "1": { "name": "Test", "state": {"reachable": True}, @@ -68,16 +62,19 @@ async def test_configure_service_with_entity( "uniqueid": "00:00:00:00:00:00:00:01-00", } } - } - with patch.dict(DECONZ_WEB_REQUEST, data): - config_entry = await setup_deconz_integration(hass, aioclient_mock) - + ], +) +@pytest.mark.usefixtures("config_entry_setup") +async def test_configure_service_with_entity( + hass: HomeAssistant, + mock_put_request: Callable[[str, str], AiohttpClientMocker], +) -> None: + """Test that service invokes pydeconz with the correct path and data.""" data = { SERVICE_ENTITY: "light.test", SERVICE_DATA: {"on": True, "attr1": 10, "attr2": 20}, } - - mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/1") + aioclient_mock = mock_put_request("/lights/1") await hass.services.async_call( DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data, blocking=True @@ -85,12 +82,10 @@ async def test_configure_service_with_entity( assert aioclient_mock.mock_calls[1][2] == {"on": True, "attr1": 10, "attr2": 20} -async def test_configure_service_with_entity_and_field( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker -) -> None: - """Test that service invokes pydeconz with the correct path and data.""" - data = { - "lights": { +@pytest.mark.parametrize( + "light_payload", + [ + { "1": { "name": "Test", "state": {"reachable": True}, @@ -98,17 +93,20 @@ async def test_configure_service_with_entity_and_field( "uniqueid": "00:00:00:00:00:00:00:01-00", } } - } - with patch.dict(DECONZ_WEB_REQUEST, data): - config_entry = await setup_deconz_integration(hass, aioclient_mock) - + ], +) +@pytest.mark.usefixtures("config_entry_setup") +async def test_configure_service_with_entity_and_field( + hass: HomeAssistant, + mock_put_request: Callable[[str, str], AiohttpClientMocker], +) -> None: + """Test that service invokes pydeconz with the correct path and data.""" data = { SERVICE_ENTITY: "light.test", SERVICE_FIELD: "/state", SERVICE_DATA: {"on": True, "attr1": 10, "attr2": 20}, } - - mock_deconz_put_request(aioclient_mock, config_entry.data, "/lights/1/state") + aioclient_mock = mock_put_request("/lights/1/state") await hass.services.async_call( DECONZ_DOMAIN, SERVICE_CONFIGURE_DEVICE, service_data=data, blocking=True @@ -116,11 +114,11 @@ async def test_configure_service_with_entity_and_field( assert aioclient_mock.mock_calls[1][2] == {"on": True, "attr1": 10, "attr2": 20} +@pytest.mark.usefixtures("config_entry_setup") async def test_configure_service_with_faulty_bridgeid( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test that service fails on a bad bridge id.""" - await setup_deconz_integration(hass, aioclient_mock) aioclient_mock.clear_requests() data = { @@ -137,12 +135,9 @@ async def test_configure_service_with_faulty_bridgeid( assert len(aioclient_mock.mock_calls) == 0 -async def test_configure_service_with_faulty_field( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker -) -> None: +@pytest.mark.usefixtures("config_entry_setup") +async def test_configure_service_with_faulty_field(hass: HomeAssistant) -> None: """Test that service fails on a bad field.""" - await setup_deconz_integration(hass, aioclient_mock) - data = {SERVICE_FIELD: "light/2", SERVICE_DATA: {}} with pytest.raises(vol.Invalid): @@ -151,11 +146,11 @@ async def test_configure_service_with_faulty_field( ) +@pytest.mark.usefixtures("config_entry_setup") async def test_configure_service_with_faulty_entity( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test that service on a non existing entity.""" - await setup_deconz_integration(hass, aioclient_mock) aioclient_mock.clear_requests() data = { @@ -171,13 +166,12 @@ async def test_configure_service_with_faulty_entity( assert len(aioclient_mock.mock_calls) == 0 +@pytest.mark.parametrize("config_entry_options", [{CONF_MASTER_GATEWAY: False}]) +@pytest.mark.usefixtures("config_entry_setup") async def test_calling_service_with_no_master_gateway_fails( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test that service call fails when no master gateway exist.""" - await setup_deconz_integration( - hass, aioclient_mock, options={CONF_MASTER_GATEWAY: False} - ) aioclient_mock.clear_requests() data = { @@ -193,18 +187,19 @@ async def test_calling_service_with_no_master_gateway_fails( assert len(aioclient_mock.mock_calls) == 0 +@pytest.mark.usefixtures("config_entry_setup") async def test_service_refresh_devices( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + deconz_payload: dict[str, Any], + mock_requests: Callable[[], None], ) -> None: """Test that service can refresh devices.""" - config_entry = await setup_deconz_integration(hass, aioclient_mock) - assert len(hass.states.async_all()) == 0 aioclient_mock.clear_requests() - data = { - "config": {}, + deconz_payload |= { "groups": { "1": { "id": "Group 1 id", @@ -234,8 +229,7 @@ async def test_service_refresh_devices( } }, } - - mock_deconz_request(aioclient_mock, config_entry.data, data) + mock_requests() await hass.services.async_call( DECONZ_DOMAIN, SERVICE_DEVICE_REFRESH, service_data={CONF_BRIDGE_ID: BRIDGEID} @@ -245,12 +239,10 @@ async def test_service_refresh_devices( assert len(hass.states.async_all()) == 5 -async def test_service_refresh_devices_trigger_no_state_update( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker -) -> None: - """Verify that gateway.ignore_state_updates are honored.""" - data = { - "sensors": { +@pytest.mark.parametrize( + "sensor_payload", + [ + { "1": { "name": "Switch 1", "type": "ZHASwitch", @@ -259,18 +251,23 @@ async def test_service_refresh_devices_trigger_no_state_update( "uniqueid": "00:00:00:00:00:00:00:01-00", } } - } - with patch.dict(DECONZ_WEB_REQUEST, data): - config_entry = await setup_deconz_integration(hass, aioclient_mock) - + ], +) +@pytest.mark.usefixtures("config_entry_setup") +async def test_service_refresh_devices_trigger_no_state_update( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + deconz_payload: dict[str, Any], + mock_requests, +) -> None: + """Verify that gateway.ignore_state_updates are honored.""" assert len(hass.states.async_all()) == 1 captured_events = async_capture_events(hass, CONF_DECONZ_EVENT) aioclient_mock.clear_requests() - data = { - "config": {}, + deconz_payload |= { "groups": { "1": { "id": "Group 1 id", @@ -300,8 +297,7 @@ async def test_service_refresh_devices_trigger_no_state_update( } }, } - - mock_deconz_request(aioclient_mock, config_entry.data, data) + mock_requests() await hass.services.async_call( DECONZ_DOMAIN, SERVICE_DEVICE_REFRESH, service_data={CONF_BRIDGE_ID: BRIDGEID} @@ -312,23 +308,23 @@ async def test_service_refresh_devices_trigger_no_state_update( assert len(captured_events) == 0 -async def test_remove_orphaned_entries_service( - hass: HomeAssistant, - device_registry: dr.DeviceRegistry, - entity_registry: er.EntityRegistry, - aioclient_mock: AiohttpClientMocker, -) -> None: - """Test service works and also don't remove more than expected.""" - data = { - "lights": { +@pytest.mark.parametrize( + "light_payload", + [ + { "1": { "name": "Light 1 name", "state": {"reachable": True}, "type": "Light", "uniqueid": "00:00:00:00:00:00:00:01-00", } - }, - "sensors": { + } + ], +) +@pytest.mark.parametrize( + "sensor_payload", + [ + { "1": { "name": "Switch 1", "type": "ZHASwitch", @@ -336,13 +332,18 @@ async def test_remove_orphaned_entries_service( "config": {"battery": 100}, "uniqueid": "00:00:00:00:00:00:00:03-00", }, - }, - } - with patch.dict(DECONZ_WEB_REQUEST, data): - config_entry = await setup_deconz_integration(hass, aioclient_mock) - + } + ], +) +async def test_remove_orphaned_entries_service( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + entity_registry: er.EntityRegistry, + config_entry_setup: ConfigEntry, +) -> None: + """Test service works and also don't remove more than expected.""" device = device_registry.async_get_or_create( - config_entry_id=config_entry.entry_id, + config_entry_id=config_entry_setup.entry_id, connections={(dr.CONNECTION_NETWORK_MAC, "123")}, ) @@ -351,7 +352,7 @@ async def test_remove_orphaned_entries_service( [ entry for entry in device_registry.devices.values() - if config_entry.entry_id in entry.config_entries + if config_entry_setup.entry_id in entry.config_entries ] ) == 5 # Host, gateway, light, switch and orphan @@ -362,12 +363,16 @@ async def test_remove_orphaned_entries_service( DECONZ_DOMAIN, "12345", suggested_object_id="Orphaned sensor", - config_entry=config_entry, + config_entry=config_entry_setup, device_id=device.id, ) assert ( - len(er.async_entries_for_config_entry(entity_registry, config_entry.entry_id)) + len( + er.async_entries_for_config_entry( + entity_registry, config_entry_setup.entry_id + ) + ) == 3 # Light, switch battery and orphan ) @@ -383,13 +388,17 @@ async def test_remove_orphaned_entries_service( [ entry for entry in device_registry.devices.values() - if config_entry.entry_id in entry.config_entries + if config_entry_setup.entry_id in entry.config_entries ] ) == 4 # Host, gateway, light and switch ) assert ( - len(er.async_entries_for_config_entry(entity_registry, config_entry.entry_id)) + len( + er.async_entries_for_config_entry( + entity_registry, config_entry_setup.entry_id + ) + ) == 2 # Light and switch battery )