//! Integration tests for `mcptest security proof` (WOR-1430).
//!
//! Offline: a conservative source scanner maps tool handlers to risky sinks
//! across TypeScript, Python, and Rust fixtures, and (with explicit consent)
//! generates a reproducible PoC suite that validates as a mcptest suite.

use std::path::PathBuf;

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

fn mcptest() -> Command {
    Command::cargo_bin("mcptest").expect("binary builds")
}

fn fixtures() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../examples/source-proof")
}

#[test]
fn maps_handlers_to_sinks_across_three_languages() {
    mcptest()
        .args(["security", "proof"])
        .arg(fixtures())
        .assert()
        .success()
        .stdout(
            predicate::str::contains("run_command") // typescript
                .and(predicate::str::contains("fetch_url")) // python
                .and(predicate::str::contains("shell")) // rust
                .and(predicate::str::contains("advisory")),
        );
}

#[test]
fn sarif_includes_source_locations() {
    let out = mcptest()
        .args(["security", "proof"])
        .arg(fixtures())
        .args(["--format", "sarif"])
        .output()
        .unwrap();
    let sarif: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
    let first = &sarif["runs"][0]["results"][0];
    assert!(first["locations"][0]["physicalLocation"]["region"]["startLine"].is_number());
    assert_eq!(first["properties"]["advisory"], true);
}

#[test]
fn poc_generation_requires_explicit_consent() {
    // --poc without --authorize/--target is a clap usage error.
    mcptest()
        .args(["security", "proof"])
        .arg(fixtures())
        .args(["--poc", "/tmp/should-not-write.yml"])
        .assert()
        .code(2);
}

#[test]
fn authorized_poc_suite_validates() {
    let dir = tempfile::tempdir().unwrap();
    let poc = dir.path().join("poc.yml");
    mcptest()
        .args(["security", "proof"])
        .arg(fixtures())
        .arg("--poc")
        .arg(&poc)
        .args(["--authorize", "--target", "node ./server.js"])
        .assert()
        .success();
    let body = std::fs::read_to_string(&poc).unwrap();
    assert!(
        body.contains("disposable, sandboxed copy"),
        "consent guidance present"
    );
    assert!(body.contains("negative_path:"), "probes input validation");
    mcptest()
        .args(["validate", "--config"])
        .arg(&poc)
        .assert()
        .success();
}

#[test]
fn fail_on_high_gates_on_advisory_findings() {
    // The fixtures contain shell-exec sinks (high), so this gate fires.
    mcptest()
        .args(["security", "proof"])
        .arg(fixtures())
        .args(["--fail-on", "high"])
        .assert()
        .code(1);
}
