210 lines
6.6 KiB
Python
210 lines
6.6 KiB
Python
"""Tests for codex.provenance — @cite scan, code_links CRUD, bib export.
|
|
|
|
All DB access is mocked; no real PostgreSQL server required.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from contextlib import contextmanager
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from codex.models import CodeLink
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _make_conn_cm(mock_conn: MagicMock) -> Any:
|
|
"""Return a context-manager factory that yields *mock_conn*."""
|
|
|
|
@contextmanager # type: ignore[arg-type]
|
|
def _cm() -> Any:
|
|
yield mock_conn
|
|
|
|
return _cm
|
|
|
|
|
|
def _dict_row(**kwargs: Any) -> dict[str, Any]:
|
|
return dict(kwargs)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. test_scan_cite_tags
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_scan_cite_tags(tmp_path: Path) -> None:
|
|
"""A .cpp file with @cite produces correct dict entries."""
|
|
from codex.provenance import scan_cite_tags
|
|
|
|
src_file = tmp_path / "algo.cpp"
|
|
src_file.write_text(
|
|
"// Helper function\n"
|
|
"void compute_hodge() {\n"
|
|
" // @cite somepaper2023\n"
|
|
" do_stuff();\n"
|
|
"}\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
results = scan_cite_tags(str(tmp_path))
|
|
|
|
assert len(results) == 1
|
|
hit = results[0]
|
|
assert hit["bibkey"] == "somepaper2023"
|
|
assert hit["line"] == "3"
|
|
assert hit["file"] == "algo.cpp"
|
|
# symbol extracted from "compute_hodge()" on the line above
|
|
assert hit["symbol"] == "compute_hodge"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. test_scan_cite_tags_no_files
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_scan_cite_tags_no_files(tmp_path: Path) -> None:
|
|
"""Empty directory returns an empty list."""
|
|
from codex.provenance import scan_cite_tags
|
|
|
|
result = scan_cite_tags(str(tmp_path))
|
|
assert result == []
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. test_add_code_link
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_add_code_link() -> None:
|
|
"""Mock conn, verify INSERT is issued and returned CodeLink has id from RETURNING."""
|
|
from codex.provenance import add_code_link
|
|
|
|
now = datetime(2026, 6, 5, tzinfo=UTC)
|
|
mock_conn = MagicMock()
|
|
mock_conn.execute.return_value.fetchone.return_value = _dict_row(id=42, added_at=now)
|
|
|
|
with patch("codex.provenance.get_conn", side_effect=_make_conn_cm(mock_conn)):
|
|
link = add_code_link(
|
|
symbol="dec::hodge_star",
|
|
paper_id="2301.07041",
|
|
role="implements Thm 3.2",
|
|
note="core algorithm",
|
|
)
|
|
|
|
assert isinstance(link, CodeLink)
|
|
assert link.id == 42
|
|
assert link.added_at == now
|
|
assert link.symbol == "dec::hodge_star"
|
|
assert link.paper_id == "2301.07041"
|
|
assert link.role == "implements Thm 3.2"
|
|
assert link.note == "core algorithm"
|
|
|
|
call_args = mock_conn.execute.call_args
|
|
sql: str = call_args[0][0]
|
|
assert "INSERT INTO code_links" in sql
|
|
assert "RETURNING" in sql
|
|
|
|
mock_conn.commit.assert_called_once()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. test_list_code_links_all
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_list_code_links_all() -> None:
|
|
"""No filter → all rows returned as CodeLink list."""
|
|
from codex.provenance import list_code_links
|
|
|
|
now = datetime(2026, 6, 5, tzinfo=UTC)
|
|
mock_conn = MagicMock()
|
|
mock_conn.execute.return_value.fetchall.return_value = [
|
|
_dict_row(id=1, symbol="foo::bar", paper_id="W111", role="uses", note=None, added_at=now),
|
|
_dict_row(id=2, symbol="baz::qux", paper_id=None, role=None, note="ref", added_at=now),
|
|
]
|
|
|
|
with patch("codex.provenance.get_conn", side_effect=_make_conn_cm(mock_conn)):
|
|
links = list_code_links()
|
|
|
|
assert len(links) == 2
|
|
assert all(isinstance(lnk, CodeLink) for lnk in links)
|
|
assert links[0].symbol == "foo::bar"
|
|
assert links[0].id == 1
|
|
assert links[1].symbol == "baz::qux"
|
|
assert links[1].paper_id is None
|
|
|
|
# No WHERE clause in the SQL
|
|
call_args = mock_conn.execute.call_args
|
|
sql: str = call_args[0][0]
|
|
assert "WHERE" not in sql
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 5. test_list_code_links_filtered
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_list_code_links_filtered() -> None:
|
|
"""Filter by paper_id → WHERE clause applied and only matching rows returned."""
|
|
from codex.provenance import list_code_links
|
|
|
|
now = datetime(2026, 6, 5, tzinfo=UTC)
|
|
mock_conn = MagicMock()
|
|
mock_conn.execute.return_value.fetchall.return_value = [
|
|
_dict_row(
|
|
id=3,
|
|
symbol="some::func",
|
|
paper_id="2301.07041",
|
|
role=None,
|
|
note=None,
|
|
added_at=now,
|
|
),
|
|
]
|
|
|
|
with patch("codex.provenance.get_conn", side_effect=_make_conn_cm(mock_conn)):
|
|
links = list_code_links(paper_id="2301.07041")
|
|
|
|
assert len(links) == 1
|
|
assert links[0].paper_id == "2301.07041"
|
|
|
|
call_args = mock_conn.execute.call_args
|
|
sql: str = call_args[0][0]
|
|
params: dict[str, Any] = call_args[0][1]
|
|
assert "WHERE paper_id" in sql
|
|
assert params["paper_id"] == "2301.07041"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 6. test_export_bib_format
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_export_bib_format() -> None:
|
|
"""Mock returns one paper row; verify valid BibTeX string output."""
|
|
from codex.provenance import export_bib
|
|
|
|
mock_conn = MagicMock()
|
|
mock_conn.execute.return_value.fetchall.return_value = [
|
|
_dict_row(
|
|
id="2301.07041",
|
|
bibkey="smith2023conformal",
|
|
title="Conformal Approaches to Laplacian Eigenmaps",
|
|
authors=["Alice Smith", "Bob Jones"],
|
|
year=2023,
|
|
),
|
|
]
|
|
|
|
with patch("codex.provenance.get_conn", side_effect=_make_conn_cm(mock_conn)):
|
|
bib = export_bib()
|
|
|
|
assert "@article{smith2023conformal," in bib
|
|
assert "title = {Conformal Approaches to Laplacian Eigenmaps}" in bib
|
|
assert "author = {Alice Smith and Bob Jones}" in bib
|
|
assert "year = {2023}" in bib
|
|
assert "note = {2301.07041}" in bib
|