UNPKG

@zerospacegg/anrubic

Version:

Anrubic - ZeroSpace.gg MCP Server for AI agents to access game data

168 lines (139 loc) 5 kB
// Tests for basic MCP protocol compliance import assert from "node:assert"; import { ChildProcess, spawn } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; import { after, before, describe, it } from "node:test"; import { fileURLToPath } from "node:url"; import { createMockMCPRequest, expectedMCPTools, restoreConsole, suppressConsole, validateMCPResponse, validateToolSchema, } from "./setup.ts"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); describe("Anrubic MCP Protocol Tests", () => { let serverProcess: ChildProcess; before(async () => { suppressConsole(); // We'll test the actual compiled server const serverPath = path.join(__dirname, "../dist/index.js"); // Ensure server is built assert.ok(fs.existsSync(serverPath), "Server must be built before testing"); }); after(() => { if (serverProcess) { serverProcess.kill(); } restoreConsole(); }); async function sendMCPRequest(request: any): Promise<any> { return new Promise((resolve, reject) => { const serverPath = path.join(__dirname, "../dist/index.js"); const process = spawn("node", [serverPath], { stdio: ["pipe", "pipe", "pipe"], }); let output = ""; let timeoutId: NodeJS.Timeout; const cleanup = () => { if (timeoutId) clearTimeout(timeoutId); process.kill(); }; // Set timeout for the request timeoutId = setTimeout(() => { cleanup(); reject(new Error("Request timed out")); }, 10000); // 10 second timeout process.stdout.on("data", (data) => { output += data.toString(); // Look for JSON-RPC response const lines = output.split("\n"); for (const line of lines) { const trimmed = line.trim(); if (trimmed.startsWith("{") && trimmed.includes('"jsonrpc"')) { try { const response = JSON.parse(trimmed); cleanup(); resolve(response); return; } catch (e) { // Not valid JSON yet, continue } } } }); process.stderr.on("data", (data) => { // Ignore stderr for now (server startup logs) }); process.on("error", (error) => { cleanup(); reject(error); }); process.on("exit", (code) => { if (code !== 0) { cleanup(); reject(new Error(`Process exited with code ${code}`)); } }); // Send the request process.stdin.write(JSON.stringify(request) + "\n"); process.stdin.end(); }); } it("should respond to tools/list request", async () => { const request = createMockMCPRequest("tools/list"); const response = await sendMCPRequest(request); validateMCPResponse(response, request.id); assert.ok(response.result.tools, "Response should contain tools array"); assert.ok(Array.isArray(response.result.tools), "Tools should be an array"); assert.ok( response.result.tools.length > 0, "Should have at least one tool" ); }); it("should have all expected MCP tools", async () => { const request = createMockMCPRequest("tools/list"); const response = await sendMCPRequest(request); const tools = response.result.tools; const toolNames = tools.map((tool: any) => tool.name); for (const expectedTool of expectedMCPTools) { assert.ok( toolNames.includes(expectedTool), `Should include tool: ${expectedTool}` ); } }); it("should have valid tool schemas", async () => { const request = createMockMCPRequest("tools/list"); const response = await sendMCPRequest(request); const tools = response.result.tools; for (const tool of tools) { assert.doesNotThrow( () => validateToolSchema(tool), `Tool ${tool.name} should have valid schema` ); } }); it("should respond with proper JSON-RPC format", async () => { const request = createMockMCPRequest("tools/list", undefined, 42); const response = await sendMCPRequest(request); assert.strictEqual(response.jsonrpc, "2.0", "Should use JSON-RPC 2.0"); assert.strictEqual(response.id, 42, "Should echo request ID"); assert.ok(response.result, "Should have result field"); assert.ok(!response.error, "Should not have error field on success"); }); it("should handle invalid method gracefully", async () => { const request = createMockMCPRequest("invalid/method"); try { const response = await sendMCPRequest(request); // If we get a response, it should be an error assert.ok(response.error, "Should return error for invalid method"); } catch (error) { // It's also acceptable if the process exits/times out for invalid methods assert.ok(true, "Server may exit or timeout for invalid methods"); } }); });