Avoid swallowing Roku errors (#72517)

This commit is contained in:
Chris Talkington 2022-05-28 21:03:13 -05:00 committed by GitHub
parent 7a0657c386
commit 46031aff8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 33 deletions

View File

@ -3,15 +3,14 @@ from __future__ import annotations
from collections.abc import Awaitable, Callable, Coroutine from collections.abc import Awaitable, Callable, Coroutine
from functools import wraps from functools import wraps
import logging
from typing import Any, TypeVar from typing import Any, TypeVar
from rokuecp import RokuConnectionError, RokuConnectionTimeoutError, RokuError from rokuecp import RokuConnectionError, RokuConnectionTimeoutError, RokuError
from typing_extensions import Concatenate, ParamSpec from typing_extensions import Concatenate, ParamSpec
from .entity import RokuEntity from homeassistant.exceptions import HomeAssistantError
_LOGGER = logging.getLogger(__name__) from .entity import RokuEntity
_RokuEntityT = TypeVar("_RokuEntityT", bound=RokuEntity) _RokuEntityT = TypeVar("_RokuEntityT", bound=RokuEntity)
_P = ParamSpec("_P") _P = ParamSpec("_P")
@ -43,14 +42,14 @@ def roku_exception_handler(
try: try:
await func(self, *args, **kwargs) await func(self, *args, **kwargs)
except RokuConnectionTimeoutError as error: except RokuConnectionTimeoutError as error:
if not ignore_timeout and self.available: if not ignore_timeout:
_LOGGER.error("Error communicating with API: %s", error) raise HomeAssistantError(
"Timeout communicating with Roku API"
) from error
except RokuConnectionError as error: except RokuConnectionError as error:
if self.available: raise HomeAssistantError("Error communicating with Roku API") from error
_LOGGER.error("Error communicating with API: %s", error)
except RokuError as error: except RokuError as error:
if self.available: raise HomeAssistantError("Invalid response from Roku API") from error
_LOGGER.error("Invalid response from API: %s", error)
return wrapper return wrapper

View File

@ -2,7 +2,13 @@
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest import pytest
from rokuecp import Application, Device as RokuDevice, RokuError from rokuecp import (
Application,
Device as RokuDevice,
RokuConnectionError,
RokuConnectionTimeoutError,
RokuError,
)
from homeassistant.components.roku.const import DOMAIN from homeassistant.components.roku.const import DOMAIN
from homeassistant.components.roku.coordinator import SCAN_INTERVAL from homeassistant.components.roku.coordinator import SCAN_INTERVAL
@ -10,6 +16,7 @@ from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
from homeassistant.components.select.const import ATTR_OPTION, ATTR_OPTIONS from homeassistant.components.select.const import ATTR_OPTION, ATTR_OPTIONS
from homeassistant.const import ATTR_ENTITY_ID, ATTR_ICON, SERVICE_SELECT_OPTION from homeassistant.const import ATTR_ENTITY_ID, ATTR_ICON, SERVICE_SELECT_OPTION
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
@ -102,11 +109,20 @@ async def test_application_state(
assert state.state == "Home" assert state.state == "Home"
@pytest.mark.parametrize(
"error, error_string",
[
(RokuConnectionError, "Error communicating with Roku API"),
(RokuConnectionTimeoutError, "Timeout communicating with Roku API"),
(RokuError, "Invalid response from Roku API"),
],
)
async def test_application_select_error( async def test_application_select_error(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_roku: MagicMock, mock_roku: MagicMock,
caplog: pytest.LogCaptureFixture, error: RokuError,
error_string: str,
) -> None: ) -> None:
"""Test error handling of the Roku selects.""" """Test error handling of the Roku selects."""
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
@ -123,8 +139,9 @@ async def test_application_select_error(
await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
mock_roku.launch.side_effect = RokuError mock_roku.launch.side_effect = error
with pytest.raises(HomeAssistantError, match=error_string):
await hass.services.async_call( await hass.services.async_call(
SELECT_DOMAIN, SELECT_DOMAIN,
SERVICE_SELECT_OPTION, SERVICE_SELECT_OPTION,
@ -138,7 +155,6 @@ async def test_application_select_error(
state = hass.states.get("select.my_roku_3_application") state = hass.states.get("select.my_roku_3_application")
assert state assert state
assert state.state == "Home" assert state.state == "Home"
assert "Invalid response from API" in caplog.text
assert mock_roku.launch.call_count == 1 assert mock_roku.launch.call_count == 1
mock_roku.launch.assert_called_with("12") mock_roku.launch.assert_called_with("12")
@ -218,11 +234,11 @@ async def test_channel_select_error(
hass: HomeAssistant, hass: HomeAssistant,
init_integration: MockConfigEntry, init_integration: MockConfigEntry,
mock_roku: MagicMock, mock_roku: MagicMock,
caplog: pytest.LogCaptureFixture,
) -> None: ) -> None:
"""Test error handling of the Roku selects.""" """Test error handling of the Roku selects."""
mock_roku.tune.side_effect = RokuError mock_roku.tune.side_effect = RokuError
with pytest.raises(HomeAssistantError, match="Invalid response from Roku API"):
await hass.services.async_call( await hass.services.async_call(
SELECT_DOMAIN, SELECT_DOMAIN,
SERVICE_SELECT_OPTION, SERVICE_SELECT_OPTION,
@ -236,6 +252,5 @@ async def test_channel_select_error(
state = hass.states.get("select.58_onn_roku_tv_channel") state = hass.states.get("select.58_onn_roku_tv_channel")
assert state assert state
assert state.state == "getTV (14.3)" assert state.state == "getTV (14.3)"
assert "Invalid response from API" in caplog.text
assert mock_roku.tune.call_count == 1 assert mock_roku.tune.call_count == 1
mock_roku.tune.assert_called_with("99.1") mock_roku.tune.assert_called_with("99.1")