Refactor WLED switch tests (#89197)

This commit is contained in:
Franck Nijhof 2023-03-06 01:46:53 +01:00 committed by GitHub
parent b14c5046e2
commit 216864d8f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 376 additions and 143 deletions

View File

@ -0,0 +1,302 @@
# serializer version: 1
# name: test_switch_state[switch.wled_rgb_light_nightlight-nightlight-called_with_on0-called_with_off0]
StateSnapshot({
'attributes': ReadOnlyDict({
'duration': 60,
'fade': True,
'friendly_name': 'WLED RGB Light Nightlight',
'icon': 'mdi:weather-night',
'target_brightness': 0,
}),
'context': <ANY>,
'entity_id': 'switch.wled_rgb_light_nightlight',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch_state[switch.wled_rgb_light_nightlight-nightlight-called_with_on0-called_with_off0].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.wled_rgb_light_nightlight',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': 'mdi:weather-night',
'original_name': 'Nightlight',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_nightlight',
'unit_of_measurement': None,
})
# ---
# name: test_switch_state[switch.wled_rgb_light_nightlight-nightlight-called_with_on0-called_with_off0].2
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'configuration_url': 'http://127.0.0.1',
'connections': set({
tuple(
'mac',
'aa:bb:cc:dd:ee:ff',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'id': <ANY>,
'identifiers': set({
tuple(
'wled',
'aabbccddeeff',
),
}),
'is_new': False,
'manufacturer': 'WLED',
'model': 'DIY light',
'name': 'WLED RGB Light',
'name_by_user': None,
'suggested_area': None,
'sw_version': '0.8.5',
'via_device_id': None,
})
# ---
# name: test_switch_state[switch.wled_rgb_light_reverse-segment-called_with_on1-called_with_off1]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Reverse',
'icon': 'mdi:swap-horizontal-bold',
}),
'context': <ANY>,
'entity_id': 'switch.wled_rgb_light_reverse',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch_state[switch.wled_rgb_light_reverse-segment-called_with_on1-called_with_off1].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.wled_rgb_light_reverse',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': 'mdi:swap-horizontal-bold',
'original_name': 'Reverse',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_reverse_0',
'unit_of_measurement': None,
})
# ---
# name: test_switch_state[switch.wled_rgb_light_reverse-segment-called_with_on1-called_with_off1].2
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'configuration_url': 'http://127.0.0.1',
'connections': set({
tuple(
'mac',
'aa:bb:cc:dd:ee:ff',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'id': <ANY>,
'identifiers': set({
tuple(
'wled',
'aabbccddeeff',
),
}),
'is_new': False,
'manufacturer': 'WLED',
'model': 'DIY light',
'name': 'WLED RGB Light',
'name_by_user': None,
'suggested_area': None,
'sw_version': '0.8.5',
'via_device_id': None,
})
# ---
# name: test_switch_state[switch.wled_rgb_light_sync_receive-sync-called_with_on2-called_with_off2]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Sync receive',
'icon': 'mdi:download-network-outline',
'udp_port': 21324,
}),
'context': <ANY>,
'entity_id': 'switch.wled_rgb_light_sync_receive',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch_state[switch.wled_rgb_light_sync_receive-sync-called_with_on2-called_with_off2].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.wled_rgb_light_sync_receive',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': 'mdi:download-network-outline',
'original_name': 'Sync receive',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_sync_receive',
'unit_of_measurement': None,
})
# ---
# name: test_switch_state[switch.wled_rgb_light_sync_receive-sync-called_with_on2-called_with_off2].2
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'configuration_url': 'http://127.0.0.1',
'connections': set({
tuple(
'mac',
'aa:bb:cc:dd:ee:ff',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'id': <ANY>,
'identifiers': set({
tuple(
'wled',
'aabbccddeeff',
),
}),
'is_new': False,
'manufacturer': 'WLED',
'model': 'DIY light',
'name': 'WLED RGB Light',
'name_by_user': None,
'suggested_area': None,
'sw_version': '0.8.5',
'via_device_id': None,
})
# ---
# name: test_switch_state[switch.wled_rgb_light_sync_send-sync-called_with_on3-called_with_off3]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Sync send',
'icon': 'mdi:upload-network-outline',
'udp_port': 21324,
}),
'context': <ANY>,
'entity_id': 'switch.wled_rgb_light_sync_send',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch_state[switch.wled_rgb_light_sync_send-sync-called_with_on3-called_with_off3].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'switch',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'switch.wled_rgb_light_sync_send',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': 'mdi:upload-network-outline',
'original_name': 'Sync send',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_sync_send',
'unit_of_measurement': None,
})
# ---
# name: test_switch_state[switch.wled_rgb_light_sync_send-sync-called_with_on3-called_with_off3].2
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'configuration_url': 'http://127.0.0.1',
'connections': set({
tuple(
'mac',
'aa:bb:cc:dd:ee:ff',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'id': <ANY>,
'identifiers': set({
tuple(
'wled',
'aabbccddeeff',
),
}),
'is_new': False,
'manufacturer': 'WLED',
'model': 'DIY light',
'name': 'WLED RGB Light',
'name_by_user': None,
'suggested_area': None,
'sw_version': '0.8.5',
'via_device_id': None,
})
# ---

View File

@ -3,29 +3,22 @@ import json
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest import pytest
from syrupy.assertion import SnapshotAssertion
from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.components.wled.const import ( from homeassistant.components.wled.const import SCAN_INTERVAL
ATTR_DURATION,
ATTR_FADE,
ATTR_TARGET_BRIGHTNESS,
ATTR_UDP_PORT,
SCAN_INTERVAL,
)
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_ICON,
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
SERVICE_TURN_ON, SERVICE_TURN_ON,
STATE_OFF, STATE_OFF,
STATE_ON, STATE_ON,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
EntityCategory,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from tests.common import async_fire_time_changed, load_fixture from tests.common import async_fire_time_changed, load_fixture
@ -33,163 +26,106 @@ from tests.common import async_fire_time_changed, load_fixture
pytestmark = pytest.mark.usefixtures("init_integration") pytestmark = pytest.mark.usefixtures("init_integration")
@pytest.mark.parametrize(
("entity_id", "method", "called_with_on", "called_with_off"),
[
(
"switch.wled_rgb_light_nightlight",
"nightlight",
{"on": True},
{"on": False},
),
(
"switch.wled_rgb_light_reverse",
"segment",
{"segment_id": 0, "reverse": True},
{"segment_id": 0, "reverse": False},
),
(
"switch.wled_rgb_light_sync_receive",
"sync",
{"receive": True},
{"receive": False},
),
(
"switch.wled_rgb_light_sync_send",
"sync",
{"send": True},
{"send": False},
),
],
)
async def test_switch_state( async def test_switch_state(
hass: HomeAssistant, entity_registry: er.EntityRegistry hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
mock_wled: MagicMock,
entity_id: str,
method: str,
called_with_on: dict[str, bool | int],
called_with_off: dict[str, bool | int],
) -> None: ) -> None:
"""Test the creation and values of the WLED switches.""" """Test the creation and values of the WLED switches."""
assert (state := hass.states.get("switch.wled_rgb_light_nightlight")) assert (state := hass.states.get(entity_id))
assert state.attributes.get(ATTR_DURATION) == 60 assert state == snapshot
assert state.attributes.get(ATTR_ICON) == "mdi:weather-night"
assert state.attributes.get(ATTR_TARGET_BRIGHTNESS) == 0
assert state.attributes.get(ATTR_FADE)
assert state.state == STATE_OFF
assert (entry := entity_registry.async_get("switch.wled_rgb_light_nightlight")) assert (entity_entry := entity_registry.async_get(state.entity_id))
assert entry.unique_id == "aabbccddeeff_nightlight" assert entity_entry == snapshot
assert entry.entity_category is EntityCategory.CONFIG
assert (state := hass.states.get("switch.wled_rgb_light_sync_send")) assert entity_entry.device_id
assert state.attributes.get(ATTR_ICON) == "mdi:upload-network-outline" assert (device_entry := device_registry.async_get(entity_entry.device_id))
assert state.attributes.get(ATTR_UDP_PORT) == 21324 assert device_entry == snapshot
assert state.state == STATE_OFF
assert (entry := entity_registry.async_get("switch.wled_rgb_light_sync_send")) # Test on/off services
assert entry.unique_id == "aabbccddeeff_sync_send" method_mock = getattr(mock_wled, method)
assert entry.entity_category is EntityCategory.CONFIG
assert (state := hass.states.get("switch.wled_rgb_light_sync_receive"))
assert state.attributes.get(ATTR_ICON) == "mdi:download-network-outline"
assert state.attributes.get(ATTR_UDP_PORT) == 21324
assert state.state == STATE_ON
assert (entry := entity_registry.async_get("switch.wled_rgb_light_sync_receive"))
assert entry.unique_id == "aabbccddeeff_sync_receive"
assert entry.entity_category is EntityCategory.CONFIG
assert (state := hass.states.get("switch.wled_rgb_light_reverse"))
assert state.attributes.get(ATTR_ICON) == "mdi:swap-horizontal-bold"
assert state.state == STATE_OFF
assert (entry := entity_registry.async_get("switch.wled_rgb_light_reverse"))
assert entry.unique_id == "aabbccddeeff_reverse_0"
assert entry.entity_category is EntityCategory.CONFIG
async def test_switch_change_state(hass: HomeAssistant, mock_wled: MagicMock) -> None:
"""Test the change of state of the WLED switches."""
# Nightlight
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_nightlight"},
blocking=True,
)
assert mock_wled.nightlight.call_count == 1
mock_wled.nightlight.assert_called_with(on=True)
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_nightlight"},
blocking=True,
)
assert mock_wled.nightlight.call_count == 2
mock_wled.nightlight.assert_called_with(on=False)
# Sync send
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_sync_send"},
blocking=True,
)
assert mock_wled.sync.call_count == 1
mock_wled.sync.assert_called_with(send=True)
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_sync_send"},
blocking=True,
)
assert mock_wled.sync.call_count == 2
mock_wled.sync.assert_called_with(send=False)
# Sync receive
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_sync_receive"},
blocking=True,
)
assert mock_wled.sync.call_count == 3
mock_wled.sync.assert_called_with(receive=False)
await hass.services.async_call( await hass.services.async_call(
SWITCH_DOMAIN, SWITCH_DOMAIN,
SERVICE_TURN_ON, SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_sync_receive"}, {ATTR_ENTITY_ID: state.entity_id},
blocking=True, blocking=True,
) )
assert mock_wled.sync.call_count == 4
mock_wled.sync.assert_called_with(receive=True)
await hass.services.async_call( assert method_mock.call_count == 1
SWITCH_DOMAIN, method_mock.assert_called_with(**called_with_on)
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_reverse"},
blocking=True,
)
assert mock_wled.segment.call_count == 1
mock_wled.segment.assert_called_with(segment_id=0, reverse=True)
await hass.services.async_call( await hass.services.async_call(
SWITCH_DOMAIN, SWITCH_DOMAIN,
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_reverse"}, {ATTR_ENTITY_ID: state.entity_id},
blocking=True, blocking=True,
) )
assert mock_wled.segment.call_count == 2
mock_wled.segment.assert_called_with(segment_id=0, reverse=False)
assert method_mock.call_count == 2
method_mock.assert_called_with(**called_with_off)
async def test_switch_error( # Test invalid response, not becoming unavailable
hass: HomeAssistant, method_mock.side_effect = WLEDError
mock_wled: MagicMock,
) -> None:
"""Test error handling of the WLED switches."""
mock_wled.nightlight.side_effect = WLEDError
with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"):
await hass.services.async_call( await hass.services.async_call(
SWITCH_DOMAIN, SWITCH_DOMAIN,
SERVICE_TURN_ON, SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_nightlight"}, {ATTR_ENTITY_ID: state.entity_id},
blocking=True, blocking=True,
) )
state = hass.states.get("switch.wled_rgb_light_nightlight") assert method_mock.call_count == 3
assert state assert (state := hass.states.get(state.entity_id))
assert state.state == STATE_OFF assert state.state != STATE_UNAVAILABLE
async def test_switch_connection_error(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test error handling of the WLED switches."""
mock_wled.nightlight.side_effect = WLEDConnectionError
# Test connection error, leading to becoming unavailable
method_mock.side_effect = WLEDConnectionError
with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"):
await hass.services.async_call( await hass.services.async_call(
SWITCH_DOMAIN, SWITCH_DOMAIN,
SERVICE_TURN_ON, SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.wled_rgb_light_nightlight"}, {ATTR_ENTITY_ID: state.entity_id},
blocking=True, blocking=True,
) )
assert (state := hass.states.get("switch.wled_rgb_light_nightlight")) assert method_mock.call_count == 4
assert (state := hass.states.get(state.entity_id))
assert state.state == STATE_UNAVAILABLE assert state.state == STATE_UNAVAILABLE
@ -199,11 +135,10 @@ async def test_switch_dynamically_handle_segments(
mock_wled: MagicMock, mock_wled: MagicMock,
) -> None: ) -> None:
"""Test if a new/deleted segment is dynamically added/removed.""" """Test if a new/deleted segment is dynamically added/removed."""
segment0 = hass.states.get("switch.wled_rgb_light_reverse")
segment1 = hass.states.get("switch.wled_rgb_light_segment_1_reverse") assert (segment0 := hass.states.get("switch.wled_rgb_light_reverse"))
assert segment0
assert segment0.state == STATE_OFF assert segment0.state == STATE_OFF
assert not segment1 assert not hass.states.get("switch.wled_rgb_light_segment_1_reverse")
# Test adding a segment dynamically... # Test adding a segment dynamically...
return_value = mock_wled.update.return_value return_value = mock_wled.update.return_value
@ -214,11 +149,9 @@ async def test_switch_dynamically_handle_segments(
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL) async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done() await hass.async_block_till_done()
segment0 = hass.states.get("switch.wled_rgb_light_reverse") assert (segment0 := hass.states.get("switch.wled_rgb_light_reverse"))
segment1 = hass.states.get("switch.wled_rgb_light_segment_1_reverse")
assert segment0
assert segment0.state == STATE_OFF assert segment0.state == STATE_OFF
assert segment1 assert (segment1 := hass.states.get("switch.wled_rgb_light_segment_1_reverse"))
assert segment1.state == STATE_ON assert segment1.state == STATE_ON
# Test remove segment again... # Test remove segment again...
@ -226,9 +159,7 @@ async def test_switch_dynamically_handle_segments(
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL) async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done() await hass.async_block_till_done()
segment0 = hass.states.get("switch.wled_rgb_light_reverse") assert (segment0 := hass.states.get("switch.wled_rgb_light_reverse"))
segment1 = hass.states.get("switch.wled_rgb_light_segment_1_reverse")
assert segment0
assert segment0.state == STATE_OFF assert segment0.state == STATE_OFF
assert segment1 assert (segment1 := hass.states.get("switch.wled_rgb_light_segment_1_reverse"))
assert segment1.state == STATE_UNAVAILABLE assert segment1.state == STATE_UNAVAILABLE