from unittest.mock import MagicMock from sync.anthology_source import iter_papers def _make_fake_paper(full_id, title, abstract, authors, venue, year, url, bibtex="", pdf_url=""): parent = MagicMock() parent.venue_acronym = venue pdf = MagicMock() pdf.url = pdf_url paper = MagicMock() paper.full_id = full_id paper.parent = parent paper.title = title paper.abstract = abstract paper.authors = authors paper.year = year paper.web_url = url paper.to_bibtex.return_value = bibtex paper.pdf = pdf return paper class FakeMarkupText: """Stand-in for acl_anthology.text.MarkupText, which exposes .as_text().""" def __init__(self, text): self._text = text def as_text(self): return self._text class FakeName: """Stand-in for acl_anthology.people.Name, exposing .as_first_last().""" def __init__(self, first, last): self.first = first self.last = last def as_first_last(self): if not self.first: return self.last return f"{self.first} {self.last}" class FakeNameSpecification: """Stand-in for acl_anthology.people.NameSpecification, exposing .name.""" def __init__(self, name): self.name = name def test_iter_papers_maps_fields(monkeypatch): fake_anthology = MagicMock() fake_anthology.papers.return_value = [ _make_fake_paper("2023.acl-long.1", "Title One", "Abstract one", ["Alice", "Bob"], "ACL", "2023", "http://x/p1", "@inproceedings{one,\n title = \"Title One\"}", "http://x/p1.pdf"), _make_fake_paper("2022.emnlp-main.2", "Title Two", None, [], "EMNLP", "2022", "http://x/p2"), ] monkeypatch.setattr("sync.anthology_source.Anthology", lambda datadir: fake_anthology) results = list(iter_papers("/fake/repo")) assert results[0] == { "id": "2023.acl-long.1", "title": "Title One", "abstract": "Abstract one", "authors": "Alice, Bob", "venue": "ACL", "year": 2023, "url": "http://x/p1", "bibtex": "@inproceedings{one,\n title = \"Title One\"}", "pdf_url": "http://x/p1.pdf", } assert results[1]["abstract"] == "" assert results[1]["authors"] == "" assert results[1]["year"] == 2022 assert results[1]["bibtex"] == "" assert results[1]["pdf_url"] == "" def test_iter_papers_handles_real_markup_text_and_name_objects(monkeypatch): """The real acl_anthology package (verified against installed 1.2.0) has Paper.title/abstract as MarkupText objects (.as_text()) and Paper.authors as a tuple of NameSpecification objects exposing `.name.as_first_last()`. """ fake_anthology = MagicMock() fake_anthology.papers.return_value = [ _make_fake_paper( "2024.naacl-long.3", FakeMarkupText("Real Title"), FakeMarkupText("Real abstract text"), [FakeNameSpecification(FakeName("Alice", "Smith")), FakeNameSpecification(FakeName(None, "Cher"))], "NAACL", "2024", "http://x/p3", "@inproceedings{smith-2024-real}", "http://x/p3.pdf", ), ] monkeypatch.setattr("sync.anthology_source.Anthology", lambda datadir: fake_anthology) results = list(iter_papers("/fake/repo")) assert results[0] == { "id": "2024.naacl-long.3", "title": "Real Title", "abstract": "Real abstract text", "authors": "Alice Smith, Cher", "venue": "NAACL", "year": 2024, "url": "http://x/p3", "bibtex": "@inproceedings{smith-2024-real}", "pdf_url": "http://x/p3.pdf", } def test_iter_papers_uses_datadir_subdirectory_of_local_checkout(monkeypatch): """anthology_path is the root of a git clone; the library expects the nested data/ directory, not the checkout root itself, and must not treat anthology_path as a repo URL to re-clone.""" captured = {} def fake_anthology_ctor(datadir): captured["datadir"] = datadir anthology = MagicMock() anthology.papers.return_value = [] return anthology monkeypatch.setattr("sync.anthology_source.Anthology", fake_anthology_ctor) list(iter_papers("/fake/checkout")) assert captured["datadir"] == "/fake/checkout/data" def test_iter_papers_bibtex_falls_back_on_error(monkeypatch): """Papers whose bibkey is NO_BIBKEY (e.g. some frontmatter) raise ValueError from to_bibtex(); the sync must yield bibtex="" for those rather than crash.""" fake_anthology = MagicMock() paper = _make_fake_paper("2023.acl-frontmatter.1", "Preface", "", [], "ACL", "2023", "http://x/pf") paper.to_bibtex.side_effect = ValueError("Cannot generate BibTeX entry without bibkey") fake_anthology.papers.return_value = [paper] monkeypatch.setattr("sync.anthology_source.Anthology", lambda datadir: fake_anthology) results = list(iter_papers("/fake/repo")) assert results[0]["bibtex"] == "" def test_iter_papers_pdf_url_falls_back_when_no_pdf(monkeypatch): """Frontmatter and some older papers have no PDF reference (Paper.pdf is None); the sync must yield pdf_url="" for those rather than crash.""" fake_anthology = MagicMock() paper = _make_fake_paper("2023.acl-frontmatter.1", "Preface", "", [], "ACL", "2023", "http://x/pf") paper.pdf = None fake_anthology.papers.return_value = [paper] monkeypatch.setattr("sync.anthology_source.Anthology", lambda datadir: fake_anthology) results = list(iter_papers("/fake/repo")) assert results[0]["pdf_url"] == ""