mcp-clickhouse

Official
import { jest, describe, it, expect, beforeEach, afterEach, } from "@jest/globals"; import { SystemPromptService } from "../../services/systemprompt-service.js"; import { handleToolCall } from "../tool-handlers.js"; import type { CallToolRequest } from "@modelcontextprotocol/sdk/types.js"; import { TOOLS } from "../../constants/tools.js"; import type { SystempromptPromptRequest, SystempromptBlockRequest, SystempromptAgentRequest, } from "../../types/index.js"; // Mock the server config jest.mock("../../config/server-config.js", () => ({ serverConfig: { port: 3000, host: "localhost", }, serverCapabilities: { tools: [], }, })); // Mock the main function jest.mock("../../index.ts", () => { const mockNotification = jest.fn().mockImplementation(async () => {}); return { main: jest.fn(), server: { notification: mockNotification, }, }; }); interface MockPrompt { id: string; metadata: { title: string; description: string; }; content: string; } const mockPrompt: MockPrompt = { id: "prompt123", metadata: { title: "Test Prompt", description: "A test prompt", }, content: "Test content", }; const mockBlock: MockPrompt = { id: "block123", metadata: { title: "Test Block", description: "A test block", }, content: "Test content", }; const mockAgent: MockPrompt = { id: "agent123", metadata: { title: "Test Agent", description: "A test agent", }, content: "Test content", }; interface UserStatus { user: { name: string; email: string; roles: string[]; }; billing: { customer: { id: string; email: string; status: string; }; subscription: Array<{ id: string; status: string; currency_code: string; billing_cycle: { frequency: number; interval: string; }; current_billing_period: { starts_at: string; ends_at: string; }; items: Array<{ product: { name: string }; price: { unit_price: { amount: string; currency_code: string }; }; }>; }>; }; api_key: string; } // Mock SystemPromptService with proper types const mockSystemPromptService = { fetchUserStatus: jest.fn<() => Promise<UserStatus>>().mockResolvedValue({ user: { name: "Test User", email: "test@example.com", roles: ["user"], }, billing: { customer: { id: "cust123", email: "test@example.com", status: "active", }, subscription: [ { id: "sub123", status: "active", currency_code: "USD", billing_cycle: { frequency: 1, interval: "month", }, current_billing_period: { starts_at: "2024-01-01T00:00:00Z", ends_at: "2024-02-01T00:00:00Z", }, items: [ { product: { name: "Pro Plan" }, price: { unit_price: { amount: "1000", currency_code: "USD" }, }, }, ], }, ], }, api_key: "test-api-key", }), getAllPrompts: jest .fn<() => Promise<MockPrompt[]>>() .mockResolvedValue([mockPrompt]), listBlocks: jest .fn<() => Promise<MockPrompt[]>>() .mockResolvedValue([mockBlock]), listAgents: jest .fn<() => Promise<MockPrompt[]>>() .mockResolvedValue([mockAgent]), createPrompt: jest .fn<() => Promise<MockPrompt>>() .mockResolvedValue(mockPrompt), editPrompt: jest .fn<() => Promise<MockPrompt>>() .mockResolvedValue(mockPrompt), deletePrompt: jest.fn<() => Promise<{}>>().mockResolvedValue({}), deleteBlock: jest.fn<() => Promise<{}>>().mockResolvedValue({}), createBlock: jest .fn<() => Promise<MockPrompt>>() .mockResolvedValue(mockBlock), editBlock: jest.fn<() => Promise<MockPrompt>>().mockResolvedValue(mockBlock), createAgent: jest .fn<() => Promise<MockPrompt>>() .mockResolvedValue(mockAgent), editAgent: jest.fn<() => Promise<MockPrompt>>().mockResolvedValue(mockAgent), }; // Mock the SystemPromptService module jest.mock("../../services/systemprompt-service.js", () => ({ SystemPromptService: { getInstance: jest.fn(() => mockSystemPromptService), }, })); // Mock the prompt handlers jest.mock("../prompt-handlers.js", () => ({ handleGetPrompt: jest.fn().mockImplementation(async () => ({ messages: [ { role: "assistant", content: { type: "text", text: "Test prompt", }, }, ], })), })); interface SamplingResponse { content: Array<{ type: string; text: string; }>; } // Mock the sampling module jest.mock("../sampling.js", () => { const mockFn = jest.fn(); mockFn.mockImplementation(async () => ({ content: [ { type: "text", text: JSON.stringify({ metadata: { title: "Test Resource", description: "A test resource", }, content: "Test content", }), }, ], })); return { sendSamplingRequest: mockFn }; }); describe("Tool Handlers", () => { let mockSamplingModule: { sendSamplingRequest: jest.Mock }; beforeEach(() => { jest.clearAllMocks(); mockSamplingModule = require("../sampling.js"); // Set up default mock implementation for sampling requests mockSamplingModule.sendSamplingRequest.mockImplementation(async () => ({ content: [ { type: "text", text: JSON.stringify({ metadata: { title: "Test Resource", description: "A test resource", }, content: "Test content", }), }, ], })); }); describe("handleToolCall", () => { describe("Heartbeat", () => { it("should handle systemprompt_heartbeat", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_heartbeat", params: {}, }, }; const result = await handleToolCall(request); expect(mockSystemPromptService.fetchUserStatus).toHaveBeenCalled(); expect(result.content[0].type).toBe("text"); expect(result.content[0].text).toContain("User Information"); expect(result.content[0].text).toContain("Billing"); }); it("should handle empty subscription list in heartbeat", async () => { mockSystemPromptService.fetchUserStatus.mockResolvedValueOnce({ user: { name: "Test User", email: "test@example.com", roles: ["user"], }, billing: { customer: { id: "cust123", email: "test@example.com", status: "active", }, subscription: [], }, api_key: "test-api-key", }); const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_heartbeat", params: {}, }, }; const result = await handleToolCall(request); expect(result.content[0].type).toBe("text"); expect(result.content[0].text).toContain("User Information"); expect(result.content[0].text).toContain("Billing"); }); it("should handle empty resource lists in fetch resources", async () => { mockSystemPromptService.getAllPrompts.mockResolvedValueOnce([]); mockSystemPromptService.listBlocks.mockResolvedValueOnce([]); mockSystemPromptService.listAgents.mockResolvedValueOnce([]); const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_fetch_resources", params: {}, }, }; const result = await handleToolCall(request); expect(result.content[0].type).toBe("text"); expect(result.content[0].text).toContain("Agents"); expect(result.content[0].text).toContain("Prompts"); expect(result.content[0].text).toContain("Resources"); }); }); describe("Resource Operations", () => { it("should handle systemprompt_fetch_resources", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_fetch_resources", params: {}, }, }; const result = await handleToolCall(request); expect(mockSystemPromptService.getAllPrompts).toHaveBeenCalled(); expect(mockSystemPromptService.listBlocks).toHaveBeenCalled(); expect(mockSystemPromptService.listAgents).toHaveBeenCalled(); expect(result.content[0].type).toBe("text"); expect(result.content[0].text).toContain("Agents"); expect(result.content[0].text).toContain("Prompts"); expect(result.content[0].text).toContain("Resources"); }); it("should handle create resource request", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_create_resource", arguments: { type: "block", userInstructions: "Create a test block", }, }, }; const result = await handleToolCall(request); expect(result.content[0].type).toBe("text"); expect(result.content[0].text).toBe( "Your request has been recieved and is being processed, we will notify you when it is complete." ); }); it("should handle update resource request", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_update_resource", arguments: { id: "test-id", type: "block", userInstructions: "Update test block", }, }, }; const result = await handleToolCall(request); expect(result.content[0].type).toBe("text"); expect(result.content[0].text).toBe( "Your request has been recieved and is being processed, we will notify you when it is complete." ); }); it("should handle systemprompt_delete_resource", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_delete_resource", arguments: { id: "test-id", }, }, }; const result = await handleToolCall(request); expect(result).toBeDefined(); }); it("should handle invalid resource type for create", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_create_resource", arguments: { type: "invalid", userInstructions: "Create an invalid resource", }, }, }; await expect(handleToolCall(request)).rejects.toThrow( "Invalid resource type: invalid" ); }); it("should handle invalid resource type for update", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_update_resource", arguments: { id: "test-id", type: "invalid", userInstructions: "Update an invalid resource", }, }, }; await expect(handleToolCall(request)).rejects.toThrow( "Invalid resource type: invalid" ); }); it("should handle delete resource failure", async () => { mockSystemPromptService.deletePrompt.mockRejectedValueOnce( new Error("Failed to delete prompt") ); mockSystemPromptService.deleteBlock.mockRejectedValueOnce( new Error("Failed to delete block") ); const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_delete_resource", arguments: { id: "nonexistent123", }, }, }; await expect(handleToolCall(request)).rejects.toThrow( "Failed to delete resource with ID nonexistent123" ); }); it("should handle missing id for delete resource", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_delete_resource", arguments: {}, }, }; await expect(handleToolCall(request)).rejects.toThrow( "ID is required for deleting a resource" ); }); it("should handle invalid params for create resource", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_create_resource", arguments: {}, }, }; await expect(handleToolCall(request)).rejects.toThrow( "Tool call failed: Missing required parameters - type and userInstructions are required" ); }); it("should handle invalid params for update resource", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_update_resource", arguments: {}, }, }; await expect(handleToolCall(request)).rejects.toThrow( "Tool call failed: Missing required parameters - id, type and userInstructions are required" ); }); it("should handle successful block deletion", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_delete_resource", arguments: { id: "test-block-id", }, }, }; const result = await handleToolCall(request); expect(result.content[0].type).toBe("text"); expect(result.content[0].text).toBe( "Successfully deleted prompt test-block-id" ); }); }); describe("Error Handling", () => { it("should handle invalid tool name", async () => { const request: CallToolRequest = { method: "tools/call", params: { name: "invalid_tool", params: {}, }, }; await expect(handleToolCall(request)).rejects.toThrow( "Unknown tool: invalid_tool" ); }); it("should handle service errors", async () => { mockSystemPromptService.fetchUserStatus.mockRejectedValueOnce( new Error("Service error") ); const request: CallToolRequest = { method: "tools/call", params: { name: "systemprompt_heartbeat", params: {}, }, }; await expect(handleToolCall(request)).rejects.toThrow("Service error"); }); }); }); });