Files
core/tests/components/redgtech/test_switch.py
Jonathan Sady do Nascimento 184bea49e2 Add redgtech integration (#136947)
Co-authored-by: luan-nvg <luannnvg@gmail.com>
2026-02-05 09:04:14 +01:00

256 lines
7.7 KiB
Python

"""Tests for the Redgtech switch platform."""
from datetime import timedelta
from unittest.mock import MagicMock
from freezegun import freeze_time
from freezegun.api import FrozenDateTimeFactory
import pytest
from redgtech_api.api import RedgtechAuthError, RedgtechConnectionError
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, snapshot_platform
@pytest.fixture
def freezer():
"""Provide a freezer fixture that works with freeze_time decorator."""
with freeze_time() as frozen_time:
yield frozen_time
@pytest.fixture
async def setup_redgtech_integration(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_redgtech_api: MagicMock,
) -> MagicMock:
"""Set up the Redgtech integration with mocked API."""
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
return mock_redgtech_api
async def test_entities(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
setup_redgtech_integration,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test entity setup."""
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_switch_turn_on(
hass: HomeAssistant,
setup_redgtech_integration: MagicMock,
) -> None:
"""Test turning a switch on."""
mock_api = setup_redgtech_integration
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.living_room_switch"},
blocking=True,
)
mock_api.set_switch_state.assert_called_once_with(
"switch_001", True, "mock_access_token"
)
async def test_switch_turn_off(
hass: HomeAssistant,
setup_redgtech_integration: MagicMock,
) -> None:
"""Test turning a switch off."""
mock_api = setup_redgtech_integration
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "switch.kitchen_switch"},
blocking=True,
)
mock_api.set_switch_state.assert_called_once_with(
"switch_002", False, "mock_access_token"
)
async def test_switch_toggle(
hass: HomeAssistant,
setup_redgtech_integration: MagicMock,
) -> None:
"""Test toggling a switch."""
mock_api = setup_redgtech_integration
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TOGGLE,
{ATTR_ENTITY_ID: "switch.living_room_switch"},
blocking=True,
)
mock_api.set_switch_state.assert_called_once_with(
"switch_001", True, "mock_access_token"
)
@pytest.mark.parametrize(
("exception", "error_message"),
[
(
RedgtechConnectionError("Connection failed"),
"Connection error with Redgtech API",
),
(
RedgtechAuthError("Auth failed"),
"Authentication failed when controlling Redgtech switch",
),
],
)
async def test_exception_handling(
hass: HomeAssistant,
setup_redgtech_integration: MagicMock,
exception: Exception,
error_message: str,
) -> None:
"""Test exception handling when controlling switches."""
mock_api = setup_redgtech_integration
mock_api.set_switch_state.side_effect = exception
with pytest.raises(HomeAssistantError, match=error_message):
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.living_room_switch"},
blocking=True,
)
async def test_switch_auth_error_with_retry(
hass: HomeAssistant,
setup_redgtech_integration: MagicMock,
) -> None:
"""Test handling auth errors with token renewal."""
mock_api = setup_redgtech_integration
# Mock fails with auth error
mock_api.set_switch_state.side_effect = RedgtechAuthError("Auth failed")
# Expect HomeAssistantError to be raised
with pytest.raises(
HomeAssistantError,
match="Authentication failed when controlling Redgtech switch",
):
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "switch.living_room_switch"},
blocking=True,
)
@freeze_time("2023-01-01 12:00:00")
async def test_coordinator_data_update_success(
hass: HomeAssistant,
setup_redgtech_integration: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test successful data update through coordinator."""
mock_api = setup_redgtech_integration
# Update mock data
mock_api.get_data.return_value = {
"boards": [
{
"endpointId": "switch_001",
"friendlyName": "Living Room Switch",
"value": True, # Changed to True
"displayCategories": ["SWITCH"],
}
]
}
# Use freezer to advance time and trigger update
freezer.tick(delta=timedelta(minutes=2))
await hass.async_block_till_done()
# Verify the entity state was updated successfully
living_room_state = hass.states.get("switch.living_room_switch")
assert living_room_state is not None
assert living_room_state.state == "on"
@freeze_time("2023-01-01 12:00:00")
async def test_coordinator_connection_error_during_update(
hass: HomeAssistant,
setup_redgtech_integration: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test coordinator handling connection errors during data updates."""
mock_api = setup_redgtech_integration
mock_api.get_data.side_effect = RedgtechConnectionError("Connection failed")
# Use freezer to advance time and trigger update
freezer.tick(delta=timedelta(minutes=2))
await hass.async_block_till_done()
# Verify entities become unavailable due to coordinator error
living_room_state = hass.states.get("switch.living_room_switch")
kitchen_state = hass.states.get("switch.kitchen_switch")
assert living_room_state.state == STATE_UNAVAILABLE
assert kitchen_state.state == STATE_UNAVAILABLE
@freeze_time("2023-01-01 12:00:00")
async def test_coordinator_auth_error_with_token_renewal(
hass: HomeAssistant,
setup_redgtech_integration: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test coordinator handling auth errors with token renewal."""
mock_api = setup_redgtech_integration
# First call fails with auth error, second succeeds after token renewal
mock_api.get_data.side_effect = [
RedgtechAuthError("Auth failed"),
{
"boards": [
{
"endpointId": "switch_001",
"friendlyName": "Living Room Switch",
"value": True,
"displayCategories": ["SWITCH"],
}
]
},
]
# Use freezer to advance time and trigger update
freezer.tick(delta=timedelta(minutes=2))
await hass.async_block_till_done()
# Verify token renewal was attempted
assert mock_api.login.call_count >= 2
# Verify entity is available after successful token renewal
living_room_state = hass.states.get("switch.living_room_switch")
assert living_room_state is not None
assert living_room_state.state != STATE_UNAVAILABLE