diff --git a/homeassistant/components/nintendo_parental_controls/coordinator.py b/homeassistant/components/nintendo_parental_controls/coordinator.py index 0b55db7ea03..abc8f0fdf4e 100644 --- a/homeassistant/components/nintendo_parental_controls/coordinator.py +++ b/homeassistant/components/nintendo_parental_controls/coordinator.py @@ -5,14 +5,18 @@ from __future__ import annotations from datetime import timedelta import logging -from pynintendoauth.exceptions import InvalidOAuthConfigurationException +from pynintendoauth.exceptions import ( + HttpException, + InvalidOAuthConfigurationException, + InvalidSessionTokenException, +) from pynintendoparental import Authenticator, NintendoParental from pynintendoparental.exceptions import NoDevicesFoundException from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryError -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import DOMAIN @@ -58,3 +62,13 @@ class NintendoUpdateCoordinator(DataUpdateCoordinator[None]): translation_domain=DOMAIN, translation_key="no_devices_found", ) from err + except InvalidSessionTokenException as err: + _LOGGER.debug("Session token invalid, will renew on next update") + raise UpdateFailed from err + except HttpException as err: + if err.error_code == "update_required": + raise ConfigEntryError( + translation_domain=DOMAIN, + translation_key="update_required", + ) from err + raise UpdateFailed(retry_after=900) from err diff --git a/homeassistant/components/nintendo_parental_controls/strings.json b/homeassistant/components/nintendo_parental_controls/strings.json index bc6b88bf5dc..dfd2fd94dfa 100644 --- a/homeassistant/components/nintendo_parental_controls/strings.json +++ b/homeassistant/components/nintendo_parental_controls/strings.json @@ -83,6 +83,9 @@ }, "no_devices_found": { "message": "No Nintendo devices found for this account." + }, + "update_required": { + "message": "The Nintendo Switch parental controls integration requires an update due to changes in Nintendo's API." } }, "services": { diff --git a/tests/components/nintendo_parental_controls/test_coordinator.py b/tests/components/nintendo_parental_controls/test_coordinator.py index f22bdaae615..3d5110264c4 100644 --- a/tests/components/nintendo_parental_controls/test_coordinator.py +++ b/tests/components/nintendo_parental_controls/test_coordinator.py @@ -2,8 +2,13 @@ from unittest.mock import AsyncMock -from pynintendoauth.exceptions import InvalidOAuthConfigurationException +from pynintendoauth.exceptions import ( + HttpException, + InvalidOAuthConfigurationException, + InvalidSessionTokenException, +) from pynintendoparental.exceptions import NoDevicesFoundException +import pytest from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant @@ -14,16 +19,62 @@ from . import setup_integration from tests.common import MockConfigEntry -async def test_invalid_authentication( +@pytest.mark.parametrize( + ("exception", "translation_key", "expected_state", "expected_log_message"), + [ + ( + InvalidOAuthConfigurationException( + status_code=401, message="Authentication failed" + ), + "invalid_auth", + ConfigEntryState.SETUP_ERROR, + None, + ), + ( + NoDevicesFoundException(), + "no_devices_found", + ConfigEntryState.SETUP_ERROR, + None, + ), + ( + HttpException( + status_code=400, error_code="update_required", message="Update required" + ), + "update_required", + ConfigEntryState.SETUP_ERROR, + None, + ), + ( + HttpException( + status_code=500, error_code="unknown", message="Unknown error" + ), + None, + ConfigEntryState.SETUP_RETRY, + None, + ), + ( + InvalidSessionTokenException( + status_code=403, error_code="invalid_token", message="Invalid token" + ), + None, + ConfigEntryState.SETUP_RETRY, + "Session token invalid, will renew on next update", + ), + ], +) +async def test_update_errors( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_nintendo_client: AsyncMock, entity_registry: er.EntityRegistry, + caplog: pytest.LogCaptureFixture, + exception: Exception, + translation_key: str, + expected_state: ConfigEntryState, + expected_log_message: str | None, ) -> None: - """Test handling of invalid authentication.""" - mock_nintendo_client.update.side_effect = InvalidOAuthConfigurationException( - status_code=401, message="Authentication failed" - ) + """Test handling of update errors.""" + mock_nintendo_client.update.side_effect = exception await setup_integration(hass, mock_config_entry) @@ -32,25 +83,13 @@ async def test_invalid_authentication( entity_registry, mock_config_entry.entry_id ) assert len(entries) == 0 - # Ensure the config entry is marked as error - assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR + # Ensure the config entry is marked as expected state + assert mock_config_entry.state is expected_state -async def test_no_devices( - hass: HomeAssistant, - mock_config_entry: MockConfigEntry, - mock_nintendo_client: AsyncMock, - entity_registry: er.EntityRegistry, -) -> None: - """Test handling of invalid authentication.""" - mock_nintendo_client.update.side_effect = NoDevicesFoundException() + # Ensure the correct translation key is used in the error + assert mock_config_entry.error_reason_translation_key == translation_key - await setup_integration(hass, mock_config_entry) - - # Ensure no entities are created - entries = er.async_entries_for_config_entry( - entity_registry, mock_config_entry.entry_id - ) - assert len(entries) == 0 - # Ensure the config entry is marked as error - assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR + # If there's an expected log message, check that it was logged + if expected_log_message: + assert expected_log_message in caplog.text