| 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"] == "" |
|
|