diff --git a/tests/test_ocr_phase41_cleanup.py b/tests/test_ocr_phase41_cleanup.py new file mode 100644 index 00000000..9c0e07fd --- /dev/null +++ b/tests/test_ocr_phase41_cleanup.py @@ -0,0 +1,45 @@ +"""Contract tests for OCR Phase 4.1 runtime cleanup semantics.""" + +from __future__ import annotations + + +def test_derived_rebuild_does_not_clear_raw_upgradable() -> None: + from paperforge.worker.ocr_rebuild import _apply_post_rebuild_version_flags + + meta = { + "derived_stale": True, + "raw_upgradable": True, + } + + updated = _apply_post_rebuild_version_flags(meta) + + assert updated["derived_stale"] is False + assert updated["raw_upgradable"] is True + + +def test_sync_runtime_summary_can_schedule_derived_rebuild_without_inline_execution() -> None: + from paperforge.services.sync_service import summarize_ocr_runtime_followups + + summary = summarize_ocr_runtime_followups( + papers=[ + {"zotero_key": "A", "derived_stale": True, "raw_upgradable": False}, + {"zotero_key": "B", "derived_stale": False, "raw_upgradable": True}, + ] + ) + + assert summary["derived_rebuild_count"] == 1 + assert summary["derived_rebuild_mode"] in {"deferred", "queued", "best_effort"} + + +def test_dirty_runtime_files_suppress_auto_derived_rebuild() -> None: + from paperforge.services.sync_service import summarize_ocr_runtime_followups + + with_dirty = summarize_ocr_runtime_followups( + papers=[ + {"zotero_key": "A", "derived_stale": True, "raw_upgradable": False}, + ], + dirty_runtime_files=True, + ) + + assert with_dirty["derived_rebuild_mode"] == "suppressed_dirty_runtime" + assert with_dirty["suppressed_keys"] == ["A"] diff --git a/tests/test_ocr_rebuild.py b/tests/test_ocr_rebuild.py index 050158bf..1db4ab91 100644 --- a/tests/test_ocr_rebuild.py +++ b/tests/test_ocr_rebuild.py @@ -17,3 +17,16 @@ def test_rebuild_selector_only_targets_derived_stale_papers() -> None: selected = select_papers_for_derived_rebuild(papers) assert selected == ["A"] + + +def test_derived_rebuild_excludes_raw_upgradable_papers() -> None: + from paperforge.worker.ocr_rebuild import select_papers_for_derived_rebuild + + papers = [ + {"zotero_key": "A", "derived_stale": True, "raw_upgradable": False}, + {"zotero_key": "B", "derived_stale": True, "raw_upgradable": True}, + ] + + selected = select_papers_for_derived_rebuild(papers) + + assert selected == ["A"] diff --git a/tests/test_sync_service_ocr_versions.py b/tests/test_sync_service_ocr_versions.py index 3dc63dbd..39b23023 100644 --- a/tests/test_sync_service_ocr_versions.py +++ b/tests/test_sync_service_ocr_versions.py @@ -13,3 +13,31 @@ def test_sync_detects_derived_drift_without_failing_sync(tmp_path) -> None: assert summary["derived_rebuild_count"] == 1 assert summary["raw_upgrade_count"] == 1 + + +def test_sync_runtime_summary_can_schedule_derived_rebuild_without_inline_execution() -> None: + from paperforge.services.sync_service import summarize_ocr_runtime_followups + + summary = summarize_ocr_runtime_followups( + papers=[ + {"zotero_key": "A", "derived_stale": True, "raw_upgradable": False}, + {"zotero_key": "B", "derived_stale": False, "raw_upgradable": True}, + ] + ) + + assert summary["derived_rebuild_count"] == 1 + assert summary["derived_rebuild_mode"] in {"deferred", "queued", "best_effort"} + + +def test_dirty_runtime_files_suppress_auto_derived_rebuild() -> None: + from paperforge.services.sync_service import summarize_ocr_runtime_followups + + with_dirty = summarize_ocr_runtime_followups( + papers=[ + {"zotero_key": "A", "derived_stale": True, "raw_upgradable": False}, + ], + dirty_runtime_files=True, + ) + + assert with_dirty["derived_rebuild_mode"] == "suppressed_dirty_runtime" + assert with_dirty["suppressed_keys"] == ["A"]