"""Tests for scripts.ra_grobid_backfill — the R-A GROBID references backfill. Only the pure, offline logic is covered (id normalization + Citation assembly); ``extract_references`` is mocked so no GROBID server or DB is required. """ from __future__ import annotations from unittest.mock import patch from codex.models import Citation from scripts.ra_grobid_backfill import _normalize_cited_id, build_grobid_citations # --------------------------------------------------------------------------- # _normalize_cited_id # --------------------------------------------------------------------------- def test_normalize_doi_url_to_bare_lowercase() -> None: assert _normalize_cited_id(doi="https://doi.org/10.1007/S00454-019-00132-8") == ( "10.1007/s00454-019-00132-8" ) def test_normalize_doi_prefixes() -> None: assert _normalize_cited_id(doi="http://doi.org/10.1/X") == "10.1/x" assert _normalize_cited_id(doi="doi:10.1/X") == "10.1/x" assert _normalize_cited_id(doi="10.1/x") == "10.1/x" def test_normalize_arxiv_prefix_stripped() -> None: assert _normalize_cited_id(arxiv_id="arXiv:2305.10988") == "2305.10988" assert _normalize_cited_id(arxiv_id="2305.10988") == "2305.10988" # Legacy ids keep their slash. assert _normalize_cited_id(arxiv_id="math/0603097") == "math/0603097" def test_normalize_doi_takes_precedence_over_arxiv() -> None: assert _normalize_cited_id(doi="10.5/y", arxiv_id="2305.10988") == "10.5/y" def test_normalize_empty_returns_none() -> None: assert _normalize_cited_id() is None assert _normalize_cited_id(doi=" ", arxiv_id="") is None # --------------------------------------------------------------------------- # build_grobid_citations # --------------------------------------------------------------------------- def _ref(doi: str = "", arxiv_id: str = "") -> dict[str, str]: return {"title": "t", "authors": "a", "year": "2020", "doi": doi, "arxiv_id": arxiv_id} def test_build_citations_normalizes_dedups_and_drops_self() -> None: paper_id = "10.1007/s00454-019-00132-8" refs = [ _ref(doi="https://doi.org/10.1/A"), # → 10.1/a _ref(doi="DOI:10.1/a"), # duplicate of the above after normalization _ref(arxiv_id="arXiv:2305.10988"), # → 2305.10988 _ref(doi="", arxiv_id=""), # no id → skipped _ref(doi="https://doi.org/10.1007/S00454-019-00132-8"), # self-cite → dropped ] with patch("scripts.ra_grobid_backfill.extract_references", return_value=refs) as mock_ex: cits = build_grobid_citations(paper_id, "some.pdf", grobid_url="http://grobid") mock_ex.assert_called_once_with("some.pdf", grobid_url="http://grobid", timeout=60.0) assert all(isinstance(c, Citation) for c in cits) assert all(c.citing_id == paper_id for c in cits) assert [c.cited_id for c in cits] == ["10.1/a", "2305.10988"] def test_build_citations_empty_when_no_refs() -> None: with patch("scripts.ra_grobid_backfill.extract_references", return_value=[]): assert build_grobid_citations("10.1/x", "x.pdf") == []