assay-cli 3.35.0

Policy-as-code gate for MCP agent tool calls, with verifiable evidence and Linux kernel enforcement.
#![allow(deprecated)]

use assert_cmd::Command;
use predicates::str::contains;
use serde_json::Value;
use std::path::PathBuf;
use tempfile::tempdir;

fn read_json(path: &std::path::Path) -> Value {
    let content = std::fs::read_to_string(path).expect("coverage report should exist");
    serde_json::from_str(&content).expect("coverage report must be valid JSON")
}

fn fixture_path(name: &str) -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../../scripts/ci/fixtures/coverage")
        .join(name)
}

#[test]
fn coverage_contract_generates_valid_report_from_basic_jsonl() {
    let dir = tempdir().unwrap();
    let out = dir.path().join("coverage.json");

    Command::cargo_bin("assay")
        .unwrap()
        .args(["coverage", "--input"])
        .arg(fixture_path("input_basic.jsonl"))
        .args(["--out"])
        .arg(&out)
        .args([
            "--declared-tool",
            "read_document",
            "--declared-tool",
            "web_search",
            "--declared-tool",
            "web_search_alt",
        ])
        .assert()
        .success();

    let report = read_json(&out);
    assert_eq!(report["schema_version"], "coverage_report_v1");
    assert_eq!(
        report["tools"]["tools_unknown"],
        serde_json::json!(["unknown_tool_x"])
    );
    assert_eq!(
        report["taxonomy"]["tool_classes_missing"],
        serde_json::json!(["unknown_tool_x"])
    );
    assert_eq!(
        report["routes"]["routes_seen"],
        serde_json::json!([
            {"count": 1, "from": "read_document", "to": "web_search_alt"},
            {"count": 1, "from": "web_search", "to": "unknown_tool_x"},
            {"count": 1, "from": "web_search_alt", "to": "web_search"}
        ])
    );
}

#[test]
fn coverage_contract_accepts_tool_name_fallback_jsonl() {
    let dir = tempdir().unwrap();
    let out = dir.path().join("coverage.json");

    Command::cargo_bin("assay")
        .unwrap()
        .args(["coverage", "--input"])
        .arg(fixture_path("input_tool_name_fallback.jsonl"))
        .args(["--out"])
        .arg(&out)
        .args([
            "--declared-tool",
            "read_document",
            "--declared-tool",
            "web_search",
            "--declared-tool",
            "web_search_alt",
        ])
        .assert()
        .success();

    let report = read_json(&out);
    assert_eq!(report["schema_version"], "coverage_report_v1");
    assert_eq!(
        report["tools"]["tools_seen"],
        serde_json::json!(["read_document", "web_search", "web_search_alt"])
    );
}

#[test]
fn coverage_contract_fails_when_tool_fields_missing() {
    let dir = tempdir().unwrap();
    let out = dir.path().join("coverage.json");

    Command::cargo_bin("assay")
        .unwrap()
        .args(["coverage", "--input"])
        .arg(fixture_path("input_missing_tool_fields.jsonl"))
        .args(["--out"])
        .arg(&out)
        .assert()
        .failure()
        .code(2)
        .stderr(contains("missing required field: 'tool' or 'tool_name'"));
}