Skip to main content
Glama
by wei
error-handlers.test.ts9.59 kB
/** * Unit Tests for Error Handlers * * Tests all error mapping functions and MCP result formatting */ import { describe, expect, it } from "vitest"; import { ZodError, z } from "zod"; import { type ErrorResponse, checkItemExists, createErrorResult, createSuccessResult, handleAPIError, handleValidationError, } from "../../src/utils/error-handlers.js"; describe("createErrorResult", () => { it("should create MCP error result with correct structure", () => { const error: ErrorResponse = { type: "validation", message: "Test error", statusCode: 400, }; const result = createErrorResult(error); expect(result.isError).toBe(true); expect(result.content).toHaveLength(1); expect(result.content[0]?.type).toBe("text"); expect(result.content[0]?.text).toContain("validation"); expect(result.content[0]?.text).toContain("Test error"); }); it("should include error details in result", () => { const error: ErrorResponse = { type: "api", message: "API failed", details: { code: "ERR_001" }, statusCode: 500, }; const result = createErrorResult(error); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("api"); expect(parsed.message).toBe("API failed"); expect(parsed.details).toEqual({ code: "ERR_001" }); expect(parsed.statusCode).toBe(500); }); }); describe("handleValidationError", () => { it("should handle ZodError with field path and message", () => { const schema = z.object({ query: z.string().min(1), }); try { schema.parse({ query: "" }); } catch (error) { if (error instanceof ZodError) { const result = handleValidationError(error); expect(result.isError).toBe(true); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("validation"); expect(parsed.message).toContain("query"); expect(parsed.statusCode).toBe(400); } } }); it("should include all validation errors in details", () => { const schema = z.object({ username: z .string() .min(1) .regex(/^[a-zA-Z0-9_]+$/), page: z.number().nonnegative(), }); try { schema.parse({ username: "", page: -1 }); } catch (error) { if (error instanceof ZodError) { const result = handleValidationError(error); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.details).toBeInstanceOf(Array); expect(parsed.details.length).toBeGreaterThan(0); } } }); it("should handle nested field paths", () => { const schema = z.object({ filters: z.object({ points: z.number().min(0), }), }); try { schema.parse({ filters: { points: -1 } }); } catch (error) { if (error instanceof ZodError) { const result = handleValidationError(error); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.message).toContain("filters.points"); } } }); }); describe("handleAPIError", () => { it("should handle timeout errors", () => { const error = new Error("Request timeout after 5000ms"); const result = handleAPIError(error, "fetching item"); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("network"); expect(parsed.message).toContain("timeout"); expect(parsed.statusCode).toBe(504); }); it("should handle rate limit errors (429)", () => { const error = new Error("HTTP 429: Too Many Requests"); const result = handleAPIError(error, "searching"); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("rate_limit"); expect(parsed.message).toContain("Rate limit exceeded"); expect(parsed.statusCode).toBe(429); }); it("should handle not found errors (404)", () => { const error = new Error("HTTP 404: Not Found"); const result = handleAPIError(error, "fetching user"); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("not_found"); expect(parsed.message).toContain("Not found"); expect(parsed.statusCode).toBe(404); }); it("should handle generic HTTP errors", () => { const error = new Error("HTTP 500: Internal Server Error"); const result = handleAPIError(error, "API request"); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("api"); expect(parsed.statusCode).toBe(500); }); it("should handle non-HTTP errors", () => { const error = new Error("Network connection failed"); const result = handleAPIError(error, "connecting"); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("api"); expect(parsed.message).toContain("Network connection failed"); }); it("should handle unknown error types", () => { const error = "Unknown error"; const result = handleAPIError(error, "operation"); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("unknown"); expect(parsed.message).toContain("Unknown error"); }); it("should include context in error message", () => { const error = new Error("Something went wrong"); const result = handleAPIError(error, "fetching front page"); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.message).toContain("fetching front page"); }); }); describe("createSuccessResult", () => { it("should create MCP success result with structured content", () => { const data = { query: "AI", hits: [], nbHits: 0, }; const result = createSuccessResult(data); expect(result.isError).toBe(false); expect(result.content).toHaveLength(1); expect(result.content[0]?.type).toBe("text"); expect(result.structuredContent).toEqual(data); }); it("should serialize data to JSON in content", () => { const data = { username: "pg", karma: 155678, }; const result = createSuccessResult(data); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.username).toBe("pg"); expect(parsed.karma).toBe(155678); }); it("should handle null values", () => { const data = { title: "Test", url: null, }; const result = createSuccessResult(data); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.url).toBeNull(); }); it("should handle arrays", () => { const data = { hits: [ { id: "1", title: "First" }, { id: "2", title: "Second" }, ], }; const result = createSuccessResult(data); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.hits).toHaveLength(2); }); }); describe("checkItemExists", () => { it("should return null for valid non-empty object", () => { const data = { id: "123", title: "Test" }; const result = checkItemExists(data, "Item", "123"); expect(result).toBeNull(); }); it("should return error result for null data", () => { const result = checkItemExists(null, "Item", "123"); expect(result).not.toBeNull(); expect(result?.isError).toBe(true); const parsed = JSON.parse(result?.content[0]?.text ?? "{}"); expect(parsed.type).toBe("not_found"); expect(parsed.message).toContain("Item not found"); expect(parsed.message).toContain("123"); }); it("should return error result for empty object", () => { const result = checkItemExists({}, "User", "pg"); expect(result).not.toBeNull(); expect(result?.isError).toBe(true); const parsed = JSON.parse(result?.content[0]?.text ?? "{}"); expect(parsed.type).toBe("not_found"); expect(parsed.message).toContain("User"); expect(parsed.message).toContain("pg"); }); it("should return error result for undefined data", () => { const result = checkItemExists(undefined, "Comment", "456"); expect(result).not.toBeNull(); expect(result?.isError).toBe(true); }); it("should use itemType in error message", () => { const result = checkItemExists(null, "Story", "789"); const parsed = JSON.parse(result?.content[0]?.text ?? "{}"); expect(parsed.message).toContain("Story not found"); }); it("should use identifier in error message", () => { const result = checkItemExists(null, "Item", "test-id-123"); const parsed = JSON.parse(result?.content[0]?.text ?? "{}"); expect(parsed.message).toContain("test-id-123"); }); }); describe("Error Type Classification", () => { it("should correctly identify validation errors", () => { const schema = z.string().min(1); try { schema.parse(""); } catch (error) { if (error instanceof ZodError) { const result = handleValidationError(error); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("validation"); } } }); it("should correctly identify network errors", () => { const error = new Error("Request timeout after 5000ms"); const result = handleAPIError(error); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("network"); }); it("should correctly identify rate limit errors", () => { const error = new Error("HTTP 429"); const result = handleAPIError(error); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("rate_limit"); }); it("should correctly identify not found errors", () => { const error = new Error("HTTP 404"); const result = handleAPIError(error); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("not_found"); }); it("should correctly identify API errors", () => { const error = new Error("HTTP 500"); const result = handleAPIError(error); const parsed = JSON.parse(result.content[0]?.text ?? "{}"); expect(parsed.type).toBe("api"); }); });

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/wei/hn-mcp-server'

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