mirror of
https://github.com/home-assistant/core.git
synced 2026-05-27 10:46:38 +01:00
Improve Israel Rail departure sensor coverage (#171397)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -79,6 +79,17 @@ class IsraelRailDataUpdateCoordinator(DataUpdateCoordinator[list[DataConnection]
|
||||
"Unable to connect and retrieve data from israelrail api",
|
||||
) from e
|
||||
|
||||
offset = 0
|
||||
now = dt_util.now()
|
||||
while offset < len(train_routes):
|
||||
route = train_routes[offset]
|
||||
if route is None:
|
||||
break
|
||||
route_departure = departure_time(route)
|
||||
if route_departure is None or route_departure >= now:
|
||||
break
|
||||
offset += 1
|
||||
|
||||
return [
|
||||
DataConnection(
|
||||
departure=departure_time(train_routes[i]),
|
||||
@@ -89,6 +100,6 @@ class IsraelRailDataUpdateCoordinator(DataUpdateCoordinator[list[DataConnection]
|
||||
start=station_name_to_id(train_routes[i].trains[0].src),
|
||||
destination=station_name_to_id(train_routes[i].trains[-1].dst),
|
||||
)
|
||||
for i in range(DEPARTURES_COUNT)
|
||||
for i in range(offset, offset + DEPARTURES_COUNT)
|
||||
if len(train_routes) > i and train_routes[i] is not None
|
||||
]
|
||||
|
||||
@@ -52,30 +52,46 @@ DEPARTURE_SENSORS: tuple[IsraelRailSensorEntityDescription, ...] = (
|
||||
)
|
||||
|
||||
SENSORS: tuple[IsraelRailSensorEntityDescription, ...] = (
|
||||
IsraelRailSensorEntityDescription(
|
||||
key="platform",
|
||||
translation_key="platform",
|
||||
value_fn=lambda data_connection: data_connection.platform,
|
||||
),
|
||||
IsraelRailSensorEntityDescription(
|
||||
key="trains",
|
||||
translation_key="trains",
|
||||
value_fn=lambda data_connection: data_connection.trains,
|
||||
),
|
||||
IsraelRailSensorEntityDescription(
|
||||
key="train_number",
|
||||
translation_key="train_number",
|
||||
value_fn=lambda data_connection: data_connection.train_number,
|
||||
),
|
||||
IsraelRailSensorEntityDescription(
|
||||
key="departure_delay",
|
||||
translation_key="departure_delay",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda data_connection: data_connection.departure_delay,
|
||||
),
|
||||
*[
|
||||
IsraelRailSensorEntityDescription(
|
||||
key=f"platform{i or ''}",
|
||||
translation_key=f"platform{i or ''}",
|
||||
value_fn=lambda data_connection: data_connection.platform,
|
||||
index=i,
|
||||
)
|
||||
for i in range(DEPARTURES_COUNT)
|
||||
],
|
||||
*[
|
||||
IsraelRailSensorEntityDescription(
|
||||
key=f"trains{i or ''}",
|
||||
translation_key=f"trains{i or ''}",
|
||||
value_fn=lambda data_connection: data_connection.trains,
|
||||
index=i,
|
||||
)
|
||||
for i in range(DEPARTURES_COUNT)
|
||||
],
|
||||
*[
|
||||
IsraelRailSensorEntityDescription(
|
||||
key=f"train_number{i or ''}",
|
||||
translation_key=f"train_number{i or ''}",
|
||||
value_fn=lambda data_connection: data_connection.train_number,
|
||||
index=i,
|
||||
)
|
||||
for i in range(DEPARTURES_COUNT)
|
||||
],
|
||||
*[
|
||||
IsraelRailSensorEntityDescription(
|
||||
key=f"departure_delay{i or ''}",
|
||||
translation_key=f"departure_delay{i or ''}",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
value_fn=lambda data_connection: data_connection.departure_delay,
|
||||
index=i,
|
||||
)
|
||||
for i in range(DEPARTURES_COUNT)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -31,14 +31,38 @@
|
||||
"departure_delay": {
|
||||
"name": "Departure delay"
|
||||
},
|
||||
"departure_delay1": {
|
||||
"name": "Departure delay +1"
|
||||
},
|
||||
"departure_delay2": {
|
||||
"name": "Departure delay +2"
|
||||
},
|
||||
"platform": {
|
||||
"name": "Platform"
|
||||
},
|
||||
"platform1": {
|
||||
"name": "Platform +1"
|
||||
},
|
||||
"platform2": {
|
||||
"name": "Platform +2"
|
||||
},
|
||||
"train_number": {
|
||||
"name": "Train number"
|
||||
},
|
||||
"train_number1": {
|
||||
"name": "Train number +1"
|
||||
},
|
||||
"train_number2": {
|
||||
"name": "Train number +2"
|
||||
},
|
||||
"trains": {
|
||||
"name": "Trains"
|
||||
},
|
||||
"trains1": {
|
||||
"name": "Trains +1"
|
||||
},
|
||||
"trains2": {
|
||||
"name": "Trains +2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +214,124 @@
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_departure_delay_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_departure_delay_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Departure delay +1',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 0,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Departure delay +1',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'departure_delay1',
|
||||
'unique_id': 'באר יעקב אשקלון_departure_delay1',
|
||||
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_departure_delay_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'device_class': 'duration',
|
||||
'friendly_name': 'Mock Title Departure delay +1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_departure_delay_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_departure_delay_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_departure_delay_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Departure delay +2',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 0,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Departure delay +2',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'departure_delay2',
|
||||
'unique_id': 'באר יעקב אשקלון_departure_delay2',
|
||||
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_departure_delay_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'device_class': 'duration',
|
||||
'friendly_name': 'Mock Title Departure delay +2',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_departure_delay_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_platform-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
@@ -265,6 +383,108 @@
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_platform_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_platform_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Platform +1',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Platform +1',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'platform1',
|
||||
'unique_id': 'באר יעקב אשקלון_platform1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_platform_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'friendly_name': 'Mock Title Platform +1',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_platform_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_platform_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_platform_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Platform +2',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Platform +2',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'platform2',
|
||||
'unique_id': 'באר יעקב אשקלון_platform2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_platform_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'friendly_name': 'Mock Title Platform +2',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_platform_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_train_number-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
@@ -316,6 +536,108 @@
|
||||
'state': '1234',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_train_number_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_train_number_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Train number +1',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Train number +1',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'train_number1',
|
||||
'unique_id': 'באר יעקב אשקלון_train_number1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_train_number_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'friendly_name': 'Mock Title Train number +1',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_train_number_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1235',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_train_number_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_train_number_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Train number +2',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Train number +2',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'train_number2',
|
||||
'unique_id': 'באר יעקב אשקלון_train_number2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_train_number_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'friendly_name': 'Mock Title Train number +2',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_train_number_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1236',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_trains-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
@@ -367,3 +689,105 @@
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_trains_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_trains_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Trains +1',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Trains +1',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'trains1',
|
||||
'unique_id': 'באר יעקב אשקלון_trains1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_trains_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'friendly_name': 'Mock Title Trains +1',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_trains_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_trains_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_trains_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Trains +2',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Trains +2',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'trains2',
|
||||
'unique_id': 'באר יעקב אשקלון_trains2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_trains_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'friendly_name': 'Mock Title Trains +2',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_trains_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.israel_rail.const import DEPARTURES_COUNT
|
||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
@@ -12,7 +14,20 @@ from homeassistant.helpers import entity_registry as er
|
||||
from . import goto_future, init_integration
|
||||
from .conftest import TRAINS, get_time, get_train_route
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
|
||||
# A moment just before the first train in TRAINS departs (10:10 UTC), so the
|
||||
# coordinator considers the head of the list as an upcoming departure.
|
||||
BEFORE_FIRST_TRAIN = "2021-10-10T10:00:00+00:00"
|
||||
|
||||
EXPECTED_ENTITY_COUNT = DEPARTURES_COUNT * 5
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def freeze_before_first_train(freezer: FrozenDateTimeFactory) -> FrozenDateTimeFactory:
|
||||
"""Freeze time before any train in TRAINS departs."""
|
||||
freezer.move_to(BEFORE_FIRST_TRAIN)
|
||||
return freezer
|
||||
|
||||
|
||||
async def test_valid_config(
|
||||
@@ -29,22 +44,23 @@ async def test_valid_config(
|
||||
|
||||
async def test_update_train(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
freeze_before_first_train: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure the train data is updated."""
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
assert len(hass.states.async_entity_ids()) == EXPECTED_ENTITY_COUNT
|
||||
departure_sensor = hass.states.get("sensor.mock_title_departure")
|
||||
expected_time = get_time(10, 10)
|
||||
assert departure_sensor.state == expected_time
|
||||
|
||||
mock_israelrail.query.return_value = TRAINS[1:]
|
||||
freeze_before_first_train.move_to("2021-10-10T10:15:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await goto_future(hass, freezer)
|
||||
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
assert len(hass.states.async_entity_ids()) == EXPECTED_ENTITY_COUNT
|
||||
departure_sensor = hass.states.get("sensor.mock_title_departure")
|
||||
expected_time = get_time(10, 20)
|
||||
assert departure_sensor.state == expected_time
|
||||
@@ -52,37 +68,37 @@ async def test_update_train(
|
||||
|
||||
async def test_fail_query(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
freeze_before_first_train: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure the integration handles query failures."""
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
assert len(hass.states.async_entity_ids()) == EXPECTED_ENTITY_COUNT
|
||||
mock_israelrail.query.side_effect = Exception("error")
|
||||
await goto_future(hass, freezer)
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
await goto_future(hass, freeze_before_first_train)
|
||||
assert len(hass.states.async_entity_ids()) == EXPECTED_ENTITY_COUNT
|
||||
departure_sensor = hass.states.get("sensor.mock_title_departure")
|
||||
assert departure_sensor.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_no_departures(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
freeze_before_first_train: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test handling when there are no departures available."""
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
assert len(hass.states.async_entity_ids()) == EXPECTED_ENTITY_COUNT
|
||||
|
||||
# Simulate no departures (e.g., after-hours)
|
||||
mock_israelrail.query.return_value = []
|
||||
|
||||
await goto_future(hass, freezer)
|
||||
await goto_future(hass, freeze_before_first_train)
|
||||
|
||||
# All sensors should still exist
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
assert len(hass.states.async_entity_ids()) == EXPECTED_ENTITY_COUNT
|
||||
|
||||
# Departure sensors should have unknown state (None)
|
||||
departure_sensor = hass.states.get("sensor.mock_title_departure")
|
||||
@@ -111,7 +127,7 @@ async def test_no_departures(
|
||||
|
||||
async def test_departure_delay(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
freeze_before_first_train: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
@@ -132,7 +148,104 @@ async def test_departure_delay(
|
||||
*TRAINS[1:],
|
||||
]
|
||||
|
||||
await goto_future(hass, freezer)
|
||||
# Refresh while still before TRAINS[0] departs, so the delay-bearing
|
||||
# first route is treated as upcoming and not skipped.
|
||||
freeze_before_first_train.move_to("2021-10-10T10:05:00+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
departure_delay_sensor = hass.states.get("sensor.mock_title_departure_delay")
|
||||
assert departure_delay_sensor.state == "7"
|
||||
|
||||
|
||||
async def test_skip_first_route_when_in_past(
|
||||
hass: HomeAssistant,
|
||||
freeze_before_first_train: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""When the first route already departed, sensors should reflect the next ones."""
|
||||
# Freeze "now" between TRAINS[0] (10:10) and TRAINS[1] (10:20) so the head
|
||||
# of the list is in the past. Coordinator should shift the window by 1.
|
||||
freeze_before_first_train.move_to("2021-10-10T10:15:00+00:00")
|
||||
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
# The +0/+1/+2 departure sensors should now show TRAINS[1], TRAINS[2], TRAINS[3].
|
||||
assert hass.states.get("sensor.mock_title_departure").state == get_time(10, 20)
|
||||
assert hass.states.get("sensor.mock_title_departure_1").state == get_time(10, 30)
|
||||
assert hass.states.get("sensor.mock_title_departure_2").state == get_time(10, 40)
|
||||
# The per-index sensors should also follow the shifted window — index 0
|
||||
# now points at the second route in the API response, index 1 at the third, etc.
|
||||
assert hass.states.get("sensor.mock_title_train_number").state == "1235"
|
||||
assert hass.states.get("sensor.mock_title_train_number_1").state == "1236"
|
||||
assert hass.states.get("sensor.mock_title_train_number_2").state == "1237"
|
||||
|
||||
|
||||
async def test_keep_first_route_when_upcoming(
|
||||
hass: HomeAssistant,
|
||||
freeze_before_first_train: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""When the first route is still upcoming, sensors should keep it."""
|
||||
# "now" sits well before TRAINS[0] (10:10), so no offset is applied.
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("sensor.mock_title_departure").state == get_time(10, 10)
|
||||
assert hass.states.get("sensor.mock_title_departure_1").state == get_time(10, 20)
|
||||
assert hass.states.get("sensor.mock_title_departure_2").state == get_time(10, 30)
|
||||
assert hass.states.get("sensor.mock_title_train_number").state == "1234"
|
||||
|
||||
|
||||
async def test_skip_first_route_with_fewer_results(
|
||||
hass: HomeAssistant,
|
||||
freeze_before_first_train: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""A short result list with a past head still yields entries from the tail."""
|
||||
# Only two routes; the first is already in the past so only one remains.
|
||||
mock_israelrail.query.return_value = [TRAINS[0], TRAINS[1]]
|
||||
freeze_before_first_train.move_to("2021-10-10T10:15:00+00:00")
|
||||
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("sensor.mock_title_departure").state == get_time(10, 20)
|
||||
# No second/third routes available after the shift.
|
||||
assert hass.states.get("sensor.mock_title_departure_1").state == STATE_UNKNOWN
|
||||
assert hass.states.get("sensor.mock_title_departure_2").state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_skip_multiple_past_routes(
|
||||
hass: HomeAssistant,
|
||||
freeze_before_first_train: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""When several routes at the head have already departed, the window shifts past them all."""
|
||||
# Freeze "now" past TRAINS[0] (10:10) and TRAINS[1] (10:20) but before TRAINS[2] (10:30).
|
||||
freeze_before_first_train.move_to("2021-10-10T10:25:00+00:00")
|
||||
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("sensor.mock_title_departure").state == get_time(10, 30)
|
||||
assert hass.states.get("sensor.mock_title_departure_1").state == get_time(10, 40)
|
||||
assert hass.states.get("sensor.mock_title_departure_2").state == get_time(10, 50)
|
||||
assert hass.states.get("sensor.mock_title_train_number").state == "1236"
|
||||
|
||||
|
||||
async def test_all_routes_in_past(
|
||||
hass: HomeAssistant,
|
||||
freeze_before_first_train: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""When every returned route has already departed, every departure sensor is unknown."""
|
||||
freeze_before_first_train.move_to("2021-10-10T11:00:00+00:00")
|
||||
|
||||
await init_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get("sensor.mock_title_departure").state == STATE_UNKNOWN
|
||||
assert hass.states.get("sensor.mock_title_departure_1").state == STATE_UNKNOWN
|
||||
assert hass.states.get("sensor.mock_title_departure_2").state == STATE_UNKNOWN
|
||||
|
||||
Reference in New Issue
Block a user