Files

160 lines
5.3 KiB
Python

"""Test Teltonika sensor platform."""
from datetime import timedelta
from unittest.mock import AsyncMock, MagicMock
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
from teltasync import TeltonikaAuthenticationError, TeltonikaConnectionError
from homeassistant.components.teltonika.const import DOMAIN
from homeassistant.config_entries import SOURCE_REAUTH
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
async def test_sensors(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
init_integration: MockConfigEntry,
) -> None:
"""Test sensor entities match snapshot."""
await snapshot_platform(hass, entity_registry, snapshot, init_integration.entry_id)
@pytest.mark.usefixtures("init_integration")
async def test_sensor_modem_removed(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_modems: MagicMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test sensor becomes unavailable when modem is removed."""
# Get initial sensor state
state = hass.states.get("sensor.rutx50_test_internal_modem_rssi")
assert state is not None
# Update coordinator with empty modem data
mock_response = MagicMock()
mock_response.data = [] # No modems
mock_modems.get_status.return_value = mock_response
freezer.tick(timedelta(seconds=31))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# Check that entity is marked as unavailable
state = hass.states.get("sensor.rutx50_test_internal_modem_rssi")
assert state is not None
# When modem is removed, entity should be marked as unavailable
# Verify through entity registry that entity exists but is unavailable
entity_entry = entity_registry.async_get("sensor.rutx50_test_internal_modem_rssi")
assert entity_entry is not None
# State should show unavailable when modem is removed
assert state.state == "unavailable"
@pytest.mark.usefixtures("init_integration")
async def test_sensor_update_failure_and_recovery(
hass: HomeAssistant,
mock_modems: AsyncMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test sensor becomes unavailable on update failure and recovers."""
# Get initial sensor state, here it should be available
state = hass.states.get("sensor.rutx50_test_internal_modem_rssi")
assert state is not None
assert state.state == "-63"
mock_modems.get_status.side_effect = TeltonikaConnectionError("Connection lost")
freezer.tick(timedelta(seconds=30))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# Sensor should now be unavailable
state = hass.states.get("sensor.rutx50_test_internal_modem_rssi")
assert state is not None
assert state.state == "unavailable"
# Simulate recovery
mock_modems.get_status.side_effect = None
freezer.tick(timedelta(seconds=30))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# Sensor should be available again with correct data
state = hass.states.get("sensor.rutx50_test_internal_modem_rssi")
assert state is not None
assert state.state == "-63"
@pytest.mark.parametrize(
("side_effect", "expect_reauth"),
[
(TeltonikaAuthenticationError("Invalid credentials"), True),
(TeltonikaConnectionError("Connection lost"), False),
],
ids=["auth_error", "connection_error"],
)
@pytest.mark.usefixtures("init_integration")
async def test_sensor_update_exception_paths(
hass: HomeAssistant,
mock_modems: AsyncMock,
freezer: FrozenDateTimeFactory,
side_effect: Exception,
expect_reauth: bool,
) -> None:
"""Test an auth error triggers reauth while a connection error stays working."""
mock_modems.get_status.side_effect = side_effect
freezer.tick(timedelta(seconds=31))
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get("sensor.rutx50_test_internal_modem_rssi")
assert state is not None
assert state.state == "unavailable"
has_reauth = any(
flow["handler"] == DOMAIN and flow["context"]["source"] == SOURCE_REAUTH
for flow in hass.config_entries.flow.async_progress()
)
assert has_reauth is expect_reauth
@pytest.mark.usefixtures("init_integration")
async def test_sensor_update_unsuccessful_response(
hass: HomeAssistant,
mock_modems: AsyncMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test an unsuccessful API response marks entities unavailable without reauth."""
mock_modems.get_status.side_effect = None
mock_modems.get_status.return_value = MagicMock(
success=False,
data=None,
errors=[MagicMock(code=999, error="API error")],
)
freezer.tick(timedelta(seconds=31))
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get("sensor.rutx50_test_internal_modem_rssi")
assert state is not None
assert state.state == "unavailable"
has_reauth = any(
flow["handler"] == DOMAIN and flow["context"]["source"] == SOURCE_REAUTH
for flow in hass.config_entries.flow.async_progress()
)
assert has_reauth is False