drep-ai 2.4.0

A local commit gate: runs the linters your repo configures, and sends changed code to an LLM for review
Documentation
//! Codex-specific doctor readiness and redaction contracts.

use std::path::Path;

use crate::cli::doctor::{DoctorArgs, run_at_with_codex};
use crate::llm::codex::CodexStatus;

fn report_with_probe(dir: &Path, probe: &dyn Fn() -> Result<CodexStatus, String>) -> String {
    let args = DoctorArgs {
        path: dir.to_path_buf(),
        config: None,
    };
    let mut out = Vec::new();
    run_at_with_codex(&mut out, &args, &dir.join("auth.toml"), probe)
        .expect("doctor remains diagnostic");
    String::from_utf8(out).expect("utf8")
}

#[test]
fn a_ready_codex_backend_reports_only_redacted_subscription_facts() {
    let dir = tempfile::tempdir().expect("tempdir");
    std::fs::write(dir.path().join("a.py"), "x = 1\n").expect("source");
    std::fs::write(
        dir.path().join("drep.toml"),
        r#"
[[llm]]
backend = "codex"
model = "gpt-5.6-sol"
reasoning_effort = "high"
"#,
    )
    .expect("config");

    let report = report_with_probe(dir.path(), &|| Ok(CodexStatus::new("0.148.0")));

    assert!(
        report.contains("gpt-5.6-sol via ChatGPT/Codex subscription"),
        "got {report}"
    );
    assert!(report.contains("Codex CLI: 0.148.0"), "got {report}");
    assert!(
        report.contains("authentication: ChatGPT-managed"),
        "got {report}"
    );
    assert!(
        report.contains("isolation: ephemeral, read-only, tools disabled"),
        "got {report}"
    );
    assert!(!report.contains("no endpoint"), "got {report}");
    assert!(!report.contains("key:"), "got {report}");
}

#[test]
fn a_codex_readiness_failure_is_actionable_but_not_a_doctor_failure() {
    let dir = tempfile::tempdir().expect("tempdir");
    std::fs::write(
        dir.path().join("drep.toml"),
        r#"
[[llm]]
backend = "codex"
model = "gpt-5.6-sol"
reasoning_effort = "high"
"#,
    )
    .expect("config");

    let report = report_with_probe(dir.path(), &|| {
        Err("Codex CLI was not found; install it and run `codex login`".to_owned())
    });

    assert!(report.contains("Codex CLI was not found"), "got {report}");
    assert!(report.contains("`codex login`"), "got {report}");
}

#[test]
fn a_disabled_codex_backend_does_not_run_the_diagnostic() {
    let dir = tempfile::tempdir().expect("tempdir");
    std::fs::write(
        dir.path().join("drep.toml"),
        r#"
[[llm]]
backend = "codex"
enabled = false
model = "gpt-5.6-sol"
reasoning_effort = "high"

[[llm]]
model = "local"
endpoint = "http://localhost:1234/v1"
"#,
    )
    .expect("config");
    let calls = std::cell::Cell::new(0);

    let report = report_with_probe(dir.path(), &|| {
        calls.set(calls.get() + 1);
        Ok(CodexStatus::new("unused"))
    });

    assert_eq!(calls.get(), 0, "disabled providers are inert");
    assert!(
        report.contains("via ChatGPT/Codex subscription (disabled - skipped)"),
        "got {report}"
    );
}

#[test]
fn a_codex_only_config_never_reads_the_http_auth_store() {
    let dir = tempfile::tempdir().expect("tempdir");
    std::fs::write(
        dir.path().join("drep.toml"),
        r#"
[[llm]]
backend = "codex"
model = "gpt-5.6-sol"
"#,
    )
    .expect("config");
    std::fs::write(dir.path().join("auth.toml"), "not valid TOML = [")
        .expect("corrupt HTTP auth store");

    let report = report_with_probe(dir.path(), &|| Ok(CodexStatus::new("0.148.0")));

    assert!(
        !report.contains("auth store could not be read"),
        "a Codex-only setup must not consult HTTP credentials: {report}"
    );
}