//! CLI smoke tests for the `mcptest` binary.
//!
//! These are lightweight checks that the compiled binary parses its top-level
//! flags, surfaces every subcommand in `--help`, and that each current stub
//! still exits cleanly. They complement the focused tests in `cli.rs` by
//! covering the breadth of the surface in one place.

use assert_cmd::Command;
use predicates::prelude::*;

fn mcptest() -> Command {
    Command::cargo_bin("mcptest").unwrap()
}

#[test]
fn version_v_flag() {
    mcptest()
        .arg("-V")
        .assert()
        .success()
        .stdout(predicate::str::starts_with("mcptest"));
}

#[test]
fn version_long_flag() {
    mcptest()
        .arg("--version")
        .assert()
        .success()
        .stdout(predicate::str::starts_with("mcptest"));
}

#[test]
fn help_lists_all_subcommands() {
    let assertion = mcptest().arg("--help").assert().success();
    let stdout = String::from_utf8(assertion.get_output().stdout.clone()).unwrap();
    // Every visible variant of the `Command` enum in `cli/mod.rs` must show
    // up in top-level help. A subcommand added to the enum but missing here
    // is a documentation regression. `exec` is deliberately absent: it is an
    // internal SDK co-process mode, hidden per WOR-1332.
    for sub in [
        "run",
        "init",
        "doctor",
        "validate",
        "schema",
        "coverage",
        "report",
        "eval",
        "completions",
        "diff",
        "discover",
        "model-compat",
        "mock",
        "mcp-server",
        "tools",
        "resources",
        "prompts",
        "capabilities",
        "generate",
        "login",
        "compliance",
        "prompt",
        "cache",
        "pipe",
        "security",
    ] {
        assert!(stdout.contains(sub), "help missing subcommand {sub}");
    }
}

#[test]
fn unknown_subcommand_exits_nonzero() {
    mcptest()
        .arg("bogus")
        .assert()
        .failure()
        .stderr(predicate::str::contains("unrecognized").or(predicate::str::contains("valid")));
}

#[test]
fn unknown_global_flag_exits_two() {
    mcptest().args(["--not-a-flag", "run"]).assert().code(2);
}

#[test]
fn run_with_no_config_prints_no_config_message() {
    // The runner is now live but the no-config path still
    // exits 0 with a friendly notice so scaffolding scripts that probe
    // an empty repo do not spuriously fail. Runs in a tempdir because
    // the bare default would otherwise resolve the repo's checked-in
    // mcptest.yml (WOR-1350).
    let dir = tempfile::tempdir().unwrap();
    mcptest()
        .current_dir(dir.path())
        .arg("run")
        .assert()
        .success()
        .stdout(predicate::str::contains("no config at"));
}

#[test]
fn init_creates_files_in_tempdir() {
    let dir = tempfile::tempdir().unwrap();
    mcptest()
        .current_dir(dir.path())
        .arg("init")
        .assert()
        .success();
}

#[test]
fn doctor_default_exits_zero() {
    mcptest().arg("doctor").assert().success();
}

#[test]
fn validate_accepts_a_minimal_config() {
    let dir = tempfile::tempdir().unwrap();
    std::fs::write(
        dir.path().join("mcptest.yaml"),
        "servers:\n  main:\n    command: [\"echo\", \"hi\"]\n",
    )
    .unwrap();
    mcptest()
        .current_dir(dir.path())
        .arg("validate")
        .assert()
        .success();
}

#[test]
fn coverage_default_exits_zero() {
    // Tempdir keeps the no-suite path: the bare default would otherwise
    // resolve the repo's checked-in mcptest.yml (WOR-1350).
    let dir = tempfile::tempdir().unwrap();
    mcptest()
        .current_dir(dir.path())
        .arg("coverage")
        .assert()
        .success();
}

#[test]
fn completions_bash_emits_script() {
    let assertion = mcptest().args(["completions", "bash"]).assert().success();
    let stdout = String::from_utf8(assertion.get_output().stdout.clone()).unwrap();
    assert!(
        stdout.contains("mcptest"),
        "bash completion should mention binary name"
    );
}

#[test]
fn completions_zsh_emits_script() {
    let assertion = mcptest().args(["completions", "zsh"]).assert().success();
    let stdout = String::from_utf8(assertion.get_output().stdout.clone()).unwrap();
    assert!(
        stdout.contains("mcptest"),
        "zsh completion should mention binary name"
    );
}

#[test]
fn completions_fish_emits_script() {
    let assertion = mcptest().args(["completions", "fish"]).assert().success();
    let stdout = String::from_utf8(assertion.get_output().stdout.clone()).unwrap();
    assert!(
        stdout.contains("mcptest"),
        "fish completion should mention binary name"
    );
}

#[test]
fn completions_unknown_shell_exits_nonzero() {
    mcptest()
        .args(["completions", "bogus-shell"])
        .assert()
        .failure();
}

#[test]
fn eval_without_evals_block_exits_zero() {
    let dir = tempfile::tempdir().unwrap();
    std::fs::write(
        dir.path().join("mcptest.yaml"),
        "servers:\n  main:\n    command: [\"echo\", \"hi\"]\n",
    )
    .unwrap();
    mcptest()
        .current_dir(dir.path())
        .arg("eval")
        .assert()
        .success();
}
