* Refactor NextBus integration to use new API This removes the `messages`, `directions`, and `attribution` attributes from the sensor. Those may be added back in the future with additional refactoring. Some existing sensors may be broken today because of deprecated Agency names. This patch will not migrate them as the migration path is ambiguous. Setting up again should work though. * Move result indexing outside of try/except
207 lines
6 KiB
Python
207 lines
6 KiB
Python
"""The tests for the nexbus sensor component."""
|
|
|
|
from collections.abc import Generator
|
|
from copy import deepcopy
|
|
from unittest.mock import MagicMock, patch
|
|
from urllib.error import HTTPError
|
|
|
|
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.coordinator import NextBusDataUpdateCoordinator
|
|
from homeassistant.config_entries import ConfigEntryState
|
|
from homeassistant.const import CONF_NAME, CONF_STOP
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.update_coordinator import UpdateFailed
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
VALID_AGENCY = "sfmta-cis"
|
|
VALID_ROUTE = "F"
|
|
VALID_STOP = "5184"
|
|
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"
|
|
|
|
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,
|
|
}
|
|
}
|
|
|
|
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},
|
|
],
|
|
}
|
|
]
|
|
|
|
NO_UPCOMING = [
|
|
{
|
|
"route": {
|
|
"title": VALID_ROUTE_TITLE,
|
|
"id": VALID_ROUTE,
|
|
},
|
|
"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,
|
|
) -> MockConfigEntry:
|
|
"""Set up the sensor and assert it's been created."""
|
|
config_entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data=config[DOMAIN],
|
|
title=f"{VALID_AGENCY_TITLE} {VALID_ROUTE_TITLE} {VALID_STOP_TITLE}",
|
|
unique_id=f"{VALID_AGENCY}_{VALID_ROUTE}_{VALID_STOP}",
|
|
)
|
|
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
|
|
|
|
|
|
async def test_predictions(
|
|
hass: HomeAssistant,
|
|
mock_nextbus: MagicMock,
|
|
mock_nextbus_lists: MagicMock,
|
|
mock_nextbus_predictions: MagicMock,
|
|
) -> None:
|
|
"""Verify that a list of messages are rendered correctly."""
|
|
|
|
await assert_setup_sensor(hass, CONFIG_BASIC)
|
|
|
|
state = hass.states.get(SENSOR_ID)
|
|
assert state is not None
|
|
assert state.state == "2019-03-28T21:09:31+00:00"
|
|
assert state.attributes["agency"] == VALID_AGENCY
|
|
assert state.attributes["route"] == VALID_ROUTE_TITLE
|
|
assert state.attributes["stop"] == VALID_STOP_TITLE
|
|
assert state.attributes["upcoming"] == "1, 2, 3, 10"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"client_exception",
|
|
[
|
|
NextBusHTTPError("failed", HTTPError("url", 500, "error", MagicMock(), None)),
|
|
NextBusFormatError("failed"),
|
|
],
|
|
)
|
|
async def test_prediction_exceptions(
|
|
hass: HomeAssistant,
|
|
mock_nextbus: MagicMock,
|
|
mock_nextbus_lists: MagicMock,
|
|
mock_nextbus_predictions: MagicMock,
|
|
client_exception: Exception,
|
|
) -> None:
|
|
"""Test that some coodinator exceptions raise UpdateFailed exceptions."""
|
|
await assert_setup_sensor(hass, CONFIG_BASIC)
|
|
coordinator: NextBusDataUpdateCoordinator = hass.data[DOMAIN][VALID_AGENCY]
|
|
mock_nextbus_predictions.side_effect = client_exception
|
|
with pytest.raises(UpdateFailed):
|
|
await coordinator._async_update_data()
|
|
|
|
|
|
async def test_custom_name(
|
|
hass: HomeAssistant,
|
|
mock_nextbus: MagicMock,
|
|
mock_nextbus_lists: MagicMock,
|
|
mock_nextbus_predictions: MagicMock,
|
|
) -> None:
|
|
"""Verify that a custom name can be set via config."""
|
|
config = deepcopy(CONFIG_BASIC)
|
|
config[DOMAIN][CONF_NAME] = "Custom Name"
|
|
|
|
await assert_setup_sensor(hass, config)
|
|
state = hass.states.get("sensor.custom_name")
|
|
assert state is not None
|
|
assert state.name == "Custom Name"
|
|
|
|
|
|
async def test_verify_no_predictions(
|
|
hass: HomeAssistant,
|
|
mock_nextbus: MagicMock,
|
|
mock_nextbus_lists: MagicMock,
|
|
mock_nextbus_predictions: MagicMock,
|
|
) -> None:
|
|
"""Verify attributes are set despite no upcoming times."""
|
|
mock_nextbus_predictions.return_value = []
|
|
await assert_setup_sensor(hass, CONFIG_BASIC)
|
|
|
|
state = hass.states.get(SENSOR_ID)
|
|
assert state is not None
|
|
assert "upcoming" not in state.attributes
|
|
assert state.state == "unknown"
|
|
|
|
|
|
async def test_verify_no_upcoming(
|
|
hass: HomeAssistant,
|
|
mock_nextbus: MagicMock,
|
|
mock_nextbus_lists: MagicMock,
|
|
mock_nextbus_predictions: MagicMock,
|
|
) -> None:
|
|
"""Verify attributes are set despite no upcoming times."""
|
|
mock_nextbus_predictions.return_value = NO_UPCOMING
|
|
await assert_setup_sensor(hass, CONFIG_BASIC)
|
|
|
|
state = hass.states.get(SENSOR_ID)
|
|
assert state is not None
|
|
assert state.attributes["upcoming"] == "No upcoming predictions"
|
|
assert state.state == "unknown"
|