mirror of
https://github.com/home-assistant/core.git
synced 2026-09-24 07:25:52 -05:00
Refactor Ultima fixtures to reduce duplication (#167731)
This commit is contained in:
@@ -124,6 +124,19 @@ def mock_smlight_client(request: pytest.FixtureRequest) -> Generator[MagicMock]:
|
||||
yield api
|
||||
|
||||
|
||||
MOCK_ULTIMA = Info(
|
||||
MAC="AA:BB:CC:DD:EE:FF",
|
||||
model="SLZB-Ultima3",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_ultima_client(mock_smlight_client: MagicMock) -> MagicMock:
|
||||
"""Configure api client to return an Ultima device."""
|
||||
mock_smlight_client.get_info.side_effect = lambda *arg, **kwargs: MOCK_ULTIMA
|
||||
return mock_smlight_client
|
||||
|
||||
|
||||
async def setup_integration(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from infrared_protocols import Command, Timing
|
||||
from pysmlight import Info
|
||||
from pysmlight.exceptions import SmlightError
|
||||
from pysmlight.models import IRPayload
|
||||
import pytest
|
||||
@@ -36,20 +35,12 @@ def platforms() -> list[Platform]:
|
||||
return [Platform.INFRARED]
|
||||
|
||||
|
||||
MOCK_ULTIMA = Info(
|
||||
MAC="AA:BB:CC:DD:EE:FF",
|
||||
model="SLZB-Ultima3",
|
||||
)
|
||||
|
||||
|
||||
async def test_infrared_setup_ultima(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test infrared entity is created for Ultima devices."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
state = hass.states.get("infrared.mock_title_ir_emitter")
|
||||
@@ -62,11 +53,6 @@ async def test_infrared_not_created_non_ultima(
|
||||
mock_smlight_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test infrared entity is not created for non-Ultima devices."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = Info(
|
||||
MAC="AA:BB:CC:DD:EE:FF",
|
||||
model="SLZB-MR1",
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
state = hass.states.get("infrared.mock_title_ir_emitter")
|
||||
@@ -76,11 +62,9 @@ async def test_infrared_not_created_non_ultima(
|
||||
async def test_infrared_send_command(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test sending IR command."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "infrared.mock_title_ir_emitter"
|
||||
@@ -93,7 +77,7 @@ async def test_infrared_send_command(
|
||||
MockCommand(),
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.send_ir_code.assert_called_once_with(
|
||||
mock_ultima_client.actions.send_ir_code.assert_called_once_with(
|
||||
IRPayload.from_raw_timings([9000, 4500, 560, 1690], freq=38000)
|
||||
)
|
||||
|
||||
@@ -101,18 +85,16 @@ async def test_infrared_send_command(
|
||||
async def test_infrared_send_command_error(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test connection error handling."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "infrared.mock_title_ir_emitter"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
|
||||
mock_smlight_client.actions.send_ir_code.side_effect = SmlightError("Failed")
|
||||
mock_ultima_client.actions.send_ir_code.side_effect = SmlightError("Failed")
|
||||
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await async_send_command(
|
||||
@@ -126,18 +108,16 @@ async def test_infrared_send_command_error(
|
||||
async def test_infrared_send_empty_command_error(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test ValueError from pysmlight is surfaced as HomeAssistantError."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "infrared.mock_title_ir_emitter"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
|
||||
mock_smlight_client.actions.send_ir_code.side_effect = ValueError("empty payload")
|
||||
mock_ultima_client.actions.send_ir_code.side_effect = ValueError("empty payload")
|
||||
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await async_send_command(
|
||||
@@ -152,11 +132,9 @@ async def test_infrared_send_empty_command_error(
|
||||
async def test_infrared_state_updated_after_send(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test that entity state is updated with a timestamp after a successful send."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "infrared.mock_title_ir_emitter"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from collections.abc import Awaitable, Callable
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from pysmlight import Info
|
||||
from pysmlight.const import AmbiEffect
|
||||
from pysmlight.exceptions import SmlightConnectionError
|
||||
from pysmlight.models import AmbilightPayload
|
||||
@@ -40,17 +39,11 @@ def platforms() -> Platform | list[Platform]:
|
||||
return [Platform.LIGHT]
|
||||
|
||||
|
||||
MOCK_ULTIMA = Info(
|
||||
MAC="AA:BB:CC:DD:EE:FF",
|
||||
model="SLZB-Ultima3",
|
||||
)
|
||||
|
||||
|
||||
def _build_fire_sse_ambilight(
|
||||
hass: HomeAssistant, mock_smlight_client: MagicMock
|
||||
hass: HomeAssistant, mock_ultima_client: MagicMock
|
||||
) -> Callable[[dict[str, object]], Awaitable[None]]:
|
||||
"""Build helper to push ambilight SSE events and wait for state updates."""
|
||||
page_callback = mock_smlight_client.sse.register_page_cb.call_args[0][1]
|
||||
page_callback = mock_ultima_client.sse.register_page_cb.call_args[0][1]
|
||||
|
||||
async def fire_ambi(changes: dict[str, object]) -> None:
|
||||
page_callback(changes)
|
||||
@@ -63,12 +56,10 @@ async def test_light_setup_ultima(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test light entity is created for Ultima devices."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
entry = await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||
@@ -82,11 +73,6 @@ async def test_light_not_created_non_ultima(
|
||||
mock_smlight_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test light entity is not created for non-Ultima devices."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = Info(
|
||||
MAC="AA:BB:CC:DD:EE:FF",
|
||||
model="SLZB-MR1",
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
state = hass.states.get("light.mock_title_ambilight")
|
||||
@@ -96,20 +82,18 @@ async def test_light_not_created_non_ultima(
|
||||
async def test_light_turn_on_off(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test turning light on and off."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "light.mock_title_ambilight"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_smlight_client)
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_ultima_client)
|
||||
|
||||
mock_smlight_client.actions.ambilight.reset_mock()
|
||||
mock_ultima_client.actions.ambilight.reset_mock()
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
@@ -117,7 +101,7 @@ async def test_light_turn_on_off(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.assert_called_once_with(
|
||||
mock_ultima_client.actions.ambilight.assert_called_once_with(
|
||||
AmbilightPayload(ultLedMode=AmbiEffect.WSULT_SOLID)
|
||||
)
|
||||
await fire_ambi({"ultLedMode": 0, "ultLedBri": 158, "ultLedColor": 0x7FACFF})
|
||||
@@ -125,7 +109,7 @@ async def test_light_turn_on_off(
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == STATE_ON
|
||||
|
||||
mock_smlight_client.actions.ambilight.reset_mock()
|
||||
mock_ultima_client.actions.ambilight.reset_mock()
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
@@ -133,7 +117,7 @@ async def test_light_turn_on_off(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.assert_called_once_with(
|
||||
mock_ultima_client.actions.ambilight.assert_called_once_with(
|
||||
AmbilightPayload(ultLedMode=AmbiEffect.WSULT_OFF)
|
||||
)
|
||||
await fire_ambi({"ultLedMode": 1})
|
||||
@@ -145,20 +129,18 @@ async def test_light_turn_on_off(
|
||||
async def test_light_brightness(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test setting brightness."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "light.mock_title_ambilight"
|
||||
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_smlight_client)
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_ultima_client)
|
||||
|
||||
# Seed current state as on so brightness-only update does not force solid mode.
|
||||
await fire_ambi({"ultLedMode": 0, "ultLedBri": 158, "ultLedColor": 0x7FACFF})
|
||||
mock_smlight_client.actions.ambilight.reset_mock()
|
||||
mock_ultima_client.actions.ambilight.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
@@ -167,7 +149,7 @@ async def test_light_brightness(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.assert_called_once_with(
|
||||
mock_ultima_client.actions.ambilight.assert_called_once_with(
|
||||
AmbilightPayload(ultLedBri=200)
|
||||
)
|
||||
await fire_ambi({"ultLedMode": 0, "ultLedBri": 200, "ultLedColor": 0x7FACFF})
|
||||
@@ -180,18 +162,16 @@ async def test_light_brightness(
|
||||
async def test_light_rgb_color(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test setting RGB color."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "light.mock_title_ambilight"
|
||||
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_smlight_client)
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_ultima_client)
|
||||
|
||||
mock_smlight_client.actions.ambilight.reset_mock()
|
||||
mock_ultima_client.actions.ambilight.reset_mock()
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
@@ -199,7 +179,7 @@ async def test_light_rgb_color(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.assert_called_once_with(
|
||||
mock_ultima_client.actions.ambilight.assert_called_once_with(
|
||||
AmbilightPayload(ultLedMode=AmbiEffect.WSULT_SOLID, ultLedColor="#ff8040")
|
||||
)
|
||||
await fire_ambi({"ultLedMode": 0, "ultLedColor": 0xFF8040})
|
||||
@@ -212,19 +192,17 @@ async def test_light_rgb_color(
|
||||
async def test_light_effect(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test setting effect."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "light.mock_title_ambilight"
|
||||
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_smlight_client)
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_ultima_client)
|
||||
|
||||
# Test Rainbow effect
|
||||
mock_smlight_client.actions.ambilight.reset_mock()
|
||||
mock_ultima_client.actions.ambilight.reset_mock()
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
@@ -232,13 +210,13 @@ async def test_light_effect(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.assert_called_once_with(
|
||||
mock_ultima_client.actions.ambilight.assert_called_once_with(
|
||||
AmbilightPayload(ultLedMode=AmbiEffect.WSULT_RAINBOW)
|
||||
)
|
||||
await fire_ambi({"ultLedMode": 3})
|
||||
|
||||
# Test Blur effect
|
||||
mock_smlight_client.actions.ambilight.reset_mock()
|
||||
mock_ultima_client.actions.ambilight.reset_mock()
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
@@ -246,7 +224,7 @@ async def test_light_effect(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.assert_called_once_with(
|
||||
mock_ultima_client.actions.ambilight.assert_called_once_with(
|
||||
AmbilightPayload(ultLedMode=AmbiEffect.WSULT_BLUR)
|
||||
)
|
||||
await fire_ambi({"ultLedMode": 2})
|
||||
@@ -259,16 +237,14 @@ async def test_light_effect(
|
||||
async def test_light_invalid_effect(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test handling of invalid effect name is ignored."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "light.mock_title_ambilight"
|
||||
|
||||
mock_smlight_client.actions.ambilight.reset_mock()
|
||||
mock_ultima_client.actions.ambilight.reset_mock()
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
@@ -276,25 +252,23 @@ async def test_light_invalid_effect(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.assert_not_called()
|
||||
mock_ultima_client.actions.ambilight.assert_not_called()
|
||||
|
||||
|
||||
async def test_light_turn_on_when_on_is_noop(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test calling turn_on with no attributes does nothing when already on."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "light.mock_title_ambilight"
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_smlight_client)
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_ultima_client)
|
||||
|
||||
await fire_ambi({"ultLedMode": 0})
|
||||
|
||||
mock_smlight_client.actions.ambilight.reset_mock()
|
||||
mock_ultima_client.actions.ambilight.reset_mock()
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
@@ -302,21 +276,19 @@ async def test_light_turn_on_when_on_is_noop(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.assert_not_called()
|
||||
mock_ultima_client.actions.ambilight.assert_not_called()
|
||||
|
||||
|
||||
async def test_light_state_handles_invalid_attributes_from_sse(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test state update gracefully handles invalid mode and invalid hex color."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "light.mock_title_ambilight"
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_smlight_client)
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_ultima_client)
|
||||
|
||||
await fire_ambi({"ultLedMode": None, "ultLedColor": "#GG0000"})
|
||||
|
||||
@@ -335,18 +307,16 @@ async def test_light_state_handles_invalid_attributes_from_sse(
|
||||
async def test_ambilight_connection_error(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_smlight_client: MagicMock,
|
||||
mock_ultima_client: MagicMock,
|
||||
) -> None:
|
||||
"""Test connection error handling."""
|
||||
mock_smlight_client.get_info.side_effect = None
|
||||
mock_smlight_client.get_info.return_value = MOCK_ULTIMA
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
entity_id = "light.mock_title_ambilight"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
mock_smlight_client.actions.ambilight.side_effect = SmlightConnectionError
|
||||
mock_ultima_client.actions.ambilight.side_effect = SmlightConnectionError
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
@@ -356,10 +326,10 @@ async def test_ambilight_connection_error(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.side_effect = None
|
||||
mock_smlight_client.actions.ambilight.reset_mock()
|
||||
mock_ultima_client.actions.ambilight.side_effect = None
|
||||
mock_ultima_client.actions.ambilight.reset_mock()
|
||||
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_smlight_client)
|
||||
fire_ambi = _build_fire_sse_ambilight(hass, mock_ultima_client)
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
@@ -368,7 +338,7 @@ async def test_ambilight_connection_error(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_smlight_client.actions.ambilight.assert_called_once_with(
|
||||
mock_ultima_client.actions.ambilight.assert_called_once_with(
|
||||
AmbilightPayload(ultLedMode=AmbiEffect.WSULT_SOLID)
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user