//! Integration tests for `mcptest manifest`: capability SBOM emit and diff.

use std::io::Write;

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

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

fn snap(body: &str) -> tempfile::NamedTempFile {
    let mut f = tempfile::Builder::new()
        .suffix(".json")
        .tempfile()
        .expect("temp file");
    f.write_all(body.as_bytes()).expect("write");
    f
}

fn yaml_file(body: &[u8]) -> tempfile::NamedTempFile {
    let mut f = tempfile::Builder::new()
        .suffix(".yaml")
        .tempfile()
        .expect("temp file");
    f.write_all(body).expect("write");
    f
}

const READONLY: &str = r#"{"tools":[{"name":"read_file","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object"}}]}"#;
const ESCALATED: &str = r#"{"tools":[{"name":"read_file","annotations":{"destructiveHint":true},"inputSchema":{"type":"object"}}]}"#;

#[test]
fn emit_classifies_tools_to_yaml() {
    let s = snap(READONLY);
    mcptest()
        .arg("manifest")
        .arg(s.path())
        .assert()
        .success()
        .stdout(predicate::str::contains("name: read_file"))
        .stdout(predicate::str::contains("class: read-only"));
}

#[test]
fn emit_is_deterministic() {
    let s = snap(READONLY);
    let a = mcptest()
        .arg("manifest")
        .arg(s.path())
        .output()
        .expect("run")
        .stdout;
    let b = mcptest()
        .arg("manifest")
        .arg(s.path())
        .output()
        .expect("run")
        .stdout;
    assert_eq!(a, b, "manifest emit must be byte-identical across runs");
}

#[test]
fn diff_flags_class_escalation_and_gates() {
    let old_snap = snap(READONLY);
    let new_snap = snap(ESCALATED);
    let old_yaml = mcptest()
        .arg("manifest")
        .arg(old_snap.path())
        .output()
        .expect("run")
        .stdout;
    let new_yaml = mcptest()
        .arg("manifest")
        .arg(new_snap.path())
        .output()
        .expect("run")
        .stdout;
    let old_f = yaml_file(&old_yaml);
    let new_f = yaml_file(&new_yaml);

    mcptest()
        .arg("manifest")
        .arg("diff")
        .arg(old_f.path())
        .arg(new_f.path())
        .arg("--fail-on-escalation")
        .assert()
        .failure()
        .code(1)
        .stdout(predicate::str::contains("escalat"));
}

#[test]
fn diff_of_identical_manifests_is_clean() {
    let s = snap(READONLY);
    let yaml = mcptest()
        .arg("manifest")
        .arg(s.path())
        .output()
        .expect("run")
        .stdout;
    let f1 = yaml_file(&yaml);
    let f2 = yaml_file(&yaml);
    mcptest()
        .arg("manifest")
        .arg("diff")
        .arg(f1.path())
        .arg(f2.path())
        .arg("--fail-on-escalation")
        .assert()
        .success()
        .stdout(predicate::str::contains("No capability changes"));
}
