diff --git a/tests/components/airly/__init__.py b/tests/components/airly/__init__.py index 05d742603da6..95531b31c47b 100644 --- a/tests/components/airly/__init__.py +++ b/tests/components/airly/__init__.py @@ -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 diff --git a/tests/components/airly/conftest.py b/tests/components/airly/conftest.py new file mode 100644 index 000000000000..d9fc8c93e61b --- /dev/null +++ b/tests/components/airly/conftest.py @@ -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 diff --git a/tests/components/airly/test_config_flow.py b/tests/components/airly/test_config_flow.py index d9ba6418401a..f5b190b310ab 100644 --- a/tests/components/airly/test_config_flow.py +++ b/tests/components/airly/test_config_flow.py @@ -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 diff --git a/tests/components/airly/test_diagnostics.py b/tests/components/airly/test_diagnostics.py index 13656f90a68f..b0527bf3176d 100644 --- a/tests/components/airly/test_diagnostics.py +++ b/tests/components/airly/test_diagnostics.py @@ -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")) diff --git a/tests/components/airly/test_init.py b/tests/components/airly/test_init.py index 8058f751c787..2c675d807398 100644 --- a/tests/components/airly/test_init.py +++ b/tests/components/airly/test_init.py @@ -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 diff --git a/tests/components/airly/test_sensor.py b/tests/components/airly/test_sensor.py index 1e723e40648c..c211625dd926 100644 --- a/tests/components/airly/test_sensor.py +++ b/tests/components/airly/test_sensor.py @@ -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 diff --git a/tests/components/airly/test_system_health.py b/tests/components/airly/test_system_health.py index 429d20f7d333..df2015a198f7 100644 --- a/tests/components/airly/test_system_health.py +++ b/tests/components/airly/test_system_health.py @@ -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()