hass.tibber_prices/tests/test_coordinator_shutdown.py
Julian Pawlowski d1376c8921 test(cleanup): add comprehensive resource cleanup tests
Added 40+ tests verifying memory leak prevention patterns:

- Listener cleanup: Time-sensitive, minute-update, lifecycle callbacks
- Timer cancellation: Quarter-hour, minute timers
- Config entry cleanup: Options update listener via async_on_unload
- Cache invalidation: Config, period, trend caches
- Storage cleanup: Cache files deleted on entry removal

Tests verify cleanup patterns exist in code (not full integration tests
due to complex mocking requirements).

Impact: Documents and tests cleanup contracts for future maintainability.
Prevents memory leaks when entities removed or config changed.
2025-11-22 04:46:11 +00:00

62 lines
2.1 KiB
Python

"""Test coordinator shutdown and cache persistence."""
from unittest.mock import AsyncMock, MagicMock
import pytest
# Import at module level to avoid PLC0415
from custom_components.tibber_prices.coordinator.core import (
TibberPricesDataUpdateCoordinator,
)
@pytest.mark.asyncio
async def test_coordinator_shutdown_saves_cache() -> None:
"""
Test that coordinator saves cache during shutdown.
This ensures no data is lost when Home Assistant shuts down.
"""
# Create mock coordinator bypassing __init__
coordinator = object.__new__(TibberPricesDataUpdateCoordinator)
# Mock the _store_cache method and listener manager
coordinator._store_cache = AsyncMock() # noqa: SLF001
mock_manager = MagicMock()
mock_manager.cancel_timers = MagicMock()
coordinator._listener_manager = mock_manager # noqa: SLF001
coordinator._log = lambda *_a, **_kw: None # noqa: SLF001
# Call shutdown
await coordinator.async_shutdown()
# Verify cache was saved
coordinator._store_cache.assert_called_once() # noqa: SLF001
# Verify timers were cancelled
mock_manager.cancel_timers.assert_called_once()
@pytest.mark.asyncio
async def test_coordinator_shutdown_handles_cache_error() -> None:
"""
Test that shutdown completes even if cache save fails.
Shutdown should be resilient and not raise exceptions.
"""
# Create mock coordinator bypassing __init__
coordinator = object.__new__(TibberPricesDataUpdateCoordinator)
# Mock _store_cache to raise an exception
coordinator._store_cache = AsyncMock(side_effect=OSError("Disk full")) # noqa: SLF001
mock_manager = MagicMock()
mock_manager.cancel_timers = MagicMock()
coordinator._listener_manager = mock_manager # noqa: SLF001
coordinator._log = lambda *_a, **_kw: None # noqa: SLF001
# Shutdown should complete without raising
await coordinator.async_shutdown()
# Verify _store_cache was called (even though it raised)
coordinator._store_cache.assert_called_once() # noqa: SLF001
# Verify timers were still cancelled despite error
mock_manager.cancel_timers.assert_called_once()