alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
381 lines (319 loc) • 10.3 kB
text/typescript
import { describe, expect, test } from "vitest";
import {
createErrorResponse,
createInternalError,
createInvalidParamsError,
createInvalidRequestError,
createMethodNotFoundError,
createNotification,
createParseError,
createResponse,
isNotification,
isValidJsonRpcRequest,
JSONRPC_VERSION,
JsonRpcErrorCodes,
JsonRpcParseError,
MCP_PROTOCOL_VERSION,
parseMessage,
} from "../index.ts";
// ---------------------------------------------------------------------------------------------------------------------
describe("jsonrpc constants", () => {
test("JSONRPC_VERSION should be 2.0", () => {
expect(JSONRPC_VERSION).toBe("2.0");
});
test("MCP_PROTOCOL_VERSION should be defined", () => {
expect(MCP_PROTOCOL_VERSION).toBe("2025-11-25");
});
test("JsonRpcErrorCodes should have correct values", () => {
expect(JsonRpcErrorCodes.PARSE_ERROR).toBe(-32700);
expect(JsonRpcErrorCodes.INVALID_REQUEST).toBe(-32600);
expect(JsonRpcErrorCodes.METHOD_NOT_FOUND).toBe(-32601);
expect(JsonRpcErrorCodes.INVALID_PARAMS).toBe(-32602);
expect(JsonRpcErrorCodes.INTERNAL_ERROR).toBe(-32603);
});
});
// ---------------------------------------------------------------------------------------------------------------------
describe("createResponse", () => {
test("should create a valid JSON-RPC response", () => {
const response = createResponse(1, { foo: "bar" });
expect(response).toEqual({
jsonrpc: "2.0",
id: 1,
result: { foo: "bar" },
});
});
test("should work with string id", () => {
const response = createResponse("abc-123", "hello");
expect(response).toEqual({
jsonrpc: "2.0",
id: "abc-123",
result: "hello",
});
});
test("should work with null result", () => {
const response = createResponse(1, null);
expect(response).toEqual({
jsonrpc: "2.0",
id: 1,
result: null,
});
});
});
// ---------------------------------------------------------------------------------------------------------------------
describe("createErrorResponse", () => {
test("should create a valid JSON-RPC error response", () => {
const response = createErrorResponse(1, {
code: -32600,
message: "Invalid request",
});
expect(response).toEqual({
jsonrpc: "2.0",
id: 1,
error: {
code: -32600,
message: "Invalid request",
},
});
});
test("should include error data if provided", () => {
const response = createErrorResponse(1, {
code: -32602,
message: "Invalid params",
data: { param: "foo" },
});
expect(response.error?.data).toEqual({ param: "foo" });
});
});
// ---------------------------------------------------------------------------------------------------------------------
describe("createNotification", () => {
test("should create a notification without params", () => {
const notification = createNotification("notifications/initialized");
expect(notification).toEqual({
jsonrpc: "2.0",
method: "notifications/initialized",
});
});
test("should create a notification with params", () => {
const notification = createNotification("notifications/progress", {
token: "abc",
value: 50,
});
expect(notification).toEqual({
jsonrpc: "2.0",
method: "notifications/progress",
params: {
token: "abc",
value: 50,
},
});
});
});
// ---------------------------------------------------------------------------------------------------------------------
describe("error builders", () => {
test("createParseError should create parse error", () => {
const error = createParseError();
expect(error.code).toBe(JsonRpcErrorCodes.PARSE_ERROR);
expect(error.message).toBe("Parse error");
});
test("createParseError should accept custom message", () => {
const error = createParseError("Custom parse error");
expect(error.message).toBe("Custom parse error");
});
test("createInvalidRequestError should create invalid request error", () => {
const error = createInvalidRequestError();
expect(error.code).toBe(JsonRpcErrorCodes.INVALID_REQUEST);
expect(error.message).toBe("Invalid request");
});
test("createMethodNotFoundError should include method name", () => {
const error = createMethodNotFoundError("tools/call");
expect(error.code).toBe(JsonRpcErrorCodes.METHOD_NOT_FOUND);
expect(error.message).toBe("Method not found: tools/call");
});
test("createInvalidParamsError should include message", () => {
const error = createInvalidParamsError("Missing required param: name");
expect(error.code).toBe(JsonRpcErrorCodes.INVALID_PARAMS);
expect(error.message).toBe("Missing required param: name");
});
test("createInternalError should include message", () => {
const error = createInternalError("Something went wrong");
expect(error.code).toBe(JsonRpcErrorCodes.INTERNAL_ERROR);
expect(error.message).toBe("Something went wrong");
});
});
// ---------------------------------------------------------------------------------------------------------------------
describe("parseMessage", () => {
test("should parse valid JSON-RPC request", () => {
const json = JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/list",
});
const request = parseMessage(json);
expect(request).toEqual({
jsonrpc: "2.0",
id: 1,
method: "tools/list",
});
});
test("should parse request with params", () => {
const json = JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: { name: "calculator", arguments: { a: 1, b: 2 } },
});
const request = parseMessage(json);
expect(request.method).toBe("tools/call");
expect(request.params).toEqual({
name: "calculator",
arguments: { a: 1, b: 2 },
});
});
test("should parse notification (no id)", () => {
const json = JSON.stringify({
jsonrpc: "2.0",
method: "notifications/initialized",
});
const request = parseMessage(json);
expect(request.id).toBeUndefined();
expect(request.method).toBe("notifications/initialized");
});
test("should throw JsonRpcParseError for invalid JSON", () => {
expect(() => parseMessage("not json")).toThrow(JsonRpcParseError);
expect(() => parseMessage("not json")).toThrow("Invalid JSON");
});
test("should throw JsonRpcParseError for invalid JSON-RPC version", () => {
const json = JSON.stringify({
jsonrpc: "1.0",
id: 1,
method: "test",
});
expect(() => parseMessage(json)).toThrow(JsonRpcParseError);
expect(() => parseMessage(json)).toThrow("Invalid JSON-RPC request");
});
test("should throw JsonRpcParseError for missing method", () => {
const json = JSON.stringify({
jsonrpc: "2.0",
id: 1,
});
expect(() => parseMessage(json)).toThrow(JsonRpcParseError);
});
});
// ---------------------------------------------------------------------------------------------------------------------
describe("isValidJsonRpcRequest", () => {
test("should return true for valid request", () => {
expect(
isValidJsonRpcRequest({
jsonrpc: "2.0",
id: 1,
method: "test",
}),
).toBe(true);
});
test("should return true for notification", () => {
expect(
isValidJsonRpcRequest({
jsonrpc: "2.0",
method: "test",
}),
).toBe(true);
});
test("should return true for request with params", () => {
expect(
isValidJsonRpcRequest({
jsonrpc: "2.0",
id: 1,
method: "test",
params: { foo: "bar" },
}),
).toBe(true);
});
test("should return false for null", () => {
expect(isValidJsonRpcRequest(null)).toBe(false);
});
test("should return false for non-object", () => {
expect(isValidJsonRpcRequest("string")).toBe(false);
expect(isValidJsonRpcRequest(123)).toBe(false);
});
test("should return false for wrong version", () => {
expect(
isValidJsonRpcRequest({
jsonrpc: "1.0",
id: 1,
method: "test",
}),
).toBe(false);
});
test("should return false for non-string method", () => {
expect(
isValidJsonRpcRequest({
jsonrpc: "2.0",
id: 1,
method: 123,
}),
).toBe(false);
});
test("should return false for invalid id type", () => {
expect(
isValidJsonRpcRequest({
jsonrpc: "2.0",
id: { foo: "bar" },
method: "test",
}),
).toBe(false);
});
test("should return false for non-object params", () => {
expect(
isValidJsonRpcRequest({
jsonrpc: "2.0",
id: 1,
method: "test",
params: "string",
}),
).toBe(false);
});
});
// ---------------------------------------------------------------------------------------------------------------------
describe("isNotification", () => {
test("should return true when id is undefined", () => {
expect(
isNotification({
jsonrpc: "2.0",
method: "test",
}),
).toBe(true);
});
test("should return false when id is present", () => {
expect(
isNotification({
jsonrpc: "2.0",
id: 1,
method: "test",
}),
).toBe(false);
});
test("should return false when id is 0", () => {
expect(
isNotification({
jsonrpc: "2.0",
id: 0,
method: "test",
}),
).toBe(false);
});
});
// ---------------------------------------------------------------------------------------------------------------------
describe("JsonRpcParseError", () => {
test("should be an instance of Error", () => {
const error = new JsonRpcParseError("test");
expect(error).toBeInstanceOf(Error);
});
test("should have correct name", () => {
const error = new JsonRpcParseError("test");
expect(error.name).toBe("JsonRpcParseError");
});
test("should have correct message", () => {
const error = new JsonRpcParseError("Custom message");
expect(error.message).toBe("Custom message");
});
});