1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-25 05:26:47 +00:00
Files
core/tests/components/velux/test_init.py
2025-12-23 14:11:40 +01:00

172 lines
5.9 KiB
Python

"""Tests for Velux integration initialization and retry behavior.
These tests verify that setup retries (ConfigEntryNotReady) are triggered
when scene or node loading fails.
They also verify that unloading the integration properly disconnects.
"""
from __future__ import annotations
from unittest.mock import patch
import pytest
from pyvlx.exception import PyVLXException
from homeassistant.components.velux.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.setup import async_setup_component
from tests.common import AsyncMock, ConfigEntry, MockConfigEntry
async def test_setup_retry_on_nodes_failure(
mock_config_entry: ConfigEntry, hass: HomeAssistant, mock_pyvlx: AsyncMock
) -> None:
"""Test that a failure loading nodes triggers setup retry.
The integration loads scenes first, then nodes. If loading raises PyVLXException,
(which could have a multitude of reasons, unfortunately there are no specialized
exceptions that give a reason), the ConfigEntry should enter SETUP_RETRY.
"""
mock_pyvlx.load_nodes.side_effect = PyVLXException("nodes boom")
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
mock_pyvlx.load_scenes.assert_awaited_once()
mock_pyvlx.load_nodes.assert_awaited_once()
async def test_setup_retry_on_oserror_during_scenes(
mock_config_entry: ConfigEntry, hass: HomeAssistant, mock_pyvlx: AsyncMock
) -> None:
"""Test that OSError during scene loading triggers setup retry.
OSError typically indicates network/connection issues when the gateway
refuses connections or is unreachable.
"""
mock_pyvlx.load_scenes.side_effect = OSError("Connection refused")
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
mock_pyvlx.load_scenes.assert_awaited_once()
mock_pyvlx.load_nodes.assert_not_called()
async def test_setup_auth_error(
mock_config_entry: ConfigEntry, hass: HomeAssistant, mock_pyvlx: AsyncMock
) -> None:
"""Test that PyVLXException with auth message raises ConfigEntryAuthFailed and starts reauth flow."""
mock_pyvlx.load_scenes.side_effect = PyVLXException(
"Login to KLF 200 failed, check credentials"
)
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
# ConfigEntryAuthFailed results in SETUP_ERROR state
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
assert flows[0]["step_id"] == "reauth_confirm"
mock_pyvlx.load_scenes.assert_awaited_once()
mock_pyvlx.load_nodes.assert_not_called()
@pytest.fixture
def platform() -> Platform:
"""Fixture to specify platform to test."""
return Platform.COVER
@pytest.mark.usefixtures("setup_integration")
async def test_unload_calls_disconnect(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_pyvlx
) -> None:
"""Test that unloading the config entry disconnects from the gateway."""
# Unload the entry
await hass.config_entries.async_unload(mock_config_entry.entry_id)
await hass.async_block_till_done()
# Verify disconnect was called
mock_pyvlx.disconnect.assert_awaited_once()
@pytest.mark.usefixtures("setup_integration")
async def test_unload_does_not_disconnect_if_platform_unload_fails(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_pyvlx
) -> None:
"""Test that disconnect is not called if platform unload fails."""
# Mock platform unload to fail
with patch(
"homeassistant.config_entries.ConfigEntries.async_unload_platforms",
return_value=False,
):
result = await hass.config_entries.async_unload(mock_config_entry.entry_id)
await hass.async_block_till_done()
# Verify unload failed
assert result is False
# Verify disconnect was NOT called since platform unload failed
mock_pyvlx.disconnect.assert_not_awaited()
@pytest.mark.usefixtures("setup_integration")
async def test_reboot_gateway_service_raises_on_exception(
hass: HomeAssistant, mock_pyvlx: AsyncMock
) -> None:
"""Test that reboot_gateway service raises HomeAssistantError on exception."""
mock_pyvlx.reboot_gateway.side_effect = OSError("Connection failed")
with pytest.raises(HomeAssistantError, match="Failed to reboot gateway"):
await hass.services.async_call(
"velux",
"reboot_gateway",
blocking=True,
)
mock_pyvlx.reboot_gateway.side_effect = PyVLXException("Reboot failed")
with pytest.raises(HomeAssistantError, match="Failed to reboot gateway"):
await hass.services.async_call(
"velux",
"reboot_gateway",
blocking=True,
)
async def test_reboot_gateway_service_raises_validation_error(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:
"""Test that reboot_gateway service raises ServiceValidationError when no gateway is loaded."""
# Add the config entry but don't set it up
mock_config_entry.add_to_hass(hass)
# Set up the velux integration's async_setup to register the service
await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
with pytest.raises(ServiceValidationError, match="No loaded Velux gateway found"):
await hass.services.async_call(
"velux",
"reboot_gateway",
blocking=True,
)