//! All MCP primitives over the built-in mock: a suite that tests a tool, a
//! resource, and a prompt against `mcptest mock` must pass.
//!
//! Before this, the mock only answered `tools/*`, and the v1 schema only
//! allowed top-level `tools:` tests, so resource/prompt suites could neither
//! validate nor run locally. This pins that all three primitives work
//! end-to-end against the shipped local test server.

#![cfg(unix)]

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

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

/// Write a mock manifest exposing one tool, one resource, and one prompt.
fn write_manifest(dir: &std::path::Path) -> std::path::PathBuf {
    let manifest = r#"mock_server:
  name: all-methods
  tools:
    - name: echo
      description: "Echo a fixed line."
      input_schema: { type: object }
      response:
        content:
          - type: text
            text: "tool ok"
  resources:
    - uri: "file:///doc.txt"
      name: doc
      mime_type: text/plain
      text: "resource body here"
  prompts:
    - name: greet
      description: "A greeting prompt."
      text: "Say hi to the user."
"#;
    let path = dir.join("all-methods.yaml");
    std::fs::write(&path, manifest).expect("write manifest");
    path
}

#[test]
fn tool_resource_and_prompt_tests_all_pass_against_the_mock() {
    let dir = tempfile::tempdir().unwrap();
    let manifest = write_manifest(dir.path());
    let bin = env!("CARGO_BIN_EXE_mcptest");
    let suite = format!(
        r#"servers:
  m:
    command: ['{bin}', 'mock', '--tools-from', '{manifest}']
tools:
  - name: tool works
    server: m
    tool: echo
    expect:
      - target: result.content[0].text
        matcher: {{ exact: "tool ok" }}
resources:
  - name: resource works
    server: m
    uri: "file:///doc.txt"
    expect:
      - target: result.contents[0].text
        matcher: {{ icontains: "resource body" }}
      - target: result.contents[0].mimeType
        matcher: {{ exact: "text/plain" }}
prompts:
  - name: prompt works
    server: m
    prompt: greet
    expect:
      - target: result.messages[0].content.text
        matcher: {{ exact: "Say hi to the user." }}
"#,
        manifest = manifest.display(),
    );
    let cfg = dir.path().join("mcptest.yaml");
    std::fs::write(&cfg, suite).unwrap();

    // Validates (the v1 schema now allows resources:/prompts: test arrays)...
    mcptest()
        .args(["validate", "--config", cfg.to_str().unwrap()])
        .assert()
        .success();

    // ...and runs: the mock serves tools/call, resources/read, and prompts/get.
    mcptest()
        .args(["run", "--config", cfg.to_str().unwrap()])
        .assert()
        .success()
        .stdout(predicate::str::contains("3 passed"));
}

#[test]
fn resource_read_for_unknown_uri_fails_the_test() {
    let dir = tempfile::tempdir().unwrap();
    let manifest = write_manifest(dir.path());
    let bin = env!("CARGO_BIN_EXE_mcptest");
    let suite = format!(
        r#"servers:
  m:
    command: ['{bin}', 'mock', '--tools-from', '{manifest}']
resources:
  - name: missing resource
    server: m
    uri: "file:///does-not-exist.txt"
    expect:
      - target: result.contents[0].text
        matcher: {{ icontains: anything }}
"#,
        manifest = manifest.display(),
    );
    let cfg = dir.path().join("mcptest.yaml");
    std::fs::write(&cfg, suite).unwrap();

    // The mock returns a JSON-RPC error for an unknown URI, so the test fails.
    mcptest()
        .args(["run", "--config", cfg.to_str().unwrap()])
        .assert()
        .failure();
}
