Skip to main content
Glama
mcp-server.test.ts25.9 kB
/** * CognitiveMCPServer Tests * * Tests for MCP server initialization, tool registration, connection handling, and error recovery. * Following TDD principles - these tests define expected behavior before implementation. * * Requirements: 11.1, 11.2, 11.3, 11.4, 11.5 */ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; // Import implementation import { CognitiveMCPServer } from "../../../server/mcp-server.js"; import type { MCPTool } from "../../../server/types.js"; // Mock all cognitive components vi.mock("../../../memory/memory-repository.js", () => ({ MemoryRepository: vi.fn().mockImplementation(() => ({ healthCheck: vi.fn().mockResolvedValue({ healthy: true }), disconnect: vi.fn().mockResolvedValue(undefined), })), })); vi.mock("../../../reasoning/orchestrator.js", () => ({ ParallelReasoningOrchestrator: vi.fn().mockImplementation(() => ({})), })); vi.mock("../../../framework/framework-selector.js", () => ({ DynamicFrameworkSelector: vi.fn().mockImplementation(() => ({})), })); vi.mock("../../../confidence/multi-dimensional-assessor.js", () => ({ MultiDimensionalConfidenceAssessor: vi.fn().mockImplementation(() => ({})), })); vi.mock("../../../bias/bias-pattern-recognizer.js", () => ({ BiasPatternRecognizer: vi.fn().mockImplementation(() => ({ detectBiases: vi.fn().mockResolvedValue([]), })), })); vi.mock("../../../emotion/circumplex-analyzer.js", () => ({ CircumplexEmotionAnalyzer: vi.fn().mockImplementation(() => ({})), })); vi.mock("../../../metacognitive/performance-monitoring-system.js", () => ({ PerformanceMonitoringSystem: vi.fn().mockImplementation(() => ({})), })); vi.mock("../../../database/connection-manager.js", () => ({ DatabaseConnectionManager: vi.fn().mockImplementation(() => ({ connect: vi.fn().mockResolvedValue(undefined), disconnect: vi.fn().mockResolvedValue(undefined), healthCheck: vi.fn().mockResolvedValue(true), })), })); vi.mock("../../../embeddings/embedding-engine.js", () => ({ EmbeddingEngine: vi.fn().mockImplementation(() => ({ initialize: vi.fn().mockResolvedValue(undefined), shutdown: vi.fn().mockResolvedValue(undefined), })), })); vi.mock("../../../utils/logger.js", () => ({ Logger: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn(), }, })); describe("CognitiveMCPServer", () => { let server: CognitiveMCPServer; beforeEach(() => { // Reset all mocks before each test vi.clearAllMocks(); server = new CognitiveMCPServer(); }); afterEach(async () => { // Cleanup: shutdown server if initialized if (server.isInitialized) { try { await server.shutdown(); } catch { // Ignore errors during cleanup } } vi.clearAllMocks(); }); describe("Server Startup and Initialization", () => { it("should start successfully with all components", async () => { // Mock all initialization methods to succeed vi.spyOn(server as any, "initializeComponents").mockResolvedValue(undefined); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); expect(server.isInitialized).toBe(true); }); it("should initialize all cognitive components", async () => { // Mock initialization to actually create the components vi.spyOn(server as any, "initializeComponents").mockImplementation(async () => { server.memoryRepository = {} as any; server.reasoningOrchestrator = {} as any; server.frameworkSelector = {} as any; server.confidenceAssessor = {} as any; server.biasDetector = {} as any; server.emotionAnalyzer = {} as any; server.performanceMonitor = {} as any; }); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); // Verify all components are initialized expect(server.memoryRepository).toBeDefined(); expect(server.reasoningOrchestrator).toBeDefined(); expect(server.frameworkSelector).toBeDefined(); expect(server.confidenceAssessor).toBeDefined(); expect(server.biasDetector).toBeDefined(); expect(server.emotionAnalyzer).toBeDefined(); expect(server.performanceMonitor).toBeDefined(); }); it("should register all tools during initialization", async () => { vi.spyOn(server as any, "initializeComponents").mockResolvedValue(undefined); vi.spyOn(server as any, "registerTools").mockImplementation(() => { // Register a test tool server.toolRegistry.registerTool({ name: "test_tool", description: "Test tool", inputSchema: { type: "object", properties: {} }, handler: async () => ({ success: true }), }); }); await server.initialize(); const tools = server.getTools(); expect(tools.length).toBeGreaterThan(0); }); it("should complete initialization within 5 seconds", async () => { vi.spyOn(server as any, "initializeComponents").mockResolvedValue(undefined); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); const startTime = Date.now(); await server.initialize(); const duration = Date.now() - startTime; expect(duration).toBeLessThan(5000); }); it("should handle initialization timeout", async () => { // Mock a component that takes too long to initialize vi.spyOn(server as any, "initializeComponents").mockImplementation( () => new Promise((resolve) => setTimeout(resolve, 10000)) ); await expect(server.initialize()).rejects.toThrow("Initialization timeout"); }); it("should not allow double initialization", async () => { vi.spyOn(server as any, "initializeComponents").mockResolvedValue(undefined); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); await expect(server.initialize()).rejects.toThrow("Server already initialized"); }); it("should rollback on initialization failure", async () => { // Mock a component initialization failure vi.spyOn(server as any, "initializeMemoryRepository").mockRejectedValue( new Error("Database connection failed") ); vi.spyOn(server as any, "rollbackInitialization").mockResolvedValue(undefined); await expect(server.initialize()).rejects.toThrow(); expect(server.isInitialized).toBe(false); }); }); describe("Server Shutdown and Cleanup", () => { beforeEach(async () => { vi.spyOn(server as any, "initializeComponents").mockResolvedValue(undefined); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); }); it("should shutdown gracefully", async () => { vi.spyOn(server as any, "shutdownComponents").mockResolvedValue(undefined); await server.shutdown(); expect(server.isInitialized).toBe(false); }); it("should cleanup all components", async () => { const shutdownSpy = vi .spyOn(server as any, "shutdownComponents") .mockResolvedValue(undefined); await server.shutdown(); expect(shutdownSpy).toHaveBeenCalled(); }); it("should terminate all connections", async () => { // Set up a mock database manager const mockDisconnect = vi.fn().mockResolvedValue(undefined); (server as any).databaseManager = { disconnect: mockDisconnect }; vi.spyOn(server as any, "shutdownComponents").mockImplementation(async () => { if ((server as any).databaseManager) { await (server as any).databaseManager.disconnect(); (server as any).databaseManager = undefined; } }); await server.shutdown(); expect(mockDisconnect).toHaveBeenCalled(); }); it("should release all resources", async () => { // Set up mock components server.memoryRepository = {} as any; server.reasoningOrchestrator = {} as any; vi.spyOn(server as any, "shutdownComponents").mockImplementation(async () => { server.memoryRepository = undefined; server.reasoningOrchestrator = undefined; }); await server.shutdown(); // Verify resources are released expect(server.memoryRepository).toBeUndefined(); expect(server.reasoningOrchestrator).toBeUndefined(); }); it("should complete shutdown within 10 seconds", async () => { vi.spyOn(server as any, "shutdownComponents").mockResolvedValue(undefined); const startTime = Date.now(); await server.shutdown(); const duration = Date.now() - startTime; expect(duration).toBeLessThan(10000); }); it("should handle shutdown errors gracefully", async () => { // Mock a component shutdown failure vi.spyOn(server as any, "shutdownComponents").mockRejectedValue( new Error("Component shutdown failed") ); // Should not throw, but log error await expect(server.shutdown()).resolves.not.toThrow(); }); it("should not allow operations after shutdown", async () => { vi.spyOn(server as any, "shutdownComponents").mockResolvedValue(undefined); await server.shutdown(); const result = await server.executeTool("test_tool", {}); expect(result.success).toBe(false); expect(result.error).toContain("Server not initialized"); }); }); }); describe("Tool Registration and Discovery", () => { let server: CognitiveMCPServer; beforeEach(async () => { server = new CognitiveMCPServer(); vi.spyOn(server as any, "initializeComponents").mockResolvedValue(undefined); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); }); afterEach(async () => { if (server.isInitialized) { try { await server.shutdown(); } catch { // Ignore errors during cleanup } } vi.clearAllMocks(); }); it("should register all required tools", async () => { // Register placeholder tool server.toolRegistry.registerTool({ name: "placeholder", description: "Placeholder tool", inputSchema: { type: "object", properties: {} }, handler: async () => ({ success: true }), }); const tools = server.getTools(); // Verify placeholder tool exists const placeholderTool = tools.find((t) => t.name === "placeholder"); expect(placeholderTool).toBeDefined(); }); it("should return valid tool schemas", async () => { server.toolRegistry.registerTool({ name: "test_tool", description: "Test tool", inputSchema: { type: "object", properties: {} }, handler: async () => ({ success: true }), }); const tools = server.getTools(); tools.forEach((tool) => { expect(tool.name).toBeDefined(); expect(tool.description).toBeDefined(); expect(tool.inputSchema).toBeDefined(); }); }); it("should allow tool discovery", async () => { const tools = server.getTools(); expect(Array.isArray(tools)).toBe(true); }); it("should prevent duplicate tool registration", async () => { const tool: MCPTool = { name: "test_tool", description: "Test tool", inputSchema: { type: "object", properties: {}, }, handler: async () => ({ success: true }), }; server.toolRegistry.registerTool(tool); expect(() => server.toolRegistry.registerTool(tool)).toThrow("Tool already registered"); }); it("should validate tool schemas on registration", async () => { const invalidTool: any = { name: "invalid_tool", // Missing required fields }; expect(() => server.toolRegistry.registerTool(invalidTool)).toThrow("Invalid tool schema"); }); it("should complete tool registration within 1 second", async () => { const startTime = Date.now(); server.toolRegistry.registerTool({ name: "test_tool", description: "Test tool", inputSchema: { type: "object", properties: {} }, handler: async () => ({ success: true }), }); const duration = Date.now() - startTime; expect(duration).toBeLessThan(1000); }); }); describe("Connection Handling and Lifecycle", () => { let server: CognitiveMCPServer; beforeEach(async () => { server = new CognitiveMCPServer(); vi.spyOn(server as any, "initializeComponents").mockImplementation(async () => { (server as any).databaseManager = { connect: vi.fn().mockResolvedValue(undefined), disconnect: vi.fn().mockResolvedValue(undefined), healthCheck: vi.fn().mockResolvedValue(true), }; }); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); }); afterEach(async () => { if (server.isInitialized) { try { await server.shutdown(); } catch { // Ignore errors during cleanup } } vi.clearAllMocks(); }); it("should establish connections successfully", async () => { const connectionStatus = server.getConnectionStatus(); expect(connectionStatus.connected).toBe(true); }); it("should manage connection state", async () => { expect(server.getConnectionStatus().state).toBe("connected"); vi.spyOn(server as any, "shutdownComponents").mockResolvedValue(undefined); await server.shutdown(); expect(server.getConnectionStatus().state).toBe("disconnected"); }); it("should handle connection errors", async () => { // Mock a connection error vi.spyOn(server as any, "checkConnection").mockResolvedValue(false); vi.spyOn(server as any, "reconnect").mockRejectedValue(new Error("Connection lost")); const status = await server.healthCheck(); expect(status.healthy).toBe(false); }); it("should implement reconnection logic", async () => { // Simulate connection loss vi.spyOn(server as any, "checkConnection").mockResolvedValue(false); // Should attempt reconnection const reconnectSpy = vi.spyOn(server as any, "reconnect").mockResolvedValue(undefined); await server.healthCheck(); expect(reconnectSpy).toHaveBeenCalled(); }); }); describe("Error Handling and Recovery", () => { let server: CognitiveMCPServer; beforeEach(() => { server = new CognitiveMCPServer(); }); afterEach(async () => { if (server.isInitialized) { try { await server.shutdown(); } catch { // Ignore errors during cleanup } } vi.clearAllMocks(); }); it("should handle component initialization failures", async () => { // Mock the entire initialization chain to fail at memory repository vi.spyOn(server as any, "initializeDatabaseManager").mockResolvedValue(undefined); vi.spyOn(server as any, "initializeEmbeddingEngine").mockResolvedValue(undefined); vi.spyOn(server as any, "initializeMemoryRepository").mockRejectedValue( new Error("Database connection failed") ); vi.spyOn(server as any, "rollbackInitialization").mockResolvedValue(undefined); await expect(server.initialize()).rejects.toThrow("Database connection failed"); expect(server.isInitialized).toBe(false); }); it("should handle tool execution errors", async () => { vi.spyOn(server as any, "initializeComponents").mockResolvedValue(undefined); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); // Mock tool that throws error server.toolRegistry.registerTool({ name: "error_tool", description: "Tool that throws error", inputSchema: { type: "object", properties: {} }, handler: async () => { throw new Error("Tool execution failed"); }, }); const result = await server.executeTool("error_tool", {}); expect(result.success).toBe(false); expect(result.error).toContain("Tool execution failed"); }); it("should implement graceful degradation", async () => { vi.spyOn(server as any, "initializeComponents").mockImplementation(async () => { server.memoryRepository = {} as any; server.reasoningOrchestrator = {} as any; server.frameworkSelector = {} as any; server.confidenceAssessor = {} as any; server.biasDetector = {} as any; server.emotionAnalyzer = {} as any; server.performanceMonitor = {} as any; (server as any).databaseManager = { healthCheck: vi.fn().mockResolvedValue(true), }; }); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); // Simulate component failure by removing it server.biasDetector = undefined; // Server should continue operating without bias detection const health = await server.healthCheck(); expect(health.degraded).toBe(true); expect(health.unavailableComponents).toContain("biasDetector"); }); it("should log all errors with context", async () => { const { Logger } = await import("../../../utils/logger.js"); const loggerSpy = vi.spyOn(Logger, "error"); vi.spyOn(server as any, "initializeComponents").mockImplementation(async () => { (server as any).databaseManager = { healthCheck: vi.fn().mockResolvedValue(true), }; }); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); // Trigger an error vi.spyOn(server as any, "checkConnection").mockRejectedValue(new Error("Connection error")); await server.healthCheck(); expect(loggerSpy).toHaveBeenCalled(); }); it("should provide user-friendly error messages", async () => { vi.spyOn(server as any, "initializeComponents").mockResolvedValue(undefined); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); const result = await server.executeTool("nonexistent_tool", {}); expect(result.success).toBe(false); expect(result.error).toContain("Tool not found"); expect(result.suggestion).toBeDefined(); }); it("should recover from transient errors", async () => { vi.spyOn(server as any, "initializeComponents").mockImplementation(async () => { server.memoryRepository = {} as any; server.reasoningOrchestrator = {} as any; server.frameworkSelector = {} as any; server.confidenceAssessor = {} as any; server.evidenceExtractor = {} as any; server.biasDetector = {} as any; server.emotionAnalyzer = {} as any; server.performanceMonitor = {} as any; (server as any).databaseManager = { healthCheck: vi.fn().mockResolvedValue(true), disconnect: vi.fn().mockResolvedValue(undefined), connect: vi.fn().mockResolvedValue(undefined), }; }); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); // Simulate transient error by making a component fail temporarily const originalMemoryRepo = server.memoryRepository; server.memoryRepository = undefined; // First call fails due to missing component let health = await server.healthCheck(); expect(health.healthy).toBe(false); expect(health.degraded).toBe(true); // Restore component (recovery) server.memoryRepository = originalMemoryRepo; // Second call succeeds health = await server.healthCheck(); expect(health.healthy).toBe(true); expect(health.degraded).toBe(false); }); }); describe("Health Check and Status", () => { let server: CognitiveMCPServer; beforeEach(async () => { server = new CognitiveMCPServer(); vi.spyOn(server as any, "initializeComponents").mockImplementation(async () => { server.memoryRepository = {} as any; server.reasoningOrchestrator = {} as any; server.frameworkSelector = {} as any; server.confidenceAssessor = {} as any; server.evidenceExtractor = {} as any; server.biasDetector = {} as any; server.emotionAnalyzer = {} as any; server.performanceMonitor = {} as any; (server as any).databaseManager = { healthCheck: vi.fn().mockResolvedValue(true), }; }); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); }); afterEach(async () => { if (server.isInitialized) { try { await server.shutdown(); } catch { // Ignore errors during cleanup } } vi.clearAllMocks(); }); it("should provide health check endpoint", async () => { vi.spyOn(server as any, "checkConnection").mockResolvedValue(true); const health = await server.healthCheck(); expect(health).toBeDefined(); expect(health.healthy).toBeDefined(); }); it("should report component status", async () => { vi.spyOn(server as any, "checkConnection").mockResolvedValue(true); const health = await server.healthCheck(); expect(health.components).toBeDefined(); expect(health.components.memoryRepository).toBe("healthy"); expect(health.components.reasoningOrchestrator).toBe("healthy"); }); it("should provide performance metrics", async () => { vi.spyOn(server as any, "checkConnection").mockResolvedValue(true); // Wait a bit to ensure uptime > 0 await new Promise((resolve) => setTimeout(resolve, 10)); const health = await server.healthCheck(); expect(health.metrics).toBeDefined(); expect(health.metrics.uptime).toBeGreaterThanOrEqual(0); expect(health.metrics.requestCount).toBeGreaterThanOrEqual(0); }); it("should perform readiness checks", async () => { vi.spyOn(server as any, "checkConnection").mockResolvedValue(true); const health = await server.healthCheck(); expect(health.ready).toBe(true); }); it("should respond to health check within 100ms", async () => { vi.spyOn(server as any, "checkConnection").mockResolvedValue(true); const startTime = Date.now(); await server.healthCheck(); const duration = Date.now() - startTime; expect(duration).toBeLessThan(100); }); it("should report unhealthy when components fail", async () => { // Simulate component failure server.memoryRepository = undefined; vi.spyOn(server as any, "checkConnection").mockResolvedValue(true); const health = await server.healthCheck(); expect(health.healthy).toBe(false); expect(health.components.memoryRepository).toBe("unhealthy"); }); it("should include timestamp in health status", async () => { vi.spyOn(server as any, "checkConnection").mockResolvedValue(true); const health = await server.healthCheck(); expect(health.timestamp).toBeDefined(); expect(new Date(health.timestamp).getTime()).toBeGreaterThan(0); }); }); describe("Tool Execution", () => { let server: CognitiveMCPServer; beforeEach(async () => { server = new CognitiveMCPServer(); vi.spyOn(server as any, "initializeComponents").mockResolvedValue(undefined); vi.spyOn(server as any, "registerTools").mockImplementation(() => {}); await server.initialize(); }); afterEach(async () => { if (server.isInitialized) { try { await server.shutdown(); } catch { // Ignore errors during cleanup } } vi.clearAllMocks(); }); it("should execute tools successfully", async () => { // Register a test tool server.toolRegistry.registerTool({ name: "test_tool", description: "Test tool", inputSchema: { type: "object", properties: {} }, handler: async () => ({ success: true, data: { value: "test" } }), }); const result = await server.executeTool("test_tool", {}); expect(result.success).toBe(true); expect((result.data as any).value).toBe("test"); }); it("should validate tool parameters", async () => { server.toolRegistry.registerTool({ name: "param_tool", description: "Tool with parameters", inputSchema: { type: "object", properties: { required_param: { type: "string" }, }, required: ["required_param"], }, handler: async (params: any) => ({ success: true, params }), }); // Missing required parameter const result = await server.executeTool("param_tool", {}); expect(result.success).toBe(false); expect(result.error).toContain("required_param"); }); it("should include metadata in responses", async () => { server.toolRegistry.registerTool({ name: "meta_tool", description: "Tool with metadata", inputSchema: { type: "object", properties: {} }, handler: async () => ({ success: true }), }); const result = await server.executeTool("meta_tool", {}); expect(result.metadata).toBeDefined(); expect(result.metadata?.timestamp).toBeDefined(); expect(result.metadata?.processingTime).toBeDefined(); expect(result.metadata?.componentsUsed).toBeDefined(); }); it("should handle tool not found", async () => { const result = await server.executeTool("nonexistent_tool", {}); expect(result.success).toBe(false); expect(result.error).toContain("Tool not found"); }); it("should handle concurrent tool executions", async () => { server.toolRegistry.registerTool({ name: "concurrent_tool", description: "Tool for concurrent testing", inputSchema: { type: "object", properties: {} }, handler: async () => { await new Promise((resolve) => setTimeout(resolve, 100)); return { success: true }; }, }); const promises = Array.from({ length: 10 }, () => server.executeTool("concurrent_tool", {})); const results = await Promise.all(promises); results.forEach((result) => { expect(result.success).toBe(true); }); }); });

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/keyurgolani/ThoughtMcp'

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