""" Tests for mempalace/retain_closets.py — 90-day closet retention enforcement. Refs: #1083, #1075 """ from __future__ import annotations import json import time from pathlib import Path import pytest from mempalace.retain_closets import ( RetentionResult, _file_age_days, enforce_retention, ) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _write_closet(directory: Path, name: str, age_days: float) -> Path: """Create a *.closet.json file with a mtime set to *age_days* ago.""" p = directory / name p.write_text(json.dumps({"drawers": [{"text": "summary", "closet": True}]})) # Set mtime to simulate age mtime = time.time() - age_days * 86400.0 import os os.utime(p, (mtime, mtime)) return p # --------------------------------------------------------------------------- # _file_age_days # --------------------------------------------------------------------------- def test_file_age_days_recent(tmp_path): p = tmp_path / "recent.closet.json" p.write_text("{}") age = _file_age_days(p) assert 0 <= age < 1 # just created def test_file_age_days_old(tmp_path): p = _write_closet(tmp_path, "old.closet.json", age_days=100) age = _file_age_days(p) assert 99 < age < 101 # --------------------------------------------------------------------------- # enforce_retention — dry_run # --------------------------------------------------------------------------- def test_dry_run_does_not_delete(tmp_path): old = _write_closet(tmp_path, "old.closet.json", age_days=100) _write_closet(tmp_path, "new.closet.json", age_days=10) result = enforce_retention(tmp_path, retention_days=90, dry_run=True) # File still exists after dry-run assert old.exists() assert result.removed == 1 # counted but not actually removed assert result.kept == 1 assert result.ok def test_dry_run_keeps_recent_files(tmp_path): _write_closet(tmp_path, "recent.closet.json", age_days=5) result = enforce_retention(tmp_path, retention_days=90, dry_run=True) assert result.removed == 0 assert result.kept == 1 # --------------------------------------------------------------------------- # enforce_retention — live mode # --------------------------------------------------------------------------- def test_live_removes_old_closets(tmp_path): old = _write_closet(tmp_path, "old.closet.json", age_days=100) new = _write_closet(tmp_path, "new.closet.json", age_days=10) result = enforce_retention(tmp_path, retention_days=90, dry_run=False) assert not old.exists() assert new.exists() assert result.removed == 1 assert result.kept == 1 assert result.ok def test_live_keeps_files_within_window(tmp_path): f = _write_closet(tmp_path, "edge.closet.json", age_days=89) result = enforce_retention(tmp_path, retention_days=90, dry_run=False) assert f.exists() assert result.removed == 0 assert result.kept == 1 def test_empty_directory_is_ok(tmp_path): result = enforce_retention(tmp_path, retention_days=90) assert result.scanned == 0 assert result.removed == 0 assert result.ok def test_subdirectory_closets_are_pruned(tmp_path): """enforce_retention should recurse into subdirs (wing directories).""" sub = tmp_path / "bezalel" sub.mkdir() old = _write_closet(sub, "hermes.closet.json", age_days=120) result = enforce_retention(tmp_path, retention_days=90, dry_run=False) assert not old.exists() assert result.removed == 1 def test_non_closet_files_ignored(tmp_path): """Non-closet files should not be counted or touched.""" (tmp_path / "readme.txt").write_text("hello") (tmp_path / "data.drawer.json").write_text("{}") result = enforce_retention(tmp_path, retention_days=90) assert result.scanned == 0 # --------------------------------------------------------------------------- # RetentionResult.ok # --------------------------------------------------------------------------- def test_retention_result_ok_with_no_errors(): r = RetentionResult(scanned=5, removed=2, kept=3) assert r.ok is True def test_retention_result_not_ok_with_errors(): r = RetentionResult(errors=["could not stat file"]) assert r.ok is False