Normalize test fixtures for irm_kmi (#182692)

This commit is contained in:
YP30
2026-09-20 17:12:29 +02:00
committed by GitHub
parent e99a7290d2
commit 14390247e6
7 changed files with 776 additions and 821 deletions
+13 -1
View File
@@ -1 +1,13 @@
"""Tests of IRM KMI integration."""
"""Tests for the IRM KMI integration."""
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
"""Set up the integration."""
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
+25 -66
View File
@@ -1,9 +1,8 @@
"""Fixtures for the IRM KMI integration tests."""
from collections.abc import Generator
from unittest.mock import MagicMock, patch
from unittest.mock import AsyncMock, MagicMock, patch
from irm_kmi_api import IrmKmiApiError
import pytest
from homeassistant.components.irm_kmi.const import DOMAIN
@@ -14,6 +13,8 @@ from homeassistant.const import (
CONF_UNIQUE_ID,
)
from .const import CURRENT_WEATHER
from tests.common import MockConfigEntry, load_json_object_fixture
@@ -21,13 +22,13 @@ from tests.common import MockConfigEntry, load_json_object_fixture
def mock_config_entry() -> MockConfigEntry:
"""Return the default mocked config entry."""
return MockConfigEntry(
title="Home",
title="Brussels",
domain=DOMAIN,
data={
CONF_LOCATION: {ATTR_LATITUDE: 50.84, ATTR_LONGITUDE: 4.35},
CONF_UNIQUE_ID: "city country",
CONF_UNIQUE_ID: "brussels be",
},
unique_id="50.84-4.35",
unique_id="brussels be",
)
@@ -39,82 +40,40 @@ def mock_setup_entry() -> Generator[None]:
@pytest.fixture
def mock_get_forecast_in_benelux():
"""Mock get_forecasts_coord() returning valid data in Benelux."""
def mock_config_flow_forecast() -> Generator[AsyncMock]:
"""Mock the config flow forecast fetch for a location in Belgium."""
with patch(
"homeassistant.components.irm_kmi.config_flow.IrmKmiApiClient.get_forecasts_coord",
return_value={"cityName": "Brussels", "country": "BE"},
):
yield
) as get_forecasts_coord:
yield get_forecasts_coord
@pytest.fixture
def mock_get_forecast_out_benelux_then_in_belgium():
"""Mock get_forecasts_coord() returning outside then inside Benelux."""
with patch(
"homeassistant.components.irm_kmi.config_flow.IrmKmiApiClient.get_forecasts_coord",
side_effect=[
{"cityName": "Outside the Benelux (Brussels)", "country": "BE"},
{"cityName": "Brussels", "country": "BE"},
],
):
yield
@pytest.fixture
def mock_get_forecast_api_error():
"""Mock get_forecasts_coord() so that it raises an error."""
with patch(
"homeassistant.components.irm_kmi.config_flow.IrmKmiApiClient.get_forecasts_coord",
side_effect=IrmKmiApiError,
):
yield
@pytest.fixture
def mock_irm_kmi_api(request: pytest.FixtureRequest) -> Generator[MagicMock]:
"""Return a mocked IrmKmi api client."""
fixture: str = "forecast.json"
forecast = load_json_object_fixture(fixture, "irm_kmi")
def mock_irm_kmi_api() -> Generator[MagicMock]:
"""Return a mocked IRM KMI client serving parsed data."""
with patch(
"homeassistant.components.irm_kmi.IrmKmiApiClientHa", autospec=True
) as irm_kmi_api_mock:
irm_kmi = irm_kmi_api_mock.return_value
irm_kmi.get_forecasts_coord.return_value = forecast
irm_kmi.get_country.return_value = "BE"
irm_kmi.get_current_weather.return_value = CURRENT_WEATHER
irm_kmi.get_daily_forecast.return_value = []
irm_kmi.get_hourly_forecast.return_value = []
yield irm_kmi
@pytest.fixture
def mock_irm_kmi_api_nl():
"""Mock get_forecasts_coord() to return a Netherlands forecast."""
fixture: str = "forecast_nl.json"
forecast = load_json_object_fixture(fixture, "irm_kmi")
with patch(
"homeassistant.components.irm_kmi.coordinator.IrmKmiApiClientHa.get_forecasts_coord",
return_value=forecast,
):
yield
def forecast_fixture() -> str:
"""Return the name of the recorded forecast to serve."""
return "forecast.json"
@pytest.fixture
def mock_irm_kmi_api_high_low_temp():
"""Mock get_forecasts_coord() to return high_low_temp forecast."""
fixture: str = "high_low_temp.json"
forecast = load_json_object_fixture(fixture, "irm_kmi")
def mock_get_forecasts_coord(forecast_fixture: str) -> Generator[AsyncMock]:
"""Mock get_forecasts_coord() to return a recorded forecast."""
with patch(
"homeassistant.components.irm_kmi.coordinator.IrmKmiApiClientHa.get_forecasts_coord",
return_value=forecast,
):
yield
@pytest.fixture
def mock_exception_irm_kmi_api(request: pytest.FixtureRequest) -> Generator[MagicMock]:
"""Return a mocked IrmKmi api client that raises on refresh."""
with patch(
"homeassistant.components.irm_kmi.IrmKmiApiClientHa", autospec=True
) as irm_kmi_api_mock:
irm_kmi = irm_kmi_api_mock.return_value
irm_kmi.refresh_forecasts_coord.side_effect = IrmKmiApiError
yield irm_kmi
"homeassistant.components.irm_kmi.IrmKmiApiClientHa.get_forecasts_coord",
return_value=load_json_object_fixture(forecast_fixture, DOMAIN),
) as get_forecasts_coord:
yield get_forecasts_coord
+15
View File
@@ -0,0 +1,15 @@
"""Constants shared by the IRM KMI integration tests."""
from irm_kmi_api import CurrentWeatherData
WEATHER_ENTITY_ID = "weather.brussels"
CURRENT_WEATHER = CurrentWeatherData(
condition="cloudy",
temperature=7.2,
wind_speed=25.0,
wind_gust_speed=50.0,
wind_bearing=180.0,
uv_index=0.7,
pressure=1015.0,
)
File diff suppressed because it is too large Load Diff
+38 -56
View File
@@ -1,7 +1,8 @@
"""Tests for the IRM KMI config flow."""
from unittest.mock import MagicMock
from unittest.mock import AsyncMock
from irm_kmi_api import IrmKmiApiError
import pytest
from homeassistant.components.irm_kmi.const import CONF_LANGUAGE_OVERRIDE, DOMAIN
@@ -15,55 +16,43 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, async_load_json_object_fixture
@pytest.mark.usefixtures("mock_setup_entry")
async def test_full_user_flow(
hass: HomeAssistant, mock_get_forecast_in_benelux: MagicMock
) -> None:
@pytest.mark.usefixtures("mock_setup_entry", "mock_config_flow_forecast")
async def test_full_user_flow(hass: HomeAssistant) -> None:
"""Test the full user configuration flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result.get("type") is FlowResultType.FORM
assert result.get("step_id") == "user"
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_LOCATION: {ATTR_LATITUDE: 50.123, ATTR_LONGITUDE: 4.456}},
)
assert result.get("type") is FlowResultType.CREATE_ENTRY
assert result.get("title") == "Brussels"
assert result.get("data") == {
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Brussels"
assert result["data"] == {
CONF_LOCATION: {ATTR_LATITUDE: 50.123, ATTR_LONGITUDE: 4.456},
CONF_UNIQUE_ID: "brussels be",
}
@pytest.mark.usefixtures("mock_setup_entry")
async def test_user_flow_home(
hass: HomeAssistant, mock_get_forecast_in_benelux: MagicMock
) -> None:
"""Test the full user configuration flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_LOCATION: {ATTR_LATITUDE: 50.123, ATTR_LONGITUDE: 4.456}},
)
assert result.get("type") is FlowResultType.CREATE_ENTRY
assert result.get("title") == "Brussels"
assert result["result"].unique_id == "brussels be"
@pytest.mark.usefixtures("mock_setup_entry")
async def test_config_flow_location_out_benelux(
hass: HomeAssistant, mock_get_forecast_out_benelux_then_in_belgium: MagicMock
hass: HomeAssistant, mock_config_flow_forecast: AsyncMock
) -> None:
"""Test configuration flow with a zone outside of Benelux."""
"""Test configuration flow with a location outside of Benelux."""
mock_config_flow_forecast.side_effect = [
await async_load_json_object_fixture(
hass, "forecast_out_of_benelux.json", DOMAIN
),
mock_config_flow_forecast.return_value,
]
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
@@ -73,22 +62,24 @@ async def test_config_flow_location_out_benelux(
user_input={CONF_LOCATION: {ATTR_LATITUDE: 0.123, ATTR_LONGITUDE: 0.456}},
)
assert result.get("type") is FlowResultType.FORM
assert result.get("step_id") == "user"
assert CONF_LOCATION in result.get("errors")
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {CONF_LOCATION: "out_of_benelux"}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_LOCATION: {ATTR_LATITUDE: 50.123, ATTR_LONGITUDE: 4.456}},
)
assert result.get("type") is FlowResultType.CREATE_ENTRY
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "Brussels"
@pytest.mark.usefixtures("mock_setup_entry")
async def test_config_flow_with_api_error(
hass: HomeAssistant, mock_get_forecast_api_error: MagicMock
hass: HomeAssistant, mock_config_flow_forecast: AsyncMock
) -> None:
"""Test when API returns an error during the configuration flow."""
mock_config_flow_forecast.side_effect = IrmKmiApiError
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
@@ -98,34 +89,27 @@ async def test_config_flow_with_api_error(
user_input={CONF_LOCATION: {ATTR_LATITUDE: 50.123, ATTR_LONGITUDE: 4.456}},
)
assert result.get("type") is FlowResultType.ABORT
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "api_error"
@pytest.mark.usefixtures("mock_setup_entry")
async def test_setup_twice_same_location(
hass: HomeAssistant, mock_get_forecast_in_benelux: MagicMock
@pytest.mark.usefixtures("mock_setup_entry", "mock_config_flow_forecast")
async def test_flow_already_configured(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:
"""Test when the user tries to set up the weather twice for the same location."""
"""Test the flow aborts when the location is already configured."""
mock_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_LOCATION: {ATTR_LATITUDE: 50.5, ATTR_LONGITUDE: 4.6}},
user_input={CONF_LOCATION: {ATTR_LATITUDE: 50.123, ATTR_LONGITUDE: 4.456}},
)
assert result.get("type") is FlowResultType.CREATE_ENTRY
# Set up a second time
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_LOCATION: {ATTR_LATITUDE: 50.5, ATTR_LONGITUDE: 4.6}},
)
assert result.get("type") is FlowResultType.ABORT
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_option_flow(
@@ -136,9 +120,7 @@ async def test_option_flow(
assert not mock_config_entry.options
result = await hass.config_entries.options.async_init(
mock_config_entry.entry_id, data=None
)
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "init"
+13 -13
View File
@@ -1,43 +1,43 @@
"""Tests for the IRM KMI integration."""
from unittest.mock import AsyncMock
from unittest.mock import MagicMock
from irm_kmi_api import IrmKmiApiError
import pytest
from homeassistant.components.irm_kmi.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from . import setup_integration
from tests.common import MockConfigEntry
@pytest.mark.usefixtures("mock_irm_kmi_api")
async def test_load_unload_config_entry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_irm_kmi_api: AsyncMock,
) -> None:
"""Test the IRM KMI configuration entry loading/unloading."""
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.LOADED
await hass.config_entries.async_unload(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert not hass.data.get(DOMAIN)
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
async def test_config_entry_not_ready(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_exception_irm_kmi_api: AsyncMock,
mock_irm_kmi_api: MagicMock,
) -> None:
"""Test the IRM KMI configuration entry not ready."""
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
mock_irm_kmi_api.refresh_forecasts_coord.side_effect = IrmKmiApiError
assert mock_exception_irm_kmi_api.refresh_forecasts_coord.call_count == 1
await setup_integration(hass, mock_config_entry)
assert mock_irm_kmi_api.refresh_forecasts_coord.call_count == 1
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
+45 -50
View File
@@ -1,6 +1,6 @@
"""Test for the weather entity of the IRM KMI integration."""
from unittest.mock import AsyncMock
from typing import Any
import pytest
from syrupy.assertion import SnapshotAssertion
@@ -13,26 +13,46 @@ from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
import homeassistant.helpers.entity_registry as er
from . import setup_integration
from .const import WEATHER_ENTITY_ID
from tests.common import MockConfigEntry, snapshot_platform
async def _get_forecast(
hass: HomeAssistant, forecast_type: str
) -> list[dict[str, Any]]:
"""Return the forecast from weather.get_forecasts."""
response = await hass.services.async_call(
WEATHER_DOMAIN,
SERVICE_GET_FORECASTS,
{
ATTR_ENTITY_ID: WEATHER_ENTITY_ID,
"type": forecast_type,
},
blocking=True,
return_response=True,
)
return response[WEATHER_ENTITY_ID]["forecast"]
@pytest.mark.usefixtures("mock_get_forecasts_coord")
@pytest.mark.parametrize("forecast_fixture", ["forecast_nl.json"])
@pytest.mark.freeze_time("2023-12-28T15:30:00+01:00")
async def test_weather_nl(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_irm_kmi_api_nl: AsyncMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
) -> None:
"""Test weather with forecast from the Netherland."""
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
await setup_integration(hass, mock_config_entry)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
@pytest.mark.usefixtures("mock_get_forecasts_coord")
@pytest.mark.parametrize("forecast_fixture", ["forecast_nl.json"])
@pytest.mark.parametrize(
"forecast_type",
["daily", "hourly"],
@@ -41,60 +61,35 @@ async def test_weather_nl(
async def test_forecast_service(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
mock_irm_kmi_api_nl: AsyncMock,
mock_config_entry: MockConfigEntry,
forecast_type: str,
) -> None:
"""Test multiple forecast."""
mock_config_entry.add_to_hass(hass)
await setup_integration(hass, mock_config_entry)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
response = await hass.services.async_call(
WEATHER_DOMAIN,
SERVICE_GET_FORECASTS,
{
ATTR_ENTITY_ID: "weather.home",
"type": forecast_type,
},
blocking=True,
return_response=True,
)
assert response == snapshot
assert await _get_forecast(hass, forecast_type) == snapshot
@pytest.mark.usefixtures("mock_get_forecasts_coord")
@pytest.mark.parametrize("forecast_fixture", ["high_low_temp.json"])
@pytest.mark.freeze_time("2024-01-21T14:15:00+01:00")
@pytest.mark.parametrize(
"forecast_type",
["daily", "hourly"],
)
async def test_weather_higher_temp_at_night(
async def test_daily_forecast_night_low_above_day_high(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_irm_kmi_api_high_low_temp: AsyncMock,
forecast_type: str,
) -> None:
"""Test templow is always lower than temperature."""
"""Test a night low above the day high is swapped into the first day."""
# Test case for https://github.com/jdejaegh/irm-kmi-ha/issues/8
mock_config_entry.add_to_hass(hass)
await setup_integration(hass, mock_config_entry)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
response = await hass.services.async_call(
WEATHER_DOMAIN,
SERVICE_GET_FORECASTS,
{
ATTR_ENTITY_ID: "weather.home",
"type": forecast_type,
},
blocking=True,
return_response=True,
)
for forecast in response["weather.home"]["forecast"]:
assert (
forecast.get("native_temperature") is None
or forecast.get("native_templow") is None
or forecast["native_temperature"] >= forecast["native_templow"]
)
assert [
(forecast["temperature"], forecast["templow"])
for forecast in await _get_forecast(hass, "daily")
] == [
(4.0, 3.0),
(10.0, 1.0),
(8.0, 3.0),
(12.0, 10.0),
(8.0, 2.0),
(8.0, 6.0),
(6.0, -2.0),
]