diff --git a/tests/components/airnow/conftest.py b/tests/components/airnow/conftest.py new file mode 100644 index 00000000000..47f20ccd883 --- /dev/null +++ b/tests/components/airnow/conftest.py @@ -0,0 +1,57 @@ +"""Define fixtures for AirNow tests.""" +import json +from unittest.mock import AsyncMock, patch + +import pytest + +from homeassistant.components.airnow import DOMAIN +from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry, load_fixture + + +@pytest.fixture(name="config_entry") +def config_entry_fixture(hass, config): + """Define a config entry fixture.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=f"{config[CONF_LATITUDE]}-{config[CONF_LONGITUDE]}", + data=config, + ) + entry.add_to_hass(hass) + return entry + + +@pytest.fixture(name="config") +def config_fixture(hass): + """Define a config entry data fixture.""" + return { + CONF_API_KEY: "abc123", + CONF_LATITUDE: 34.053718, + CONF_LONGITUDE: -118.244842, + CONF_RADIUS: 75, + } + + +@pytest.fixture(name="data", scope="session") +def data_fixture(): + """Define a fixture for response data.""" + return json.loads(load_fixture("response.json", "airnow")) + + +@pytest.fixture(name="mock_api_get") +def mock_api_get_fixture(data): + """Define a fixture for a mock "get" coroutine function.""" + return AsyncMock(return_value=data) + + +@pytest.fixture(name="setup_airnow") +async def setup_airnow_fixture(hass, config, mock_api_get): + """Define a fixture to set up AirNow.""" + with patch("pyairnow.WebServiceAPI._get", mock_api_get), patch( + "homeassistant.components.airnow.config_flow.WebServiceAPI._get", mock_api_get + ), patch("homeassistant.components.airnow.PLATFORMS", []): + assert await async_setup_component(hass, DOMAIN, config) + await hass.async_block_till_done() + yield diff --git a/tests/components/airnow/fixtures/__init__.py b/tests/components/airnow/fixtures/__init__.py new file mode 100644 index 00000000000..328b7a792e2 --- /dev/null +++ b/tests/components/airnow/fixtures/__init__.py @@ -0,0 +1 @@ +"""Define AirNow response fixture data.""" diff --git a/tests/components/airnow/fixtures/response.json b/tests/components/airnow/fixtures/response.json new file mode 100644 index 00000000000..91029f5531f --- /dev/null +++ b/tests/components/airnow/fixtures/response.json @@ -0,0 +1,47 @@ +[ + { + "DateObserved": "2020-12-20", + "HourObserved": 15, + "LocalTimeZone": "PST", + "ReportingArea": "Central LA CO", + "StateCode": "CA", + "Latitude": 34.0663, + "Longitude": -118.2266, + "ParameterName": "O3", + "AQI": 44, + "Category": { + "Number": 1, + "Name": "Good" + } + }, + { + "DateObserved": "2020-12-20", + "HourObserved": 15, + "LocalTimeZone": "PST", + "ReportingArea": "Central LA CO", + "StateCode": "CA", + "Latitude": 34.0663, + "Longitude": -118.2266, + "ParameterName": "PM2.5", + "AQI": 37, + "Category": { + "Number": 1, + "Name": "Good" + } + }, + { + "DateObserved": "2020-12-20", + "HourObserved": 15, + "LocalTimeZone": "PST", + "ReportingArea": "Central LA CO", + "StateCode": "CA", + "Latitude": 34.0663, + "Longitude": -118.2266, + "ParameterName": "PM10", + "AQI": 11, + "Category": { + "Number": 1, + "Name": "Good" + } + } +] diff --git a/tests/components/airnow/test_config_flow.py b/tests/components/airnow/test_config_flow.py index 02236e826e5..dddd51c8450 100644 --- a/tests/components/airnow/test_config_flow.py +++ b/tests/components/airnow/test_config_flow.py @@ -1,183 +1,75 @@ """Test the AirNow config flow.""" -from unittest.mock import patch +from unittest.mock import AsyncMock from pyairnow.errors import AirNowError, InvalidKeyError +import pytest from homeassistant import config_entries, data_entry_flow from homeassistant.components.airnow.const import DOMAIN -from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS - -from tests.common import MockConfigEntry - -CONFIG = { - CONF_API_KEY: "abc123", - CONF_LATITUDE: 34.053718, - CONF_LONGITUDE: -118.244842, - CONF_RADIUS: 75, -} - -# Mock AirNow Response -MOCK_RESPONSE = [ - { - "DateObserved": "2020-12-20", - "HourObserved": 15, - "LocalTimeZone": "PST", - "ReportingArea": "Central LA CO", - "StateCode": "CA", - "Latitude": 34.0663, - "Longitude": -118.2266, - "ParameterName": "O3", - "AQI": 44, - "Category": { - "Number": 1, - "Name": "Good", - }, - }, - { - "DateObserved": "2020-12-20", - "HourObserved": 15, - "LocalTimeZone": "PST", - "ReportingArea": "Central LA CO", - "StateCode": "CA", - "Latitude": 34.0663, - "Longitude": -118.2266, - "ParameterName": "PM2.5", - "AQI": 37, - "Category": { - "Number": 1, - "Name": "Good", - }, - }, - { - "DateObserved": "2020-12-20", - "HourObserved": 15, - "LocalTimeZone": "PST", - "ReportingArea": "Central LA CO", - "StateCode": "CA", - "Latitude": 34.0663, - "Longitude": -118.2266, - "ParameterName": "PM10", - "AQI": 11, - "Category": { - "Number": 1, - "Name": "Good", - }, - }, -] -async def test_form(hass): +async def test_form(hass, config, setup_airnow): """Test we get the form.""" - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == data_entry_flow.FlowResultType.FORM assert result["errors"] == {} - with patch("pyairnow.WebServiceAPI._get", return_value=MOCK_RESPONSE), patch( - "homeassistant.components.airnow.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - CONFIG, - ) - - await hass.async_block_till_done() - + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], config) assert result2["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY - assert result2["data"] == CONFIG - assert len(mock_setup_entry.mock_calls) == 1 + assert result2["data"] == config -async def test_form_invalid_auth(hass): +@pytest.mark.parametrize("mock_api_get", [AsyncMock(side_effect=InvalidKeyError)]) +async def test_form_invalid_auth(hass, config, setup_airnow): """Test we handle invalid auth.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - - with patch( - "pyairnow.WebServiceAPI._get", - side_effect=InvalidKeyError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - CONFIG, - ) - + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], config) assert result2["type"] == "form" assert result2["errors"] == {"base": "invalid_auth"} -async def test_form_invalid_location(hass): +@pytest.mark.parametrize("data", [{}]) +async def test_form_invalid_location(hass, config, setup_airnow): """Test we handle invalid location.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - - with patch("pyairnow.WebServiceAPI._get", return_value={}): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - CONFIG, - ) - + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], config) assert result2["type"] == "form" assert result2["errors"] == {"base": "invalid_location"} -async def test_form_cannot_connect(hass): +@pytest.mark.parametrize("mock_api_get", [AsyncMock(side_effect=AirNowError)]) +async def test_form_cannot_connect(hass, config, setup_airnow): """Test we handle cannot connect error.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - - with patch( - "pyairnow.WebServiceAPI._get", - side_effect=AirNowError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - CONFIG, - ) - + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], config) assert result2["type"] == "form" assert result2["errors"] == {"base": "cannot_connect"} -async def test_form_unexpected(hass): +@pytest.mark.parametrize("mock_api_get", [AsyncMock(side_effect=RuntimeError)]) +async def test_form_unexpected(hass, config, setup_airnow): """Test we handle an unexpected error.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - - with patch( - "homeassistant.components.airnow.config_flow.validate_input", - side_effect=RuntimeError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - CONFIG, - ) - + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], config) assert result2["type"] == "form" assert result2["errors"] == {"base": "unknown"} -async def test_entry_already_exists(hass): +async def test_entry_already_exists(hass, config, config_entry): """Test that the form aborts if the Lat/Lng is already configured.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - - mock_id = f"{CONFIG[CONF_LATITUDE]}-{CONFIG[CONF_LONGITUDE]}" - mock_entry = MockConfigEntry(domain=DOMAIN, unique_id=mock_id) - mock_entry.add_to_hass(hass) - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - CONFIG, - ) - + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], config) assert result2["type"] == "abort" assert result2["reason"] == "already_configured"