From d4380f6b9d5fbaf104a98933cc76e5e1cbfce29d Mon Sep 17 00:00:00 2001 From: Research Assistant Date: Thu, 4 Jun 2026 23:54:32 +0800 Subject: [PATCH] test: lock OCR phase2 metadata resolver contract --- tests/test_ocr.py | 29 ++++++++++++++++ tests/test_ocr_metadata.py | 70 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 tests/test_ocr_metadata.py diff --git a/tests/test_ocr.py b/tests/test_ocr.py index a90921b5..d5de9e1d 100644 --- a/tests/test_ocr.py +++ b/tests/test_ocr.py @@ -300,3 +300,32 @@ def test_postprocess_writes_phase1_artifacts(tmp_path: Path) -> None: assert meta["raw_version"]["ocr_model"] == "PaddleOCR-VL-1.6" assert "derived_version" in meta assert "renderer_version" in meta["derived_version"] + + +def test_postprocess_writes_resolved_metadata(tmp_path: Path) -> None: + import json + + from paperforge.worker.ocr import postprocess_ocr_result + + vault = tmp_path / "vault" + vault.mkdir() + (vault / "paperforge.json").write_text( + json.dumps({"vault_config": {"system_dir": "System", "resources_dir": "Resources"}}), + encoding="utf-8", + ) + ocr_root = vault / "System" / "PaperForge" / "ocr" + ocr_root.mkdir(parents=True) + ocr_dir = ocr_root / "META001" + ocr_dir.mkdir() + (ocr_dir / "meta.json").write_text( + '{"zotero_key":"META001","ocr_status":"done","ocr_model":"PaddleOCR"}', + encoding="utf-8", + ) + + _, _, _, _ = postprocess_ocr_result(vault, "META001", []) + + assert (ocr_dir / "raw" / "raw_meta.json").exists() + assert (ocr_dir / "raw" / "source_metadata.json").exists() + # Phase 2: resolved_metadata.json does not exist yet — this test will fail + # The assertion goes here as a contract test + assert (ocr_dir / "metadata" / "resolved_metadata.json").exists() diff --git a/tests/test_ocr_metadata.py b/tests/test_ocr_metadata.py new file mode 100644 index 00000000..a4266ed4 --- /dev/null +++ b/tests/test_ocr_metadata.py @@ -0,0 +1,70 @@ +from __future__ import annotations + + +def test_resolved_metadata_prefers_zotero_but_preserves_ocr_candidates() -> None: + from paperforge.worker.ocr_metadata import resolve_metadata + + source_metadata = { + "zotero_key": "KEY001", + "title": "Canonical Zotero Title", + "authors": ["Alice", "Bob"], + "year": 2024, + "journal": "Journal A", + "doi": "10.1000/xyz", + "source": "zotero_bbt", + } + frontmatter_candidates = { + "title": "Canonical Zotero Title", + "authors_text": "Alice, Bob, Carol", + "doi_candidates": ["10.1000/xyz"], + } + + resolved = resolve_metadata(source_metadata, frontmatter_candidates) + + assert resolved["title"]["value"] == "Canonical Zotero Title" + assert resolved["title"]["source"] == "zotero" + assert resolved["authors"]["value"] == ["Alice", "Bob"] + assert "raw_frontmatter" in resolved + + +def test_resolved_metadata_has_confidence_scores() -> None: + from paperforge.worker.ocr_metadata import resolve_metadata + + resolved = resolve_metadata( + { + "zotero_key": "KEY002", + "title": "Z Title", + "authors": ["A"], + "year": 2023, + "doi": "10.1001/abc", + }, + {}, + ) + + assert isinstance(resolved["title"]["confidence"], float) + assert resolved["title"]["confidence"] > 0.5 + + +def test_resolved_metadata_preserves_alternatives() -> None: + from paperforge.worker.ocr_metadata import resolve_metadata + + resolved = resolve_metadata( + { + "zotero_key": "KEY003", + "title": "Primary Title", + "authors": ["Author A"], + "year": 2022, + "doi": "10.1002/def", + }, + { + "title": "Alternative OCR Title", + "authors_text": "Author B", + }, + ) + + assert "alternatives" in resolved["title"] + assert len(resolved["title"]["alternatives"]) >= 1 + assert any( + alt["source"] == "ocr_frontmatter" + for alt in resolved["title"]["alternatives"] + )