diff --git a/custom_components/houseplan/__init__.py b/custom_components/houseplan/__init__.py index dadd696..b079ab9 100755 --- a/custom_components/houseplan/__init__.py +++ b/custom_components/houseplan/__init__.py @@ -142,6 +142,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: HouseplanConfigEntry) -> except Exception: # noqa: BLE001 — housekeeping must never fail a setup _LOGGER.exception("House Plan: sweeping unreferenced files failed") + data.sweep = _sweep await _sweep() entry.async_on_unload( async_track_time_interval(hass, _sweep, timedelta(hours=24)) diff --git a/custom_components/houseplan/store.py b/custom_components/houseplan/store.py index 66d8762..79f3003 100644 --- a/custom_components/houseplan/store.py +++ b/custom_components/houseplan/store.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio +from collections.abc import Awaitable, Callable from dataclasses import dataclass, field from typing import Any @@ -42,6 +43,11 @@ class HouseplanData: # One lock for every load→modify→save cycle of both stores: prevents # lost updates from concurrent WS calls and makes the rev check atomic. write_lock: asyncio.Lock = field(default_factory=asyncio.Lock) + # Collect files nothing references any more. Set during setup, which also + # runs it once and schedules it daily. Exposed so it can be invoked + # directly — a test that fakes a 24 h jump proves the timer fires, not that + # the work happens, and those are different claims. + sweep: Callable[[], Awaitable[None]] | None = None HouseplanConfigEntry = ConfigEntry[HouseplanData] diff --git a/tests_backend/test_ha_websocket.py b/tests_backend/test_ha_websocket.py index acc3c37..8c237be 100644 --- a/tests_backend/test_ha_websocket.py +++ b/tests_backend/test_ha_websocket.py @@ -938,14 +938,15 @@ async def test_startup_sweep_collects_what_no_commit_will( ), "the emptied staging folder goes with its last file" -async def test_daily_sweep_callback_collects_too( +async def test_periodic_sweep_collects_too( hass: HomeAssistant, hass_ws_client: WebSocketGenerator ) -> None: - """The interval path, exercised on its own by firing the scheduled callback.""" - import datetime as dt + """The scheduled pass, invoked directly rather than by faking a 24 h jump. - from homeassistant.util import dt as dt_util - from pytest_homeassistant_custom_component.common import async_fire_time_changed + Firing a time change proves the timer fires; awaiting the callback proves it + does the work. This asserts the second, which is the part that regressed. + """ + from custom_components.houseplan.store import get_data await _setup(hass) client = await hass_ws_client(hass) @@ -954,7 +955,9 @@ async def test_daily_sweep_callback_collects_too( await _seed_aged(hass, list(p.values())) - async_fire_time_changed(hass, dt_util.utcnow() + dt.timedelta(hours=25)) + data = get_data(hass) + assert data is not None and data.sweep is not None, "setup must publish the sweep" + await data.sweep() await hass.async_block_till_done() await _assert_swept(hass, p)