diff --git a/homeassistant/components/overseerr/config_flow.py b/homeassistant/components/overseerr/config_flow.py index 2ad0c8d6d61..e2994212bfe 100644 --- a/homeassistant/components/overseerr/config_flow.py +++ b/homeassistant/components/overseerr/config_flow.py @@ -2,8 +2,11 @@ from typing import Any -from python_overseerr import OverseerrClient -from python_overseerr.exceptions import OverseerrError +from python_overseerr import ( + OverseerrAuthenticationError, + OverseerrClient, + OverseerrError, +) import voluptuous as vol from yarl import URL @@ -47,6 +50,8 @@ class OverseerrConfigFlow(ConfigFlow, domain=DOMAIN): ) try: await client.get_request_count() + except OverseerrAuthenticationError: + errors["base"] = "invalid_auth" except OverseerrError: errors["base"] = "cannot_connect" else: diff --git a/homeassistant/components/overseerr/coordinator.py b/homeassistant/components/overseerr/coordinator.py index c8512d764f4..75a7d8d73d7 100644 --- a/homeassistant/components/overseerr/coordinator.py +++ b/homeassistant/components/overseerr/coordinator.py @@ -2,13 +2,18 @@ from datetime import timedelta -from python_overseerr import OverseerrClient, RequestCount -from python_overseerr.exceptions import OverseerrConnectionError +from python_overseerr import ( + OverseerrAuthenticationError, + OverseerrClient, + OverseerrConnectionError, + RequestCount, +) from yarl import URL from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT, CONF_SSL from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryError from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -47,6 +52,11 @@ class OverseerrCoordinator(DataUpdateCoordinator[RequestCount]): """Fetch data from API endpoint.""" try: return await self.client.get_request_count() + except OverseerrAuthenticationError as err: + raise ConfigEntryError( + translation_domain=DOMAIN, + translation_key="auth_error", + ) from err except OverseerrConnectionError as err: raise UpdateFailed( translation_domain=DOMAIN, diff --git a/homeassistant/components/overseerr/strings.json b/homeassistant/components/overseerr/strings.json index 968b8c5b533..25b53303611 100644 --- a/homeassistant/components/overseerr/strings.json +++ b/homeassistant/components/overseerr/strings.json @@ -17,6 +17,7 @@ }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", "invalid_host": "The provided URL is not a valid host." } }, @@ -66,6 +67,9 @@ "connection_error": { "message": "Error connecting to the Overseerr instance: {error}" }, + "auth_error": { + "message": "Invalid API key." + }, "not_loaded": { "message": "{target} is not loaded." }, diff --git a/tests/components/overseerr/test_config_flow.py b/tests/components/overseerr/test_config_flow.py index 487c843ff1c..937d697b8cb 100644 --- a/tests/components/overseerr/test_config_flow.py +++ b/tests/components/overseerr/test_config_flow.py @@ -3,7 +3,10 @@ from unittest.mock import AsyncMock, patch import pytest -from python_overseerr.exceptions import OverseerrConnectionError +from python_overseerr.exceptions import ( + OverseerrAuthenticationError, + OverseerrConnectionError, +) from homeassistant.components.overseerr.const import DOMAIN from homeassistant.config_entries import SOURCE_USER @@ -61,13 +64,22 @@ async def test_full_flow( } +@pytest.mark.parametrize( + ("exception", "error"), + [ + (OverseerrAuthenticationError, "invalid_auth"), + (OverseerrConnectionError, "cannot_connect"), + ], +) async def test_flow_errors( hass: HomeAssistant, mock_overseerr_client: AsyncMock, mock_setup_entry: AsyncMock, + exception: Exception, + error: str, ) -> None: """Test flow errors.""" - mock_overseerr_client.get_request_count.side_effect = OverseerrConnectionError() + mock_overseerr_client.get_request_count.side_effect = exception result = await hass.config_entries.flow.async_init( DOMAIN, @@ -82,7 +94,7 @@ async def test_flow_errors( ) assert result["type"] is FlowResultType.FORM - assert result["errors"] == {"base": "cannot_connect"} + assert result["errors"] == {"base": error} mock_overseerr_client.get_request_count.side_effect = None diff --git a/tests/components/overseerr/test_init.py b/tests/components/overseerr/test_init.py index 4c6897ed316..27c9f3fb3e9 100644 --- a/tests/components/overseerr/test_init.py +++ b/tests/components/overseerr/test_init.py @@ -4,6 +4,7 @@ from typing import Any from unittest.mock import AsyncMock, patch import pytest +from python_overseerr import OverseerrAuthenticationError, OverseerrConnectionError from python_overseerr.models import WebhookNotificationOptions from syrupy import SnapshotAssertion @@ -14,6 +15,7 @@ from homeassistant.components.overseerr import ( REGISTERED_NOTIFICATIONS, ) from homeassistant.components.overseerr.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr @@ -23,6 +25,28 @@ from tests.common import MockConfigEntry from tests.components.cloud import mock_cloud +@pytest.mark.parametrize( + ("exception", "config_entry_state"), + [ + (OverseerrAuthenticationError, ConfigEntryState.SETUP_ERROR), + (OverseerrConnectionError, ConfigEntryState.SETUP_RETRY), + ], +) +async def test_initialization_errors( + hass: HomeAssistant, + mock_overseerr_client: AsyncMock, + mock_config_entry: MockConfigEntry, + exception: Exception, + config_entry_state: ConfigEntryState, +) -> None: + """Test the Overseerr integration initialization errors.""" + mock_overseerr_client.get_request_count.side_effect = exception + + await setup_integration(hass, mock_config_entry) + + assert mock_config_entry.state == config_entry_state + + async def test_device_info( hass: HomeAssistant, snapshot: SnapshotAssertion,