diff --git a/homeassistant/components/freebox/__init__.py b/homeassistant/components/freebox/__init__.py index 6c5a66e284f4..f2804665ac9d 100644 --- a/homeassistant/components/freebox/__init__.py +++ b/homeassistant/components/freebox/__init__.py @@ -3,6 +3,7 @@ from datetime import timedelta import logging +from aiohttp import ClientError from freebox_api.exceptions import HttpRequestError from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP, Platform @@ -81,15 +82,16 @@ async def async_migrate_entry(hass: HomeAssistant, entry: FreeboxConfigEntry) -> async def async_setup_entry(hass: HomeAssistant, entry: FreeboxConfigEntry) -> bool: """Set up Freebox entry.""" api = await get_api(hass, entry.data[CONF_HOST]) + # The library raises its own error only for a request the router refused, + # and leaves everything the transport can throw to aiohttp try: await api.open(entry.data[CONF_HOST], entry.data[CONF_PORT]) - except HttpRequestError as err: + freebox_config = await api.system.get_config() + router = FreeboxRouter(hass, entry, api, freebox_config) + await router.update_all() + except (HttpRequestError, ClientError, TimeoutError) as err: raise ConfigEntryNotReady from err - freebox_config = await api.system.get_config() - - router = FreeboxRouter(hass, entry, api, freebox_config) - await router.update_all() entry.async_on_unload( async_track_time_interval(hass, router.update_all, SCAN_INTERVAL) ) diff --git a/tests/components/freebox/test_init.py b/tests/components/freebox/test_init.py index 05bf5da90164..4ff84db92fe5 100644 --- a/tests/components/freebox/test_init.py +++ b/tests/components/freebox/test_init.py @@ -1,8 +1,11 @@ """Tests for the Freebox init.""" +from collections.abc import Callable from copy import deepcopy from unittest.mock import ANY, Mock +from aiohttp import ClientError +from freebox_api.exceptions import HttpRequestError from freezegun.api import FrozenDateTimeFactory import pytest from pytest_unordered import unordered @@ -45,6 +48,40 @@ async def test_setup(hass: HomeAssistant, router: Mock) -> None: assert router().open.call_count == 1 +@pytest.mark.parametrize( + "error", + [HttpRequestError("Boom"), ClientError("Boom"), TimeoutError], +) +@pytest.mark.parametrize( + "failing_call", + [ + pytest.param(lambda api: api.open, id="open"), + pytest.param(lambda api: api.system.get_config, id="get_config"), + pytest.param(lambda api: api.lan.get_interfaces, id="get_interfaces"), + ], +) +async def test_setup_retries_when_the_router_cannot_be_reached( + hass: HomeAssistant, + router: Mock, + error: Exception, + failing_call: Callable[[Mock], Mock], +) -> None: + """Test that setup is retried when the router cannot be reached.""" + failing_call(router()).side_effect = error + + entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT}, + unique_id=MOCK_HOST, + version=2, + ) + entry.add_to_hass(hass) + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.SETUP_RETRY + + async def test_setup_import(hass: HomeAssistant, router: Mock) -> None: """Test setup of integration from import."""