core/tests/components/nextbus/test_sensor.py

271 lines
9.0 KiB
Python

"""The tests for the nexbus sensor component."""
from copy import deepcopy
from unittest.mock import patch
import pytest
import homeassistant.components.nextbus.sensor as nextbus
import homeassistant.components.sensor as sensor
from tests.common import assert_setup_component, async_setup_component
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"
SENSOR_ID_SHORT = "sensor.sf_muni_f"
CONFIG_BASIC = {
"sensor": {
"platform": "nextbus",
"agency": VALID_AGENCY,
"route": VALID_ROUTE,
"stop": VALID_STOP,
}
}
CONFIG_INVALID_MISSING = {"sensor": {"platform": "nextbus"}}
BASIC_RESULTS = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
"routeTitle": VALID_ROUTE_TITLE,
"stopTitle": VALID_STOP_TITLE,
"direction": {
"title": "Outbound",
"prediction": [
{"minutes": "1", "epochTime": "1553807371000"},
{"minutes": "2", "epochTime": "1553807372000"},
{"minutes": "3", "epochTime": "1553807373000"},
],
},
}
}
async def assert_setup_sensor(hass, config, count=1):
"""Set up the sensor and assert it's been created."""
with assert_setup_component(count):
assert await async_setup_component(hass, sensor.DOMAIN, config)
@pytest.fixture
def mock_nextbus():
"""Create a mock py_nextbus module."""
with patch(
"homeassistant.components.nextbus.sensor.NextBusClient"
) as NextBusClient:
yield NextBusClient
@pytest.fixture
def mock_nextbus_predictions(mock_nextbus):
"""Create a mock of NextBusClient predictions."""
instance = mock_nextbus.return_value
instance.get_predictions_for_multi_stops.return_value = BASIC_RESULTS
yield instance.get_predictions_for_multi_stops
@pytest.fixture
def mock_nextbus_lists(mock_nextbus):
"""Mock all list functions in nextbus to test validate logic."""
instance = mock_nextbus.return_value
instance.get_agency_list.return_value = {
"agency": [{"tag": "sf-muni", "title": "San Francisco Muni"}]
}
instance.get_route_list.return_value = {
"route": [{"tag": "F", "title": "F - Market & Wharves"}]
}
instance.get_route_config.return_value = {
"route": {"stop": [{"tag": "5650", "title": "Market St & 7th St"}]}
}
async def test_valid_config(hass, mock_nextbus, mock_nextbus_lists):
"""Test that sensor is set up properly with valid config."""
await assert_setup_sensor(hass, CONFIG_BASIC)
async def test_invalid_config(hass, mock_nextbus, mock_nextbus_lists):
"""Checks that component is not setup when missing information."""
await assert_setup_sensor(hass, CONFIG_INVALID_MISSING, count=0)
async def test_validate_tags(hass, mock_nextbus, mock_nextbus_lists):
"""Test that additional validation against the API is successful."""
# with self.subTest('Valid everything'):
assert nextbus.validate_tags(mock_nextbus(), VALID_AGENCY, VALID_ROUTE, VALID_STOP)
# with self.subTest('Invalid agency'):
assert not nextbus.validate_tags(
mock_nextbus(), "not-valid", VALID_ROUTE, VALID_STOP
)
# with self.subTest('Invalid route'):
assert not nextbus.validate_tags(mock_nextbus(), VALID_AGENCY, "0", VALID_STOP)
# with self.subTest('Invalid stop'):
assert not nextbus.validate_tags(mock_nextbus(), VALID_AGENCY, VALID_ROUTE, 0)
async def test_verify_valid_state(
hass, mock_nextbus, mock_nextbus_lists, mock_nextbus_predictions
):
"""Verify all attributes are set from a valid response."""
await assert_setup_sensor(hass, CONFIG_BASIC)
mock_nextbus_predictions.assert_called_once_with(
[{"stop_tag": VALID_STOP, "route_tag": VALID_ROUTE}], VALID_AGENCY
)
state = hass.states.get(SENSOR_ID_SHORT)
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"
assert state.attributes["upcoming"] == "1, 2, 3"
async def test_message_dict(
hass, mock_nextbus, mock_nextbus_lists, mock_nextbus_predictions
):
"""Verify that a single dict message is rendered correctly."""
mock_nextbus_predictions.return_value = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
"routeTitle": VALID_ROUTE_TITLE,
"stopTitle": VALID_STOP_TITLE,
"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)
state = hass.states.get(SENSOR_ID_SHORT)
assert state is not None
assert state.attributes["message"] == "Message"
async def test_message_list(
hass, mock_nextbus, mock_nextbus_lists, mock_nextbus_predictions
):
"""Verify that a list of messages are rendered correctly."""
mock_nextbus_predictions.return_value = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
"routeTitle": VALID_ROUTE_TITLE,
"stopTitle": VALID_STOP_TITLE,
"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)
state = hass.states.get(SENSOR_ID_SHORT)
assert state is not None
assert state.attributes["message"] == "Message 1 -- Message 2"
async def test_direction_list(
hass, mock_nextbus, mock_nextbus_lists, mock_nextbus_predictions
):
"""Verify that a list of messages are rendered correctly."""
mock_nextbus_predictions.return_value = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
"routeTitle": VALID_ROUTE_TITLE,
"stopTitle": VALID_STOP_TITLE,
"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)
state = hass.states.get(SENSOR_ID_SHORT)
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"
async def test_custom_name(
hass, mock_nextbus, mock_nextbus_lists, mock_nextbus_predictions
):
"""Verify that a custom name can be set via config."""
config = deepcopy(CONFIG_BASIC)
config["sensor"]["name"] = "Custom Name"
await assert_setup_sensor(hass, config)
state = hass.states.get("sensor.custom_name")
assert state is not None
async def test_no_predictions(
hass, mock_nextbus, mock_nextbus_predictions, mock_nextbus_lists
):
"""Verify there are no exceptions when no predictions are returned."""
mock_nextbus_predictions.return_value = {}
await assert_setup_sensor(hass, CONFIG_BASIC)
state = hass.states.get(SENSOR_ID_SHORT)
assert state is not None
assert state.state == "unknown"
async def test_verify_no_upcoming(
hass, mock_nextbus, mock_nextbus_lists, mock_nextbus_predictions
):
"""Verify attributes are set despite no upcoming times."""
mock_nextbus_predictions.return_value = {
"predictions": {
"agencyTitle": VALID_AGENCY_TITLE,
"routeTitle": VALID_ROUTE_TITLE,
"stopTitle": VALID_STOP_TITLE,
"direction": {"title": "Outbound", "prediction": []},
}
}
await assert_setup_sensor(hass, CONFIG_BASIC)
state = hass.states.get(SENSOR_ID_SHORT)
assert state is not None
assert state.state == "unknown"
assert state.attributes["upcoming"] == "No upcoming predictions"