mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 16:38:31 +00:00
fix: the guard has to be per-case, not blanket
Protecting every file of a live space also protected the ones a commit had just superseded, and gave rejected uploads immortality. The distinction that matters is narrower: a space with NO plan_url has had its image detached and may want it back; a space that has one can only be holding its own rejects. Attachments: staging folders keep the hour, marker folders get the month. Also: the layout event test asserted the order of separately fired bus events, which nothing promises — it came back [2,1,3] in CI.
This commit is contained in:
@@ -363,6 +363,7 @@ async def test_abandoned_uploads_are_collected_once_old(
|
||||
_url, orphan = await _upload(client, "r3", b"abandoned")
|
||||
old = time.time() - PLAN_ORPHAN_TTL_S - 60
|
||||
os.utime(plans / orphan, (old, old))
|
||||
# r3 still HAS a plan, so this really is a rejected upload — collectable
|
||||
|
||||
ok = await _save(client, await _cfg([{"id": "r3", "plan_url": url0}]), rev)
|
||||
assert ok["success"]
|
||||
@@ -609,7 +610,8 @@ async def test_layout_keeps_its_revision_and_announces_changes(
|
||||
assert not bad["success"] and bad["error"]["code"] == "conflict"
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert [e["rev"] for e in events] == [1, 2, 3]
|
||||
# the bus does not promise ordering between separately fired events
|
||||
assert sorted(e["rev"] for e in events) == [1, 2, 3]
|
||||
|
||||
|
||||
async def test_uploaded_svg_is_sandboxed_and_a_pdf_is_not(
|
||||
@@ -862,7 +864,7 @@ async def _seed_aged(hass, names) -> None:
|
||||
|
||||
from custom_components.houseplan.const import SCHEDULED_GRACE_S
|
||||
|
||||
old = time.time() - SCHEDULED_GRACE_S - 60
|
||||
old = time.time() - SCHEDULED_GRACE_S - 60 # past every grace
|
||||
|
||||
def _do() -> None:
|
||||
for path in names:
|
||||
|
||||
@@ -589,26 +589,34 @@ def test_scheduled_collection_never_takes_a_detached_plan(tmp_path):
|
||||
assert (d / "f2.tok.png").is_file(), "and still touches nothing else of a live space"
|
||||
|
||||
|
||||
def test_scheduled_collection_keeps_a_live_marker_s_files(tmp_path):
|
||||
def test_attachment_grace_is_a_month_outside_a_staging_folder(tmp_path):
|
||||
"""A staging folder is unambiguous; a marker folder is not.
|
||||
|
||||
`up_*` only ever holds an upload from a dialog that was never saved, so an
|
||||
hour is right there. A marker's own folder may hold a file that is merely
|
||||
unreferenced at the moment, and one hour of that turned out to be a way to
|
||||
lose data (2026-07-28).
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
|
||||
files = tmp_path / "files"
|
||||
(files / "m1").mkdir(parents=True)
|
||||
(files / "up_abandoned").mkdir(parents=True)
|
||||
(files / "dead").mkdir(parents=True)
|
||||
old = time.time() - const.SCHEDULED_GRACE_S - 60
|
||||
for p in ((files / "m1" / "unlinked.pdf"), (files / "dead" / "x.pdf")):
|
||||
p.write_bytes(b"x")
|
||||
os.utime(p, (old, old))
|
||||
staging = files / "up_abandoned" / "manual.pdf"
|
||||
staging.write_bytes(b"x")
|
||||
hour_ago = time.time() - const.PLAN_ORPHAN_TTL_S - 60
|
||||
os.utime(staging, (hour_ago, hour_ago))
|
||||
month_ago = time.time() - const.SCHEDULED_GRACE_S - 60
|
||||
|
||||
for path, when in (
|
||||
((files / "m1" / "recent.pdf"), hour_ago),
|
||||
((files / "m1" / "ancient.pdf"), month_ago),
|
||||
((files / "up_abandoned" / "manual.pdf"), hour_ago),
|
||||
):
|
||||
path.write_bytes(b"x")
|
||||
os.utime(path, (when, when))
|
||||
|
||||
cfg = {"markers": [{"id": "m1", "pdfs": []}]}
|
||||
removed = plans.collect_attachments(files, cfg, cfg)
|
||||
assert (files / "m1" / "unlinked.pdf").is_file(), "the marker exists; leave its files alone"
|
||||
assert not staging.exists(), "a cancelled dialog is still collected after an hour"
|
||||
assert not (files / "dead" / "x.pdf").exists(), "a marker that is gone ages out"
|
||||
assert (files / "m1" / "recent.pdf").is_file(), "an hour is not enough for a marker file"
|
||||
assert not (files / "m1" / "ancient.pdf").exists(), "a month is"
|
||||
assert not (files / "up_abandoned").exists(), "a cancelled dialog still goes after an hour"
|
||||
assert removed == 2
|
||||
|
||||
Reference in New Issue
Block a user