@zerospacegg/anrubic
Version:
Anrubic - ZeroSpace.gg MCP Server for AI agents to access game data
168 lines (139 loc) • 5 kB
text/typescript
// 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");
}
});
});