feat(sources): OpenAlex, SemanticScholar, arXiv API clients
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
126
tests/sources/test_semanticscholar.py
Normal file
126
tests/sources/test_semanticscholar.py
Normal file
@@ -0,0 +1,126 @@
|
||||
"""Tests for codex.sources.semanticscholar."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from codex.models import Citation
|
||||
from codex.sources import semanticscholar
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_SAMPLE_REFS_RESPONSE = {
|
||||
"data": [
|
||||
{
|
||||
"citedPaper": {
|
||||
"paperId": "abc123",
|
||||
"externalIds": {"DOI": "10.1000/xyz1"},
|
||||
},
|
||||
"contexts": ["This approach was first described in [1]."],
|
||||
},
|
||||
{
|
||||
"citedPaper": {
|
||||
"paperId": "def456",
|
||||
"externalIds": {"ArXiv": "2301.07041"},
|
||||
},
|
||||
"contexts": [],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
_SAMPLE_RECS_RESPONSE = {
|
||||
"recommendedPapers": [
|
||||
{"paperId": "rec_id_1"},
|
||||
{"paperId": "rec_id_2"},
|
||||
{"paperId": "rec_id_3"},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# fetch_references
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_fetch_references_returns_citations(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""fetch_references maps API response to Citation objects correctly."""
|
||||
|
||||
def mock_get(url: str, params: dict[str, object] | None = None) -> httpx.Response:
|
||||
return httpx.Response(200, json=_SAMPLE_REFS_RESPONSE)
|
||||
|
||||
monkeypatch.setattr(semanticscholar, "_get", mock_get)
|
||||
|
||||
citations = semanticscholar.fetch_references("abc123")
|
||||
|
||||
assert len(citations) == 2
|
||||
assert all(isinstance(c, Citation) for c in citations)
|
||||
|
||||
# First citation uses DOI and has context
|
||||
assert citations[0].citing_id == "abc123"
|
||||
assert citations[0].cited_id == "10.1000/xyz1"
|
||||
assert citations[0].context == "This approach was first described in [1]."
|
||||
|
||||
# Second citation falls back to ArXiv ID, no context
|
||||
assert citations[1].citing_id == "abc123"
|
||||
assert citations[1].cited_id == "2301.07041"
|
||||
assert citations[1].context is None
|
||||
|
||||
|
||||
def test_fetch_references_404_returns_empty(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""A 404 response should return an empty list."""
|
||||
|
||||
def mock_get(url: str, params: dict[str, object] | None = None) -> httpx.Response:
|
||||
raise httpx.HTTPStatusError(
|
||||
"Not Found",
|
||||
request=httpx.Request("GET", url),
|
||||
response=httpx.Response(404, request=httpx.Request("GET", url)),
|
||||
)
|
||||
|
||||
monkeypatch.setattr(semanticscholar, "_get", mock_get)
|
||||
|
||||
result = semanticscholar.fetch_references("nonexistent")
|
||||
assert result == []
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# fetch_recommendations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_fetch_recommendations_returns_list_of_str(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""fetch_recommendations returns a flat list of paperId strings."""
|
||||
|
||||
def mock_get(url: str, params: dict[str, object] | None = None) -> httpx.Response:
|
||||
return httpx.Response(200, json=_SAMPLE_RECS_RESPONSE)
|
||||
|
||||
monkeypatch.setattr(semanticscholar, "_get", mock_get)
|
||||
|
||||
result = semanticscholar.fetch_recommendations("abc123", limit=3)
|
||||
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 3
|
||||
assert all(isinstance(r, str) for r in result)
|
||||
assert result == ["rec_id_1", "rec_id_2", "rec_id_3"]
|
||||
|
||||
|
||||
def test_fetch_recommendations_404_returns_empty(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""A 404 response should return an empty list."""
|
||||
|
||||
def mock_get(url: str, params: dict[str, object] | None = None) -> httpx.Response:
|
||||
raise httpx.HTTPStatusError(
|
||||
"Not Found",
|
||||
request=httpx.Request("GET", url),
|
||||
response=httpx.Response(404, request=httpx.Request("GET", url)),
|
||||
)
|
||||
|
||||
monkeypatch.setattr(semanticscholar, "_get", mock_get)
|
||||
|
||||
result = semanticscholar.fetch_recommendations("nonexistent")
|
||||
assert result == []
|
||||
Reference in New Issue
Block a user