mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 09:23:17 -04:00
Use mock client in Airly tests (#182485)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user