"""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