/**
* This test module tests that index.ts can be imported and works correctly
*/
import { vi } from 'vitest';
// Mock logger first
const mockLogger = {
info: vi.fn(),
debug: vi.fn(),
error: vi.fn(),
warn: vi.fn(),
};
vi.mock('../utils/logging/logger.js', () => ({
createLogger: vi.fn(() => mockLogger),
}));
// Mock feature flags
const mockFeatures = {
toolDiscovery: false,
enhancedLogging: false,
metrics: false,
cache: false,
};
vi.mock('../config/features.js', () => ({
getFeatureFlags: vi.fn(() => mockFeatures),
logFeatureFlags: vi.fn(),
}));
// Mock environment config
vi.mock('../config/environment.js', () => ({
getEnvironmentConfig: vi.fn(() => ({
discovery: {
directories: ['./src/tools/test'],
patterns: ['*.tool.js', '*.tool.mjs'],
recursive: true,
includeCategories: [],
excludeCategories: [],
includeTags: [],
excludeTags: [],
},
})),
}));
// Set up all mocks BEFORE any imports - using unstable_mockModule for ES modules
vi.mock('@modelcontextprotocol/sdk/server/mcp.js', () => ({
McpServer: vi.fn().mockImplementation(() => ({
registerTool: vi.fn(),
connect: vi.fn(),
setRequestHandler: vi.fn(),
})),
}));
vi.mock('@modelcontextprotocol/sdk/server/stdio.js', () => ({
StdioServerTransport: vi.fn(),
}));
vi.mock('../server/tool-registry.js', () => ({
ToolRegistry: vi.fn().mockImplementation(() => ({
registerTool: vi.fn(),
getTools: vi.fn().mockReturnValue([]),
})),
}));
vi.mock('../handlers/base/handler.factory.js', () => ({
createDefaultHandlerDeps: vi.fn(() => ({
client: {},
logger: {
info: vi.fn(),
debug: vi.fn(),
error: vi.fn(),
warn: vi.fn(),
},
})),
createBaseHandlerFactory: vi.fn(),
}));
vi.mock('../server/tool-registration.js', () => ({
registerDeepSourceTools: vi.fn(),
}));
const mockMcpServer = {
getRegisteredTools: vi.fn().mockReturnValue(['projects', 'issues']),
getMcpServer: vi.fn().mockReturnValue({
registerTool: vi.fn(),
}),
start: vi.fn(),
discoverTools: vi.fn().mockResolvedValue([]),
getToolsInfo: vi.fn().mockReturnValue([]),
};
vi.mock('../server/mcp-server.js', () => ({
DeepSourceMCPServer: {
create: vi.fn().mockResolvedValue(mockMcpServer),
},
}));
// Now import test utilities after mocks are set
const { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } = await import('vitest');
describe('index.ts module', () => {
// Import the module
let indexModule: {
mcpServer: {
current: {
getRegisteredTools: () => string[];
getMcpServer: () => unknown;
start?: () => Promise<void>;
};
};
getMcpServer: () => unknown;
handleCliArgs: (args: string[]) => boolean;
initializeServer: () => Promise<void>;
};
let originalEnv: typeof process.env;
let consoleLogSpy: ReturnType<typeof vi.spyOn>;
beforeAll(async () => {
// Save original env
originalEnv = { ...process.env };
// Set required environment variables for testing
process.env.DEEPSOURCE_API_KEY = 'test-key';
process.env.NODE_ENV = 'test';
// Import the module - this should trigger the code execution
// and provide us with the exported mcpServer object
indexModule = await import('../index.js');
});
beforeEach(() => {
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {
// Intentionally empty - suppressing console output during tests
return undefined;
});
vi.clearAllMocks();
});
afterEach(() => {
consoleLogSpy.mockRestore();
});
afterAll(() => {
// Restore original env
process.env = originalEnv;
});
it('should export an mcpServer instance', () => {
expect(indexModule.mcpServer).toBeDefined();
expect(indexModule.mcpServer.current).toBeDefined();
});
it('should have a server with appropriate methods', () => {
expect(typeof indexModule.mcpServer.current.getRegisteredTools).toBe('function');
expect(typeof indexModule.getMcpServer).toBe('function');
});
describe('handleCliArgs', () => {
it('should handle --version flag', () => {
const result = indexModule.handleCliArgs(['--version']);
expect(result).toBe(true);
expect(consoleLogSpy).toHaveBeenCalledWith(
expect.stringContaining('deepsource-mcp-server version')
);
});
it('should handle -v flag', () => {
const result = indexModule.handleCliArgs(['-v']);
expect(result).toBe(true);
expect(consoleLogSpy).toHaveBeenCalledWith(
expect.stringContaining('deepsource-mcp-server version')
);
});
it('should show enabled features with version flag when features are enabled', () => {
mockFeatures.toolDiscovery = true;
mockFeatures.enhancedLogging = true;
const result = indexModule.handleCliArgs(['--version']);
expect(result).toBe(true);
expect(consoleLogSpy).toHaveBeenCalledWith(
expect.stringContaining('Enabled features: toolDiscovery, enhancedLogging')
);
// Reset
mockFeatures.toolDiscovery = false;
mockFeatures.enhancedLogging = false;
});
it('should handle --help flag', () => {
const result = indexModule.handleCliArgs(['--help']);
expect(result).toBe(true);
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('DeepSource MCP Server'));
expect(consoleLogSpy).toHaveBeenCalledWith(
expect.stringContaining('Usage: deepsource-mcp-server')
);
});
it('should return false for unknown args', () => {
const result = indexModule.handleCliArgs(['--unknown']);
expect(result).toBe(false);
expect(consoleLogSpy).not.toHaveBeenCalled();
});
});
describe('Tool discovery and feature flags', () => {
it('should log feature flags when enhancedLogging is enabled', async () => {
const { logFeatureFlags } = await import('../config/features.js');
mockFeatures.enhancedLogging = true;
await indexModule.initializeServer();
expect(logFeatureFlags).toHaveBeenCalled();
// Reset
mockFeatures.enhancedLogging = false;
});
it('should log feature flags in development mode', async () => {
const { logFeatureFlags } = await import('../config/features.js');
const originalEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'development';
await indexModule.initializeServer();
expect(logFeatureFlags).toHaveBeenCalled();
// Reset
process.env.NODE_ENV = originalEnv;
});
it('should perform tool discovery when feature is enabled', async () => {
mockFeatures.toolDiscovery = true;
await indexModule.initializeServer();
expect(mockMcpServer.discoverTools).toHaveBeenCalled();
expect(mockLogger.info).toHaveBeenCalledWith(
'Tool discovery enabled, scanning for additional tools...'
);
// Reset
mockFeatures.toolDiscovery = false;
});
it('should log discovered tools when found', async () => {
mockFeatures.toolDiscovery = true;
mockMcpServer.discoverTools.mockResolvedValueOnce(['tool1', 'tool2']);
await indexModule.initializeServer();
expect(mockMcpServer.discoverTools).toHaveBeenCalled();
expect(mockLogger.info).toHaveBeenCalledWith(
'Discovered 2 additional tools',
expect.objectContaining({ tools: ['tool1', 'tool2'] })
);
// Reset
mockFeatures.toolDiscovery = false;
mockMcpServer.discoverTools.mockResolvedValue([]);
});
it('should not log when no tools discovered', async () => {
mockFeatures.toolDiscovery = true;
mockMcpServer.discoverTools.mockResolvedValueOnce([]);
await indexModule.initializeServer();
expect(mockMcpServer.discoverTools).toHaveBeenCalled();
// Should not log about discovered tools when array is empty
expect(mockLogger.info).not.toHaveBeenCalledWith(
expect.stringContaining('Discovered'),
expect.any(Object)
);
// Reset
mockFeatures.toolDiscovery = false;
});
it('should pass discovery config when tool discovery is enabled', async () => {
const { DeepSourceMCPServer } = await import('../server/mcp-server.js');
mockFeatures.toolDiscovery = true;
await indexModule.initializeServer();
expect(DeepSourceMCPServer.create).toHaveBeenCalledWith(
expect.objectContaining({
autoRegisterTools: true,
autoStart: false,
discoveryConfig: {
directories: ['./src/tools/test'],
patterns: ['*.tool.js', '*.tool.mjs'],
recursive: true,
includeCategories: [],
excludeCategories: [],
includeTags: [],
excludeTags: [],
},
})
);
// Reset
mockFeatures.toolDiscovery = false;
});
it('should log enabled features on startup', async () => {
mockFeatures.toolDiscovery = true;
mockFeatures.metrics = true;
await indexModule.initializeServer();
expect(mockLogger.info).toHaveBeenCalledWith(
'DeepSource MCP Server initialized successfully',
expect.objectContaining({
features: ['toolDiscovery', 'metrics'],
})
);
// Reset
mockFeatures.toolDiscovery = false;
mockFeatures.metrics = false;
});
});
});