Refactor WLED number tests (#88582)

This commit is contained in:
Franck Nijhof 2023-03-06 01:49:01 +01:00 committed by GitHub
parent a0ff95cef8
commit ff485d4648
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 426 additions and 209 deletions

View file

@ -0,0 +1,335 @@
# serializer version: 1
# name: test_numbers[number.wled_rgb_light_segment_1_intensity-42-intensity]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Segment 1 Intensity',
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'context': <ANY>,
'entity_id': 'number.wled_rgb_light_segment_1_intensity',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '64',
})
# ---
# name: test_numbers[number.wled_rgb_light_segment_1_intensity-42-intensity].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.wled_rgb_light_segment_1_intensity',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Segment 1 Intensity',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_intensity_1',
'unit_of_measurement': None,
})
# ---
# name: test_numbers[number.wled_rgb_light_segment_1_intensity-42-intensity].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_numbers[number.wled_rgb_light_segment_1_speed-42-speed]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Segment 1 Speed',
'icon': 'mdi:speedometer',
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'context': <ANY>,
'entity_id': 'number.wled_rgb_light_segment_1_speed',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '16',
})
# ---
# name: test_numbers[number.wled_rgb_light_segment_1_speed-42-speed].1
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.wled_rgb_light_segment_1_speed',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': 'mdi:speedometer',
'original_name': 'Segment 1 Speed',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_speed_1',
'unit_of_measurement': None,
})
# ---
# name: test_numbers[number.wled_rgb_light_segment_1_speed-42-speed].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_speed_state[number.wled_rgb_light_segment_1_intensity-42-intensity]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Segment 1 Intensity',
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'context': <ANY>,
'entity_id': 'number.wled_rgb_light_segment_1_intensity',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '64',
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_intensity-42-intensity].1
EntityRegistryEntrySnapshot({
'_display_repr': <UndefinedType._singleton: 0>,
'_partial_repr': <UndefinedType._singleton: 0>,
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.wled_rgb_light_segment_1_intensity',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Segment 1 Intensity',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_intensity_1',
'unit_of_measurement': None,
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_intensity-42-intensity].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_speed_state[number.wled_rgb_light_segment_1_speed-42-speed]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Segment 1 Speed',
'icon': 'mdi:speedometer',
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'context': <ANY>,
'entity_id': 'number.wled_rgb_light_segment_1_speed',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '16',
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_speed-42-speed].1
EntityRegistryEntrySnapshot({
'_display_repr': <UndefinedType._singleton: 0>,
'_partial_repr': <UndefinedType._singleton: 0>,
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.wled_rgb_light_segment_1_speed',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': 'mdi:speedometer',
'original_name': 'Segment 1 Speed',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_speed_1',
'unit_of_measurement': None,
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_speed-42-speed].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,21 +3,19 @@ import json
from unittest.mock import MagicMock
import pytest
from syrupy.assertion import SnapshotAssertion
from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError
from homeassistant.components.number import (
ATTR_MAX,
ATTR_MIN,
ATTR_STEP,
ATTR_VALUE,
DOMAIN as NUMBER_DOMAIN,
SERVICE_SET_VALUE,
)
from homeassistant.components.wled.const import SCAN_INTERVAL
from homeassistant.const import ATTR_ENTITY_ID, ATTR_ICON, STATE_UNAVAILABLE
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant
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
from tests.common import async_fire_time_changed, load_fixture
@ -25,52 +23,106 @@ from tests.common import async_fire_time_changed, load_fixture
pytestmark = pytest.mark.usefixtures("init_integration")
async def test_speed_state(
hass: HomeAssistant, entity_registry: er.EntityRegistry
@pytest.mark.parametrize(
("entity_id", "value", "called_arg"),
[
("number.wled_rgb_light_segment_1_speed", 42, "speed"),
("number.wled_rgb_light_segment_1_intensity", 42, "intensity"),
],
)
async def test_numbers(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
mock_wled: MagicMock,
entity_id: str,
value: int,
called_arg: str,
) -> None:
"""Test the creation and values of the WLED numbers."""
# First segment of the strip
assert (state := hass.states.get("number.wled_rgb_light_segment_1_speed"))
assert state.attributes.get(ATTR_ICON) == "mdi:speedometer"
assert state.attributes.get(ATTR_MAX) == 255
assert state.attributes.get(ATTR_MIN) == 0
assert state.attributes.get(ATTR_STEP) == 1
assert state.state == "16"
assert (state := hass.states.get(entity_id))
assert state == snapshot
assert (entry := entity_registry.async_get("number.wled_rgb_light_segment_1_speed"))
assert entry.unique_id == "aabbccddeeff_speed_1"
assert (entity_entry := entity_registry.async_get(state.entity_id))
assert entity_entry == snapshot
assert entity_entry.device_id
assert (device_entry := device_registry.async_get(entity_entry.device_id))
assert device_entry == snapshot
async def test_speed_segment_change_state(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test the value change of the WLED segments."""
# Test a regular state change service call
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_speed",
ATTR_VALUE: 42,
},
{ATTR_ENTITY_ID: entity_id, ATTR_VALUE: value},
blocking=True,
)
assert mock_wled.segment.call_count == 1
mock_wled.segment.assert_called_with(
segment_id=1,
speed=42,
)
mock_wled.segment.assert_called_with(segment_id=1, **{called_arg: value})
# Test with WLED error
mock_wled.segment.side_effect = WLEDError
with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: entity_id, ATTR_VALUE: value},
blocking=True,
)
assert mock_wled.segment.call_count == 2
# Ensure the entity is still available
assert (state := hass.states.get(entity_id))
assert state.state != STATE_UNAVAILABLE
# Test when a connection error occurs
mock_wled.segment.side_effect = WLEDConnectionError
with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: entity_id, ATTR_VALUE: value},
blocking=True,
)
assert mock_wled.segment.call_count == 3
# Ensure the entity became unavailable after the connection error
assert (state := hass.states.get(entity_id))
assert state.state == STATE_UNAVAILABLE
@pytest.mark.parametrize("device_fixture", ["rgb_single_segment"])
@pytest.mark.parametrize(
("entity_id_segment0", "state_segment0", "entity_id_segment1", "state_segment1"),
[
(
"number.wled_rgb_light_speed",
"32",
"number.wled_rgb_light_segment_1_speed",
"16",
),
(
"number.wled_rgb_light_intensity",
"128",
"number.wled_rgb_light_segment_1_intensity",
"64",
),
],
)
async def test_speed_dynamically_handle_segments(
hass: HomeAssistant,
mock_wled: MagicMock,
entity_id_segment0: str,
entity_id_segment1: str,
state_segment0: str,
state_segment1: str,
) -> None:
"""Test if a new/deleted segment is dynamically added/removed."""
assert (segment0 := hass.states.get("number.wled_rgb_light_speed"))
assert segment0.state == "32"
assert not hass.states.get("number.wled_rgb_light_segment_1_speed")
assert (segment0 := hass.states.get(entity_id_segment0))
assert segment0.state == state_segment0
assert not hass.states.get(entity_id_segment1)
# Test adding a segment dynamically...
return_value = mock_wled.update.return_value
@ -81,187 +133,17 @@ async def test_speed_dynamically_handle_segments(
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
assert (segment0 := hass.states.get("number.wled_rgb_light_speed"))
assert segment0.state == "32"
assert (segment1 := hass.states.get("number.wled_rgb_light_segment_1_speed"))
assert segment1.state == "16"
assert (segment0 := hass.states.get(entity_id_segment0))
assert segment0.state == state_segment0
assert (segment1 := hass.states.get(entity_id_segment1))
assert segment1.state == state_segment1
# Test remove segment again...
mock_wled.update.return_value = return_value
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
assert (segment0 := hass.states.get("number.wled_rgb_light_speed"))
assert segment0.state == "32"
assert (segment1 := hass.states.get("number.wled_rgb_light_segment_1_speed"))
assert (segment0 := hass.states.get(entity_id_segment0))
assert segment0.state == state_segment0
assert (segment1 := hass.states.get(entity_id_segment1))
assert segment1.state == STATE_UNAVAILABLE
async def test_speed_error(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test error handling of the WLED numbers."""
mock_wled.segment.side_effect = WLEDError
with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_speed",
ATTR_VALUE: 42,
},
blocking=True,
)
assert (state := hass.states.get("number.wled_rgb_light_segment_1_speed"))
assert state.state == "16"
assert mock_wled.segment.call_count == 1
mock_wled.segment.assert_called_with(segment_id=1, speed=42)
async def test_speed_connection_error(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test error handling of the WLED numbers."""
mock_wled.segment.side_effect = WLEDConnectionError
with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_speed",
ATTR_VALUE: 42,
},
blocking=True,
)
assert (state := hass.states.get("number.wled_rgb_light_segment_1_speed"))
assert state.state == STATE_UNAVAILABLE
assert mock_wled.segment.call_count == 1
mock_wled.segment.assert_called_with(segment_id=1, speed=42)
async def test_intensity_state(
hass: HomeAssistant, entity_registry: er.EntityRegistry
) -> None:
"""Test the creation and values of the WLED numbers."""
# First segment of the strip
assert (state := hass.states.get("number.wled_rgb_light_segment_1_intensity"))
assert state.attributes.get(ATTR_ICON) is None
assert state.attributes.get(ATTR_MAX) == 255
assert state.attributes.get(ATTR_MIN) == 0
assert state.attributes.get(ATTR_STEP) == 1
assert state.state == "64"
assert (
entry := entity_registry.async_get("number.wled_rgb_light_segment_1_intensity")
)
assert entry.unique_id == "aabbccddeeff_intensity_1"
async def test_intensity_segment_change_state(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test the value change of the WLED segments."""
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_intensity",
ATTR_VALUE: 128,
},
blocking=True,
)
assert mock_wled.segment.call_count == 1
mock_wled.segment.assert_called_with(
segment_id=1,
intensity=128,
)
@pytest.mark.parametrize("device_fixture", ["rgb_single_segment"])
async def test_intensity_dynamically_handle_segments(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test if a new/deleted segment is dynamically added/removed."""
assert (segment0 := hass.states.get("number.wled_rgb_light_intensity"))
assert segment0.state == "128"
assert not hass.states.get("number.wled_rgb_light_segment_1_intensity")
# Test adding a segment dynamically...
return_value = mock_wled.update.return_value
mock_wled.update.return_value = WLEDDevice(
json.loads(load_fixture("wled/rgb.json"))
)
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
assert (segment0 := hass.states.get("number.wled_rgb_light_intensity"))
assert segment0.state == "128"
assert (segment1 := hass.states.get("number.wled_rgb_light_segment_1_intensity"))
assert segment1.state == "64"
# Test remove segment again...
mock_wled.update.return_value = return_value
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
assert (segment0 := hass.states.get("number.wled_rgb_light_intensity"))
assert segment0.state == "128"
assert (segment1 := hass.states.get("number.wled_rgb_light_segment_1_intensity"))
assert segment1.state == STATE_UNAVAILABLE
async def test_intensity_error(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test error handling of the WLED numbers."""
mock_wled.segment.side_effect = WLEDError
with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_intensity",
ATTR_VALUE: 21,
},
blocking=True,
)
assert (state := hass.states.get("number.wled_rgb_light_segment_1_intensity"))
assert state.state == "64"
assert mock_wled.segment.call_count == 1
mock_wled.segment.assert_called_with(segment_id=1, intensity=21)
async def test_intensity_connection_error(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test error handling of the WLED numbers."""
mock_wled.segment.side_effect = WLEDConnectionError
with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{
ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_intensity",
ATTR_VALUE: 128,
},
blocking=True,
)
assert (state := hass.states.get("number.wled_rgb_light_segment_1_intensity"))
assert state.state == STATE_UNAVAILABLE
assert mock_wled.segment.call_count == 1
mock_wled.segment.assert_called_with(segment_id=1, intensity=128)