//! Integration test for WOR-1326: a stdio server that exits before
//! answering `initialize` must fail fast (well under the 30s request
//! timeout) with an error that names the exit status, quotes the tail
//! of the child's stderr, and links its troubleshooting entry.

#![cfg(unix)] // fabricates the dying server with `sh`

use std::time::{Duration, Instant};

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

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

/// A config whose server prints one stderr line and exits 7 without
/// ever speaking MCP, mimicking `node` failing on a missing module.
fn dying_server_config(dir: &std::path::Path) -> std::path::PathBuf {
    let cfg = dir.join("mcptest.yaml");
    std::fs::write(
        &cfg,
        concat!(
            "servers:\n",
            "  dying:\n",
            "    command: [\"sh\", \"-c\", \"echo \\\"Error: Cannot find module '/nope.js'\\\" >&2; exit 7\"]\n",
            "tools:\n",
            "  - name: t\n",
            "    server: dying\n",
            "    tool: echo\n",
            "    args: {}\n",
            "    expect:\n",
            "      - target: result.isError\n",
            "        matcher: { exact: false }\n",
        ),
    )
    .unwrap();
    cfg
}

#[test]
fn run_against_an_immediately_exiting_server_fails_fast_with_status_and_stderr() {
    let dir = tempfile::tempdir().unwrap();
    let cfg = dying_server_config(dir.path());

    let started = Instant::now();
    mcptest()
        .args(["run", "--config", cfg.to_str().unwrap()])
        .assert()
        .failure()
        .stderr(predicate::str::contains("connect to `dying` failed"))
        .stderr(predicate::str::contains("exited during startup"))
        .stderr(predicate::str::contains("exit status: 7"))
        .stderr(predicate::str::contains("Cannot find module '/nope.js'"))
        .stderr(predicate::str::contains(format!(
            "see: {}",
            mcptest_core::error_anchors::SERVER_EXITED_DURING_STARTUP
        )));
    let elapsed = started.elapsed();
    assert!(
        elapsed < Duration::from_secs(10),
        "run took {elapsed:?}; a dead child must abort the handshake \
         well before the 30s initialize timeout"
    );
}
