← Files Codex AdvisorARCHIVED FILE

tests/test_advisor_cli.py

17.2 KB · Oct 3, 2026 · 06:33 UTC

↓ Download file

"""Black-box tests for the local Advisor settings entrypoint."""

from __future__ import annotations

import json
import os
import signal
import subprocess
import sys
import tempfile
import time
import unittest
import uuid
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
CLI = ROOT / "scripts" / "advisor-config.sh"
PARENT = "56565656-5656-7565-8565-565656565656"
FAKE_CODEX = r'''#!/usr/bin/env python3
import json, os, signal, sys, time
from pathlib import Path

if sys.argv[1:] == ["--version"]:
    print(os.environ.get("FAKE_CODEX_VERSION", "codex-cli 0.153.2"))
    raise SystemExit(0)
if sys.argv[1:] == ["app-server", "--help"]:
    print("--ignore-user-config --ignore-rules")
    raise SystemExit(0)
if len(sys.argv) > 1 and sys.argv[1] == "app-server":
    raise SystemExit(90)

args = sys.argv[1:]
model = args[args.index("--model") + 1]
effort = args[args.index("-c") + 1].split('"')[1]
output = Path(args[args.index("--output-last-message") + 1])
log = Path(os.environ["FAKE_CODEX_LOG"])
rows = json.loads(log.read_text()) if log.exists() else []
rows.append({"model": model, "effort": effort})
log.write_text(json.dumps(rows))
attempt_file = Path(os.environ.get("FAKE_ATTEMPT_FILE", str(log) + ".attempt"))
attempt = int(attempt_file.read_text()) + 1 if attempt_file.exists() else 1
attempt_file.write_text(str(attempt))
case = os.environ.get("FAKE_CODEX_CASE", "valid")

if case == "hang":
    ready = Path(os.environ["FAKE_READY_MARKER"])
    terminated = Path(os.environ["FAKE_TERM_MARKER"])
    def stop(*_args):
        terminated.write_text("terminated")
        raise SystemExit(143)
    signal.signal(signal.SIGTERM, stop)
    ready.write_text(str(os.getpid()))
    while True:
        time.sleep(1)

child = f"{attempt:08x}-1111-7111-8111-{attempt:012x}"
sessions = Path(os.environ["CODEX_HOME"]) / "sessions" / "fixture"
sessions.mkdir(parents=True, exist_ok=True)
events = [
    {"type": "session_meta", "payload": {"id": child, "source": "exec", "originator": "codex_exec"}},
    {"type": "turn_context", "payload": {"model": model, "effort": effort, "sandbox_policy": {"type": "read-only"}, "permission_profile": {"type": "managed"}}},
]
(sessions / f"rollout-fake-{child}.jsonl").write_text("".join(json.dumps(row) + "\n" for row in events))
print(json.dumps({"type": "thread.started", "thread_id": child}))
response = {
    "recommendation": "neutral path", "why": "reason", "strongest_objection": "objection",
    "change_my_mind": "evidence", "acceptance_checks": ["check"], "risks": "none",
    "follow_up_areas": "none",
}
if case == "invalid-once" and attempt == 1:
    response.pop("risks")
output.write_text(json.dumps(response))
'''


class AdvisorCliTests(unittest.TestCase):
    def setUp(self) -> None:
        self.temporary = tempfile.TemporaryDirectory()
        self.root = Path(self.temporary.name)
        self.home = self.root / "codex"
        self.home.mkdir(mode=0o700)
        self.binary = self.root / "bin"
        self.binary.mkdir()
        self.fake = self.binary / "codex"
        self.fake.write_text(FAKE_CODEX, encoding="utf-8")
        self.fake.chmod(0o700)
        self.log = self.root / "codex.log"
        self.attempts = self.root / "attempts"
        self.env = {
            **os.environ,
            "CODEX_HOME": str(self.home),
            "CODEX_THREAD_ID": PARENT,
            "PATH": f"{self.binary}:{os.environ['PATH']}",
            "FAKE_CODEX_LOG": str(self.log),
            "FAKE_ATTEMPT_FILE": str(self.attempts),
        }
        self.write_parent_fixture("read-only")

    def tearDown(self) -> None:
        self.temporary.cleanup()

    def write_parent_fixture(self, sandbox: str) -> None:
        sessions = self.home / "sessions" / "fixture"
        sessions.mkdir(parents=True, exist_ok=True)
        rollout = sessions / f"rollout-fake-{PARENT}.jsonl"
        rows = [
            {"type": "session_meta", "payload": {"id": PARENT, "agent_role": "root"}},
            {"type": "turn_context", "payload": {"sandbox_policy": {"type": sandbox}, "permission_profile": {"type": "managed"}}},
        ]
        rollout.write_text("".join(json.dumps(row) + "\n" for row in rows))

    def run_cli(
        self, *args: str, env: dict[str, str] | None = None, timeout: float = 10
    ) -> subprocess.CompletedProcess[str]:
        return subprocess.run(
            [str(CLI), *args], text=True, capture_output=True,
            env=env or self.env, check=False, timeout=timeout,
        )

    def invocations(self) -> list[dict[str, str]]:
        return json.loads(self.log.read_text()) if self.log.exists() else []

    def add(self, model: str = "manual/model") -> None:
        result = self.run_cli("--json", "models", "add", model)
        self.assertEqual(result.returncode, 0, result.stderr)

    def test_help_json_cold_start_and_doctor_inspect_actual_parent(self) -> None:
        help_result = self.run_cli("--help")
        self.assertEqual(help_result.returncode, 0)
        for command in ("show", "models", "set", "preset", "doctor", "reset", "restore", "deadline", "journal"):
            self.assertIn(command, help_result.stdout)
        show = self.run_cli("--json", "show")
        self.assertEqual(show.returncode, 0, show.stderr)
        self.assertEqual(len(show.stdout.splitlines()), 1)
        current = json.loads(show.stdout)
        self.assertEqual(current["selections"]["standard"]["model"], "gpt-5.6-terra")
        self.assertTrue(current["selection_revision"].startswith("sha256:"))
        self.assertTrue(current["live_config"]["path"].endswith("advisor.toml"))
        doctor = self.run_cli("--json", "doctor")
        self.assertEqual(doctor.returncode, 0, doctor.stderr)
        payload = json.loads(doctor.stdout)
        self.assertEqual(payload["parent_runtime"]["status"], "available")
        self.assertEqual(payload["parent_runtime"]["sandbox_policy_type"], "read-only")
        self.assertIn("inspecting parent runtime evidence", doctor.stderr)
        self.assertFalse((self.home / "advisor").exists())
        self.assertEqual(self.invocations(), [])

    def test_pristine_default_resolve_works_on_newer_cli_without_setup(self) -> None:
        for version in ("codex-cli 0.153.4", "codex-cli 9.0.0"):
            result = self.run_cli(
                "--json", "resolve", "--tier", "standard",
                env={**self.env, "FAKE_CODEX_VERSION": version},
            )
            self.assertEqual(result.returncode, 0, result.stderr)
            launch = json.loads(result.stdout)["launch"]
            self.assertEqual(
                (launch["model"], launch["effort"]), ("gpt-5.6-terra", "high")
            )
            self.assertFalse((self.home / "advisor").exists())
            self.assertEqual(self.invocations(), [])

    def test_doctor_reports_missing_malformed_and_unsafe_evidence_without_inference(self) -> None:
        rollout = self.home / "sessions" / "fixture" / f"rollout-fake-{PARENT}.jsonl"
        rollout.write_text("not-json\n")
        malformed = json.loads(self.run_cli("--json", "doctor").stdout)
        self.assertEqual(malformed["parent_runtime"]["status"], "unavailable")
        self.write_parent_fixture("danger-full-access")
        unsafe = json.loads(self.run_cli("--json", "doctor").stdout)
        self.assertEqual(unsafe["parent_runtime"]["status"], "unavailable")
        rollout.unlink()
        missing = json.loads(self.run_cli("--json", "doctor").stdout)
        self.assertEqual(missing["parent_runtime"]["status"], "unavailable")
        self.assertEqual(self.invocations(), [])

    def test_add_test_select_preset_and_pair_freshness(self) -> None:
        self.add()
        unauthorized = self.run_cli(
            "--json", "models", "test", "manual/model", "--effort", "high",
            "--parent-thread", PARENT,
        )
        self.assertEqual(unauthorized.returncode, 2)
        self.assertEqual(self.invocations(), [])
        tested = self.run_cli(
            "--json", "models", "test", "manual/model", "--effort", "high",
            "--authorize-usage", "--parent-thread", PARENT,
            "--sessions-dir", str(self.home / "sessions"),
        )
        self.assertEqual(tested.returncode, 0, tested.stderr)
        self.assertFalse(json.loads(tested.stdout)["compatibility"]["selected"])
        self.assertEqual(len(self.invocations()), 1)
        listed = json.loads(self.run_cli("--json", "models", "list").stdout)
        receipts = listed["models"]["candidates"][0]["tested_compatibility"]
        self.assertEqual([(row["effort"], row["current"]) for row in receipts], [("high", True)])
        stale_env = {**self.env, "FAKE_CODEX_VERSION": "codex-cli 0.154.0"}
        retained = json.loads(self.run_cli("--json", "models", "list", env=stale_env).stdout)
        self.assertTrue(retained["models"]["candidates"][0]["tested_compatibility"][0]["current"])
        selected = self.run_cli(
            "--json", "set", "--tier", "standard", "--model", "manual/model", "--effort", "high"
        )
        self.assertEqual(selected.returncode, 2)
        self.assertIn("advisor.toml", selected.stdout)
        preset = self.run_cli(
            "--json", "preset", "manual", "--model", "manual/model", "--effort", "high"
        )
        self.assertEqual(preset.returncode, 0, preset.stderr)
        status = json.loads(self.run_cli("--json", "status").stdout)
        self.assertEqual(status["selections"]["standard"]["model"], "gpt-5.6-terra")

    def test_canary_uses_at_most_two_attempts_and_records_only_success(self) -> None:
        self.add()
        result = self.run_cli(
            "--json", "models", "test", "manual/model", "--effort", "high",
            "--authorize-usage", "--parent-thread", PARENT,
            "--sessions-dir", str(self.home / "sessions"),
            env={**self.env, "FAKE_CODEX_CASE": "invalid-once"},
        )
        self.assertEqual(result.returncode, 0, result.stderr)
        self.assertEqual(len(self.invocations()), 2)
        self.assertIn("one fresh corrective retry", result.stderr)
        catalog = json.loads((self.home / "advisor" / "catalog.json").read_text())
        self.assertEqual(len(catalog["compatibility"]), 1)

    def test_invalid_settings_recovery_reset_deadline_journal_and_errors(self) -> None:
        self.assertEqual(self.run_cli("--json", "deadline", "120").returncode, 0)
        self.assertEqual(self.run_cli("--json", "journal", "enable").returncode, 0)
        self.add("kept/model")
        settings = self.home / "advisor" / "settings.json"
        prior = self.home / "advisor" / "settings.previous.json"
        malformed = b'{"schema_version":1,"nested":' + b"[" * 1000
        settings.write_bytes(malformed)
        invalid = self.run_cli("--json", "show")
        self.assertEqual(invalid.returncode, 0)
        self.assertEqual(json.loads(invalid.stdout)["legacy_settings"], "invalid")
        restored = self.run_cli("--json", "restore")
        self.assertEqual(restored.returncode, 0, restored.stderr)
        invalid_copy = self.home / "advisor" / "settings.invalid.json"
        self.assertEqual(invalid_copy.read_bytes(), malformed)
        self.assertTrue(prior.exists())
        self.assertEqual(self.run_cli("--json", "deadline", "120").returncode, 0)
        self.assertEqual(self.run_cli("--json", "journal", "enable").returncode, 0)
        reset = self.run_cli("--json", "reset")
        self.assertEqual(reset.returncode, 2)
        self.assertIn("advisor.toml", reset.stdout)
        catalog = json.loads((self.home / "advisor" / "catalog.json").read_text())
        self.assertEqual(catalog["candidates"][0]["model"], "kept/model")
        self.assertEqual(self.run_cli("--json", "journal", "disable").returncode, 0)
        self.assertEqual(self.run_cli("--json", "journal", "clear").returncode, 0)

    def test_doctor_reports_dependencies_stale_selection_and_last_failure(self) -> None:
        self.add()
        settings_dir = self.home / "advisor"
        settings = {
            "schema_version": 1, "revision": 1,
            "selections": {"standard": {"model": "manual/model", "effort": "high"}, "specialist": {"model": "gpt-6-sol", "effort": "high"}},
            "presets": {}, "deadline_seconds": 300, "usage_journal_enabled": True,
        }
        (settings_dir / "settings.json").write_text(json.dumps(settings) + "\n")
        os.chmod(settings_dir / "settings.json", 0o600)
        record = {
            "schema_version": 1, "consultation_id": str(uuid.uuid4()),
            "started_at": "2026-09-04T12:00:00Z", "finished_at": "2026-09-04T12:00:01Z",
            "tier": "standard", "model": "manual/model", "effort": "high",
            "outcome": "failed", "transport_contract_version": "1.4", "total_duration_ms": 1,
            "attempts": [{"number": 1, "duration_ms": 1, "outcome": "runtime_failed", "usage": {"input": None, "cached_input": None, "output": None, "reasoning": None}}],
            "totals": {"input": None, "cached_input": None, "output": None, "reasoning": None},
        }
        written = self.run_cli("--json", "_journal-record", json.dumps(record))
        self.assertEqual(written.returncode, 0, written.stderr)
        doctor = json.loads(self.run_cli("--json", "doctor").stdout)
        self.assertEqual(doctor["live_config"]["pairs"]["standard"]["model"], "gpt-5.6-terra")
        self.assertTrue(doctor["live_config"]["source_revision"].startswith("sha256:"))
        self.assertIsNone(doctor["stale_selections"])
        self.assertEqual(doctor["last_content_free_failure"]["outcome"], "failed")
        self.assertEqual(doctor["dependencies"]["codex"]["status"], "available")
        minimal = self.root / "minimal-bin"
        minimal.mkdir()
        (minimal / "python3").symlink_to(Path(sys.executable))
        (minimal / "dirname").symlink_to("/usr/bin/dirname")
        no_codex = {**self.env, "PATH": str(minimal)}
        missing = json.loads(self.run_cli("--json", "doctor", env=no_codex).stdout)
        self.assertEqual(missing["dependencies"]["codex"]["status"], "missing")
        self.assertEqual(missing["dependencies"]["jq"], "missing")
        no_python_bin = self.root / "no-python-bin"
        no_python_bin.mkdir()
        (no_python_bin / "dirname").symlink_to("/usr/bin/dirname")
        no_python = self.run_cli(
            "--json", "doctor", env={**self.env, "PATH": str(no_python_bin)}
        )
        self.assertEqual(no_python.returncode, 2)
        self.assertIn("python3 is unavailable", no_python.stderr)

    def test_real_cli_natural_deadline_reaps_child_and_preserves_unrelated(self) -> None:
        self.add()
        self.assertEqual(self.run_cli("--json", "deadline", "30").returncode, 0)
        ready = self.root / "deadline-ready"
        terminated = self.root / "deadline-terminated"
        unrelated = subprocess.Popen(["sleep", "45"])
        started = time.monotonic()
        try:
            result = self.run_cli(
                "--json", "models", "test", "manual/model", "--effort", "high",
                "--authorize-usage", "--parent-thread", PARENT,
                "--sessions-dir", str(self.home / "sessions"),
                env={**self.env, "FAKE_CODEX_CASE": "hang", "FAKE_READY_MARKER": str(ready), "FAKE_TERM_MARKER": str(terminated)},
                timeout=38,
            )
            elapsed = time.monotonic() - started
            self.assertEqual(result.returncode, 2)
            self.assertGreaterEqual(elapsed, 28)
            self.assertLess(elapsed, 37)
            self.assertTrue(ready.exists())
            self.assertTrue(terminated.exists())
            self.assertIsNone(unrelated.poll())
            transport_root = self.home / ".tmp" / "advisor-transport"
            self.assertFalse(any(transport_root.glob("run.*")))
            self.assertFalse(any((self.home / "advisor" / "canaries").glob("*.json")))
        finally:
            unrelated.terminate()
            unrelated.wait(timeout=3)

    def test_real_cli_caller_cancellation_reaps_child_and_preserves_unrelated(self) -> None:
        self.add()
        ready = self.root / "cancel-ready"
        terminated = self.root / "cancel-terminated"
        unrelated = subprocess.Popen(["sleep", "20"])
        process = subprocess.Popen(
            [str(CLI), "--json", "models", "test", "manual/model", "--effort", "high", "--authorize-usage", "--parent-thread", PARENT, "--sessions-dir", str(self.home / "sessions")],
            text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
            env={**self.env, "FAKE_CODEX_CASE": "hang", "FAKE_READY_MARKER": str(ready), "FAKE_TERM_MARKER": str(terminated)},
        )
        try:
            started = time.monotonic()
            while not ready.exists() and time.monotonic() - started < 5:
                time.sleep(0.05)
            self.assertTrue(ready.exists())
            process.send_signal(signal.SIGTERM)
            process.wait(timeout=8)
            self.assertTrue(terminated.exists())
            self.assertIsNone(unrelated.poll())
            transport_root = self.home / ".tmp" / "advisor-transport"
            self.assertFalse(any(transport_root.glob("run.*")))
            self.assertFalse(any((self.home / "advisor" / "canaries").glob("*.json")))
        finally:
            if process.poll() is None:
                process.kill()
            if process.stdout:
                process.stdout.close()
            if process.stderr:
                process.stderr.close()
            unrelated.terminate()
            unrelated.wait(timeout=3)


if __name__ == "__main__":
    unittest.main()

SHA-256: b1b2ad9415e560da63ff6f8f8cfb76233b828de6fb459f306e3a6b58b3100f64