1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 21:06:19 +00:00
Files

272 lines
8.3 KiB
Python

"""Test Home Assistant Hardware beta firmware switch entity."""
from __future__ import annotations
from collections.abc import AsyncGenerator
from unittest.mock import Mock, call, patch
import pytest
from homeassistant.components.homeassistant_hardware.coordinator import (
FirmwareUpdateCoordinator,
)
from homeassistant.components.homeassistant_hardware.switch import (
BaseBetaFirmwareSwitch,
)
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
EntityCategory,
Platform,
)
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.setup import async_setup_component
from .common import TEST_DOMAIN, TEST_FIRMWARE_RELEASES_URL, TEST_MANIFEST
from tests.common import (
MockConfigEntry,
MockModule,
MockPlatform,
mock_config_flow,
mock_integration,
mock_platform,
mock_restore_cache,
)
TEST_DEVICE = "/dev/serial/by-id/test-device-12345"
TEST_SWITCH_ENTITY_ID = "switch.mock_device_beta_firmware_updates"
class MockBetaFirmwareSwitch(BaseBetaFirmwareSwitch):
"""Mock beta firmware switch for testing."""
def __init__(
self,
coordinator: FirmwareUpdateCoordinator,
config_entry: ConfigEntry,
) -> None:
"""Initialize the mock beta firmware switch."""
super().__init__(coordinator, config_entry)
self._attr_unique_id = "beta_firmware"
self._attr_name = "Beta firmware updates"
self._attr_device_info = DeviceInfo(
identifiers={(TEST_DOMAIN, "test_device")},
name="Mock Device",
model="Mock Model",
manufacturer="Mock Manufacturer",
)
def _mock_async_create_switch_entity(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> MockBetaFirmwareSwitch:
"""Create a mock switch entity."""
session = async_get_clientsession(hass)
coordinator = FirmwareUpdateCoordinator(
hass,
config_entry,
session,
TEST_FIRMWARE_RELEASES_URL,
)
entity = MockBetaFirmwareSwitch(coordinator, config_entry)
async_add_entities([entity])
return entity
async def mock_async_setup_entry(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Set up test config entry."""
await hass.config_entries.async_forward_entry_setups(
config_entry, [Platform.SWITCH]
)
return True
async def mock_async_setup_switch_entities(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the beta firmware switch config entry."""
_mock_async_create_switch_entity(hass, config_entry, async_add_entities)
@pytest.fixture(name="mock_firmware_client")
def mock_firmware_client_fixture():
"""Create a mock firmware update client."""
with patch(
"homeassistant.components.homeassistant_hardware.coordinator.FirmwareUpdateClient",
autospec=True,
) as mock_client:
mock_client.return_value.async_update_data.return_value = TEST_MANIFEST
mock_client.return_value.update_prerelease = Mock()
yield mock_client.return_value
@pytest.fixture(name="switch_config_entry")
async def mock_switch_config_entry(
hass: HomeAssistant,
mock_firmware_client,
) -> AsyncGenerator[ConfigEntry]:
"""Set up a mock config entry for testing."""
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(hass, "homeassistant_hardware", {})
mock_integration(
hass,
MockModule(
TEST_DOMAIN,
async_setup_entry=mock_async_setup_entry,
),
built_in=False,
)
mock_platform(hass, "test.config_flow")
mock_platform(
hass,
"test.switch",
MockPlatform(async_setup_entry=mock_async_setup_switch_entities),
)
# Set up a mock integration using the hardware switch entity
config_entry = MockConfigEntry(
domain=TEST_DOMAIN,
data={
"device": TEST_DEVICE,
},
)
config_entry.add_to_hass(hass)
with mock_config_flow(TEST_DOMAIN, ConfigFlow):
yield config_entry
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_switch_default_off_state(
hass: HomeAssistant,
switch_config_entry: ConfigEntry,
mock_firmware_client,
) -> None:
"""Test switch defaults to off when no previous state."""
assert await hass.config_entries.async_setup(switch_config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get(TEST_SWITCH_ENTITY_ID)
assert state is not None
assert state.state == STATE_OFF
# Verify coordinator was called with False during setup
assert mock_firmware_client.update_prerelease.mock_calls == [call(False)]
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
("initial_state", "expected_state", "expected_prerelease"),
[
(STATE_ON, STATE_ON, True),
(STATE_OFF, STATE_OFF, False),
],
)
async def test_switch_restore_state(
hass: HomeAssistant,
switch_config_entry: ConfigEntry,
mock_firmware_client,
initial_state: str,
expected_state: str,
expected_prerelease: bool,
) -> None:
"""Test switch restores previous state and has correct entity attributes."""
mock_restore_cache(hass, [State(TEST_SWITCH_ENTITY_ID, initial_state)])
assert await hass.config_entries.async_setup(switch_config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get(TEST_SWITCH_ENTITY_ID)
assert state is not None
assert state.state == expected_state
assert state.attributes.get("friendly_name") == "Mock Device Beta firmware updates"
# Verify coordinator was called with correct value during setup
assert mock_firmware_client.update_prerelease.mock_calls == [
call(expected_prerelease)
]
# Verify entity registry attributes
entity_registry = er.async_get(hass)
entity_entry = entity_registry.async_get(TEST_SWITCH_ENTITY_ID)
assert entity_entry is not None
assert entity_entry.entity_category == EntityCategory.CONFIG
assert entity_entry.translation_key == "beta_firmware"
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
("service", "target_state", "expected_prerelease"),
[
(SERVICE_TURN_ON, STATE_ON, True),
(SERVICE_TURN_OFF, STATE_OFF, False),
],
)
async def test_switch_turn_on_off(
hass: HomeAssistant,
switch_config_entry: ConfigEntry,
mock_firmware_client,
service: str,
target_state: str,
expected_prerelease: bool,
) -> None:
"""Test turning switch on/off updates state and coordinator."""
# Start with opposite state
mock_restore_cache(
hass,
[
State(
TEST_SWITCH_ENTITY_ID,
STATE_ON if service == SERVICE_TURN_OFF else STATE_OFF,
)
],
)
# Track async_refresh calls
with patch(
"homeassistant.components.homeassistant_hardware.coordinator.FirmwareUpdateCoordinator.async_refresh"
) as mock_refresh:
assert await hass.config_entries.async_setup(switch_config_entry.entry_id)
await hass.async_block_till_done()
# Reset mocks after setup
mock_firmware_client.update_prerelease.reset_mock()
mock_refresh.reset_mock()
# Call the service
await hass.services.async_call(
SWITCH_DOMAIN,
service,
{ATTR_ENTITY_ID: TEST_SWITCH_ENTITY_ID},
blocking=True,
)
# Verify state changed
state = hass.states.get(TEST_SWITCH_ENTITY_ID)
assert state is not None
assert state.state == target_state
# Verify coordinator methods were called
assert mock_firmware_client.update_prerelease.mock_calls == [
call(expected_prerelease)
]
assert len(mock_refresh.mock_calls) == 1