cc-libs/tools/mcp-bridge/test/probe-computers.test.ts

187 lines
6.8 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { WebSocket } from "ws";
import { LinkRegistry } from "../src/link-server.js";
import { handleMcpRequest } from "../src/mcp-server.js";
test("probe-computers returns no computers message when registry is empty", async () => {
const registry = new LinkRegistry();
assert.equal(await registry.probeComputers(10), "No computers connected.");
});
test("probe-computers aggregates multiple successful responses", async () => {
const registry = new LinkRegistry();
const computer1 = new FakeSocket();
const computer2 = new FakeSocket();
registry.register({ computerId: 12, label: "base-turtle", ws: computer1 as unknown as WebSocket, connectedAt: 1, lastSeenAt: 1 });
registry.register({ computerId: 13, label: "miner-1", ws: computer2 as unknown as WebSocket, connectedAt: 1, lastSeenAt: 1 });
const promise = registry.probeComputers(50);
computer1.respond(registry, 12, "pong from 12 (Label: base-turtle)");
computer2.respond(registry, 13, "pong from 13 (Label: miner-1)");
assert.equal(await promise, "pong from 12 (Label: base-turtle)\npong from 13 (Label: miner-1)");
});
test("probe-computers reports timeout for a connected computer that does not answer", async () => {
const registry = new LinkRegistry();
registry.register({ computerId: 14, label: "farm-turtle", ws: new FakeSocket() as unknown as WebSocket, connectedAt: 1, lastSeenAt: 1 });
assert.equal(await registry.probeComputers(5), "timeout from 14 (Label: farm-turtle)");
});
test("MCP tool call returns text content", async () => {
const registry = new LinkRegistry();
const response = await handleMcpRequest(
{ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "probe-computers", arguments: {} } },
registry,
10,
);
assert.deepEqual(response, {
jsonrpc: "2.0",
id: 1,
result: { content: [{ type: "text", text: "No computers connected." }] },
});
});
test("MCP tools/list includes exec-lua schema", async () => {
const registry = new LinkRegistry();
const response = await handleMcpRequest({ jsonrpc: "2.0", id: 1, method: "tools/list" }, registry, 10);
assert.deepEqual(response, {
jsonrpc: "2.0",
id: 1,
result: {
tools: [
{
name: "probe-computers",
description: "Probe all linked ComputerCraft computers.",
inputSchema: { type: "object", properties: {}, additionalProperties: false },
},
{
name: "exec-lua",
description: "Execute Lua code on a linked ComputerCraft computer.",
inputSchema: {
type: "object",
properties: {
computerId: { type: "number", description: "ComputerCraft computer id to execute on." },
code: { type: "string", description: "Lua source code to execute." },
timeoutMs: { type: "number", description: "Optional host-side timeout in milliseconds, max 30000." },
},
required: ["computerId", "code"],
additionalProperties: false,
},
},
],
},
});
});
test("exec-lua sends code to the selected computer", async () => {
const registry = new LinkRegistry();
const computer = new FakeSocket();
registry.register({ computerId: 12, label: "base-turtle", ws: computer as unknown as WebSocket, connectedAt: 1, lastSeenAt: 1 });
const promise = registry.execLua(12, "print('hi') return 2 + 3", 50);
assert.deepEqual(computer.lastRequest(), {
type: "request",
id: computer.lastRequest().id,
method: "exec-lua",
params: { code: "print('hi') return 2 + 3" },
});
computer.respond(registry, 12, { returns: [{ type: "number", value: 5 }], output: "hi\n" });
assert.equal(
await promise,
'computer: 12 (Label: base-turtle)\nok: true\nreturns: [{"type":"number","value":5}]\noutput:\nhi',
);
});
test("exec-lua reports unknown computer", async () => {
const registry = new LinkRegistry();
assert.equal(await registry.execLua(99, "return 1", 50), "No computer with id 99 connected.");
});
test("exec-lua formats computer error responses", async () => {
const registry = new LinkRegistry();
const computer = new FakeSocket();
registry.register({ computerId: 12, label: "base-turtle", ws: computer as unknown as WebSocket, connectedAt: 1, lastSeenAt: 1 });
const promise = registry.execLua(12, "print('before'); error('boom')", 50);
computer.respondError(registry, 12, "boom", { output: "before\n" });
assert.equal(await promise, "computer: 12 (Label: base-turtle)\nok: false\nerror: boom\noutput:\nbefore");
});
test("exec-lua reports timeouts", async () => {
const registry = new LinkRegistry();
registry.register({ computerId: 12, label: "base-turtle", ws: new FakeSocket() as unknown as WebSocket, connectedAt: 1, lastSeenAt: 1 });
assert.equal(await registry.execLua(12, "while true do end", 5), "computer: 12 (Label: base-turtle)\nok: false\nerror: timeout");
});
test("MCP exec-lua validates required arguments", async () => {
const registry = new LinkRegistry();
const response = await handleMcpRequest(
{ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: "exec-lua", arguments: { computerId: 1 } } },
registry,
10,
);
assert.deepEqual(response, {
jsonrpc: "2.0",
id: 2,
error: { code: -32602, message: "code must be a non-empty string" },
});
});
test("MCP exec-lua validates timeoutMs", async () => {
const registry = new LinkRegistry();
const response = await handleMcpRequest(
{ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: "exec-lua", arguments: { computerId: 1, code: "return 1", timeoutMs: 0 } } },
registry,
10,
);
assert.deepEqual(response, {
jsonrpc: "2.0",
id: 2,
error: { code: -32602, message: "timeoutMs must be a positive finite number" },
});
});
class FakeSocket {
sent: unknown[] = [];
send(data: unknown): void {
this.sent.push(data);
}
close(): void {
return;
}
lastRequest(): { id: string; type: string; method: string; params?: unknown } {
const last = this.sent.at(-1);
assert.equal(typeof last, "string");
return JSON.parse(last as string) as { id: string; type: string; method: string; params?: unknown };
}
respond(registry: LinkRegistry, computerId: number, result: unknown): void {
const request = this.lastRequest();
registry.handleFrame(this as unknown as WebSocket, JSON.stringify({ type: "response", id: request.id, ok: true, result }));
assert.equal(computerId > 0, true);
}
respondError(registry: LinkRegistry, computerId: number, error: string, result: unknown): void {
const request = this.lastRequest();
registry.handleFrame(this as unknown as WebSocket, JSON.stringify({ type: "response", id: request.id, ok: false, error, result }));
assert.equal(computerId > 0, true);
}
}