diff --git a/homeassistant/components/duckdns/__init__.py b/homeassistant/components/duckdns/__init__.py index e9149fe8903c..42fb32f2643d 100644 --- a/homeassistant/components/duckdns/__init__.py +++ b/homeassistant/components/duckdns/__init__.py @@ -1,4 +1,4 @@ -"""Integrate with DuckDNS.""" +"""Duck DNS integration.""" from __future__ import annotations diff --git a/homeassistant/components/duckdns/services.py b/homeassistant/components/duckdns/services.py index 43a601cb2ef2..83534d26cec9 100644 --- a/homeassistant/components/duckdns/services.py +++ b/homeassistant/components/duckdns/services.py @@ -2,11 +2,12 @@ from __future__ import annotations +from aiohttp import ClientError import voluptuous as vol from homeassistant.const import CONF_ACCESS_TOKEN, CONF_DOMAIN from homeassistant.core import HomeAssistant, ServiceCall, callback -from homeassistant.exceptions import ServiceValidationError +from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.selector import ConfigEntrySelector @@ -62,9 +63,25 @@ async def update_domain_service(call: ServiceCall) -> None: session = async_get_clientsession(call.hass) - await update_duckdns( - session, - entry.data[CONF_DOMAIN], - entry.data[CONF_ACCESS_TOKEN], - txt=call.data.get(ATTR_TXT), - ) + try: + if not await update_duckdns( + session, + entry.data[CONF_DOMAIN], + entry.data[CONF_ACCESS_TOKEN], + txt=call.data.get(ATTR_TXT), + ): + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="update_failed", + translation_placeholders={ + CONF_DOMAIN: entry.data[CONF_DOMAIN], + }, + ) + except ClientError as e: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="connection_error", + translation_placeholders={ + CONF_DOMAIN: entry.data[CONF_DOMAIN], + }, + ) from e diff --git a/tests/components/duckdns/test_init.py b/tests/components/duckdns/test_init.py index 1324c3be3577..ffe3ceeba745 100644 --- a/tests/components/duckdns/test_init.py +++ b/tests/components/duckdns/test_init.py @@ -18,7 +18,7 @@ from homeassistant.components.duckdns.helpers import UPDATE_URL from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_ACCESS_TOKEN, CONF_DOMAIN from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ServiceValidationError +from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.util.dt import utcnow from .conftest import TEST_SUBDOMAIN, TEST_TOKEN @@ -202,6 +202,43 @@ async def test_service_exceptions( ) +@pytest.mark.parametrize( + ("side_effect", "exception_msg"), + [ + ( + False, + "Updating Duck DNS domain homeassistant failed", + ), + ( + ClientError, + "Updating Duck DNS domain homeassistant failed due to a connection error", + ), + ], +) +@pytest.mark.usefixtures("setup_duckdns") +async def test_service_request_exception( + hass: HomeAssistant, + config_entry: MockConfigEntry, + side_effect: Exception | bool, + exception_msg: str, +) -> None: + """Test service request exception.""" + + with ( + patch( + "homeassistant.components.duckdns.services.update_duckdns", + side_effect=[side_effect], + ), + pytest.raises(HomeAssistantError, match=exception_msg), + ): + await hass.services.async_call( + DOMAIN, + SERVICE_SET_TXT, + {ATTR_CONFIG_ENTRY: config_entry.entry_id}, + blocking=True, + ) + + @pytest.mark.usefixtures("setup_duckdns") async def test_service_select_entry( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker