Intermediate commit
This commit is contained in:
parent
0e9e49df16
commit
3d4cfeff1a
65 changed files with 5507 additions and 333 deletions
|
|
@ -1,6 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db.models import Publication, PublicationIdentifier
|
||||
from app.services.domains.arxiv import application as arxiv_service
|
||||
from app.services.domains.publication_identifiers import application as identifier_service
|
||||
from app.services.domains.publication_identifiers.types import IdentifierKind
|
||||
from app.services.domains.publications.types import UnreadPublicationItem
|
||||
|
||||
|
||||
def test_derive_display_identifier_prefers_doi_over_arxiv() -> None:
|
||||
|
|
@ -40,7 +48,7 @@ def test_derive_display_identifier_uses_pmcid_when_present() -> None:
|
|||
|
||||
def test_normalize_arxiv_id_handles_urls() -> None:
|
||||
from app.services.domains.publication_identifiers.normalize import normalize_arxiv_id
|
||||
|
||||
|
||||
assert normalize_arxiv_id("https://arxiv.org/abs/1504.08025") == "1504.08025"
|
||||
assert normalize_arxiv_id("http://arxiv.org/pdf/1504.08025v2.pdf") == "1504.08025v2"
|
||||
assert normalize_arxiv_id("https://arxiv.org/html/1504.08025v2") == "1504.08025v2"
|
||||
|
|
@ -52,7 +60,8 @@ def test_normalize_arxiv_id_handles_urls() -> None:
|
|||
|
||||
def test_normalize_arxiv_id_handles_raw_text() -> None:
|
||||
from app.services.domains.publication_identifiers.normalize import normalize_arxiv_id
|
||||
|
||||
|
||||
assert normalize_arxiv_id("1504.08025v1") == "1504.08025v1"
|
||||
assert normalize_arxiv_id("arXiv:1504.08025") == "1504.08025"
|
||||
assert normalize_arxiv_id("arxiv: 1504.08025v1") == "1504.08025v1"
|
||||
assert normalize_arxiv_id("Preprint at arXiv:math/9901123v2") == "math/9901123v2"
|
||||
|
|
@ -61,7 +70,7 @@ def test_normalize_arxiv_id_handles_raw_text() -> None:
|
|||
|
||||
def test_normalize_pmcid_handles_urls_and_text() -> None:
|
||||
from app.services.domains.publication_identifiers.normalize import normalize_pmcid
|
||||
|
||||
|
||||
assert normalize_pmcid("https://pmc.ncbi.nlm.nih.gov/articles/PMC2175868/") == "PMC2175868"
|
||||
assert normalize_pmcid("http://www.ncbi.nlm.nih.gov/pmc/articles/PMC1234567") == "PMC1234567"
|
||||
assert normalize_pmcid("PMCID: PMC1234567") == "PMC1234567"
|
||||
|
|
@ -71,15 +80,140 @@ def test_normalize_pmcid_handles_urls_and_text() -> None:
|
|||
|
||||
def test_normalize_pmid_handles_urls() -> None:
|
||||
from app.services.domains.publication_identifiers.normalize import normalize_pmid
|
||||
|
||||
|
||||
assert normalize_pmid("https://pubmed.ncbi.nlm.nih.gov/12345678/") == "12345678"
|
||||
assert normalize_pmid("http://pubmed.ncbi.nlm.nih.gov/12345678") == "12345678"
|
||||
assert normalize_pmid("https://pubmed.ncbi.nlm.nih.gov/not-a-pmid/") is None
|
||||
|
||||
|
||||
import pytest
|
||||
from app.services.domains.arxiv import application as arxiv_service
|
||||
from app.services.domains.publications.types import UnreadPublicationItem
|
||||
def _publication(*, title: str, pub_url: str | None = None) -> Publication:
|
||||
return Publication(
|
||||
cluster_id=None,
|
||||
fingerprint_sha256="f" * 64,
|
||||
title_raw=title,
|
||||
title_normalized=title.lower(),
|
||||
year=2024,
|
||||
citation_count=0,
|
||||
author_text=None,
|
||||
venue_text=None,
|
||||
pub_url=pub_url,
|
||||
pdf_url=None,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_discover_and_sync_skips_arxiv_when_crossref_finds_doi(
|
||||
db_session: AsyncSession,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
publication = _publication(title="Reliable Paper Title for DOI discovery")
|
||||
db_session.add(publication)
|
||||
await db_session.flush()
|
||||
|
||||
async def _fake_crossref(*, item):
|
||||
return "10.1000/strong-doi"
|
||||
|
||||
async def _fail_arxiv(*, item, request_email=None, timeout_seconds=None):
|
||||
raise AssertionError("arXiv should be skipped after strong DOI evidence.")
|
||||
|
||||
monkeypatch.setattr(
|
||||
"app.services.domains.crossref.application.discover_doi_for_publication",
|
||||
_fake_crossref,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.services.domains.arxiv.application.discover_arxiv_id_for_publication",
|
||||
_fail_arxiv,
|
||||
)
|
||||
|
||||
await identifier_service.discover_and_sync_identifiers_for_publication(
|
||||
db_session,
|
||||
publication=publication,
|
||||
scholar_label="Ada Lovelace",
|
||||
)
|
||||
|
||||
result = await db_session.execute(
|
||||
select(PublicationIdentifier).where(
|
||||
PublicationIdentifier.publication_id == int(publication.id),
|
||||
PublicationIdentifier.kind == IdentifierKind.DOI.value,
|
||||
)
|
||||
)
|
||||
assert result.scalar_one_or_none() is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_discover_and_sync_skips_arxiv_for_low_quality_title(
|
||||
db_session: AsyncSession,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
publication = _publication(title="AI 2024")
|
||||
db_session.add(publication)
|
||||
await db_session.flush()
|
||||
calls = {"count": 0}
|
||||
|
||||
async def _fake_crossref(*, item):
|
||||
return None
|
||||
|
||||
async def _fake_arxiv(*, item, request_email=None, timeout_seconds=None):
|
||||
calls["count"] += 1
|
||||
return "1234.5678"
|
||||
|
||||
monkeypatch.setattr(
|
||||
"app.services.domains.crossref.application.discover_doi_for_publication",
|
||||
_fake_crossref,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.services.domains.arxiv.application.discover_arxiv_id_for_publication",
|
||||
_fake_arxiv,
|
||||
)
|
||||
|
||||
await identifier_service.discover_and_sync_identifiers_for_publication(
|
||||
db_session,
|
||||
publication=publication,
|
||||
scholar_label="Alan Turing",
|
||||
)
|
||||
assert calls["count"] == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_discover_and_sync_calls_arxiv_when_item_is_eligible(
|
||||
db_session: AsyncSession,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
publication = _publication(title="Neural Representation Learning for Graph Signals")
|
||||
db_session.add(publication)
|
||||
await db_session.flush()
|
||||
|
||||
async def _fake_crossref(*, item):
|
||||
return None
|
||||
|
||||
async def _fake_arxiv(*, item, request_email=None, timeout_seconds=None):
|
||||
return "2501.00001v2"
|
||||
|
||||
monkeypatch.setattr(
|
||||
"app.services.domains.crossref.application.discover_doi_for_publication",
|
||||
_fake_crossref,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.services.domains.arxiv.application.discover_arxiv_id_for_publication",
|
||||
_fake_arxiv,
|
||||
)
|
||||
|
||||
await identifier_service.discover_and_sync_identifiers_for_publication(
|
||||
db_session,
|
||||
publication=publication,
|
||||
scholar_label="Grace Hopper",
|
||||
)
|
||||
|
||||
result = await db_session.execute(
|
||||
select(PublicationIdentifier).where(
|
||||
PublicationIdentifier.publication_id == int(publication.id),
|
||||
PublicationIdentifier.kind == IdentifierKind.ARXIV.value,
|
||||
)
|
||||
)
|
||||
identifier = result.scalar_one_or_none()
|
||||
assert identifier is not None
|
||||
assert identifier.value_normalized == "2501.00001v2"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_discover_arxiv_id_returns_none_if_no_title() -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue