Use mock client in Airly tests (#182485)

This commit is contained in:
Maciej Bieniek
2026-09-18 20:53:50 +02:00
committed by GitHub
parent 6ece6743c6
commit 9958b3680a
7 changed files with 289 additions and 224 deletions
+6 -34
View File
@@ -1,44 +1,16 @@
"""Tests for Airly."""
from homeassistant.components.airly.const import DOMAIN
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry, async_load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
API_NEAREST_URL = "https://airapi.airly.eu/v2/measurements/nearest?lat=12.300000&lng=45.600000&maxDistanceKM=5.000000"
API_POINT_URL = (
"https://airapi.airly.eu/v2/measurements/point?lat=12.300000&lng=45.600000"
)
HEADERS = {
"X-RateLimit-Limit-day": "100",
"X-RateLimit-Remaining-day": "42",
}
from tests.common import MockConfigEntry
async def init_integration(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> MockConfigEntry:
hass: HomeAssistant,
config_entry: MockConfigEntry,
) -> None:
"""Set up the Airly integration in Home Assistant."""
entry = MockConfigEntry(
domain=DOMAIN,
title="Home",
entry_id="3bd2acb0e4f0476d40865546d0d91921",
unique_id="12.3-45.6",
data={
"api_key": "foo",
"latitude": 12.3,
"longitude": 45.6,
},
)
config_entry.add_to_hass(hass)
aioclient_mock.get(
API_POINT_URL,
text=await async_load_fixture(hass, "valid_station.json", DOMAIN),
headers=HEADERS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
return entry
+79
View File
@@ -0,0 +1,79 @@
"""Fixtures for the Airly integration tests."""
from collections.abc import Generator
from unittest.mock import AsyncMock, MagicMock, patch
from airly.measurements import Measurement
import pytest
from homeassistant.components.airly.const import DOMAIN
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from tests.common import MockConfigEntry, load_json_object_fixture
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return the default mocked config entry."""
return MockConfigEntry(
domain=DOMAIN,
title="Home",
entry_id="3bd2acb0e4f0476d40865546d0d91921",
unique_id="12.3-45.6",
data={
CONF_API_KEY: "foo",
CONF_LATITUDE: 12.3,
CONF_LONGITUDE: 45.6,
},
)
def _measurements(filename: str) -> Measurement:
"""Build Airly measurements from a fixture."""
data = load_json_object_fixture(filename, DOMAIN)["current"]
return Measurement(data)
@pytest.fixture
def mock_airly_measurements() -> Measurement:
"""Return the default mocked Airly measurements."""
return _measurements("valid_station.json")
@pytest.fixture
def mock_airly_no_station_measurements() -> Measurement:
"""Return the mocked Airly measurements for an area without sensors."""
return _measurements("no_station.json")
@pytest.fixture
def mock_airly() -> Generator[MagicMock]:
"""Mock the Airly client class."""
with (
patch(
"homeassistant.components.airly.coordinator.Airly", autospec=True
) as mock_airly,
patch("homeassistant.components.airly.config_flow.Airly", new=mock_airly),
):
yield mock_airly
@pytest.fixture
def mock_airly_client(
mock_airly: MagicMock,
mock_airly_measurements: Measurement,
) -> MagicMock:
"""Mock an Airly client instance."""
client = mock_airly.return_value
for measurements in (
client.create_measurements_session_point.return_value,
client.create_measurements_session_nearest.return_value,
):
measurements.current = mock_airly_measurements
measurements.update = AsyncMock()
client.requests_remaining = 42
client.requests_per_day = 100
return client
+57 -68
View File
@@ -2,10 +2,11 @@
from collections.abc import Generator
from http import HTTPStatus
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import AsyncMock, MagicMock, Mock, patch
from aiohttp import ClientConnectorError
from airly.exceptions import AirlyError
from airly.measurements import Measurement
import pytest
from homeassistant.components.airly.const import CONF_USE_NEAREST, DEFAULT_NAME, DOMAIN
@@ -14,10 +15,7 @@ from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from . import API_NEAREST_URL, API_POINT_URL
from tests.common import MockConfigEntry, async_load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.common import MockConfigEntry
CONFIG = {
CONF_API_KEY: "foo",
@@ -36,14 +34,15 @@ def mock_setup_entry() -> Generator[AsyncMock]:
async def test_invalid_api_key(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_airly_client: MagicMock,
) -> None:
"""Test that errors are shown when API key is invalid."""
aioclient_mock.get(
API_POINT_URL,
exc=AirlyError(
HTTPStatus.UNAUTHORIZED, {"message": "Invalid authentication credentials"}
),
point_measurements = (
mock_airly_client.create_measurements_session_point.return_value
)
point_measurements.update.side_effect = AirlyError(
HTTPStatus.UNAUTHORIZED, {"message": "Invalid authentication credentials"}
)
result = await hass.config_entries.flow.async_init(
@@ -59,10 +58,7 @@ async def test_invalid_api_key(
assert result["errors"] == {"base": "invalid_api_key"}
aioclient_mock.clear_requests()
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
point_measurements.update.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONFIG
@@ -77,16 +73,17 @@ async def test_invalid_api_key(
async def test_invalid_location(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_airly_client: MagicMock,
mock_airly_measurements: Measurement,
mock_airly_no_station_measurements: Measurement,
) -> None:
"""Test that errors are shown when location is invalid."""
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
mock_airly_client.create_measurements_session_point.return_value.current = (
mock_airly_no_station_measurements
)
aioclient_mock.get(
API_NEAREST_URL,
exc=AirlyError(HTTPStatus.NOT_FOUND, {"message": "Installation was not found"}),
mock_airly_client.create_measurements_session_nearest.return_value.update.side_effect = AirlyError(
HTTPStatus.NOT_FOUND, {"message": "Installation was not found"}
)
result = await hass.config_entries.flow.async_init(
@@ -102,9 +99,8 @@ async def test_invalid_location(
assert result["errors"] == {"base": "wrong_location"}
aioclient_mock.clear_requests()
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
mock_airly_client.create_measurements_session_point.return_value.current = (
mock_airly_measurements
)
result = await hass.config_entries.flow.async_configure(
@@ -120,16 +116,16 @@ async def test_invalid_location(
async def test_invalid_location_for_point_and_nearest(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_airly_client: MagicMock,
mock_airly_no_station_measurements: Measurement,
) -> None:
"""Test an abort when the location is wrong for the point and nearest methods."""
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
mock_airly_client.create_measurements_session_point.return_value.current = (
mock_airly_no_station_measurements
)
aioclient_mock.get(
API_NEAREST_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
mock_airly_client.create_measurements_session_nearest.return_value.current = (
mock_airly_no_station_measurements
)
result = await hass.config_entries.flow.async_init(
@@ -148,12 +144,10 @@ async def test_invalid_location_for_point_and_nearest(
async def test_duplicate_error(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_airly_client: MagicMock,
) -> None:
"""Test that errors are shown when duplicates are added."""
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
MockConfigEntry(domain=DOMAIN, unique_id="12.3-45.6", data=CONFIG).add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
@@ -172,13 +166,10 @@ async def test_duplicate_error(
async def test_create_entry(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_airly_client: MagicMock,
) -> None:
"""Test that the user step works."""
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
@@ -199,17 +190,13 @@ async def test_create_entry(
async def test_create_entry_with_nearest_method(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_airly_client: MagicMock,
mock_airly_no_station_measurements: Measurement,
) -> None:
"""Test that the user step works with nearest method."""
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
)
aioclient_mock.get(
API_NEAREST_URL,
text=await async_load_fixture(hass, "valid_station.json", DOMAIN),
mock_airly_client.create_measurements_session_point.return_value.current = (
mock_airly_no_station_measurements
)
result = await hass.config_entries.flow.async_init(
@@ -240,7 +227,7 @@ async def test_create_entry_with_nearest_method(
)
async def test_cannot_connect(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_airly_client: MagicMock,
exception: Exception,
error: str,
) -> None:
@@ -252,19 +239,20 @@ async def test_cannot_connect(
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with patch("airly.measurements.MeasurementsSession.update", side_effect=exception):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONFIG
)
point_measurements = (
mock_airly_client.create_measurements_session_point.return_value
)
point_measurements.update.side_effect = exception
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONFIG
)
assert result["errors"] == {"base": error}
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
aioclient_mock.clear_requests()
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
point_measurements.update.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONFIG
@@ -285,7 +273,7 @@ async def test_cannot_connect(
)
async def test_unknown_error(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_airly_client: MagicMock,
exception: Exception,
error: str,
) -> None:
@@ -297,19 +285,20 @@ async def test_unknown_error(
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
with patch("airly.measurements.MeasurementsSession.update", side_effect=exception):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONFIG
)
point_measurements = (
mock_airly_client.create_measurements_session_point.return_value
)
point_measurements.update.side_effect = exception
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONFIG
)
assert result["errors"] == {"base": error}
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
aioclient_mock.clear_requests()
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
point_measurements.update.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=CONFIG
+8 -4
View File
@@ -1,5 +1,6 @@
"""Test Airly diagnostics."""
import pytest
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
@@ -7,20 +8,23 @@ from homeassistant.core import HomeAssistant
from . import init_integration
from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import ClientSessionGenerator
@pytest.mark.usefixtures("mock_airly_client")
async def test_entry_diagnostics(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
hass_client: ClientSessionGenerator,
snapshot: SnapshotAssertion,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test config entry diagnostics."""
entry = await init_integration(hass, aioclient_mock)
await init_integration(hass, mock_config_entry)
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
result = await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry
)
assert result == snapshot(exclude=props("created_at", "modified_at"))
+98 -92
View File
@@ -1,29 +1,37 @@
"""Test init of Airly integration."""
from typing import Any
from unittest.mock import MagicMock
from airly.measurements import Measurement
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_DOMAIN
from homeassistant.components.airly.const import DOMAIN
from homeassistant.components.airly.const import CONF_USE_NEAREST, DOMAIN
from homeassistant.components.airly.coordinator import set_update_interval
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.const import (
CONF_API_KEY,
CONF_LATITUDE,
CONF_LONGITUDE,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import API_POINT_URL, init_integration
from . import init_integration
from tests.common import MockConfigEntry, async_fire_time_changed, async_load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.common import MockConfigEntry, async_fire_time_changed
@pytest.mark.usefixtures("mock_airly_client")
async def test_async_setup_entry(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test a successful setup entry."""
await init_integration(hass, aioclient_mock)
await init_integration(hass, mock_config_entry)
state = hass.states.get("sensor.home_pm2_5")
assert state is not None
@@ -31,108 +39,108 @@ async def test_async_setup_entry(
assert state.state == "4.37"
async def test_config_not_ready(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
async def test_async_setup_entry_with_nearest(
hass: HomeAssistant,
mock_airly_client: MagicMock,
) -> None:
"""Test for setup failure if connection to Airly is missing."""
"""Test a successful setup entry with nearest station."""
entry = MockConfigEntry(
domain=DOMAIN,
title="Home",
unique_id="12.3-45.6",
data={
"api_key": "foo",
"latitude": 12.3,
"longitude": 45.6,
"use_nearest": True,
CONF_API_KEY: "foo",
CONF_LATITUDE: 12.3,
CONF_LONGITUDE: 45.6,
CONF_USE_NEAREST: True,
},
)
aioclient_mock.get(API_POINT_URL, exc=ConnectionError())
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
assert entry.state is ConfigEntryState.SETUP_RETRY
await init_integration(hass, entry)
assert entry.state is ConfigEntryState.LOADED
mock_airly_client.create_measurements_session_nearest.assert_called_once_with(
12.3, 45.6, max_distance_km=5
)
mock_airly_client.create_measurements_session_point.assert_not_called()
state = hass.states.get("sensor.home_pm2_5")
assert state is not None
assert state.state == "4.37"
async def test_config_without_unique_id(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
async def test_config_not_ready(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_airly_client: MagicMock,
) -> None:
"""Test for setup failure if connection to Airly is missing."""
mock_airly_client.create_measurements_session_point.return_value.update.side_effect = ConnectionError()
await init_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
@pytest.mark.usefixtures("mock_airly_client")
async def test_config_without_unique_id(hass: HomeAssistant) -> None:
"""Test for setup entry without unique_id."""
entry = MockConfigEntry(
domain=DOMAIN,
title="Home",
data={
"api_key": "foo",
"latitude": 12.3,
"longitude": 45.6,
CONF_API_KEY: "foo",
CONF_LATITUDE: 12.3,
CONF_LONGITUDE: 45.6,
},
)
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await init_integration(hass, entry)
assert entry.state is ConfigEntryState.LOADED
assert entry.unique_id == "12.3-45.6"
async def test_config_with_turned_off_station(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_airly_client: MagicMock,
mock_airly_no_station_measurements: Measurement,
) -> None:
"""Test for setup entry for a turned off measuring station."""
entry = MockConfigEntry(
domain=DOMAIN,
title="Home",
unique_id="12.3-45.6",
data={
"api_key": "foo",
"latitude": 12.3,
"longitude": 45.6,
},
mock_airly_client.create_measurements_session_point.return_value.current = (
mock_airly_no_station_measurements
)
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "no_station.json", DOMAIN)
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
assert entry.state is ConfigEntryState.SETUP_RETRY
await init_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
async def test_update_interval(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_airly_client: MagicMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test correct update interval when the number of configured instances changes."""
REMAINING_REQUESTS = 15
HEADERS = {
"X-RateLimit-Limit-day": "100",
"X-RateLimit-Remaining-day": str(REMAINING_REQUESTS),
}
mock_airly_client.requests_remaining = REMAINING_REQUESTS
entry = MockConfigEntry(
domain=DOMAIN,
title="Home",
unique_id="12.3-45.6",
data={
"api_key": "foo",
"latitude": 12.3,
"longitude": 45.6,
CONF_API_KEY: "foo",
CONF_LATITUDE: 12.3,
CONF_LONGITUDE: 45.6,
},
)
aioclient_mock.get(
API_POINT_URL,
text=await async_load_fixture(hass, "valid_station.json", DOMAIN),
headers=HEADERS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
await init_integration(hass, entry)
instances = 1
assert aioclient_mock.call_count == 1
create_measurements = mock_airly_client.create_measurements_session_point
update_measurements = create_measurements.return_value.update
assert create_measurements.call_count == 1
assert update_measurements.call_count == 1
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert entry.state is ConfigEntryState.LOADED
@@ -141,8 +149,11 @@ async def test_update_interval(
async_fire_time_changed(hass)
await hass.async_block_till_done()
# call_count should increase by one because we have one instance configured
assert aioclient_mock.call_count == 2
# update should be called once more because we have one instance configured.
# The measurements session is created once per entry, so create is not
# called again on refresh.
assert create_measurements.call_count == 1
assert update_measurements.call_count == 2
# Now we add the second Airly instance
entry = MockConfigEntry(
@@ -150,23 +161,17 @@ async def test_update_interval(
title="Work",
unique_id="66.66-111.11",
data={
"api_key": "foo",
"latitude": 66.66,
"longitude": 111.11,
CONF_API_KEY: "foo",
CONF_LATITUDE: 66.66,
CONF_LONGITUDE: 111.11,
},
)
aioclient_mock.get(
"https://airapi.airly.eu/v2/measurements/point?lat=66.660000&lng=111.110000",
text=await async_load_fixture(hass, "valid_station.json", DOMAIN),
headers=HEADERS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
await init_integration(hass, entry)
instances = 2
assert aioclient_mock.call_count == 3
assert create_measurements.call_count == 2
assert update_measurements.call_count == 3
assert len(hass.config_entries.async_entries(DOMAIN)) == 2
assert entry.state is ConfigEntryState.LOADED
@@ -175,30 +180,34 @@ async def test_update_interval(
async_fire_time_changed(hass)
await hass.async_block_till_done()
# call_count should increase by two because we have two instances configured
assert aioclient_mock.call_count == 5
# update should be called once more per instance because we have two
# instances configured
assert create_measurements.call_count == 2
assert update_measurements.call_count == 5
@pytest.mark.usefixtures("mock_airly_client")
async def test_unload_entry(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test successful unload of entry."""
entry = await init_integration(hass, aioclient_mock)
await init_integration(hass, mock_config_entry)
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert entry.state is ConfigEntryState.LOADED
assert mock_config_entry.state is ConfigEntryState.LOADED
assert await hass.config_entries.async_unload(entry.entry_id)
assert await hass.config_entries.async_unload(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.NOT_LOADED
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
assert not hass.data.get(DOMAIN)
@pytest.mark.parametrize("old_identifier", [(DOMAIN, 123, 456), (DOMAIN, "123", "456")])
@pytest.mark.usefixtures("mock_airly_client")
async def test_migrate_device_entry(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
old_identifier: tuple[str, Any, Any],
device_registry: dr.DeviceRegistry,
) -> None:
@@ -208,15 +217,11 @@ async def test_migrate_device_entry(
title="Home",
unique_id="123-456",
data={
"api_key": "foo",
"latitude": 123,
"longitude": 456,
CONF_API_KEY: "foo",
CONF_LATITUDE: 123,
CONF_LONGITUDE: 456,
},
)
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
config_entry.add_to_hass(hass)
device_entry = device_registry.async_get_or_create(
@@ -234,8 +239,9 @@ async def test_migrate_device_entry(
async def test_remove_air_quality_entities(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
mock_airly_client: MagicMock,
) -> None:
"""Test remove air_quality entities from registry."""
entity_registry.async_get_or_create(
@@ -246,7 +252,7 @@ async def test_remove_air_quality_entities(
disabled_by=None,
)
await init_integration(hass, aioclient_mock)
await init_integration(hass, mock_config_entry)
entry = entity_registry.async_get("air_quality.home")
assert entry is None
+29 -21
View File
@@ -1,14 +1,14 @@
"""Test sensor of Airly integration."""
from collections.abc import Generator
from datetime import timedelta
from http import HTTPStatus
from unittest.mock import patch
from unittest.mock import MagicMock, patch
from airly.exceptions import AirlyError
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.airly.const import DOMAIN
from homeassistant.components.homeassistant import (
DOMAIN as HOMEASSISTANT_DOMAIN,
SERVICE_UPDATE_ENTITY,
@@ -19,23 +19,31 @@ from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
from . import API_POINT_URL, init_integration
from . import init_integration
from tests.common import async_fire_time_changed, async_load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.common import MockConfigEntry, async_fire_time_changed
@pytest.fixture(autouse=True)
def override_platforms() -> Generator[None]:
"""Override PLATFORMS."""
with patch("homeassistant.components.airly.PLATFORMS", [Platform.SENSOR]):
yield
@pytest.mark.usefixtures("mock_airly_client")
async def test_sensor(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test states of the sensor."""
with patch("homeassistant.components.airly.PLATFORMS", [Platform.SENSOR]):
entry = await init_integration(hass, aioclient_mock)
await init_integration(hass, mock_config_entry)
entity_entries = er.async_entries_for_config_entry(entity_registry, entry.entry_id)
entity_entries = er.async_entries_for_config_entry(
entity_registry, mock_config_entry.entry_id
)
assert entity_entries
for entity_entry in entity_entries:
@@ -53,22 +61,22 @@ async def test_sensor(
)
async def test_availability(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_config_entry: MockConfigEntry,
mock_airly_client: MagicMock,
exception: Exception,
) -> None:
"""Ensure that we mark the entities unavailable correctly.
Test when service is offline.
"""
await init_integration(hass, aioclient_mock)
await init_integration(hass, mock_config_entry)
state = hass.states.get("sensor.home_humidity")
assert state
assert state.state != STATE_UNAVAILABLE
assert state.state == "68.35"
aioclient_mock.clear_requests()
aioclient_mock.get(API_POINT_URL, exc=exception)
mock_airly_client.create_measurements_session_point.return_value.update.side_effect = exception
future = utcnow() + timedelta(minutes=60)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
@@ -77,10 +85,7 @@ async def test_availability(
assert state
assert state.state == STATE_UNAVAILABLE
aioclient_mock.clear_requests()
aioclient_mock.get(
API_POINT_URL, text=await async_load_fixture(hass, "valid_station.json", DOMAIN)
)
mock_airly_client.create_measurements_session_point.return_value.update.side_effect = None
future = utcnow() + timedelta(minutes=120)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
@@ -92,12 +97,15 @@ async def test_availability(
async def test_manual_update_entity(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_airly_client: MagicMock,
) -> None:
"""Test manual update entity via service homeassistant/update_entity."""
await init_integration(hass, aioclient_mock)
await init_integration(hass, mock_config_entry)
call_count = aioclient_mock.call_count
measurements = mock_airly_client.create_measurements_session_point.return_value
call_count = measurements.update.call_count
await async_setup_component(hass, HOMEASSISTANT_DOMAIN, {})
await hass.services.async_call(
HOMEASSISTANT_DOMAIN,
@@ -106,4 +114,4 @@ async def test_manual_update_entity(
blocking=True,
)
assert aioclient_mock.call_count == call_count + 1
assert measurements.update.call_count == call_count + 1
+12 -5
View File
@@ -1,6 +1,7 @@
"""Test Airly system health."""
import asyncio
from unittest.mock import MagicMock
from aiohttp import ClientError
@@ -10,17 +11,20 @@ from homeassistant.setup import async_setup_component
from . import init_integration
from tests.common import get_system_health_info
from tests.common import MockConfigEntry, get_system_health_info
from tests.test_util.aiohttp import AiohttpClientMocker
async def test_airly_system_health(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_config_entry: MockConfigEntry,
mock_airly_client: MagicMock,
) -> None:
"""Test Airly system health."""
aioclient_mock.get("https://airapi.airly.eu/v2/", text="")
await init_integration(hass, aioclient_mock)
await init_integration(hass, mock_config_entry)
assert await async_setup_component(hass, "system_health", {})
await hass.async_block_till_done()
@@ -36,12 +40,15 @@ async def test_airly_system_health(
async def test_airly_system_health_fail(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_config_entry: MockConfigEntry,
mock_airly_client: MagicMock,
) -> None:
"""Test Airly system health."""
aioclient_mock.get("https://airapi.airly.eu/v2/", exc=ClientError)
await init_integration(hass, aioclient_mock)
await init_integration(hass, mock_config_entry)
assert await async_setup_component(hass, "system_health", {})
await hass.async_block_till_done()