Skip to content

Commit 91086d0

Browse files
kylegordonclaude
andcommitted
Add tests for uncovered exception and edge-case branches
Cover five previously untested paths: - `_get_position_time`: parse_datetime returns None (fallback to raw value) - `_get_position_time`: parse_datetime raises ValueError (fallback to raw value) - `_get_mode` / `_get_mode_attrs`: device.mode is None → returns None / {} - `PetTracerDeviceTracker.available`: returns False when device absent from coordinator - `async_step_reauth_confirm`: unknown exception → errors["base"] = "unknown" Also assert `last_contact` key in device tracker attributes test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7f7ed6f commit 91086d0

3 files changed

Lines changed: 94 additions & 0 deletions

File tree

tests/test_config_flow.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,38 @@ async def test_reauth_flow_success(hass, mock_setup_entry):
218218
assert entry.data[CONF_PASSWORD] == "new_password"
219219

220220

221+
async def test_reauth_flow_unknown_exception(hass, mock_setup_entry):
222+
"""Test reauth flow with unexpected exception."""
223+
from pytest_homeassistant_custom_component.common import MockConfigEntry
224+
225+
entry = MockConfigEntry(
226+
domain=DOMAIN,
227+
data={
228+
CONF_USERNAME: "test@example.com",
229+
CONF_PASSWORD: "old_password",
230+
},
231+
unique_id="test@example.com",
232+
)
233+
entry.add_to_hass(hass)
234+
235+
result = await entry.start_reauth_flow(hass)
236+
237+
with patch(
238+
"custom_components.pettracer.config_flow.PetTracerClient"
239+
) as mock_client:
240+
client_instance = MagicMock()
241+
client_instance.login = AsyncMock(side_effect=Exception("Unexpected error"))
242+
mock_client.return_value = client_instance
243+
244+
result2 = await hass.config_entries.flow.async_configure(
245+
result["flow_id"],
246+
{CONF_PASSWORD: "new_password"},
247+
)
248+
249+
assert result2["type"] == FlowResultType.FORM
250+
assert result2["errors"] == {"base": "unknown"}
251+
252+
221253
async def test_reauth_flow_invalid_auth(hass, mock_setup_entry):
222254
"""Test reauth flow with invalid credentials."""
223255
from pytest_homeassistant_custom_component.common import MockConfigEntry

tests/test_device_tracker.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ async def test_device_tracker_attributes(hass, mock_device):
132132

133133
assert "battery_voltage_mv" in attributes
134134
assert attributes["battery_voltage_mv"] == 4100
135+
assert "last_contact" in attributes
135136
assert "satellites" in attributes
136137
assert attributes["satellites"] == 8
137138
assert "signal_strength" in attributes
@@ -246,6 +247,20 @@ async def test_device_tracker_no_details(hass):
246247
assert device_info["sw_version"] is None
247248

248249

250+
async def test_device_tracker_unavailable(hass, mock_device):
251+
"""Test device tracker available property when device is removed from coordinator."""
252+
from custom_components.pettracer.device_tracker import PetTracerDeviceTracker
253+
254+
coordinator = MagicMock()
255+
coordinator.data = {"devices": [mock_device]}
256+
257+
tracker = PetTracerDeviceTracker(coordinator, mock_device)
258+
assert tracker.available is True
259+
260+
coordinator.data = {"devices": []}
261+
assert tracker.available is False
262+
263+
249264
async def test_device_tracker_partial_attributes(hass):
250265
"""Test device tracker with partial attribute data."""
251266
from custom_components.pettracer.device_tracker import PetTracerDeviceTracker

tests/test_sensor.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,53 @@ async def test_sensor_no_details(hass):
367367
assert device_info["sw_version"] is None
368368

369369

370+
async def test_position_time_parse_returns_none(hass, mock_device):
371+
"""Test position time falls back to raw value when parse_datetime returns None."""
372+
coordinator = MagicMock()
373+
coordinator.data = {"devices": [mock_device]}
374+
375+
mock_device.lastPos.timeMeasure = "not-a-real-datetime"
376+
377+
description = next(d for d in SENSOR_DESCRIPTIONS if d.key == "position_time")
378+
sensor = PetTracerSensor(coordinator, mock_device, description)
379+
380+
with patch(
381+
"custom_components.pettracer.sensor.parse_datetime", return_value=None
382+
):
383+
assert sensor.native_value == "not-a-real-datetime"
384+
385+
386+
async def test_position_time_parse_exception(hass, mock_device):
387+
"""Test position time falls back to raw value when parse_datetime raises."""
388+
coordinator = MagicMock()
389+
coordinator.data = {"devices": [mock_device]}
390+
391+
mock_device.lastPos.timeMeasure = "bad-value"
392+
393+
description = next(d for d in SENSOR_DESCRIPTIONS if d.key == "position_time")
394+
sensor = PetTracerSensor(coordinator, mock_device, description)
395+
396+
with patch(
397+
"custom_components.pettracer.sensor.parse_datetime",
398+
side_effect=ValueError("unparseable"),
399+
):
400+
assert sensor.native_value == "bad-value"
401+
402+
403+
async def test_mode_sensor_none_mode(hass, mock_device):
404+
"""Test mode sensor returns None when device.mode is None."""
405+
coordinator = MagicMock()
406+
407+
description = next(d for d in SENSOR_DESCRIPTIONS if d.key == "mode")
408+
sensor = PetTracerSensor(coordinator, mock_device, description)
409+
410+
mock_device.mode = None
411+
coordinator.data = {"devices": [mock_device]}
412+
413+
assert sensor.native_value is None
414+
assert sensor.extra_state_attributes == {}
415+
416+
370417
async def test_battery_percentage_edge_cases(hass, mock_device):
371418
"""Test battery percentage calculation with edge cases."""
372419
coordinator = MagicMock()

0 commit comments

Comments
 (0)