Add _detect_conflicts() using adversative keyword heuristic (but, however, in contrast, …) between chunks of different bibkeys; results populate CompileReport.conflicts and appear in wiki/log.md. Also add CompileReport.quarantined field (used by Fix 2). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
228 lines
6.9 KiB
Python
228 lines
6.9 KiB
Python
"""Tests for ``codex wiki check`` CLI command.
|
|
|
|
Verifies:
|
|
- Exit 0 when all pages are grounded (no ⚠ in any page).
|
|
- Exit 1 when at least one page contains an ungrounded claim (⚠ marker).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import textwrap
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
|
|
from codex.cli import app
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Fixtures
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.fixture
|
|
def wiki_dir_all_grounded(tmp_path: Path) -> Path:
|
|
"""Create a wiki directory with only grounded pages (no ⚠)."""
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir()
|
|
|
|
(wiki / "discrete-conformal-map.md").write_text(
|
|
textwrap.dedent(
|
|
"""\
|
|
# Discrete Conformal Map
|
|
|
|
A discrete conformal map is defined by logarithmic scale factors. [BPS2015 #chunk 0]
|
|
The variational principle gives the optimum. [BPS2015 #chunk 1]
|
|
"""
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
(wiki / "circle-packing.md").write_text(
|
|
textwrap.dedent(
|
|
"""\
|
|
# Circle Packing
|
|
|
|
Circle packing is studied via the Koebe-Andreev-Thurston theorem. [Thurston1985 #page 3]
|
|
"""
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
return wiki
|
|
|
|
|
|
@pytest.fixture
|
|
def wiki_dir_with_ungrounded(tmp_path: Path) -> Path:
|
|
"""Create a wiki directory where one page has an ungrounded claim (⚠)."""
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir()
|
|
|
|
(wiki / "lobachevsky-function.md").write_text(
|
|
textwrap.dedent(
|
|
"""\
|
|
# Lobachevsky Function
|
|
|
|
The volume formula is V = L(γ₁)+L(γ₂)+L(γ₃). [Springborn2008 #chunk 16]
|
|
⚠ The closed form L(x) = -∫₀ˣ log|2 sin t| dt. [Springborn2008 #chunk 99]
|
|
"""
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
return wiki
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_wiki_check_exit_0_when_all_grounded(
|
|
wiki_dir_all_grounded: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""codex wiki check exits 0 when no ⚠ in any page."""
|
|
from unittest.mock import MagicMock
|
|
|
|
from codex.config import Settings
|
|
|
|
mock_settings = MagicMock(spec=Settings)
|
|
mock_settings.wiki_dir = str(wiki_dir_all_grounded)
|
|
monkeypatch.setattr("codex.cli.get_settings", lambda: mock_settings, raising=False)
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(app, ["wiki", "check", "--output-dir", str(wiki_dir_all_grounded)])
|
|
assert result.exit_code == 0, (
|
|
f"Expected exit 0, got {result.exit_code}. Output: {result.output}"
|
|
)
|
|
|
|
|
|
def test_wiki_check_exit_1_when_ungrounded(
|
|
wiki_dir_with_ungrounded: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""codex wiki check exits 1 when at least one ⚠ is present."""
|
|
from unittest.mock import MagicMock
|
|
|
|
from codex.config import Settings
|
|
|
|
mock_settings = MagicMock(spec=Settings)
|
|
mock_settings.wiki_dir = str(wiki_dir_with_ungrounded)
|
|
monkeypatch.setattr("codex.cli.get_settings", lambda: mock_settings, raising=False)
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(app, ["wiki", "check", "--output-dir", str(wiki_dir_with_ungrounded)])
|
|
assert result.exit_code == 1, (
|
|
f"Expected exit 1, got {result.exit_code}. Output: {result.output}"
|
|
)
|
|
|
|
|
|
def test_wiki_check_empty_dir_exits_0(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""codex wiki check exits 0 when wiki dir has no .md pages (nothing to check)."""
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir()
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
from codex.config import Settings
|
|
|
|
mock_settings = MagicMock(spec=Settings)
|
|
mock_settings.wiki_dir = str(wiki)
|
|
monkeypatch.setattr("codex.cli.get_settings", lambda: mock_settings, raising=False)
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(app, ["wiki", "check", "--output-dir", str(wiki)])
|
|
assert result.exit_code == 0
|
|
|
|
|
|
def test_wiki_check_index_and_log_ignored(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""codex wiki check ignores index.md and log.md even if they contain ⚠."""
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir()
|
|
|
|
# index.md and log.md with ⚠ — must NOT trigger exit 1
|
|
(wiki / "index.md").write_text("# Wiki Index\n\n⚠ some note\n", encoding="utf-8")
|
|
(wiki / "log.md").write_text("# Log\n\n⚠ ungrounded\n", encoding="utf-8")
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
from codex.config import Settings
|
|
|
|
mock_settings = MagicMock(spec=Settings)
|
|
mock_settings.wiki_dir = str(wiki)
|
|
monkeypatch.setattr("codex.cli.get_settings", lambda: mock_settings, raising=False)
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(app, ["wiki", "check", "--output-dir", str(wiki)])
|
|
assert result.exit_code == 0, (
|
|
f"index.md / log.md should be excluded. exit={result.exit_code}, out={result.output}"
|
|
)
|
|
|
|
|
|
def test_wiki_check_exit_2_when_draft_not_empty(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""codex wiki check exits 2 when wiki/draft/ is non-empty (quarantined pages)."""
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir()
|
|
draft = wiki / "draft"
|
|
draft.mkdir()
|
|
|
|
(draft / "bad-concept.md").write_text(
|
|
"# Bad Concept\n\n⚠ Ungrounded hallucination. [FakeBib2025 #chunk 0]\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
from codex.config import Settings
|
|
|
|
mock_settings = MagicMock(spec=Settings)
|
|
mock_settings.wiki_dir = str(wiki)
|
|
monkeypatch.setattr("codex.cli.get_settings", lambda: mock_settings, raising=False)
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(app, ["wiki", "check", "--output-dir", str(wiki)])
|
|
assert result.exit_code == 2, (
|
|
f"Expected exit 2 (quarantined pages), got {result.exit_code}. Output: {result.output}"
|
|
)
|
|
|
|
|
|
def test_wiki_check_exit_2_takes_priority_over_exit_1(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""Exit 2 (quarantined) takes priority over exit 1 (ungrounded in wiki/)."""
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir()
|
|
draft = wiki / "draft"
|
|
draft.mkdir()
|
|
|
|
# Main wiki with ungrounded claim
|
|
(wiki / "concept-a.md").write_text(
|
|
"# Concept A\n\n⚠ Ungrounded claim. [SomeBib #chunk 0]\n",
|
|
encoding="utf-8",
|
|
)
|
|
# Draft with quarantined page
|
|
(draft / "concept-b.md").write_text(
|
|
"# Concept B\n\nQuarantined content.\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
from codex.config import Settings
|
|
|
|
mock_settings = MagicMock(spec=Settings)
|
|
mock_settings.wiki_dir = str(wiki)
|
|
monkeypatch.setattr("codex.cli.get_settings", lambda: mock_settings, raising=False)
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(app, ["wiki", "check", "--output-dir", str(wiki)])
|
|
assert result.exit_code == 2
|