Files
edgeever/apps/api/src/mcp-json-rpc.test.mjs
T

53 lines
2.0 KiB
JavaScript

import { describe, expect, test } from "bun:test";
import { AppError } from "./app-error";
import {
asRecord,
decodeBase64Data,
escapeMarkdownImageAlt,
getJsonRpcId,
getRequiredString,
getRequiredStringArray,
jsonRpcError,
jsonRpcResult,
mapMcpToolError,
} from "./mcp-json-rpc";
describe("MCP JSON-RPC helpers", () => {
test("builds protocol results and errors", () => {
expect(jsonRpcResult(1, { ok: true })).toEqual({ jsonrpc: "2.0", id: 1, result: { ok: true } });
expect(jsonRpcError("request", -32602, "Invalid params", { field: "memoId" })).toEqual({
jsonrpc: "2.0",
id: "request",
error: { code: -32602, message: "Invalid params", data: { field: "memoId" } },
});
});
test("accepts only valid IDs, records, and required values", () => {
expect(getJsonRpcId({ id: 0 })).toBe(0);
expect(getJsonRpcId({ id: false })).toBeNull();
expect(asRecord(["not", "a", "record"])).toEqual({});
expect(getRequiredString(" memo_1 ", "memoId")).toBe("memo_1");
expect(getRequiredStringArray(["one", 2, "two"], "items")).toEqual(["one", "two"]);
expect(() => getRequiredString(" ", "memoId")).toThrow(AppError);
});
test("decodes raw and data URL base64 payloads", async () => {
expect(new TextDecoder().decode(await decodeBase64Data("RWRnZUV2ZXI="))).toBe("EdgeEver");
expect(new TextDecoder().decode(await decodeBase64Data("data:text/plain;base64,RWRnZUV2ZXI="))).toBe("EdgeEver");
});
test("escapes Markdown image labels", () => {
expect(escapeMarkdownImageAlt("[cover] \\ draft")).toBe("\\[cover\\] \\\\ draft");
});
test("maps application errors to stable MCP error codes", () => {
expect(mapMcpToolError(new AppError("memo_missing", "Memo not found", 404))).toEqual({
rpcCode: -32004,
status: 404,
message: "Memo not found",
data: { code: "memo_missing" },
});
expect(mapMcpToolError(new Error("boom"))).toMatchObject({ rpcCode: -32000, status: 400, message: "boom" });
});
});