Skip to main content
Glama
error-handling.test.ts4.38 kB
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { Sandbox } from "./sandbox.js"; import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; // Per AGENTS.md: keep tests strict and focused on failure paths. describe("Error handling and edge cases", () => { let sandbox: Sandbox; let mockMcpClients: Map<string, Client>; beforeEach(async () => { mockMcpClients = new Map(); const okClient: Partial<Client> = { callTool: vi.fn().mockResolvedValue({ content: [{ type: "text", text: "OK" }], isError: false, }) as any, }; mockMcpClients.set("filesystem", okClient as Client); sandbox = new Sandbox(mockMcpClients as any); await sandbox.initialize(); }); afterEach(async () => { await sandbox.cleanup(); vi.clearAllMocks(); }); it("1) 文件不存在: readFile(nonexistent.txt) → 抛出并被沙箱捕获", async () => { const client = mockMcpClients.get("filesystem") as any; // Simulate MCP server returning an error payload for missing file client.callTool.mockResolvedValueOnce({ isError: true, content: [ { type: "text", text: "ENOENT: no such file or directory, open 'nonexistent.txt'", }, ], }); const code = ` import { readFile } from "./servers/filesystem/read_file.js"; await readFile({ path: "nonexistent.txt" }); `; const res = await sandbox.executeCode(code); expect(res.success).toBe(false); expect(res.error || "").toMatch( /ENOENT|nonexistent\.txt|read_file failed/i, ); }); it("2) MCP Server 连接失败: 未注册的服务器名", async () => { // Use a fresh sandbox with NO clients registered const sandbox2 = new Sandbox(new Map() as any); await sandbox2.initialize(); try { const code = ` import { readFile } from "./servers/filesystem/read_file.js"; await readFile({ path: "test.txt" }); `; const res = await sandbox2.executeCode(code); expect(res.success).toBe(false); expect(res.error || "").toMatch( /MCP server not connected: \s*filesystem/i, ); } finally { await sandbox2.cleanup(); } }); it("3) 无效的 MCP 响应格式: 返回非预期结构但应优雅回退", async () => { const client = mockMcpClients.get("filesystem") as any; // Return a shape that read_file.js does not explicitly handle client.callTool.mockResolvedValueOnce({ foo: "bar", nested: { a: 1 } }); const code = ` import { readFile } from "./servers/filesystem/read_file.js"; const out = await readFile({ path: "whatever.txt" }); console.log(out); `; const res = await sandbox.executeCode(code); expect(res.success).toBe(true); // read_file.ts falls back to JSON.stringify(resp) expect(res.output || "").toMatch(/"foo"\s*:\s*"bar"/); expect(res.output || "").toMatch(/"nested"/); }); it("4) IPC 通信失败: 子进程缺少 process.send", async () => { const code = ` import { readFile } from "./servers/filesystem/read_file.js"; // Simulate IPC not available in child process (process as any).send = undefined; await readFile({ path: "test.txt" }); `; const res = await sandbox.executeCode(code); expect(res.success).toBe(false); expect(res.error || "").toMatch( /IPC channel not available: process\.send is undefined/, ); }); it("5) 超出 CPU 限制: 计算卡死应在 10s 超时终止", async () => { const code = "while(true) {}"; const start = Date.now(); const res = await sandbox.executeCode(code); const elapsed = Date.now() - start; expect(res.success).toBe(false); expect(res.error || "").toMatch(/timeout|killed/i); expect(elapsed).toBeGreaterThanOrEqual(9000); }, 15000); // Optional memory stress (disabled by default to avoid OOM on CI) it.skip("5b) 超出内存限制: 大量分配应崩溃/被终止 (可在受控环境启用)", async () => { const code = ` const chunks: Buffer[] = []; for (let i = 0; i < 1024; i++) { chunks.push(Buffer.alloc(32 * 1024 * 1024)); // ~32MB each } console.log('allocated', chunks.length); `; const res = await sandbox.executeCode(code); expect(res.success).toBe(false); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cexll/code-mode-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server