From 4a3de6038897a7fb52ed88349bdaa7b7b25389fb Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Fri, 2 Oct 2026 18:00:03 +0200 Subject: [PATCH] Raise HomeAssistantError when nextdns actions fail on invalid API key (#184060) --- homeassistant/components/nextdns/button.py | 9 +++- .../components/nextdns/quality_scale.yaml | 4 +- homeassistant/components/nextdns/strings.json | 3 ++ homeassistant/components/nextdns/switch.py | 22 +++++++-- tests/components/nextdns/test_button.py | 18 ++++--- tests/components/nextdns/test_switch.py | 49 ++++++++++++++++--- 6 files changed, 82 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/nextdns/button.py b/homeassistant/components/nextdns/button.py index feee17e269ed..72cbae1a23c7 100644 --- a/homeassistant/components/nextdns/button.py +++ b/homeassistant/components/nextdns/button.py @@ -62,5 +62,12 @@ class NextDnsButton(NextDnsEntity, ButtonEntity): "error": repr(err), }, ) from err - except InvalidApiKeyError: + except InvalidApiKeyError as err: self.coordinator.config_entry.async_start_reauth(self.hass) + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="auth_error", + translation_placeholders={ + "entry": self.coordinator.config_entry.title, + }, + ) from err diff --git a/homeassistant/components/nextdns/quality_scale.yaml b/homeassistant/components/nextdns/quality_scale.yaml index 18eb018e4f10..ad1a24c692f1 100644 --- a/homeassistant/components/nextdns/quality_scale.yaml +++ b/homeassistant/components/nextdns/quality_scale.yaml @@ -30,9 +30,7 @@ rules: unique-config-entry: done # Silver - action-exceptions: - status: exempt - comment: The integration does not register services. + action-exceptions: done config-entry-unloading: done docs-configuration-parameters: status: exempt diff --git a/homeassistant/components/nextdns/strings.json b/homeassistant/components/nextdns/strings.json index dfc3d6b18226..f2e6d6908d19 100644 --- a/homeassistant/components/nextdns/strings.json +++ b/homeassistant/components/nextdns/strings.json @@ -426,6 +426,9 @@ "method_error": { "message": "An error occurred while calling the NextDNS API method for {entity}: {error}" }, + "set_setting_error": { + "message": "An error occurred while changing the setting for {entity}" + }, "update_error": { "message": "An error occurred while retrieving data from the NextDNS API for {entry}: {error}" } diff --git a/homeassistant/components/nextdns/switch.py b/homeassistant/components/nextdns/switch.py index c921190f342a..6f83178d0eff 100644 --- a/homeassistant/components/nextdns/switch.py +++ b/homeassistant/components/nextdns/switch.py @@ -622,10 +622,22 @@ class NextDnsSwitch(NextDnsEntity, SwitchEntity): "error": repr(err), }, ) from err - except InvalidApiKeyError: + except InvalidApiKeyError as err: self.coordinator.config_entry.async_start_reauth(self.hass) - return + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="auth_error", + translation_placeholders={ + "entry": self.coordinator.config_entry.title, + }, + ) from err - if result: - self._attr_is_on = new_state - self.async_write_ha_state() + if not result: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="set_setting_error", + translation_placeholders={"entity": self.entity_id}, + ) + + self._attr_is_on = new_state + self.async_write_ha_state() diff --git a/tests/components/nextdns/test_button.py b/tests/components/nextdns/test_button.py index 4b42851f7115..3dabd005571b 100644 --- a/tests/components/nextdns/test_button.py +++ b/tests/components/nextdns/test_button.py @@ -97,17 +97,21 @@ async def test_button_auth_error( mock_config_entry: MockConfigEntry, mock_nextdns_client: AsyncMock, ) -> None: - """Tests that the press action starts re-auth flow.""" + """Tests that the press action raises an error and starts re-auth flow.""" await init_integration(hass, mock_config_entry) mock_nextdns_client.clear_logs.side_effect = InvalidApiKeyError - await hass.services.async_call( - BUTTON_DOMAIN, - SERVICE_PRESS, - {ATTR_ENTITY_ID: "button.fake_profile_clear_logs"}, - blocking=True, - ) + with pytest.raises( + HomeAssistantError, + match="Authentication failed for NextDNS, please update your API key", + ): + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: "button.fake_profile_clear_logs"}, + blocking=True, + ) assert mock_config_entry.state is ConfigEntryState.LOADED diff --git a/tests/components/nextdns/test_switch.py b/tests/components/nextdns/test_switch.py index 6f805e6ff51b..acf89d51e14f 100644 --- a/tests/components/nextdns/test_switch.py +++ b/tests/components/nextdns/test_switch.py @@ -183,17 +183,21 @@ async def test_switch_auth_error( mock_config_entry: MockConfigEntry, mock_nextdns_client: AsyncMock, ) -> None: - """Tests that the turn on/off action starts re-auth flow.""" + """Tests that the turn on/off action raises an error and starts re-auth flow.""" await init_integration(hass, mock_config_entry) mock_nextdns_client.set_setting.side_effect = InvalidApiKeyError - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_ON, - {ATTR_ENTITY_ID: "switch.fake_profile_block_page"}, - blocking=True, - ) + with pytest.raises( + HomeAssistantError, + match="Authentication failed for NextDNS, please update your API key", + ): + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.fake_profile_block_page"}, + blocking=True, + ) assert mock_config_entry.state is ConfigEntryState.LOADED @@ -207,3 +211,34 @@ async def test_switch_auth_error( assert "context" in flow assert flow["context"].get("source") == SOURCE_REAUTH assert flow["context"].get("entry_id") == mock_config_entry.entry_id + + +async def test_switch_set_setting_failure( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_nextdns_client: AsyncMock, +) -> None: + """Tests that the turn on action raises an error when the API reports failure.""" + await init_integration(hass, mock_config_entry) + + mock_nextdns_client.set_setting.return_value = False + + state = hass.states.get("switch.fake_profile_block_page") + assert state + assert state.state == STATE_OFF + + with pytest.raises( + HomeAssistantError, + match="An error occurred while changing the setting for" + " switch.fake_profile_block_page", + ): + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.fake_profile_block_page"}, + blocking=True, + ) + + state = hass.states.get("switch.fake_profile_block_page") + assert state + assert state.state == STATE_OFF