AkurAI Build
Menu

AkurAI-Build

public

Latest change 7255093e06e11217a8aa00ce4b6f336d3a5fa654 - Add named service identities and akurai_repo_protect by Ólafur Búi Ólafsson

#[path = "support/mcp.rs"]
mod support;

use std::{fs, path::Path, process::Command};

use anyhow::{Context, Result, ensure};
use serde_json::{Value, json};
use support::McpSession;

/// Verify the MCP tool set produces valid results across a full lifecycle:
/// bootstrap via the remaining CLI subcommands, then repository and query
/// tools over stdio.
#[test]
fn mcp_json_contract_across_lifecycle() -> Result<()> {
    let work = tempfile::tempdir()?;
    let data = work.path().join("data");
    let key = work.path().join("key.bin");
    let repo = work.path().join("repo");
    fs::create_dir(&repo)?;

    // init a bare git repo
    let init = Command::new("git")
        .arg("init")
        .args(["-b", "main"])
        .arg(&repo)
        .output()?;
    ensure!(init.status.success(), "git init failed");

    // keygen and migrate remain plain CLI subcommands.
    let out = cli(&data, &key, &["keygen", "--out", support::str(&key)?])?;
    assert!(out["ok"] == true, "keygen envelope: {out}");
    assert!(out["data"]["created"] == true, "key should be created");

    let out = cli(&data, &key, &["migrate"])?;
    assert!(out["ok"] == true, "migrate envelope: {out}");
    assert!(
        out["data"]["migrations_applied"]
            .as_i64()
            .is_some_and(|n| n >= 0),
        "migrate should report applied count"
    );

    let mut mcp = McpSession::start(&data, &key, false)?;

    let added = mcp.call(
        "akurai_repo_add",
        json!({"name": "test-app", "url": support::str(&repo)?, "branch": "main"}),
    )?;
    assert_eq!(added["name"], "test-app");

    let repos = mcp.call("akurai_repo_list", json!({}))?;
    let repos = repos.as_array().context("repo list should be array")?;
    assert_eq!(repos.len(), 1);

    let workers = mcp.call("akurai_workers", json!({}))?;
    let workers = workers.as_array().context("workers should be array")?;
    assert!(workers.is_empty(), "no workers registered yet");

    let runs = mcp.call("akurai_runs", json!({}))?;
    let runs = runs.as_array().context("runs should be an array")?;
    assert!(runs.is_empty(), "no runs executed yet");

    let renamed = mcp.call(
        "akurai_repo_rename",
        json!({"old": "test-app", "new": "test-app-2"}),
    )?;
    assert_eq!(renamed["name"], "test-app-2");

    let visibility = mcp.call(
        "akurai_repo_visibility",
        json!({"repository": "test-app-2", "visibility": "public"}),
    )?;
    assert_eq!(visibility["visibility"], "public");

    let protected = mcp.call(
        "akurai_repo_protect",
        json!({"repository": "test-app-2", "protected": true, "owner": "reviewer"}),
    )?;
    assert_eq!(protected["protect_default_branch"], true);
    assert_eq!(protected["owner_sub"], "reviewer");

    let error = mcp.call_error("akurai_repo_list", json!({"unknown_field": true}))?;
    assert!(
        !error.is_empty(),
        "unknown-field call should report an error"
    );

    Ok(())
}

/// Run the CLI expecting success and return parsed JSON.
fn cli(data: &Path, key: &Path, arguments: &[&str]) -> Result<Value> {
    let output = Command::new(env!("CARGO_BIN_EXE_akurai"))
        .args([
            "--data",
            support::str(data)?,
            "--key-file",
            support::str(key)?,
        ])
        .args(arguments)
        .output()?;
    let value: Value = serde_json::from_slice(&output.stdout).with_context(|| {
        format!(
            "invalid CLI JSON: {}",
            String::from_utf8_lossy(&output.stdout)
        )
    })?;
    ensure!(
        output.status.success(),
        "CLI failed ({value}): {}",
        String::from_utf8_lossy(&output.stderr)
    );
    Ok(value)
}