mirror of
https://github.com/home-assistant/core.git
synced 2026-09-24 23:41:48 -05:00
Add ScorpionTrack diagnostics (#181656)
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
"""Diagnostics support for ScorpionTrack."""
|
||||
|
||||
from dataclasses import asdict
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.diagnostics import async_redact_data
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_SHARE_TOKEN
|
||||
from .coordinator import ScorpionTrackConfigEntry
|
||||
|
||||
TO_REDACT = {
|
||||
CONF_SHARE_TOKEN,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
"address",
|
||||
"id",
|
||||
"name",
|
||||
"owner_name",
|
||||
"registration",
|
||||
"title",
|
||||
"token",
|
||||
}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
hass: HomeAssistant, entry: ScorpionTrackConfigEntry
|
||||
) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
coordinator = entry.runtime_data
|
||||
data = asdict(coordinator.data)
|
||||
# The redactor traverses lists, but not tuples.
|
||||
data["vehicles"] = list(data["vehicles"])
|
||||
|
||||
return {
|
||||
"entry_data": async_redact_data(entry.data, TO_REDACT),
|
||||
"last_update_success": coordinator.last_update_success,
|
||||
"data": async_redact_data(data, TO_REDACT),
|
||||
}
|
||||
@@ -50,7 +50,7 @@ rules:
|
||||
test-coverage: done
|
||||
# Gold
|
||||
devices: done
|
||||
diagnostics: todo
|
||||
diagnostics: done
|
||||
discovery:
|
||||
status: exempt
|
||||
comment: |
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
# serializer version: 1
|
||||
# name: test_diagnostics[failed_update]
|
||||
dict({
|
||||
'data': dict({
|
||||
'created_at': '2026-09-04T12:00:00+00:00',
|
||||
'distance_units': 'miles',
|
||||
'expires_at': '2026-10-05T12:00:00+00:00',
|
||||
'id': '**REDACTED**',
|
||||
'owner_name': '**REDACTED**',
|
||||
'title': '**REDACTED**',
|
||||
'token': '**REDACTED**',
|
||||
'vehicles': list([
|
||||
dict({
|
||||
'id': '**REDACTED**',
|
||||
'make': 'Volkswagen',
|
||||
'model': 'Golf R',
|
||||
'name': '**REDACTED**',
|
||||
'position': dict({
|
||||
'address': '**REDACTED**',
|
||||
'bearing': 182.0,
|
||||
'ignition': True,
|
||||
'latitude': '**REDACTED**',
|
||||
'longitude': '**REDACTED**',
|
||||
'speed_kmh': 48.3,
|
||||
'timestamp': '2026-09-05T12:00:00+00:00',
|
||||
}),
|
||||
'registration': '**REDACTED**',
|
||||
'status': 'Moving',
|
||||
}),
|
||||
dict({
|
||||
'id': '**REDACTED**',
|
||||
'make': 'Volkswagen',
|
||||
'model': 'Golf R',
|
||||
'name': '**REDACTED**',
|
||||
'position': dict({
|
||||
'address': '**REDACTED**',
|
||||
'bearing': 182.0,
|
||||
'ignition': True,
|
||||
'latitude': '**REDACTED**',
|
||||
'longitude': '**REDACTED**',
|
||||
'speed_kmh': 48.3,
|
||||
'timestamp': '2026-09-05T12:00:00+00:00',
|
||||
}),
|
||||
'registration': '**REDACTED**',
|
||||
'status': 'Moving',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
'entry_data': dict({
|
||||
'share_token': '**REDACTED**',
|
||||
}),
|
||||
'last_update_success': False,
|
||||
})
|
||||
# ---
|
||||
# name: test_diagnostics[successful_update]
|
||||
dict({
|
||||
'data': dict({
|
||||
'created_at': '2026-09-04T12:00:00+00:00',
|
||||
'distance_units': 'miles',
|
||||
'expires_at': '2026-10-05T12:00:00+00:00',
|
||||
'id': '**REDACTED**',
|
||||
'owner_name': '**REDACTED**',
|
||||
'title': '**REDACTED**',
|
||||
'token': '**REDACTED**',
|
||||
'vehicles': list([
|
||||
dict({
|
||||
'id': '**REDACTED**',
|
||||
'make': 'Volkswagen',
|
||||
'model': 'Golf R',
|
||||
'name': '**REDACTED**',
|
||||
'position': dict({
|
||||
'address': '**REDACTED**',
|
||||
'bearing': 182.0,
|
||||
'ignition': True,
|
||||
'latitude': '**REDACTED**',
|
||||
'longitude': '**REDACTED**',
|
||||
'speed_kmh': 48.3,
|
||||
'timestamp': '2026-09-05T12:00:00+00:00',
|
||||
}),
|
||||
'registration': '**REDACTED**',
|
||||
'status': 'Moving',
|
||||
}),
|
||||
dict({
|
||||
'id': '**REDACTED**',
|
||||
'make': 'Volkswagen',
|
||||
'model': 'Golf R',
|
||||
'name': '**REDACTED**',
|
||||
'position': dict({
|
||||
'address': '**REDACTED**',
|
||||
'bearing': 182.0,
|
||||
'ignition': True,
|
||||
'latitude': '**REDACTED**',
|
||||
'longitude': '**REDACTED**',
|
||||
'speed_kmh': 48.3,
|
||||
'timestamp': '2026-09-05T12:00:00+00:00',
|
||||
}),
|
||||
'registration': '**REDACTED**',
|
||||
'status': 'Moving',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
'entry_data': dict({
|
||||
'share_token': '**REDACTED**',
|
||||
}),
|
||||
'last_update_success': True,
|
||||
})
|
||||
# ---
|
||||
# name: test_diagnostics_missing_position
|
||||
dict({
|
||||
'data': dict({
|
||||
'created_at': '2026-09-04T12:00:00+00:00',
|
||||
'distance_units': 'miles',
|
||||
'expires_at': '2026-10-05T12:00:00+00:00',
|
||||
'id': '**REDACTED**',
|
||||
'owner_name': '**REDACTED**',
|
||||
'title': '**REDACTED**',
|
||||
'token': '**REDACTED**',
|
||||
'vehicles': list([
|
||||
dict({
|
||||
'id': '**REDACTED**',
|
||||
'make': 'Volkswagen',
|
||||
'model': 'Golf R',
|
||||
'name': '**REDACTED**',
|
||||
'position': dict({
|
||||
'address': None,
|
||||
'bearing': None,
|
||||
'ignition': None,
|
||||
'latitude': None,
|
||||
'longitude': None,
|
||||
'speed_kmh': None,
|
||||
'timestamp': None,
|
||||
}),
|
||||
'registration': '**REDACTED**',
|
||||
'status': 'unknown',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
'entry_data': dict({
|
||||
'share_token': '**REDACTED**',
|
||||
}),
|
||||
'last_update_success': True,
|
||||
})
|
||||
# ---
|
||||
@@ -0,0 +1,97 @@
|
||||
"""Test ScorpionTrack diagnostics."""
|
||||
|
||||
from dataclasses import replace
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from pyscorpiontrack import ScorpionTrackConnectionError, ScorpionTrackShare
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.scorpiontrack.const import CONF_SHARE_TOKEN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
pytestmark = pytest.mark.freeze_time("2026-09-07T12:00:00Z")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"update_error",
|
||||
[
|
||||
pytest.param(None, id="successful_update"),
|
||||
pytest.param(
|
||||
ScorpionTrackConnectionError("Failed request for canonical-token"),
|
||||
id="failed_update",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_scorpiontrack_client: AsyncMock,
|
||||
mock_share: ScorpionTrackShare,
|
||||
snapshot: SnapshotAssertion,
|
||||
update_error: ScorpionTrackConnectionError | None,
|
||||
) -> None:
|
||||
"""Test redaction for every vehicle without fetching or changing cached data."""
|
||||
vehicle = mock_share.vehicles[0]
|
||||
share = replace(
|
||||
mock_share,
|
||||
vehicles=(vehicle, replace(vehicle, id=2, registration="EF34 GHI")),
|
||||
)
|
||||
mock_scorpiontrack_client.async_get_share.return_value = share
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
mock_scorpiontrack_client.async_get_share.side_effect = update_error
|
||||
await mock_config_entry.runtime_data.async_refresh()
|
||||
mock_scorpiontrack_client.async_get_share.reset_mock()
|
||||
|
||||
result = await get_diagnostics_for_config_entry(
|
||||
hass, hass_client, mock_config_entry
|
||||
)
|
||||
|
||||
assert result == snapshot
|
||||
mock_scorpiontrack_client.async_get_share.assert_not_awaited()
|
||||
assert mock_config_entry.runtime_data.data == share
|
||||
assert mock_config_entry.data[CONF_SHARE_TOKEN] == "canonical-token"
|
||||
|
||||
|
||||
async def test_diagnostics_missing_position(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_scorpiontrack_client: AsyncMock,
|
||||
mock_share: ScorpionTrackShare,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test diagnostics when a vehicle has no position data."""
|
||||
vehicle = mock_share.vehicles[0]
|
||||
mock_scorpiontrack_client.async_get_share.return_value = replace(
|
||||
mock_share,
|
||||
vehicles=(
|
||||
replace(
|
||||
vehicle,
|
||||
position=replace(
|
||||
vehicle.position,
|
||||
latitude=None,
|
||||
longitude=None,
|
||||
timestamp=None,
|
||||
speed_kmh=None,
|
||||
ignition=None,
|
||||
bearing=None,
|
||||
address=None,
|
||||
),
|
||||
status="unknown",
|
||||
),
|
||||
),
|
||||
)
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert (
|
||||
await get_diagnostics_for_config_entry(hass, hass_client, mock_config_entry)
|
||||
== snapshot
|
||||
)
|
||||
Reference in New Issue
Block a user