//! Gated end-to-end: resolve a real `vault://` reference through `mcptest run`.
//!
//! This is deliberately excluded from the required CI gate (`#[ignore]`), per
//! the project rule that e2e against a real backend is a local or occasional
//! step. It proves the whole path: a `vault://` reference in a server's env is
//! resolved from a live Vault during the run, the run succeeds, and the
//! resolved plaintext is redacted from the output.
//!
//! Setup (a throwaway dev Vault is enough):
//!
//! ```sh
//! vault server -dev                       # prints a root token + addr
//! export VAULT_ADDR=http://127.0.0.1:8200
//! export VAULT_TOKEN=<dev root token>
//! vault kv put secret/mcptest-e2e token=s3cr3t-e2e-value
//! export MCPTEST_E2E_EXPECT=s3cr3t-e2e-value
//! npm install --prefix examples/reference-server
//! cargo test -p mcptest --test vault_e2e -- --ignored
//! ```

use std::io::Write;
use std::path::PathBuf;

use assert_cmd::Command;

/// Repo root, derived from this crate's manifest dir (`crates/mcptest`).
fn repo_root() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("..")
        .join("..")
}

#[test]
#[ignore = "needs a real Vault; see the module docs for setup"]
fn vault_reference_resolves_through_run_and_is_redacted() {
    // Belt and suspenders: the test is already #[ignore], but skip cleanly if
    // a runner invokes it without the credentials it needs.
    let (Ok(_addr), Ok(_token)) = (std::env::var("VAULT_ADDR"), std::env::var("VAULT_TOKEN"))
    else {
        eprintln!("skipping: set VAULT_ADDR and VAULT_TOKEN to run this e2e");
        return;
    };

    let server = repo_root().join("examples/reference-server/src/server.js");
    let suite = format!(
        "servers:\n  \
           reference:\n    \
             command: [\"node\", \"{}\"]\n    \
             env:\n      \
               NODE_NO_WARNINGS: \"1\"\n      \
               INJECTED_SECRET: vault://secret/data/mcptest-e2e?key=token\n\
         tools:\n  \
           - name: \"greet returns a friendly hello\"\n    \
             server: reference\n    \
             tool: \"greet\"\n    \
             args:\n      name: \"Ada\"\n    \
             expect:\n      \
               - target: \"result.content[0].text\"\n        \
                 matcher:\n          exact: \"Hello, Ada!\"\n",
        server.display()
    );

    let mut file = tempfile::NamedTempFile::new().expect("temp suite");
    file.write_all(suite.as_bytes()).expect("write suite");

    let output = Command::cargo_bin("mcptest")
        .expect("binary builds")
        .args(["run", "--config"])
        .arg(file.path())
        .output()
        .expect("run mcptest");

    // The reference (in the server's env) resolved from Vault, so the run got
    // past interpolation and the greet test passed.
    assert!(
        output.status.success(),
        "run failed (did the reference resolve?):\n{}",
        String::from_utf8_lossy(&output.stderr)
    );

    // The resolved plaintext must not appear on any output stream.
    if let Ok(expected) = std::env::var("MCPTEST_E2E_EXPECT") {
        let combined = format!(
            "{}{}",
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr)
        );
        assert!(
            !combined.contains(&expected),
            "the resolved Vault secret leaked into the run output"
        );
    }
}
