mirror of
https://github.com/home-assistant/core.git
synced 2026-05-22 16:30:27 +01:00
a471f7059f
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
140 lines
4.4 KiB
Python
140 lines
4.4 KiB
Python
"""Tests for the Risco integration."""
|
|
|
|
import logging
|
|
from typing import Any, cast
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
from pyrisco import OperationError
|
|
import pytest
|
|
|
|
from homeassistant.components.risco.const import (
|
|
CONF_COMMUNICATION_DELAY,
|
|
DEFAULT_CONCURRENCY,
|
|
DOMAIN,
|
|
TYPE_LOCAL,
|
|
)
|
|
from homeassistant.const import CONF_HOST, CONF_PIN, CONF_PORT, CONF_TYPE
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_error_handler() -> MagicMock:
|
|
"""Create a mock for add_error_handler."""
|
|
with patch("homeassistant.components.risco.RiscoLocal.add_error_handler") as mock:
|
|
yield mock
|
|
|
|
|
|
async def test_connection_reset(
|
|
hass: HomeAssistant,
|
|
two_zone_local: dict[int, Any],
|
|
mock_error_handler: MagicMock,
|
|
setup_risco_local: MockConfigEntry,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Test that ConnectionResetError triggers a reload but generic errors do not."""
|
|
callback = mock_error_handler.call_args.args[0]
|
|
assert callback is not None
|
|
|
|
local_data = setup_risco_local.runtime_data.local_data
|
|
disconnect_mock = cast(AsyncMock, local_data.system.disconnect)
|
|
connect_mock = cast(AsyncMock, local_data.system.connect)
|
|
|
|
caplog.set_level(logging.DEBUG, logger="homeassistant.components.risco")
|
|
|
|
# Generic error should not trigger reload — unload/setup not invoked again
|
|
await callback(Exception())
|
|
await hass.async_block_till_done()
|
|
disconnect_mock.assert_not_called()
|
|
assert connect_mock.call_count == 1
|
|
|
|
# ConnectionResetError should trigger a reload
|
|
await callback(ConnectionResetError())
|
|
await hass.async_block_till_done()
|
|
disconnect_mock.assert_awaited_once()
|
|
assert connect_mock.call_count == 2
|
|
assert "Disconnected from panel. Reloading integration" in caplog.text
|
|
|
|
|
|
async def test_unload_handles_disconnect_error(
|
|
hass: HomeAssistant,
|
|
two_zone_local: dict[int, Any],
|
|
setup_risco_local: MockConfigEntry,
|
|
) -> None:
|
|
"""Test unload succeeds when local disconnect errors out."""
|
|
with patch(
|
|
"homeassistant.components.risco.RiscoLocal.disconnect",
|
|
side_effect=RuntimeError("disconnect failed"),
|
|
) as disconnect_mock:
|
|
assert await hass.config_entries.async_unload(setup_risco_local.entry_id)
|
|
disconnect_mock.assert_awaited_once()
|
|
|
|
|
|
async def test_local_setup_uses_stored_communication_delay(
|
|
hass: HomeAssistant,
|
|
) -> None:
|
|
"""Test local setup passes stored communication delay to the client."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data={
|
|
CONF_TYPE: TYPE_LOCAL,
|
|
CONF_HOST: "test-host",
|
|
CONF_PORT: 5004,
|
|
CONF_PIN: "1234",
|
|
CONF_COMMUNICATION_DELAY: 2,
|
|
},
|
|
)
|
|
config_entry.add_to_hass(hass)
|
|
|
|
with (
|
|
patch(
|
|
"homeassistant.components.risco.RiscoLocal", autospec=True
|
|
) as risco_local,
|
|
patch.object(
|
|
hass.config_entries,
|
|
"async_forward_entry_setups",
|
|
AsyncMock(return_value=True),
|
|
),
|
|
):
|
|
risco_local.return_value.connect = AsyncMock()
|
|
|
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
risco_local.assert_called_once_with(
|
|
"test-host",
|
|
5004,
|
|
"1234",
|
|
communication_delay=2,
|
|
concurrency=DEFAULT_CONCURRENCY,
|
|
)
|
|
|
|
|
|
async def test_clock_operation_error_is_downgraded(
|
|
hass: HomeAssistant,
|
|
two_zone_local: dict[int, Any],
|
|
mock_error_handler: MagicMock,
|
|
setup_risco_local: MockConfigEntry,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Test CLOCK keep-alive operation errors warn without triggering reload."""
|
|
callback = mock_error_handler.call_args.args[0]
|
|
assert callback is not None
|
|
|
|
local_data = setup_risco_local.runtime_data.local_data
|
|
disconnect_mock = cast(AsyncMock, local_data.system.disconnect)
|
|
|
|
caplog.set_level(logging.WARNING, logger="homeassistant.components.risco")
|
|
await callback(OperationError("Timeout in command: CLOCK"))
|
|
|
|
disconnect_mock.assert_not_awaited()
|
|
assert "Error in Risco library" not in caplog.text
|
|
|
|
expected_warning = (
|
|
"Risco keep-alive timeout for entry "
|
|
f"{setup_risco_local.title} (host: "
|
|
f"{setup_risco_local.data.get(CONF_HOST, 'unknown')})"
|
|
)
|
|
assert expected_warning in caplog.text
|