"""Shared synthesis fixtures — keep DB/LLM/embed fully mocked.""" from __future__ import annotations from pathlib import Path from typing import Any from unittest.mock import MagicMock import pytest # Shared corpus fixture used across the test suite. FIXTURE_CHUNKS: list[dict[str, Any]] = [ { "id": 1, "paper_id": "springborn-2008", "ord": 16, "content": ( "For an ideal tetrahedron with dihedral angles the hyperbolic volume is " "V = L(gamma1) + L(gamma2) + L(gamma3). " "The volume function V0 is strictly concave on the angle domain." ), "bibkey": "Springborn2008", }, { "id": 2, "paper_id": "bps-2015", "ord": 3, "content": ( "A discrete conformal map is defined by logarithmic scale factors u_i " "at each vertex such that the edge lengths satisfy a compatibility condition." ), "bibkey": "BobenkoPinkallSpringborn2015", }, { "id": 3, "paper_id": "luo-2004", "ord": 0, "content": ( "The combinatorial Yamabe flow drives the edge lengths towards a target " "curvature on a triangulated surface." ), "bibkey": "Luo2004", }, ] class StubLLM: """Deterministic mock LLM that returns a preset response.""" def __init__(self, response: str | list[str]) -> None: if isinstance(response, str): self._responses = [response] else: self._responses = list(response) self._calls: list[tuple[str, str]] = [] def generate(self, prompt: str, model: str) -> str: self._calls.append((prompt, model)) if not self._responses: return "" if len(self._responses) == 1: return self._responses[0] return self._responses.pop(0) @property def calls(self) -> list[tuple[str, str]]: return list(self._calls) @pytest.fixture def stub_llm() -> StubLLM: """Default stub returning empty string (override per test as needed).""" return StubLLM("") @pytest.fixture def fixture_chunks() -> list[dict[str, Any]]: return [dict(c) for c in FIXTURE_CHUNKS] @pytest.fixture def mock_retrieve(monkeypatch: pytest.MonkeyPatch) -> None: """Patch _retrieve_chunks to return deterministic fixture chunks (no DB).""" monkeypatch.setattr( "codex.synthesis._retrieve_chunks", lambda queries, top_k: [dict(c) for c in FIXTURE_CHUNKS], ) @pytest.fixture def mock_paper_titles(monkeypatch: pytest.MonkeyPatch) -> None: """Patch _list_paper_titles to return a deterministic three-paper corpus.""" monkeypatch.setattr( "codex.synthesis._list_paper_titles", lambda db_conn: [ ("Springborn2008", "A variational principle for weighted Delaunay triangulations"), ( "BobenkoPinkallSpringborn2015", "Discrete conformal maps and ideal hyperbolic polyhedra", ), ("Luo2004", "Combinatorial Yamabe Flow on Surfaces"), ], ) @pytest.fixture def mock_settings(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Path: """Settings stub pointing leads_dir at tmp_path/leads.""" from codex.config import Settings leads_path = tmp_path / "leads" mock = MagicMock(spec=Settings) mock.leads_dir = str(leads_path) mock.synthesis_llm_model = "test-model" mock.synthesis_llm_url = None mock.ollama_base_url = "http://localhost:11434" mock.synthesis_top_k = 6 mock.synthesis_min_grounded_ratio = 0.5 mock.synthesis_gap_min_coverage = 2 mock.wiki_dir = str(tmp_path / "wiki") monkeypatch.setattr("codex.synthesis.get_settings", lambda: mock) return leads_path