//! Integration tests for `mcptest evidence`.
//!
//! The command reads a serialized run report and emits / verifies a portable
//! evidence artifact, so these tests need no server or network: they write a
//! temp report, run the binary, and assert on the exit code and the artifact.

use std::io::Write;

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

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

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

/// A minimal `mcptest run --format json` report with run metadata. `ended_at`
/// is set old so the max-age test can reject it; the commit SHA is a fake the
/// ancestry check cannot resolve (so it stays indeterminate, not a rejection).
fn report_json() -> String {
    let commit = "a".repeat(40);
    format!(
        r#"{{
  "schema_version": "0.1",
  "mcptest_version": "1.0.0",
  "generated_at": "2020-01-01T00:00:00Z",
  "run_id": "01J0",
  "summary": {{ "total": 3, "passed": 2, "failed": 1, "skipped": 0, "duration_ms": 12 }},
  "tests": [],
  "metadata": {{
    "run_id": "01J0",
    "mcptest_version": "1.0.0",
    "runner_environment": "local",
    "os": "macos",
    "arch": "aarch64",
    "ended_at": "2020-01-01T00:00:00Z",
    "mcp_protocol_version": "2025-03-26",
    "config_hash": "sha256:beef",
    "commit_sha": "{commit}",
    "servers": [{{ "name": "weather", "transport": "stdio", "auth": "none" }}]
  }}
}}"#
    )
}

#[test]
fn evidence_emits_the_artifact_to_stdout() {
    let report = temp(&report_json());
    mcptest()
        .arg("evidence")
        .arg(report.path())
        .assert()
        .code(0)
        .stdout(
            predicate::str::contains("mcptest.dev/evidence/v1")
                .and(predicate::str::contains("\"tests_total\": 3"))
                .and(predicate::str::contains(
                    "\"corpus_version\": \"sha256:beef\"",
                )),
        );
}

#[test]
fn evidence_emit_then_verify_round_trips() {
    let report = temp(&report_json());
    let out = tempfile::NamedTempFile::new().expect("temp file");
    mcptest()
        .arg("evidence")
        .arg(report.path())
        .arg("--out")
        .arg(out.path())
        .assert()
        .code(0);
    // No max-age, no signature requirement: the artifact verifies.
    mcptest()
        .arg("evidence")
        .arg("verify")
        .arg(out.path())
        .assert()
        .code(0)
        .stdout(predicate::str::contains("accepted"));
}

#[test]
fn evidence_verify_rejects_unsigned_when_required() {
    let report = temp(&report_json());
    let out = tempfile::NamedTempFile::new().expect("temp file");
    mcptest()
        .arg("evidence")
        .arg(report.path())
        .arg("--out")
        .arg(out.path())
        .assert()
        .code(0);
    mcptest()
        .arg("evidence")
        .arg("verify")
        .arg(out.path())
        .arg("--require-signed")
        .assert()
        .code(1)
        .stdout(predicate::str::contains("unsigned"));
}

#[test]
fn evidence_verify_rejects_stale_evidence() {
    let report = temp(&report_json());
    let out = tempfile::NamedTempFile::new().expect("temp file");
    mcptest()
        .arg("evidence")
        .arg(report.path())
        .arg("--out")
        .arg(out.path())
        .assert()
        .code(0);
    // The artifact's generated_at is in 2020; a one-hour max age rejects it.
    mcptest()
        .arg("evidence")
        .arg("verify")
        .arg(out.path())
        .arg("--max-age")
        .arg("1h")
        .assert()
        .code(1)
        .stdout(predicate::str::contains("stale"));
}

#[test]
fn evidence_reports_a_report_without_metadata_as_exit_two() {
    let report = temp(
        r#"{"schema_version":"0.1","mcptest_version":"1.0.0","generated_at":"2020-01-01T00:00:00Z","run_id":"x","summary":{"total":0,"passed":0,"failed":0,"skipped":0,"duration_ms":0},"tests":[]}"#,
    );
    mcptest()
        .arg("evidence")
        .arg(report.path())
        .assert()
        .code(2)
        .stderr(predicate::str::contains("no run metadata"));
}

#[test]
fn evidence_is_advertised_in_help() {
    mcptest()
        .arg("--help")
        .assert()
        .stdout(predicate::str::contains("evidence"));
}
