AkurAI Build
Menu

Public repository / 054e5cda0171bd13facff3c16a38f2048a1052cc

AkurAI-Mail / tools/akurai-mail-mcp.test.ts

Latest change 7a284f40b2637792d0599c38c4cac0342da676d7 - Merge live EC2 ops copies into scripts/ and tools/ by Ólafur Búi Ólafsson

// Integration tests for akurai-mail-mcp.ts: spawn the MCP server over line-delimited
// stdio against a fake server CLI that records its argv and returns canned JSON.
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";

const MCP = join(import.meta.dir, "akurai-mail-mcp.ts");
let dir = "";
let cli = "";
let callsLog = "";

const FAKE_CLI = `#!/usr/bin/env bash
printf '%s\\n' "$*" >> "$CALLS_LOG"
case "$1" in
  webmail-read) echo '{"ok":true,"message":{"from":"Spammer <Bad@Example.com>"}}' ;;
  status) echo '{"ok":true,"users":[{"email":"a@olibuijr.com"},{"email":"b@olibuijr.com"}]}' ;;
  *) echo '{"ok":true}' ;;
esac
`;

beforeAll(async () => {
  const base = join(import.meta.dir, ".test-tmp");
  await mkdir(base, { recursive: true });
  dir = await mkdtemp(join(base, "mcp-"));
  cli = join(dir, "fake-cli");
  callsLog = join(dir, "calls.log");
  await writeFile(cli, FAKE_CLI);
  await chmod(cli, 0o755);
});

afterAll(async () => {
  await rm(join(import.meta.dir, ".test-tmp"), { recursive: true, force: true });
});

async function callTool(name: string, args: Record<string, unknown>, scope = "admin"): Promise<{ result?: unknown; error?: string; calls: string[] }> {
  await writeFile(callsLog, "");
  const proc = Bun.spawn(["bun", "run", MCP], {
    stdin: "pipe",
    stdout: "pipe",
    stderr: "pipe",
    env: { ...process.env, AKURAI_MAIL_SERVER_CLI: cli, AKURAI_MAIL_MCP_AUDIT_LOG: join(dir, "audit.jsonl"), AKURAI_MAIL_MCP_SCOPE: scope, CALLS_LOG: callsLog },
  });
  proc.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name, arguments: args } }) + "\n");
  proc.stdin.end();
  const out = await new Response(proc.stdout).text();
  await proc.exited;
  const reply = out.split("\n").filter((line) => line.startsWith("{")).map((line) => JSON.parse(line)).find((msg) => msg.id === 1);
  const calls = (await readFile(callsLog, "utf8")).split("\n").filter(Boolean);
  if (reply?.error) return { error: String(reply.error.message), calls };
  return { result: JSON.parse(reply.result.content[0].text), calls };
}

describe("merged live admin tier", () => {
  test("mail_accounts lists users from status", async () => {
    const { result, calls } = await callTool("mail_accounts", {});
    expect(result).toEqual({ ok: true, accounts: ["a@olibuijr.com", "b@olibuijr.com"] });
    expect(calls).toEqual(["status"]);
  });

  test("admin tools are blocked below admin scope", async () => {
    const { error, calls } = await callTool("mail_user_add", { email: "x@olibuijr.com" }, "send");
    expect(error).toContain('requires "admin" scope');
    expect(calls).toEqual([]);
  });
});

describe("anti-spam / quarantine tools", () => {
  test("policy get uses the spam-summary subcommand", async () => {
    const { calls } = await callTool("mail_spam_policy_get", {});
    expect(calls).toEqual(["spam-summary"]);
  });

  test("quarantine list reads the Spam folder", async () => {
    const { calls } = await callTool("mail_quarantine_list", { account: "a@olibuijr.com", limit: 5 });
    expect(calls).toEqual(["webmail-state a@olibuijr.com --folder Spam --limit 5 --cursor "]);
  });

  test("mutations require an approvalProof", async () => {
    const { error, calls } = await callTool("mail_spam_scan", { approvalProof: "nope" }, "draft");
    expect(error).toContain("approvalProof");
    expect(calls).toEqual([]);
  });

  test("ALLOW is an accepted approvalProof", async () => {
    const { calls } = await callTool("mail_spam_scan", { account: "a@olibuijr.com", approvalProof: "ALLOW" }, "draft");
    expect(calls).toEqual(["anti-spam-scan --email a@olibuijr.com"]);
  });

  test("release moves to Inbox and allowlists the sender", async () => {
    const { result, calls } = await callTool("mail_quarantine_release", { account: "a@olibuijr.com", messageIds: ["m1"], allowSender: true, reason: "legit", approvalProof: "ALLOW" }, "draft");
    expect(calls[0]).toBe("webmail-read a@olibuijr.com m1");
    expect(calls[1]).toBe("webmail-action a@olibuijr.com m1 move --target Inbox");
    expect(calls[2]).toStartWith("anti-spam-config ");
    const payload = JSON.parse(calls[2]!.slice("anti-spam-config ".length));
    expect(payload).toMatchObject({ field: "allowlist", mode: "add", values: ["bad@example.com"], disposition: "allow", reason: "legit" });
    expect(result).toMatchObject({ ok: true, folder: "Inbox", senders: ["bad@example.com"] });
  });

  test("confirm with block blocklists the sender without moving", async () => {
    const { calls } = await callTool("mail_quarantine_confirm", { account: "a@olibuijr.com", messageIds: ["m1"], block: true, reason: "spam", approvalProof: "ALLOW" }, "draft");
    expect(calls.length).toBe(2);
    expect(JSON.parse(calls[1]!.slice("anti-spam-config ".length))).toMatchObject({ field: "blocklist", values: ["bad@example.com"], disposition: "block" });
  });

  test("policy change maps domain kind to domainBlocklist with version guard", async () => {
    const { calls } = await callTool("mail_spam_policy_change", { operation: "add", kind: "domain", value: "spam.example", disposition: "block", reason: "r", expectedVersion: 3, approvalProof: "ALLOW" }, "draft");
    expect(JSON.parse(calls[0]!.slice("anti-spam-config ".length))).toMatchObject({ field: "domainBlocklist", mode: "add", values: ["spam.example"], expectedVersion: 3 });
  });
});