hass-core/tests/components/nextbus/test_sensor.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

308 lines
9.6 KiB
Python
Raw Normal View History

"""The tests for the nexbus sensor component."""
from copy import deepcopy
2023-09-19 08:10:29 -07:00
from unittest.mock import MagicMock, patch
from urllib.error import HTTPError
2024-04-09 18:34:35 +02:00
from py_nextbus.client import NextBusFormatError, NextBusHTTPError
import pytest
from typing_extensions import Generator
2023-09-19 08:10:29 -07:00
from homeassistant.components import sensor
2023-12-13 17:05:37 +01:00
from homeassistant.components.nextbus.const import CONF_AGENCY, CONF_ROUTE, DOMAIN
from homeassistant.components.nextbus.coordinator import NextBusDataUpdateCoordinator
2023-09-19 08:10:29 -07:00
from homeassistant.config_entries import ConfigEntryState
2023-12-13 17:05:37 +01:00
from homeassistant.const import CONF_NAME, CONF_STOP
2024-04-09 18:34:35 +02:00
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import UpdateFailed
2023-09-19 08:10:29 -07:00
from tests.common import MockConfigEntry
VALID_AGENCY = "sf-muni"
VALID_ROUTE = "F"
VALID_STOP = "5650"
VALID_AGENCY_TITLE = "San Francisco Muni"
VALID_ROUTE_TITLE = "F-Market & Wharves"
VALID_STOP_TITLE = "Market St & 7th St"
2023-09-19 08:10:29 -07:00
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 = {
2023-09-19 08:10:29 -07:00
DOMAIN: {
CONF_AGENCY: VALID_AGENCY,
CONF_ROUTE: VALID_ROUTE,
CONF_STOP: VALID_STOP,
}
}
BASIC_RESULTS = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
2023-09-19 08:10:29 -07:00
"agencyTag": VALID_AGENCY,
"routeTitle": VALID_ROUTE_TITLE,
2023-09-19 08:10:29 -07:00
"routeTag": VALID_ROUTE,
"stopTitle": VALID_STOP_TITLE,
2023-09-19 08:10:29 -07:00
"stopTag": VALID_STOP,
"direction": {
"title": "Outbound",
"prediction": [
{"minutes": "1", "epochTime": "1553807371000"},
{"minutes": "2", "epochTime": "1553807372000"},
{"minutes": "3", "epochTime": "1553807373000"},
{"minutes": "10", "epochTime": "1553807380000"},
],
},
}
}
@pytest.fixture
def mock_nextbus() -> Generator[MagicMock]:
"""Create a mock py_nextbus module."""
with patch("homeassistant.components.nextbus.coordinator.NextBusClient") as client:
2023-09-19 08:10:29 -07:00
yield client
@pytest.fixture
2023-09-19 08:10:29 -07:00
def mock_nextbus_predictions(
mock_nextbus: MagicMock,
) -> Generator[MagicMock]:
"""Create a mock of NextBusClient predictions."""
instance = mock_nextbus.return_value
instance.get_predictions_for_multi_stops.return_value = BASIC_RESULTS
return instance.get_predictions_for_multi_stops
2023-09-19 08:10:29 -07:00
async def assert_setup_sensor(
hass: HomeAssistant,
config: dict[str, dict[str, str]],
2023-09-19 08:10:29 -07:00
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)
2023-09-19 08:10:29 -07:00
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
2023-09-19 08:10:29 -07:00
assert config_entry.state is expected_state
2023-09-19 08:10:29 -07:00
return config_entry
async def test_message_dict(
2023-09-19 08:10:29 -07:00
hass: HomeAssistant,
mock_nextbus: MagicMock,
mock_nextbus_lists: MagicMock,
mock_nextbus_predictions: MagicMock,
) -> None:
"""Verify that a single dict message is rendered correctly."""
mock_nextbus_predictions.return_value = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
2023-09-19 08:10:29 -07:00
"agencyTag": VALID_AGENCY,
"routeTitle": VALID_ROUTE_TITLE,
2023-09-19 08:10:29 -07:00
"routeTag": VALID_ROUTE,
"stopTitle": VALID_STOP_TITLE,
2023-09-19 08:10:29 -07:00
"stopTag": VALID_STOP,
"message": {"text": "Message"},
"direction": {
"title": "Outbound",
"prediction": [
{"minutes": "1", "epochTime": "1553807371000"},
{"minutes": "2", "epochTime": "1553807372000"},
{"minutes": "3", "epochTime": "1553807373000"},
],
},
}
}
await assert_setup_sensor(hass, CONFIG_BASIC)
2023-09-19 08:10:29 -07:00
state = hass.states.get(SENSOR_ID)
assert state is not None
assert state.attributes["message"] == "Message"
async def test_message_list(
2023-09-19 08:10:29 -07:00
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.return_value = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
2023-09-19 08:10:29 -07:00
"agencyTag": VALID_AGENCY,
"routeTitle": VALID_ROUTE_TITLE,
2023-09-19 08:10:29 -07:00
"routeTag": VALID_ROUTE,
"stopTitle": VALID_STOP_TITLE,
2023-09-19 08:10:29 -07:00
"stopTag": VALID_STOP,
"message": [{"text": "Message 1"}, {"text": "Message 2"}],
"direction": {
"title": "Outbound",
"prediction": [
{"minutes": "1", "epochTime": "1553807371000"},
{"minutes": "2", "epochTime": "1553807372000"},
{"minutes": "3", "epochTime": "1553807373000"},
],
},
}
}
await assert_setup_sensor(hass, CONFIG_BASIC)
2023-09-19 08:10:29 -07:00
state = hass.states.get(SENSOR_ID)
assert state is not None
assert state.attributes["message"] == "Message 1 -- Message 2"
async def test_direction_list(
2023-09-19 08:10:29 -07:00
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.return_value = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
2023-09-19 08:10:29 -07:00
"agencyTag": VALID_AGENCY,
"routeTitle": VALID_ROUTE_TITLE,
2023-09-19 08:10:29 -07:00
"routeTag": VALID_ROUTE,
"stopTitle": VALID_STOP_TITLE,
2023-09-19 08:10:29 -07:00
"stopTag": VALID_STOP,
"message": [{"text": "Message 1"}, {"text": "Message 2"}],
"direction": [
{
"title": "Outbound",
"prediction": [
{"minutes": "1", "epochTime": "1553807371000"},
{"minutes": "2", "epochTime": "1553807372000"},
{"minutes": "3", "epochTime": "1553807373000"},
],
},
{
"title": "Outbound 2",
"prediction": {"minutes": "0", "epochTime": "1553807374000"},
},
],
}
}
await assert_setup_sensor(hass, CONFIG_BASIC)
2023-09-19 08:10:29 -07:00
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_TITLE
assert state.attributes["route"] == VALID_ROUTE_TITLE
assert state.attributes["stop"] == VALID_STOP_TITLE
assert state.attributes["direction"] == "Outbound, Outbound 2"
assert state.attributes["upcoming"] == "0, 1, 2, 3"
@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(
2023-09-19 08:10:29 -07:00
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)
2023-09-19 08:10:29 -07:00
config[DOMAIN][CONF_NAME] = "Custom Name"
await assert_setup_sensor(hass, config)
state = hass.states.get("sensor.custom_name")
assert state is not None
2023-09-19 08:10:29 -07:00
assert state.name == "Custom Name"
2023-09-19 08:10:29 -07:00
@pytest.mark.parametrize(
"prediction_results",
[
2023-09-19 08:10:29 -07:00
{},
{"Error": "Failed"},
],
2023-09-19 08:10:29 -07:00
)
async def test_no_predictions(
2023-09-19 08:10:29 -07:00
hass: HomeAssistant,
mock_nextbus: MagicMock,
mock_nextbus_predictions: MagicMock,
mock_nextbus_lists: MagicMock,
prediction_results: dict[str, str],
) -> None:
"""Verify there are no exceptions when no predictions are returned."""
2023-09-19 08:10:29 -07:00
mock_nextbus_predictions.return_value = prediction_results
await assert_setup_sensor(hass, CONFIG_BASIC)
2023-09-19 08:10:29 -07:00
state = hass.states.get(SENSOR_ID)
assert state is not None
assert state.state == "unknown"
async def test_verify_no_upcoming(
2023-09-19 08:10:29 -07:00
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 = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
2023-09-19 08:10:29 -07:00
"agencyTag": VALID_AGENCY,
"routeTitle": VALID_ROUTE_TITLE,
2023-09-19 08:10:29 -07:00
"routeTag": VALID_ROUTE,
"stopTitle": VALID_STOP_TITLE,
2023-09-19 08:10:29 -07:00
"stopTag": VALID_STOP,
"direction": {"title": "Outbound", "prediction": []},
}
}
await assert_setup_sensor(hass, CONFIG_BASIC)
2023-09-19 08:10:29 -07:00
state = hass.states.get(SENSOR_ID)
assert state is not None
assert state.state == "unknown"
assert state.attributes["upcoming"] == "No upcoming predictions"