fix(prune): protect dir iteration, add OSError handling, improve test coverage

This commit is contained in:
Research Assistant 2026-05-18 21:53:14 +08:00
parent f10d36d205
commit 20ff03ed8e
2 changed files with 29 additions and 4 deletions

View file

@ -18,7 +18,12 @@ def _collect_orphan_candidates(lit_dir: Path, fresh_keys: set[str]) -> list[dict
for domain_dir in sorted(lit_dir.iterdir()):
if not domain_dir.is_dir():
continue
for sub in sorted(domain_dir.iterdir()):
try:
entries = sorted(domain_dir.iterdir())
except OSError as exc:
logger.warning("prune: cannot scan %s: %s", domain_dir, exc)
continue
for sub in entries:
if not sub.is_dir():
continue
parts = sub.name.split(" - ", 1)
@ -89,12 +94,14 @@ def prune_orphan_papers(
ocr = c["ocr_dir"]
if ocr and ocr.exists():
shutil.rmtree(ocr, ignore_errors=True)
counts["ocr"] += 1
if not ocr.exists():
counts["ocr"] += 1
ws = c["workspace_dir"]
if ws.exists():
shutil.rmtree(ws, ignore_errors=True)
counts["workspace"] += 1
if not ws.exists():
counts["workspace"] += 1
try:
n = delete_paper_vectors(vault, key)

View file

@ -8,6 +8,7 @@ import pytest
from paperforge.worker.prune import (
_collect_orphan_candidates,
_resolve_ocr_dir,
prune_orphan_papers,
)
@ -65,6 +66,14 @@ class TestCollectOrphanCandidates:
assert returned_keys == {"key2", "key3"}
class TestResolveOcrDir:
"""_resolve_ocr_dir(vault, key)"""
def test_resolves_ocr_dir_name_is_key(self, tmp_path: Path) -> None:
result = _resolve_ocr_dir(tmp_path, "testkey123")
assert result.name == "testkey123"
class TestPruneOrphanPapers:
"""prune_orphan_papers(vault, fresh_index, dry_run)"""
@ -124,7 +133,16 @@ class TestPruneOrphanPapers:
prune_orphan_papers(tmp_path, fresh_index=fresh_index, dry_run=True)
assert calls == []
def test_orphan_not_in_fresh_index_is_skipped(self, tmp_path: Path) -> None:
def test_empty_fresh_index_does_not_crash(self, tmp_path: Path) -> None:
result = prune_orphan_papers(tmp_path, fresh_index={}, dry_run=True)
assert result["preview"] == []
def test_handles_missing_lit_dir(self, tmp_path: Path) -> None:
no_lit = tmp_path / "NoLiterature"
result = prune_orphan_papers(no_lit, fresh_index={"schema_version": "3", "items": []}, dry_run=False)
assert result["deleted"] == []
def test_active_papers_are_not_deleted(self, tmp_path: Path) -> None:
lit = tmp_path / "Resources" / "Literature" / "CS"
ws = lit / "key1 - Active Paper"
ws.mkdir(parents=True)