Skip to main content
Glama

1MCP Server

server.test.ts13.9 kB
import { ServerManager } from '@src/core/server/serverManager.js'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { ExpressServer } from './server.js'; // Mock all external dependencies vi.mock('express', () => { const mockRouter = () => ({ use: vi.fn(), get: vi.fn(), post: vi.fn(), delete: vi.fn(), }); const mockApp = () => ({ use: vi.fn(), listen: vi.fn((port, host, callback) => { if (callback) callback(); }), }); const mockExpress = Object.assign(vi.fn(mockApp), { Router: vi.fn(mockRouter), }); return { default: mockExpress, Router: mockRouter, }; }); vi.mock('body-parser', () => ({ default: { json: vi.fn(() => 'json-middleware'), urlencoded: vi.fn(() => 'urlencoded-middleware'), }, })); vi.mock('cors', () => ({ default: vi.fn(() => 'cors-middleware'), })); vi.mock('@modelcontextprotocol/sdk/server/auth/router.js', () => ({ mcpAuthRouter: vi.fn(() => 'auth-router'), })); vi.mock('@src/logger/logger.js', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn(), }, })); vi.mock('./middlewares/errorHandler.js', () => ({ default: 'error-handler', })); vi.mock('./middlewares/securityMiddleware.js', () => ({ setupSecurityMiddleware: vi.fn(() => [vi.fn()]), })); vi.mock('./middlewares/scopeAuthMiddleware.js', () => ({ createScopeAuthMiddleware: vi.fn(() => vi.fn()), })); vi.mock('./middlewares/httpRequestLogger.js', () => ({ httpRequestLogger: vi.fn(), })); vi.mock('./routes/streamableHttpRoutes.js', () => ({ setupStreamableHttpRoutes: vi.fn(), })); vi.mock('./routes/sseRoutes.js', () => ({ setupSseRoutes: vi.fn(), })); vi.mock('./routes/oauthRoutes.js', () => ({ default: vi.fn(() => 'oauth-routes'), })); vi.mock('./routes/healthRoutes.js', () => ({ default: vi.fn(() => 'health-routes'), })); vi.mock('@src/auth/sdkOAuthServerProvider.js', () => ({ SDKOAuthServerProvider: vi.fn().mockImplementation(() => ({ shutdown: vi.fn(), })), })); vi.mock('@src/core/server/agentConfig.js', () => ({ AgentConfigManager: { getInstance: vi.fn(), }, })); vi.mock('../../core/server/serverManager.js', () => ({ ServerManager: vi.fn(), })); describe('ExpressServer', () => { let mockApp: any; let mockServerManager: ServerManager; let mockConfigManager: any; let expressServer: ExpressServer; beforeEach(async () => { vi.clearAllMocks(); // Mock Express app mockApp = { use: vi.fn(), set: vi.fn(), listen: vi.fn((port, host, callback) => { if (callback) callback(); }), }; // Mock Express constructor and Router const express = await import('express'); const mockRouter = { use: vi.fn(), get: vi.fn(), post: vi.fn(), delete: vi.fn(), } as any; vi.mocked(express.default).mockReturnValue(mockApp); vi.mocked(express.default.Router).mockReturnValue(mockRouter); // Mock ServerManager mockServerManager = { getClients: vi.fn(() => new Map()), getServer: vi.fn(), } as any; // Mock AgentConfigManager mockConfigManager = { getSessionStoragePath: vi.fn(() => '/tmp/sessions'), isEnhancedSecurityEnabled: vi.fn(() => false), getTrustProxy: vi.fn(() => 'loopback'), getConfig: vi.fn(() => ({ host: 'localhost', port: 3050 })), getRateLimitWindowMs: vi.fn(() => 900000), getRateLimitMax: vi.fn(() => 100), isAuthEnabled: vi.fn(() => false), getUrl: vi.fn(() => 'http://localhost:3050'), }; const { AgentConfigManager } = await import('@src/core/server/agentConfig.js'); vi.mocked(AgentConfigManager.getInstance).mockReturnValue(mockConfigManager); // Ensure SDKOAuthServerProvider returns an object with shutdown method const { SDKOAuthServerProvider } = await import('../../auth/sdkOAuthServerProvider.js'); vi.mocked(SDKOAuthServerProvider).mockImplementation( () => ({ shutdown: vi.fn(), }) as any, ); }); afterEach(() => { vi.resetAllMocks(); }); describe('Constructor', () => { it('should create ExpressServer instance', async () => { expressServer = new ExpressServer(mockServerManager); expect(expressServer).toBeInstanceOf(ExpressServer); }); it('should setup middleware and routes', async () => { expressServer = new ExpressServer(mockServerManager); // Verify middleware setup expect(mockApp.use).toHaveBeenCalled(); expect(mockApp.use.mock.calls.length).toBeGreaterThan(3); }); it('should handle enhanced security when enabled', async () => { const { setupSecurityMiddleware } = await import('./middlewares/securityMiddleware.js'); vi.mocked(setupSecurityMiddleware).mockReturnValue([vi.fn()]); mockConfigManager.isEnhancedSecurityEnabled.mockReturnValue(true); expressServer = new ExpressServer(mockServerManager); expect(mockApp.use).toHaveBeenCalled(); }); it('should initialize with custom session storage path', async () => { mockConfigManager.getSessionStoragePath.mockReturnValue('/custom/path'); expressServer = new ExpressServer(mockServerManager); expect(expressServer).toBeInstanceOf(ExpressServer); }); }); describe('Server Operations', () => { it('should start server successfully', async () => { expressServer = new ExpressServer(mockServerManager); expect(() => { expressServer.start(); }).not.toThrow(); expect(mockApp.listen).toHaveBeenCalledWith(3050, 'localhost', expect.any(Function)); }); it('should start server with custom configuration', async () => { mockConfigManager.getConfig.mockReturnValue({ host: '0.0.0.0', port: 8080 }); expressServer = new ExpressServer(mockServerManager); expressServer.start(); expect(mockApp.listen).toHaveBeenCalledWith(8080, '0.0.0.0', expect.any(Function)); }); it('should shutdown gracefully', async () => { expressServer = new ExpressServer(mockServerManager); expect(() => { expressServer.shutdown(); }).not.toThrow(); }); it('should handle authentication enabled status', async () => { mockConfigManager.isAuthEnabled.mockReturnValue(true); expressServer = new ExpressServer(mockServerManager); expressServer.start(); expect(mockApp.listen).toHaveBeenCalled(); }); }); describe('Trust Proxy Configuration', () => { it('should set trust proxy to default loopback value', async () => { mockConfigManager.getTrustProxy.mockReturnValue('loopback'); expressServer = new ExpressServer(mockServerManager); expect(mockApp.set).toHaveBeenCalledWith('trust proxy', 'loopback'); }); it('should set trust proxy to boolean true', async () => { mockConfigManager.getTrustProxy.mockReturnValue(true); expressServer = new ExpressServer(mockServerManager); expect(mockApp.set).toHaveBeenCalledWith('trust proxy', true); }); it('should set trust proxy to boolean false', async () => { mockConfigManager.getTrustProxy.mockReturnValue(false); expressServer = new ExpressServer(mockServerManager); expect(mockApp.set).toHaveBeenCalledWith('trust proxy', false); }); it('should set trust proxy to custom IP address', async () => { mockConfigManager.getTrustProxy.mockReturnValue('192.168.1.1'); expressServer = new ExpressServer(mockServerManager); expect(mockApp.set).toHaveBeenCalledWith('trust proxy', '192.168.1.1'); }); it('should set trust proxy to CIDR range', async () => { mockConfigManager.getTrustProxy.mockReturnValue('192.168.0.0/16'); expressServer = new ExpressServer(mockServerManager); expect(mockApp.set).toHaveBeenCalledWith('trust proxy', '192.168.0.0/16'); }); it('should set trust proxy to preset values', async () => { const presets = ['loopback', 'linklocal', 'uniquelocal']; for (const preset of presets) { vi.clearAllMocks(); mockConfigManager.getTrustProxy.mockReturnValue(preset); expressServer = new ExpressServer(mockServerManager); expect(mockApp.set).toHaveBeenCalledWith('trust proxy', preset); } }); it('should call getTrustProxy exactly once during construction', async () => { mockConfigManager.getTrustProxy.mockReturnValue('loopback'); expressServer = new ExpressServer(mockServerManager); expect(mockConfigManager.getTrustProxy).toHaveBeenCalledTimes(1); }); it('should set trust proxy before middleware setup', async () => { mockConfigManager.getTrustProxy.mockReturnValue('loopback'); expressServer = new ExpressServer(mockServerManager); // Trust proxy should be set before any middleware const setCallIndex = mockApp.set.mock.invocationCallOrder[0]; const useCallIndex = mockApp.use.mock.invocationCallOrder[0]; expect(setCallIndex).toBeLessThan(useCallIndex); }); }); describe('Configuration Handling', () => { it('should handle missing config gracefully', async () => { mockConfigManager.getConfig.mockReturnValue({ host: 'localhost', port: 3000 }); expect(() => { new ExpressServer(mockServerManager); }).not.toThrow(); }); it('should handle undefined session storage path', async () => { mockConfigManager.getSessionStoragePath.mockReturnValue(undefined); expect(() => { new ExpressServer(mockServerManager); }).not.toThrow(); }); it('should handle custom rate limiting configuration', async () => { mockConfigManager.getRateLimitWindowMs.mockReturnValue(600000); mockConfigManager.getRateLimitMax.mockReturnValue(50); expect(() => { new ExpressServer(mockServerManager); }).not.toThrow(); }); it('should work with minimal server manager', async () => { const minimalServerManager = {} as ServerManager; expect(() => { new ExpressServer(minimalServerManager); }).not.toThrow(); }); }); describe('Middleware Setup', () => { it('should register multiple middleware components', async () => { expressServer = new ExpressServer(mockServerManager); // Should have registered multiple middleware calls expect(mockApp.use.mock.calls.length).toBeGreaterThanOrEqual(4); }); it('should handle security middleware conditionally', async () => { const { setupSecurityMiddleware } = await import('./middlewares/securityMiddleware.js'); vi.mocked(setupSecurityMiddleware).mockReturnValue([vi.fn()]); // Test with security disabled mockConfigManager.isEnhancedSecurityEnabled.mockReturnValue(false); new ExpressServer(mockServerManager); // Test with security enabled mockConfigManager.isEnhancedSecurityEnabled.mockReturnValue(true); new ExpressServer(mockServerManager); expect(mockApp.use).toHaveBeenCalled(); }); }); describe('Route Setup', () => { it('should setup OAuth routes', async () => { expressServer = new ExpressServer(mockServerManager); // Should have called app.use for OAuth routes expect(mockApp.use).toHaveBeenCalled(); }); it('should setup MCP transport routes', async () => { expressServer = new ExpressServer(mockServerManager); // Should have setup routes expect(mockApp.use).toHaveBeenCalled(); }); it('should handle route setup with different configurations', async () => { // Test with different host/port configurations mockConfigManager.getConfig.mockReturnValue({ host: 'example.com', port: 9000 }); expect(() => { new ExpressServer(mockServerManager); }).not.toThrow(); }); }); describe('Error Handling', () => { it('should handle initialization errors gracefully', async () => { // Test with problematic config const configError = new Error('Config error'); mockConfigManager.getConfig.mockImplementation(() => { throw configError; }); mockConfigManager.getUrl.mockImplementation(() => { throw configError; }); expect(() => { new ExpressServer(mockServerManager); }).toThrow('Config error'); }); it('should handle shutdown with no OAuth provider', async () => { expressServer = new ExpressServer(mockServerManager); expect(() => { expressServer.shutdown(); }).not.toThrow(); }); }); describe('Integration', () => { it('should initialize all components in proper sequence', async () => { const setupCallOrder: string[] = []; mockApp.use.mockImplementation((middleware: any) => { if (typeof middleware === 'string') { setupCallOrder.push(middleware); } return mockApp; }); expressServer = new ExpressServer(mockServerManager); // Verify some middleware was set up expect(mockApp.use).toHaveBeenCalled(); expect(mockApp.use.mock.calls.length).toBeGreaterThan(0); }); it('should handle complete server lifecycle', async () => { expressServer = new ExpressServer(mockServerManager); // Start server expect(() => { expressServer.start(); }).not.toThrow(); // Shutdown server expect(() => { expressServer.shutdown(); }).not.toThrow(); }); it('should maintain state consistency', async () => { expressServer = new ExpressServer(mockServerManager); // Server should be in valid state expect(expressServer).toBeInstanceOf(ExpressServer); // Should be able to start multiple times expressServer.start(); expressServer.start(); expect(mockApp.listen).toHaveBeenCalledTimes(2); }); }); });

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/1mcp-app/agent'

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