"""test_graceful.py — missing/unavailable features must degrade, never crash. All tools that wrap optional features (wiki, synthesis leads, provenance) must return a structured "feature not available" response instead of raising. """ from __future__ import annotations import json import tempfile from pathlib import Path from unittest.mock import patch # --------------------------------------------------------------------------- # ask — always returns "not available" (RAG not implemented) # --------------------------------------------------------------------------- def test_ask_returns_not_available() -> None: """ask() must return {'error': 'not available'} — RAG not yet implemented.""" from codex.mcp_server import ask result = ask("What is conformal geometry?") assert result == {"error": "not available"} # --------------------------------------------------------------------------- # wiki_read — page missing → "feature not available" # --------------------------------------------------------------------------- def test_wiki_read_missing_page_returns_not_available() -> None: """wiki_read() with a nonexistent page must return {"error": "feature not available"}.""" from codex.mcp_server import wiki_read with ( tempfile.TemporaryDirectory() as tmpdir, patch("codex.config.get_settings") as mock_cfg, ): mock_cfg.return_value.wiki_dir = tmpdir result = wiki_read("nonexistent_concept") assert result == {"error": "feature not available"} def test_wiki_read_existing_page_returns_markdown() -> None: """wiki_read() with an existing page must return {markdown, sources}.""" from codex.mcp_server import wiki_read with ( tempfile.TemporaryDirectory() as tmpdir, patch("codex.config.get_settings") as mock_cfg, ): mock_cfg.return_value.wiki_dir = tmpdir # Write a fake wiki page page = Path(tmpdir) / "conformal_maps.md" page.write_text("# Conformal Maps\n\nSome content.", encoding="utf-8") result = wiki_read("conformal_maps") assert "markdown" in result assert "sources" in result assert "# Conformal Maps" in str(result["markdown"]) def test_wiki_read_exception_returns_not_available() -> None: """wiki_read() must degrade gracefully when get_settings raises.""" from codex.mcp_server import wiki_read with patch("codex.config.get_settings", side_effect=RuntimeError("config error")): result = wiki_read("any_slug") assert result == {"error": "feature not available"} # --------------------------------------------------------------------------- # wiki_list — missing wiki dir → empty list # --------------------------------------------------------------------------- def test_wiki_list_missing_dir_returns_empty() -> None: """wiki_list() must return [] when the wiki directory does not exist.""" from codex.mcp_server import wiki_list with ( tempfile.TemporaryDirectory() as tmpdir, patch("codex.config.get_settings") as mock_cfg, ): mock_cfg.return_value.wiki_dir = str(Path(tmpdir) / "no_wiki_here") result = wiki_list() assert result == [] def test_wiki_list_existing_dir_returns_pages() -> None: """wiki_list() must return page entries for .md files in the wiki dir.""" from codex.mcp_server import wiki_list with ( tempfile.TemporaryDirectory() as tmpdir, patch("codex.config.get_settings") as mock_cfg, ): mock_cfg.return_value.wiki_dir = tmpdir (Path(tmpdir) / "concept_a.md").write_text("# A", encoding="utf-8") (Path(tmpdir) / "concept_b.md").write_text("# B", encoding="utf-8") (Path(tmpdir) / "index.md").write_text("# Index", encoding="utf-8") # must be skipped result = wiki_list() slugs = {r["slug"] for r in result} assert "concept_a" in slugs assert "concept_b" in slugs assert "index" not in slugs # index.md is excluded # --------------------------------------------------------------------------- # discover_leads — domain function raises → error dict, no crash # --------------------------------------------------------------------------- def test_discover_leads_db_error_returns_error() -> None: """discover_leads() must return [{"error": ...}] when the discovery call fails.""" from codex.mcp_server import discover_leads # Patch the domain function directly so the exception always surfaces with patch("codex.discover.discovery_leads", side_effect=RuntimeError("no DB")): result = discover_leads(limit=5) assert len(result) == 1 assert "error" in result[0] # --------------------------------------------------------------------------- # synthesis_browse — missing leads dir → "feature not available" # --------------------------------------------------------------------------- def test_synthesis_browse_missing_dir_returns_not_available() -> None: """synthesis_browse() must return [{'error': 'feature not available'}] when leads/ absent.""" from codex.mcp_server import synthesis_browse with ( tempfile.TemporaryDirectory() as tmpdir, patch("codex.config.get_settings") as mock_cfg, ): mock_cfg.return_value.leads_dir = str(Path(tmpdir) / "no_leads_here") result = synthesis_browse() assert result == [{"error": "feature not available"}] def test_synthesis_browse_existing_dir_returns_items() -> None: """synthesis_browse() must parse and return JSON files from the leads dir.""" from codex.mcp_server import synthesis_browse with ( tempfile.TemporaryDirectory() as tmpdir, patch("codex.config.get_settings") as mock_cfg, ): leads_dir = Path(tmpdir) / "leads" leads_dir.mkdir() (leads_dir / "lead1.json").write_text( json.dumps({"kind": "connection", "title": "C1"}), encoding="utf-8" ) (leads_dir / "lead2.json").write_text( json.dumps({"kind": "conjecture", "title": "C2"}), encoding="utf-8" ) mock_cfg.return_value.leads_dir = str(leads_dir) result = synthesis_browse(kind="all") assert len(result) == 2 kinds = {str(r.get("kind")) for r in result} assert "connection" in kinds assert "conjecture" in kinds def test_synthesis_browse_kind_filter() -> None: """synthesis_browse(kind='conjecture') must return only conjectures.""" from codex.mcp_server import synthesis_browse with ( tempfile.TemporaryDirectory() as tmpdir, patch("codex.config.get_settings") as mock_cfg, ): leads_dir = Path(tmpdir) / "leads" leads_dir.mkdir() (leads_dir / "lead1.json").write_text( json.dumps({"kind": "connection", "title": "C1"}), encoding="utf-8" ) (leads_dir / "lead2.json").write_text( json.dumps({"kind": "conjecture", "title": "C2"}), encoding="utf-8" ) mock_cfg.return_value.leads_dir = str(leads_dir) result = synthesis_browse(kind="conjecture") assert all(str(r.get("kind")) == "conjecture" for r in result) assert len(result) == 1 # --------------------------------------------------------------------------- # provenance_verify — exception → "feature not available" # --------------------------------------------------------------------------- def test_provenance_verify_exception_returns_not_available() -> None: """provenance_verify() must return [{"error": ...}] on scan failure.""" from codex.mcp_server import provenance_verify with patch("codex.provenance.scan_cite_tags", side_effect=RuntimeError("scan failed")): result = provenance_verify("/some/nonexistent/path") assert len(result) == 1 assert "error" in result[0]