Improve exception handling in Nord Pool (#130386)

* Improve exception handling in Nord Pool

* Improve auth string

* Remove auth
This commit is contained in:
G Johansson 2024-11-11 23:02:48 +01:00 committed by GitHub
parent d1c3e1caa9
commit 3eab72b2aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 78 deletions

View File

@ -4,7 +4,12 @@ from __future__ import annotations
from typing import Any from typing import Any
from pynordpool import Currency, NordPoolClient, NordPoolError from pynordpool import (
Currency,
NordPoolClient,
NordPoolEmptyResponseError,
NordPoolError,
)
from pynordpool.const import AREAS from pynordpool.const import AREAS
import voluptuous as vol import voluptuous as vol
@ -53,17 +58,16 @@ async def test_api(hass: HomeAssistant, user_input: dict[str, Any]) -> dict[str,
"""Test fetch data from Nord Pool.""" """Test fetch data from Nord Pool."""
client = NordPoolClient(async_get_clientsession(hass)) client = NordPoolClient(async_get_clientsession(hass))
try: try:
data = await client.async_get_delivery_period( await client.async_get_delivery_period(
dt_util.now(), dt_util.now(),
Currency(user_input[CONF_CURRENCY]), Currency(user_input[CONF_CURRENCY]),
user_input[CONF_AREAS], user_input[CONF_AREAS],
) )
except NordPoolEmptyResponseError:
return {"base": "no_data"}
except NordPoolError: except NordPoolError:
return {"base": "cannot_connect"} return {"base": "cannot_connect"}
if not data.raw:
return {"base": "no_data"}
return {} return {}

View File

@ -9,8 +9,8 @@ from typing import TYPE_CHECKING
from pynordpool import ( from pynordpool import (
Currency, Currency,
DeliveryPeriodData, DeliveryPeriodData,
NordPoolAuthenticationError,
NordPoolClient, NordPoolClient,
NordPoolEmptyResponseError,
NordPoolError, NordPoolError,
NordPoolResponseError, NordPoolResponseError,
) )
@ -19,7 +19,7 @@ from homeassistant.const import CONF_CURRENCY
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from .const import CONF_AREAS, DOMAIN, LOGGER from .const import CONF_AREAS, DOMAIN, LOGGER
@ -75,8 +75,8 @@ class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodData]):
Currency(self.config_entry.data[CONF_CURRENCY]), Currency(self.config_entry.data[CONF_CURRENCY]),
self.config_entry.data[CONF_AREAS], self.config_entry.data[CONF_AREAS],
) )
except NordPoolAuthenticationError as error: except NordPoolEmptyResponseError as error:
LOGGER.error("Authentication error: %s", error) LOGGER.debug("Empty response error: %s", error)
self.async_set_update_error(error) self.async_set_update_error(error)
return return
except NordPoolResponseError as error: except NordPoolResponseError as error:
@ -88,8 +88,4 @@ class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodData]):
self.async_set_update_error(error) self.async_set_update_error(error)
return return
if not data.raw:
self.async_set_update_error(UpdateFailed("No data"))
return
self.async_set_updated_data(data) self.async_set_updated_data(data)

View File

@ -2,13 +2,12 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import replace
from unittest.mock import patch from unittest.mock import patch
from pynordpool import ( from pynordpool import (
DeliveryPeriodData, DeliveryPeriodData,
NordPoolAuthenticationError,
NordPoolConnectionError, NordPoolConnectionError,
NordPoolEmptyResponseError,
NordPoolError, NordPoolError,
NordPoolResponseError, NordPoolResponseError,
) )
@ -71,7 +70,7 @@ async def test_single_config_entry(
("error_message", "p_error"), ("error_message", "p_error"),
[ [
(NordPoolConnectionError, "cannot_connect"), (NordPoolConnectionError, "cannot_connect"),
(NordPoolAuthenticationError, "cannot_connect"), (NordPoolEmptyResponseError, "no_data"),
(NordPoolError, "cannot_connect"), (NordPoolError, "cannot_connect"),
(NordPoolResponseError, "cannot_connect"), (NordPoolResponseError, "cannot_connect"),
], ],
@ -116,44 +115,6 @@ async def test_cannot_connect(
assert result["data"] == {"areas": ["SE3", "SE4"], "currency": "SEK"} assert result["data"] == {"areas": ["SE3", "SE4"], "currency": "SEK"}
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
async def test_empty_data(hass: HomeAssistant, get_data: DeliveryPeriodData) -> None:
"""Test empty data error."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == config_entries.SOURCE_USER
invalid_data = replace(get_data, raw={})
with patch(
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
return_value=invalid_data,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=ENTRY_CONFIG,
)
assert result["errors"] == {"base": "no_data"}
with patch(
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
return_value=get_data,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=ENTRY_CONFIG,
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Nord Pool"
assert result["data"] == {"areas": ["SE3", "SE4"], "currency": "SEK"}
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00") @pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
async def test_reconfigure( async def test_reconfigure(
hass: HomeAssistant, hass: HomeAssistant,
@ -193,7 +154,7 @@ async def test_reconfigure(
("error_message", "p_error"), ("error_message", "p_error"),
[ [
(NordPoolConnectionError, "cannot_connect"), (NordPoolConnectionError, "cannot_connect"),
(NordPoolAuthenticationError, "cannot_connect"), (NordPoolEmptyResponseError, "no_data"),
(NordPoolError, "cannot_connect"), (NordPoolError, "cannot_connect"),
(NordPoolResponseError, "cannot_connect"), (NordPoolResponseError, "cannot_connect"),
], ],

View File

@ -9,6 +9,7 @@ from freezegun.api import FrozenDateTimeFactory
from pynordpool import ( from pynordpool import (
DeliveryPeriodData, DeliveryPeriodData,
NordPoolAuthenticationError, NordPoolAuthenticationError,
NordPoolEmptyResponseError,
NordPoolError, NordPoolError,
NordPoolResponseError, NordPoolResponseError,
) )
@ -18,14 +19,13 @@ from homeassistant.components.nordpool.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import STATE_UNAVAILABLE from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util
from . import ENTRY_CONFIG from . import ENTRY_CONFIG
from tests.common import MockConfigEntry, async_fire_time_changed from tests.common import MockConfigEntry, async_fire_time_changed
@pytest.mark.freeze_time("2024-11-05T12:00:00+00:00") @pytest.mark.freeze_time("2024-11-05T10:00:00+00:00")
async def test_coordinator( async def test_coordinator(
hass: HomeAssistant, hass: HomeAssistant,
get_data: DeliveryPeriodData, get_data: DeliveryPeriodData,
@ -51,7 +51,7 @@ async def test_coordinator(
await hass.async_block_till_done() await hass.async_block_till_done()
mock_data.assert_called_once() mock_data.assert_called_once()
state = hass.states.get("sensor.nord_pool_se3_current_price") state = hass.states.get("sensor.nord_pool_se3_current_price")
assert state.state == "0.94949" assert state.state == "0.92737"
mock_data.reset_mock() mock_data.reset_mock()
mock_data.side_effect = NordPoolError("error") mock_data.side_effect = NordPoolError("error")
@ -74,6 +74,17 @@ async def test_coordinator(
assert "Authentication error" in caplog.text assert "Authentication error" in caplog.text
mock_data.reset_mock() mock_data.reset_mock()
assert "Empty response" not in caplog.text
mock_data.side_effect = NordPoolEmptyResponseError("Empty response")
freezer.tick(timedelta(hours=1))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
mock_data.assert_called_once()
state = hass.states.get("sensor.nord_pool_se3_current_price")
assert state.state == STATE_UNAVAILABLE
assert "Empty response" in caplog.text
mock_data.reset_mock()
assert "Response error" not in caplog.text assert "Response error" not in caplog.text
mock_data.side_effect = NordPoolResponseError("Response error") mock_data.side_effect = NordPoolResponseError("Response error")
freezer.tick(timedelta(hours=1)) freezer.tick(timedelta(hours=1))
@ -85,25 +96,6 @@ async def test_coordinator(
assert "Response error" in caplog.text assert "Response error" in caplog.text
mock_data.reset_mock() mock_data.reset_mock()
mock_data.return_value = DeliveryPeriodData(
raw={},
requested_date="2024-11-05",
updated_at=dt_util.utcnow(),
entries=[],
block_prices=[],
currency="SEK",
exchange_rate=1,
area_average={},
)
mock_data.side_effect = None
freezer.tick(timedelta(hours=1))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_data.assert_called_once()
state = hass.states.get("sensor.nord_pool_se3_current_price")
assert state.state == STATE_UNAVAILABLE
mock_data.reset_mock()
mock_data.return_value = get_data mock_data.return_value = get_data
mock_data.side_effect = None mock_data.side_effect = None
freezer.tick(timedelta(hours=1)) freezer.tick(timedelta(hours=1))
@ -111,4 +103,4 @@ async def test_coordinator(
await hass.async_block_till_done() await hass.async_block_till_done()
mock_data.assert_called_once() mock_data.assert_called_once()
state = hass.states.get("sensor.nord_pool_se3_current_price") state = hass.states.get("sensor.nord_pool_se3_current_price")
assert state.state == "1.81983" assert state.state == "1.81645"