Do not link nextbus coordinator to config entry (#128151)
* Do not link nextbus coordinator to config entry * Refactor tests and add specific failure test * Use ConfigEntryNotReady * Cleanup coordinatorpull/128293/head
parent
d15a9a4359
commit
7e56b595a0
|
@ -3,6 +3,7 @@
|
|||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_STOP, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import CONF_AGENCY, CONF_ROUTE, DOMAIN
|
||||
from .coordinator import NextBusDataUpdateCoordinator
|
||||
|
@ -27,7 +28,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
|
||||
coordinator.add_stop_route(entry_stop, entry.data[CONF_ROUTE])
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
await coordinator.async_refresh()
|
||||
if not coordinator.last_update_success:
|
||||
raise ConfigEntryNotReady from coordinator.last_exception
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ class NextBusDataUpdateCoordinator(DataUpdateCoordinator):
|
|||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
config_entry=None, # It is shared between multiple entries
|
||||
name=DOMAIN,
|
||||
update_interval=timedelta(seconds=30),
|
||||
)
|
||||
|
@ -48,13 +49,6 @@ class NextBusDataUpdateCoordinator(DataUpdateCoordinator):
|
|||
"""Check if this coordinator is tracking any routes."""
|
||||
return len(self._route_stops) > 0
|
||||
|
||||
async def async_shutdown(self) -> None:
|
||||
"""If there are no more routes, cancel any scheduled call, and ignore new runs."""
|
||||
if self.has_routes():
|
||||
return
|
||||
|
||||
await super().async_shutdown()
|
||||
|
||||
async def _async_update_data(self) -> dict[str, Any]:
|
||||
"""Fetch data from NextBus."""
|
||||
|
||||
|
|
|
@ -1 +1,34 @@
|
|||
"""The tests for the nexbus component."""
|
||||
|
||||
from homeassistant.components.nextbus.const import CONF_AGENCY, CONF_ROUTE, DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_STOP
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import VALID_AGENCY_TITLE, VALID_ROUTE_TITLE, VALID_STOP_TITLE
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def assert_setup_sensor(
|
||||
hass: HomeAssistant,
|
||||
config: dict[str, dict[str, str]],
|
||||
expected_state=ConfigEntryState.LOADED,
|
||||
route_title: str = VALID_ROUTE_TITLE,
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the sensor and assert it's been created."""
|
||||
unique_id = f"{config[DOMAIN][CONF_AGENCY]}_{config[DOMAIN][CONF_ROUTE]}_{config[DOMAIN][CONF_STOP]}"
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=config[DOMAIN],
|
||||
title=f"{VALID_AGENCY_TITLE} {route_title} {VALID_STOP_TITLE}",
|
||||
unique_id=unique_id,
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state is expected_state
|
||||
|
||||
return config_entry
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
"""Test helpers for NextBus tests."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from .const import BASIC_RESULTS
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
params=[
|
||||
|
@ -128,3 +131,21 @@ def mock_nextbus_lists(
|
|||
instance.route_details.side_effect = route_details_side_effect
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_nextbus() -> Generator[MagicMock]:
|
||||
"""Create a mock py_nextbus module."""
|
||||
with patch("homeassistant.components.nextbus.coordinator.NextBusClient") as client:
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_nextbus_predictions(
|
||||
mock_nextbus: MagicMock,
|
||||
) -> Generator[MagicMock]:
|
||||
"""Create a mock of NextBusClient predictions."""
|
||||
instance = mock_nextbus.return_value
|
||||
instance.predictions_for_stop.return_value = BASIC_RESULTS
|
||||
|
||||
return instance.predictions_for_stop
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
"""Constants for NextBus tests."""
|
||||
|
||||
from homeassistant.components.nextbus.const import CONF_AGENCY, CONF_ROUTE, DOMAIN
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.const import CONF_STOP
|
||||
|
||||
VALID_AGENCY = "sfmta-cis"
|
||||
VALID_ROUTE = "F"
|
||||
VALID_STOP = "5184"
|
||||
VALID_COORDINATOR_KEY = f"{VALID_AGENCY}-{VALID_STOP}"
|
||||
VALID_AGENCY_TITLE = "San Francisco Muni"
|
||||
VALID_ROUTE_TITLE = "F-Market & Wharves"
|
||||
VALID_STOP_TITLE = "Market St & 7th St"
|
||||
SENSOR_ID = "sensor.san_francisco_muni_f_market_wharves_market_st_7th_st"
|
||||
|
||||
ROUTE_2 = "G"
|
||||
ROUTE_TITLE_2 = "G-Market & Wharves"
|
||||
SENSOR_ID_2 = "sensor.san_francisco_muni_g_market_wharves_market_st_7th_st"
|
||||
|
||||
PLATFORM_CONFIG = {
|
||||
SENSOR_DOMAIN: {
|
||||
"platform": DOMAIN,
|
||||
CONF_AGENCY: VALID_AGENCY,
|
||||
CONF_ROUTE: VALID_ROUTE,
|
||||
CONF_STOP: VALID_STOP,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
CONFIG_BASIC = {
|
||||
DOMAIN: {
|
||||
CONF_AGENCY: VALID_AGENCY,
|
||||
CONF_ROUTE: VALID_ROUTE,
|
||||
CONF_STOP: VALID_STOP,
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG_BASIC_2 = {
|
||||
DOMAIN: {
|
||||
CONF_AGENCY: VALID_AGENCY,
|
||||
CONF_ROUTE: ROUTE_2,
|
||||
CONF_STOP: VALID_STOP,
|
||||
}
|
||||
}
|
||||
|
||||
BASIC_RESULTS = [
|
||||
{
|
||||
"route": {
|
||||
"title": VALID_ROUTE_TITLE,
|
||||
"id": VALID_ROUTE,
|
||||
},
|
||||
"stop": {
|
||||
"name": VALID_STOP_TITLE,
|
||||
"id": VALID_STOP,
|
||||
},
|
||||
"values": [
|
||||
{"minutes": 1, "timestamp": 1553807371000},
|
||||
{"minutes": 2, "timestamp": 1553807372000},
|
||||
{"minutes": 3, "timestamp": 1553807373000},
|
||||
{"minutes": 10, "timestamp": 1553807380000},
|
||||
],
|
||||
},
|
||||
{
|
||||
"route": {
|
||||
"title": ROUTE_TITLE_2,
|
||||
"id": ROUTE_2,
|
||||
},
|
||||
"stop": {
|
||||
"name": VALID_STOP_TITLE,
|
||||
"id": VALID_STOP,
|
||||
},
|
||||
"values": [
|
||||
{"minutes": 90, "timestamp": 1553807379000},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
NO_UPCOMING = [
|
||||
{
|
||||
"route": {
|
||||
"title": VALID_ROUTE_TITLE,
|
||||
"id": VALID_ROUTE,
|
||||
},
|
||||
"stop": {
|
||||
"name": VALID_STOP_TITLE,
|
||||
"id": VALID_STOP,
|
||||
},
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"route": {
|
||||
"title": ROUTE_TITLE_2,
|
||||
"id": ROUTE_2,
|
||||
},
|
||||
"stop": {
|
||||
"name": VALID_STOP_TITLE,
|
||||
"id": VALID_STOP,
|
||||
},
|
||||
"values": [],
|
||||
},
|
||||
]
|
|
@ -0,0 +1,27 @@
|
|||
"""The tests for the nexbus sensor component."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
from urllib.error import HTTPError
|
||||
|
||||
from homeassistant.components.nextbus.coordinator import NextBusHTTPError
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import assert_setup_sensor
|
||||
from .const import CONFIG_BASIC
|
||||
|
||||
|
||||
async def test_setup_retry(
|
||||
hass: HomeAssistant,
|
||||
mock_nextbus: MagicMock,
|
||||
mock_nextbus_lists: MagicMock,
|
||||
mock_nextbus_predictions: MagicMock,
|
||||
) -> None:
|
||||
"""Verify that a list of messages are rendered correctly."""
|
||||
|
||||
mock_nextbus_predictions.side_effect = NextBusHTTPError(
|
||||
"failed", HTTPError("url", 500, "error", MagicMock(), None)
|
||||
)
|
||||
await assert_setup_sensor(
|
||||
hass, CONFIG_BASIC, expected_state=ConfigEntryState.SETUP_RETRY
|
||||
)
|
|
@ -1,161 +1,36 @@
|
|||
"""The tests for the nexbus sensor component."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from copy import deepcopy
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import MagicMock
|
||||
from urllib.error import HTTPError
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from py_nextbus.client import NextBusFormatError, NextBusHTTPError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import sensor
|
||||
from homeassistant.components.nextbus.const import CONF_AGENCY, CONF_ROUTE, DOMAIN
|
||||
from homeassistant.components.nextbus.const import DOMAIN
|
||||
from homeassistant.components.nextbus.coordinator import NextBusDataUpdateCoordinator
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_NAME, CONF_STOP
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import UpdateFailed
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from . import assert_setup_sensor
|
||||
from .const import (
|
||||
BASIC_RESULTS,
|
||||
CONFIG_BASIC,
|
||||
CONFIG_BASIC_2,
|
||||
NO_UPCOMING,
|
||||
ROUTE_TITLE_2,
|
||||
SENSOR_ID,
|
||||
SENSOR_ID_2,
|
||||
VALID_AGENCY,
|
||||
VALID_COORDINATOR_KEY,
|
||||
VALID_ROUTE_TITLE,
|
||||
VALID_STOP_TITLE,
|
||||
)
|
||||
|
||||
VALID_AGENCY = "sfmta-cis"
|
||||
VALID_ROUTE = "F"
|
||||
VALID_STOP = "5184"
|
||||
VALID_COORDINATOR_KEY = f"{VALID_AGENCY}-{VALID_STOP}"
|
||||
VALID_AGENCY_TITLE = "San Francisco Muni"
|
||||
VALID_ROUTE_TITLE = "F-Market & Wharves"
|
||||
VALID_STOP_TITLE = "Market St & 7th St"
|
||||
SENSOR_ID = "sensor.san_francisco_muni_f_market_wharves_market_st_7th_st"
|
||||
|
||||
ROUTE_2 = "G"
|
||||
ROUTE_TITLE_2 = "G-Market & Wharves"
|
||||
SENSOR_ID_2 = "sensor.san_francisco_muni_g_market_wharves_market_st_7th_st"
|
||||
|
||||
PLATFORM_CONFIG = {
|
||||
sensor.DOMAIN: {
|
||||
"platform": DOMAIN,
|
||||
CONF_AGENCY: VALID_AGENCY,
|
||||
CONF_ROUTE: VALID_ROUTE,
|
||||
CONF_STOP: VALID_STOP,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
CONFIG_BASIC = {
|
||||
DOMAIN: {
|
||||
CONF_AGENCY: VALID_AGENCY,
|
||||
CONF_ROUTE: VALID_ROUTE,
|
||||
CONF_STOP: VALID_STOP,
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG_BASIC_2 = {
|
||||
DOMAIN: {
|
||||
CONF_AGENCY: VALID_AGENCY,
|
||||
CONF_ROUTE: ROUTE_2,
|
||||
CONF_STOP: VALID_STOP,
|
||||
}
|
||||
}
|
||||
|
||||
BASIC_RESULTS = [
|
||||
{
|
||||
"route": {
|
||||
"title": VALID_ROUTE_TITLE,
|
||||
"id": VALID_ROUTE,
|
||||
},
|
||||
"stop": {
|
||||
"name": VALID_STOP_TITLE,
|
||||
"id": VALID_STOP,
|
||||
},
|
||||
"values": [
|
||||
{"minutes": 1, "timestamp": 1553807371000},
|
||||
{"minutes": 2, "timestamp": 1553807372000},
|
||||
{"minutes": 3, "timestamp": 1553807373000},
|
||||
{"minutes": 10, "timestamp": 1553807380000},
|
||||
],
|
||||
},
|
||||
{
|
||||
"route": {
|
||||
"title": ROUTE_TITLE_2,
|
||||
"id": ROUTE_2,
|
||||
},
|
||||
"stop": {
|
||||
"name": VALID_STOP_TITLE,
|
||||
"id": VALID_STOP,
|
||||
},
|
||||
"values": [
|
||||
{"minutes": 90, "timestamp": 1553807379000},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
NO_UPCOMING = [
|
||||
{
|
||||
"route": {
|
||||
"title": VALID_ROUTE_TITLE,
|
||||
"id": VALID_ROUTE,
|
||||
},
|
||||
"stop": {
|
||||
"name": VALID_STOP_TITLE,
|
||||
"id": VALID_STOP,
|
||||
},
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"route": {
|
||||
"title": ROUTE_TITLE_2,
|
||||
"id": ROUTE_2,
|
||||
},
|
||||
"stop": {
|
||||
"name": VALID_STOP_TITLE,
|
||||
"id": VALID_STOP,
|
||||
},
|
||||
"values": [],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_nextbus() -> Generator[MagicMock]:
|
||||
"""Create a mock py_nextbus module."""
|
||||
with patch("homeassistant.components.nextbus.coordinator.NextBusClient") as client:
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_nextbus_predictions(
|
||||
mock_nextbus: MagicMock,
|
||||
) -> Generator[MagicMock]:
|
||||
"""Create a mock of NextBusClient predictions."""
|
||||
instance = mock_nextbus.return_value
|
||||
instance.predictions_for_stop.return_value = BASIC_RESULTS
|
||||
|
||||
return instance.predictions_for_stop
|
||||
|
||||
|
||||
async def assert_setup_sensor(
|
||||
hass: HomeAssistant,
|
||||
config: dict[str, dict[str, str]],
|
||||
expected_state=ConfigEntryState.LOADED,
|
||||
route_title: str = VALID_ROUTE_TITLE,
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the sensor and assert it's been created."""
|
||||
unique_id = f"{config[DOMAIN][CONF_AGENCY]}_{config[DOMAIN][CONF_ROUTE]}_{config[DOMAIN][CONF_STOP]}"
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=config[DOMAIN],
|
||||
title=f"{VALID_AGENCY_TITLE} {route_title} {VALID_STOP_TITLE}",
|
||||
unique_id=unique_id,
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state is expected_state
|
||||
|
||||
return config_entry
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
|
||||
async def test_predictions(
|
||||
|
|
Loading…
Reference in New Issue