AccuWeather tests refactoring (#116923)
* Add mock_accuweather_client * Improve tests * Fix exceptions * Remove unneeded update_listener() * Fix arguments for fixtures --------- Co-authored-by: Maciej Bieniek <478555+bieniu@users.noreply.github.com>
This commit is contained in:
parent
72d6b4d1c9
commit
09be56964d
9 changed files with 305 additions and 387 deletions
|
@ -64,8 +64,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
await coordinator_observation.async_config_entry_first_refresh()
|
||||
await coordinator_daily_forecast.async_config_entry_first_refresh()
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = AccuWeatherData(
|
||||
coordinator_observation=coordinator_observation,
|
||||
coordinator_daily_forecast=coordinator_daily_forecast,
|
||||
|
@ -92,8 +90,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update listener."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
"""Tests for AccuWeather."""
|
||||
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
from homeassistant.components.accuweather.const import DOMAIN
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
load_json_array_fixture,
|
||||
load_json_object_fixture,
|
||||
)
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def init_integration(hass, unsupported_icon=False) -> MockConfigEntry:
|
||||
async def init_integration(hass) -> MockConfigEntry:
|
||||
"""Set up the AccuWeather integration in Home Assistant."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
@ -25,29 +19,8 @@ async def init_integration(hass, unsupported_icon=False) -> MockConfigEntry:
|
|||
},
|
||||
)
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||
|
||||
if unsupported_icon:
|
||||
current["WeatherIcon"] = 999
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast",
|
||||
return_value=forecast,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return entry
|
||||
|
|
36
tests/components/accuweather/conftest.py
Normal file
36
tests/components/accuweather/conftest.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
"""Common fixtures for the AccuWeather tests."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.accuweather.const import DOMAIN
|
||||
|
||||
from tests.common import load_json_array_fixture, load_json_object_fixture
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_accuweather_client() -> Generator[AsyncMock, None, None]:
|
||||
"""Mock a AccuWeather client."""
|
||||
current = load_json_object_fixture("current_conditions_data.json", DOMAIN)
|
||||
forecast = load_json_array_fixture("forecast_data.json", DOMAIN)
|
||||
location = load_json_object_fixture("location_data.json", DOMAIN)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather", autospec=True
|
||||
) as mock_client,
|
||||
patch(
|
||||
"homeassistant.components.accuweather.config_flow.AccuWeather",
|
||||
new=mock_client,
|
||||
),
|
||||
):
|
||||
client = mock_client.return_value
|
||||
client.async_get_location.return_value = location
|
||||
client.async_get_current_conditions.return_value = current
|
||||
client.async_get_daily_forecast.return_value = forecast
|
||||
client.location_key = "0123456"
|
||||
client.requests_remaining = 10
|
||||
|
||||
yield client
|
|
@ -1,6 +1,6 @@
|
|||
"""Define tests for the AccuWeather config flow."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from accuweather import ApiError, InvalidApiKeyError, RequestsExceededError
|
||||
|
||||
|
@ -10,7 +10,7 @@ from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CON
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from tests.common import MockConfigEntry, load_json_object_fixture
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
VALID_CONFIG = {
|
||||
CONF_NAME: "abcd",
|
||||
|
@ -48,95 +48,90 @@ async def test_api_key_too_short(hass: HomeAssistant) -> None:
|
|||
assert result["errors"] == {CONF_API_KEY: "invalid_api_key"}
|
||||
|
||||
|
||||
async def test_invalid_api_key(hass: HomeAssistant) -> None:
|
||||
async def test_invalid_api_key(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test that errors are shown when API key is invalid."""
|
||||
with patch(
|
||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||
side_effect=InvalidApiKeyError("Invalid API key"),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
mock_accuweather_client.async_get_location.side_effect = InvalidApiKeyError(
|
||||
"Invalid API key"
|
||||
)
|
||||
|
||||
assert result["errors"] == {CONF_API_KEY: "invalid_api_key"}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
|
||||
assert result["errors"] == {CONF_API_KEY: "invalid_api_key"}
|
||||
|
||||
|
||||
async def test_api_error(hass: HomeAssistant) -> None:
|
||||
async def test_api_error(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test API error."""
|
||||
with patch(
|
||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||
side_effect=ApiError("Invalid response from AccuWeather API"),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
mock_accuweather_client.async_get_location.side_effect = ApiError(
|
||||
"Invalid response from AccuWeather API"
|
||||
)
|
||||
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_requests_exceeded_error(hass: HomeAssistant) -> None:
|
||||
async def test_requests_exceeded_error(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test requests exceeded error."""
|
||||
with patch(
|
||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||
side_effect=RequestsExceededError(
|
||||
"The allowed number of requests has been exceeded"
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
mock_accuweather_client.async_get_location.side_effect = RequestsExceededError(
|
||||
"The allowed number of requests has been exceeded"
|
||||
)
|
||||
|
||||
assert result["errors"] == {CONF_API_KEY: "requests_exceeded"}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
|
||||
assert result["errors"] == {CONF_API_KEY: "requests_exceeded"}
|
||||
|
||||
|
||||
async def test_integration_already_exists(hass: HomeAssistant) -> None:
|
||||
async def test_integration_already_exists(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test we only allow a single config flow."""
|
||||
with patch(
|
||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||
return_value=load_json_object_fixture("accuweather/location_data.json"),
|
||||
):
|
||||
MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="123456",
|
||||
data=VALID_CONFIG,
|
||||
).add_to_hass(hass)
|
||||
MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="123456",
|
||||
data=VALID_CONFIG,
|
||||
).add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "single_instance_allowed"
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "single_instance_allowed"
|
||||
|
||||
|
||||
async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
async def test_create_entry(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test that the user step works."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||
return_value=load_json_object_fixture("accuweather/location_data.json"),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.async_setup_entry", return_value=True
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "abcd"
|
||||
assert result["data"][CONF_NAME] == "abcd"
|
||||
assert result["data"][CONF_LATITUDE] == 55.55
|
||||
assert result["data"][CONF_LONGITUDE] == 122.12
|
||||
assert result["data"][CONF_API_KEY] == "32-character-string-1234567890qw"
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "abcd"
|
||||
assert result["data"][CONF_NAME] == "abcd"
|
||||
assert result["data"][CONF_LATITUDE] == 55.55
|
||||
assert result["data"][CONF_LONGITUDE] == 122.12
|
||||
assert result["data"][CONF_API_KEY] == "32-character-string-1234567890qw"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
"""Test AccuWeather diagnostics."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
@ -13,6 +15,7 @@ from tests.typing import ClientSessionGenerator
|
|||
async def test_entry_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test config entry diagnostics."""
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
"""Test init of AccuWeather integration."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from accuweather import ApiError
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from homeassistant.components.accuweather.const import (
|
||||
DOMAIN,
|
||||
|
@ -14,19 +15,15 @@ from homeassistant.config_entries import ConfigEntryState
|
|||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
async_fire_time_changed,
|
||||
load_json_array_fixture,
|
||||
load_json_object_fixture,
|
||||
)
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def test_async_setup_entry(hass: HomeAssistant) -> None:
|
||||
async def test_async_setup_entry(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test a successful setup entry."""
|
||||
await init_integration(hass)
|
||||
|
||||
|
@ -36,7 +33,9 @@ async def test_async_setup_entry(hass: HomeAssistant) -> None:
|
|||
assert state.state == "sunny"
|
||||
|
||||
|
||||
async def test_config_not_ready(hass: HomeAssistant) -> None:
|
||||
async def test_config_not_ready(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test for setup failure if connection to AccuWeather is missing."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
@ -50,16 +49,18 @@ async def test_config_not_ready(hass: HomeAssistant) -> None:
|
|||
},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||
side_effect=ApiError("API Error"),
|
||||
):
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
mock_accuweather_client.async_get_current_conditions.side_effect = ApiError(
|
||||
"API Error"
|
||||
)
|
||||
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
async def test_unload_entry(hass: HomeAssistant) -> None:
|
||||
async def test_unload_entry(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test successful unload of entry."""
|
||||
entry = await init_integration(hass)
|
||||
|
||||
|
@ -73,41 +74,36 @@ async def test_unload_entry(hass: HomeAssistant) -> None:
|
|||
assert not hass.data.get(DOMAIN)
|
||||
|
||||
|
||||
async def test_update_interval(hass: HomeAssistant) -> None:
|
||||
async def test_update_interval(
|
||||
hass: HomeAssistant,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test correct update interval."""
|
||||
entry = await init_integration(hass)
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||
assert mock_accuweather_client.async_get_current_conditions.call_count == 1
|
||||
assert mock_accuweather_client.async_get_daily_forecast.call_count == 1
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
) as mock_current,
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast",
|
||||
return_value=forecast,
|
||||
) as mock_forecast,
|
||||
):
|
||||
assert mock_current.call_count == 0
|
||||
assert mock_forecast.call_count == 0
|
||||
freezer.tick(UPDATE_INTERVAL_OBSERVATION)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL_OBSERVATION)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_accuweather_client.async_get_current_conditions.call_count == 2
|
||||
|
||||
assert mock_current.call_count == 1
|
||||
freezer.tick(UPDATE_INTERVAL_DAILY_FORECAST)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL_DAILY_FORECAST)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_forecast.call_count == 1
|
||||
assert mock_accuweather_client.async_get_daily_forecast.call_count == 2
|
||||
|
||||
|
||||
async def test_remove_ozone_sensors(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test remove ozone sensors from registry."""
|
||||
entity_registry.async_get_or_create(
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
"""Test sensor of AccuWeather integration."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import PropertyMock, patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from accuweather import ApiError, InvalidApiKeyError, RequestsExceededError
|
||||
from aiohttp.client_exceptions import ClientConnectorError
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.accuweather.const import UPDATE_INTERVAL_DAILY_FORECAST
|
||||
from homeassistant.components.accuweather.const import (
|
||||
UPDATE_INTERVAL_DAILY_FORECAST,
|
||||
UPDATE_INTERVAL_OBSERVATION,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
|
@ -21,23 +24,18 @@ from homeassistant.const import (
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.dt import utcnow
|
||||
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
|
||||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import (
|
||||
async_fire_time_changed,
|
||||
load_json_array_fixture,
|
||||
load_json_object_fixture,
|
||||
snapshot_platform,
|
||||
)
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
|
||||
|
||||
async def test_sensor(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: None,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test states of the sensor."""
|
||||
|
@ -46,64 +44,59 @@ async def test_sensor(
|
|||
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||
|
||||
|
||||
async def test_availability(hass: HomeAssistant) -> None:
|
||||
async def test_availability(
|
||||
hass: HomeAssistant,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Ensure that we mark the entities unavailable correctly when service is offline."""
|
||||
entity_id = "sensor.home_cloud_ceiling"
|
||||
await init_integration(hass)
|
||||
|
||||
state = hass.states.get("sensor.home_cloud_ceiling")
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "3200.0"
|
||||
|
||||
future = utcnow() + timedelta(minutes=60)
|
||||
with patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
side_effect=ConnectionError(),
|
||||
):
|
||||
async_fire_time_changed(hass, future)
|
||||
await hass.async_block_till_done()
|
||||
mock_accuweather_client.async_get_current_conditions.side_effect = ConnectionError
|
||||
|
||||
state = hass.states.get("sensor.home_cloud_ceiling")
|
||||
assert state
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
freezer.tick(UPDATE_INTERVAL_OBSERVATION)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
future = utcnow() + timedelta(minutes=120)
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=load_json_object_fixture(
|
||||
"accuweather/current_conditions_data.json"
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
async_fire_time_changed(hass, future)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
state = hass.states.get("sensor.home_cloud_ceiling")
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "3200.0"
|
||||
mock_accuweather_client.async_get_current_conditions.side_effect = None
|
||||
|
||||
freezer.tick(UPDATE_INTERVAL_OBSERVATION)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "3200.0"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"exception",
|
||||
[
|
||||
ApiError,
|
||||
ApiError("API Error"),
|
||||
ConnectionError,
|
||||
ClientConnectorError,
|
||||
InvalidApiKeyError,
|
||||
RequestsExceededError,
|
||||
InvalidApiKeyError("Invalid API key"),
|
||||
RequestsExceededError("Requests exceeded"),
|
||||
],
|
||||
)
|
||||
async def test_availability_forecast(hass: HomeAssistant, exception: Exception) -> None:
|
||||
async def test_availability_forecast(
|
||||
hass: HomeAssistant,
|
||||
exception: Exception,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Ensure that we mark the entities unavailable correctly when service is offline."""
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||
entity_id = "sensor.home_hours_of_sun_day_2"
|
||||
|
||||
await init_integration(hass)
|
||||
|
@ -113,45 +106,21 @@ async def test_availability_forecast(hass: HomeAssistant, exception: Exception)
|
|||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "5.7"
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast",
|
||||
side_effect=exception,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL_DAILY_FORECAST)
|
||||
await hass.async_block_till_done()
|
||||
mock_accuweather_client.async_get_daily_forecast.side_effect = exception
|
||||
|
||||
freezer.tick(UPDATE_INTERVAL_DAILY_FORECAST)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast",
|
||||
return_value=forecast,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
async_fire_time_changed(hass, utcnow() + UPDATE_INTERVAL_DAILY_FORECAST * 2)
|
||||
await hass.async_block_till_done()
|
||||
mock_accuweather_client.async_get_daily_forecast.side_effect = None
|
||||
|
||||
freezer.tick(UPDATE_INTERVAL_DAILY_FORECAST)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -159,35 +128,29 @@ async def test_availability_forecast(hass: HomeAssistant, exception: Exception)
|
|||
assert state.state == "5.7"
|
||||
|
||||
|
||||
async def test_manual_update_entity(hass: HomeAssistant) -> None:
|
||||
async def test_manual_update_entity(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test manual update entity via service homeassistant/update_entity."""
|
||||
await init_integration(hass)
|
||||
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
assert mock_accuweather_client.async_get_current_conditions.call_count == 1
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
) as mock_current,
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant",
|
||||
"update_entity",
|
||||
{ATTR_ENTITY_ID: ["sensor.home_cloud_ceiling"]},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_current.call_count == 1
|
||||
await hass.services.async_call(
|
||||
"homeassistant",
|
||||
"update_entity",
|
||||
{ATTR_ENTITY_ID: ["sensor.home_cloud_ceiling"]},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert mock_accuweather_client.async_get_current_conditions.call_count == 2
|
||||
|
||||
|
||||
async def test_sensor_imperial_units(hass: HomeAssistant) -> None:
|
||||
async def test_sensor_imperial_units(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test states of the sensor without forecast."""
|
||||
hass.config.units = US_CUSTOMARY_SYSTEM
|
||||
await init_integration(hass)
|
||||
|
@ -210,37 +173,30 @@ async def test_sensor_imperial_units(hass: HomeAssistant) -> None:
|
|||
)
|
||||
|
||||
|
||||
async def test_state_update(hass: HomeAssistant) -> None:
|
||||
async def test_state_update(
|
||||
hass: HomeAssistant,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Ensure the sensor state changes after updating the data."""
|
||||
entity_id = "sensor.home_cloud_ceiling"
|
||||
|
||||
await init_integration(hass)
|
||||
|
||||
state = hass.states.get("sensor.home_cloud_ceiling")
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "3200.0"
|
||||
|
||||
future = utcnow() + timedelta(minutes=60)
|
||||
mock_accuweather_client.async_get_current_conditions.return_value["Ceiling"][
|
||||
"Metric"
|
||||
]["Value"] = 3300
|
||||
|
||||
current_condition = load_json_object_fixture(
|
||||
"accuweather/current_conditions_data.json"
|
||||
)
|
||||
current_condition["Ceiling"]["Metric"]["Value"] = 3300
|
||||
freezer.tick(UPDATE_INTERVAL_OBSERVATION)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current_condition,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
async_fire_time_changed(hass, future)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("sensor.home_cloud_ceiling")
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "3300"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "3300"
|
||||
|
|
|
@ -1,34 +1,32 @@
|
|||
"""Test AccuWeather system health."""
|
||||
|
||||
import asyncio
|
||||
from unittest.mock import Mock
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from aiohttp import ClientError
|
||||
|
||||
from homeassistant.components.accuweather import AccuWeatherData
|
||||
from homeassistant.components.accuweather.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import get_system_health_info
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
async def test_accuweather_system_health(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test AccuWeather system health."""
|
||||
aioclient_mock.get("https://dataservice.accuweather.com/", text="")
|
||||
hass.config.components.add(DOMAIN)
|
||||
|
||||
await init_integration(hass)
|
||||
assert await async_setup_component(hass, "system_health", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN]["0123xyz"] = AccuWeatherData(
|
||||
coordinator_observation=Mock(accuweather=Mock(requests_remaining="42")),
|
||||
coordinator_daily_forecast=Mock(),
|
||||
)
|
||||
|
||||
info = await get_system_health_info(hass, DOMAIN)
|
||||
|
||||
for key, val in info.items():
|
||||
|
@ -37,25 +35,22 @@ async def test_accuweather_system_health(
|
|||
|
||||
assert info == {
|
||||
"can_reach_server": "ok",
|
||||
"remaining_requests": "42",
|
||||
"remaining_requests": 10,
|
||||
}
|
||||
|
||||
|
||||
async def test_accuweather_system_health_fail(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test AccuWeather system health."""
|
||||
aioclient_mock.get("https://dataservice.accuweather.com/", exc=ClientError)
|
||||
hass.config.components.add(DOMAIN)
|
||||
|
||||
await init_integration(hass)
|
||||
assert await async_setup_component(hass, "system_health", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN]["0123xyz"] = AccuWeatherData(
|
||||
coordinator_observation=Mock(accuweather=Mock(requests_remaining="0")),
|
||||
coordinator_daily_forecast=Mock(),
|
||||
)
|
||||
|
||||
info = await get_system_health_info(hass, DOMAIN)
|
||||
|
||||
for key, val in info.items():
|
||||
|
@ -64,5 +59,5 @@ async def test_accuweather_system_health_fail(
|
|||
|
||||
assert info == {
|
||||
"can_reach_server": {"type": "failed", "error": "unreachable"},
|
||||
"remaining_requests": "0",
|
||||
"remaining_requests": 10,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""Test weather of AccuWeather integration."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import PropertyMock, patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
|
@ -18,21 +18,18 @@ from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import (
|
||||
async_fire_time_changed,
|
||||
load_json_array_fixture,
|
||||
load_json_object_fixture,
|
||||
snapshot_platform,
|
||||
)
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
|
||||
async def test_weather(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test states of the weather without forecast."""
|
||||
with patch("homeassistant.components.accuweather.PLATFORMS", [Platform.WEATHER]):
|
||||
|
@ -40,81 +37,71 @@ async def test_weather(
|
|||
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
||||
|
||||
|
||||
async def test_availability(hass: HomeAssistant) -> None:
|
||||
async def test_availability(
|
||||
hass: HomeAssistant,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Ensure that we mark the entities unavailable correctly when service is offline."""
|
||||
entity_id = "weather.home"
|
||||
await init_integration(hass)
|
||||
|
||||
state = hass.states.get("weather.home")
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "sunny"
|
||||
|
||||
future = utcnow() + timedelta(minutes=60)
|
||||
with patch(
|
||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||
side_effect=ConnectionError(),
|
||||
):
|
||||
async_fire_time_changed(hass, future)
|
||||
await hass.async_block_till_done()
|
||||
mock_accuweather_client.async_get_current_conditions.side_effect = ConnectionError
|
||||
|
||||
state = hass.states.get("weather.home")
|
||||
assert state
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
freezer.tick(UPDATE_INTERVAL_DAILY_FORECAST)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
future = utcnow() + timedelta(minutes=120)
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=load_json_object_fixture(
|
||||
"accuweather/current_conditions_data.json"
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
async_fire_time_changed(hass, future)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
state = hass.states.get("weather.home")
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "sunny"
|
||||
mock_accuweather_client.async_get_current_conditions.side_effect = None
|
||||
|
||||
freezer.tick(UPDATE_INTERVAL_DAILY_FORECAST)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
assert state.state == "sunny"
|
||||
|
||||
|
||||
async def test_manual_update_entity(hass: HomeAssistant) -> None:
|
||||
async def test_manual_update_entity(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test manual update entity via service homeassistant/update_entity."""
|
||||
await init_integration(hass)
|
||||
|
||||
await async_setup_component(hass, "homeassistant", {})
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
assert mock_accuweather_client.async_get_current_conditions.call_count == 1
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
) as mock_current,
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant",
|
||||
"update_entity",
|
||||
{ATTR_ENTITY_ID: ["weather.home"]},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_current.call_count == 1
|
||||
await hass.services.async_call(
|
||||
"homeassistant",
|
||||
"update_entity",
|
||||
{ATTR_ENTITY_ID: ["weather.home"]},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert mock_accuweather_client.async_get_current_conditions.call_count == 2
|
||||
|
||||
|
||||
async def test_unsupported_condition_icon_data(hass: HomeAssistant) -> None:
|
||||
async def test_unsupported_condition_icon_data(
|
||||
hass: HomeAssistant, mock_accuweather_client: AsyncMock
|
||||
) -> None:
|
||||
"""Test with unsupported condition icon data."""
|
||||
await init_integration(hass, unsupported_icon=True)
|
||||
mock_accuweather_client.async_get_current_conditions.return_value["WeatherIcon"] = (
|
||||
999
|
||||
)
|
||||
|
||||
await init_integration(hass)
|
||||
|
||||
state = hass.states.get("weather.home")
|
||||
assert state.attributes.get(ATTR_FORECAST_CONDITION) is None
|
||||
|
@ -130,6 +117,7 @@ async def test_unsupported_condition_icon_data(hass: HomeAssistant) -> None:
|
|||
async def test_forecast_service(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
service: str,
|
||||
) -> None:
|
||||
"""Test multiple forecast."""
|
||||
|
@ -153,6 +141,7 @@ async def test_forecast_subscription(
|
|||
hass_ws_client: WebSocketGenerator,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_accuweather_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test multiple forecast."""
|
||||
client = await hass_ws_client(hass)
|
||||
|
@ -179,27 +168,9 @@ async def test_forecast_subscription(
|
|||
assert forecast1 != []
|
||||
assert forecast1 == snapshot
|
||||
|
||||
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||
return_value=current,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.async_get_daily_forecast",
|
||||
return_value=forecast,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||
new_callable=PropertyMock,
|
||||
return_value=10,
|
||||
),
|
||||
):
|
||||
freezer.tick(UPDATE_INTERVAL_DAILY_FORECAST + timedelta(seconds=1))
|
||||
await hass.async_block_till_done()
|
||||
msg = await client.receive_json()
|
||||
freezer.tick(UPDATE_INTERVAL_DAILY_FORECAST + timedelta(seconds=1))
|
||||
await hass.async_block_till_done()
|
||||
msg = await client.receive_json()
|
||||
|
||||
assert msg["id"] == subscription_id
|
||||
assert msg["type"] == "event"
|
||||
|
|
Loading…
Add table
Reference in a new issue