Skip to main content
Glama
mcp-shared-fixtures.js.backup12.2 kB
import { vi } from 'vitest'; /** * Shared test fixtures and utilities for MCP test suite * This file contains common mocks, test data, and utilities used across multiple test files */ // Mock the mssql module first (must be hoisted) export const setupMssqlMock = () => { vi.mock('mssql', () => ({ default: { connect: vi.fn(), ConnectionPool: vi.fn() }, connect: vi.fn(), ConnectionPool: vi.fn() })); }; // Mock StdioServerTransport at the module level export const mockStdioTransport = { connect: vi.fn() }; export const setupStdioMock = () => { vi.mock('@modelcontextprotocol/sdk/server/stdio.js', () => ({ StdioServerTransport: vi.fn(() => mockStdioTransport) })); }; // Environment variable utilities export const originalEnv = process.env; export const getDefaultTestEnv = () => ({ ...originalEnv, SQL_SERVER_HOST: 'localhost', SQL_SERVER_PORT: '1433', SQL_SERVER_DATABASE: 'master', SQL_SERVER_USER: 'testuser', SQL_SERVER_PASSWORD: 'testpass' }); export const setupTestEnvironment = (customEnv = {}) => { process.env = { ...getDefaultTestEnv(), ...customEnv }; }; export const resetEnvironment = () => { process.env = originalEnv; }; // Mock objects for testing export const mockRequest = { query: vi.fn(), timeout: 30000 }; export const mockPool = { request: vi.fn(() => mockRequest), connected: true, close: vi.fn() }; // Comprehensive test data export const testData = { sampleDatabases: [ { database_name: 'TestDB1', database_id: 5, create_date: '2024-01-01T00:00:00.000Z', collation_name: 'SQL_Latin1_General_CP1_CI_AS', status: 'ONLINE' }, { database_name: 'TestDB2', database_id: 6, create_date: '2024-01-02T00:00:00.000Z', collation_name: 'SQL_Latin1_General_CP1_CI_AS', status: 'ONLINE' } ], sampleTables: [ { database_name: 'TestDB', schema_name: 'dbo', table_name: 'Users', table_type: 'BASE TABLE' }, { database_name: 'TestDB', schema_name: 'dbo', table_name: 'Orders', table_type: 'BASE TABLE' } ], sampleTableSchema: [ { column_name: 'id', data_type: 'int', max_length: null, precision: 10, scale: 0, is_nullable: 'NO', default_value: null, is_primary_key: 'YES' }, { column_name: 'name', data_type: 'varchar', max_length: 100, precision: null, scale: null, is_nullable: 'YES', default_value: null, is_primary_key: 'NO' } ], sampleTableData: [ { id: 1, name: 'John Doe', email: 'john@example.com' }, { id: 2, name: 'Jane Smith', email: 'jane@example.com' }, { id: 3, name: 'Bob Johnson', email: 'bob@example.com' } ], sampleForeignKeys: [ { constraint_name: 'FK_Orders_Users', table_schema: 'dbo', table_name: 'Orders', column_name: 'user_id', referenced_table_schema: 'dbo', referenced_table_name: 'Users', referenced_column_name: 'id' } ], sampleExecutionPlan: [ { StmtText: 'SELECT * FROM Users', StmtId: 1, NodeId: 1, Parent: 0, PhysicalOp: 'Clustered Index Scan', LogicalOp: 'Clustered Index Scan', EstimateRows: 1000, EstimateIO: 0.5, EstimateCPU: 0.1 } ] }; // Common test utilities export const createMockMcpServer = async (customConfig = {}) => { // Import SqlServerMCP first const { SqlServerMCP } = await import('../../index.js'); // Mock the setupToolHandlers to prevent actual MCP server initialization vi.spyOn(SqlServerMCP.prototype, 'setupToolHandlers').mockImplementation(() => {}); const server = new SqlServerMCP(); server.pool = null; // Reset pool // Apply any custom configuration Object.assign(server, customConfig); return server; }; export const resetMocks = () => { vi.clearAllMocks(); mockPool.connected = true; mockRequest.query.mockResolvedValue({ recordset: [], recordsets: [[]], rowsAffected: [0] }); }; export const setupDefaultMockResponses = () => { mockRequest.query.mockResolvedValue({ recordset: [], recordsets: [[]], rowsAffected: [0] }); }; // Security configuration helpers export const getSecurityConfigs = () => ({ readOnlyMode: { SQL_SERVER_READ_ONLY: 'true', SQL_SERVER_ALLOW_DESTRUCTIVE_OPERATIONS: 'false', SQL_SERVER_ALLOW_SCHEMA_CHANGES: 'false' }, dataAnalysisMode: { SQL_SERVER_READ_ONLY: 'false', SQL_SERVER_ALLOW_DESTRUCTIVE_OPERATIONS: 'true', SQL_SERVER_ALLOW_SCHEMA_CHANGES: 'false' }, fullAccessMode: { SQL_SERVER_READ_ONLY: 'false', SQL_SERVER_ALLOW_DESTRUCTIVE_OPERATIONS: 'true', SQL_SERVER_ALLOW_SCHEMA_CHANGES: 'true' } }); // Performance monitoring test data export const performanceTestData = { samplePerformanceStats: { summary: { totalQueries: 100, avgDuration: 150.5, slowQueries: 5, errorRate: 0.02 }, connectionHealth: { poolSize: 5, activeConnections: 3, idleConnections: 2 } }, sampleQueryMetrics: [ { toolName: 'execute_query', query: 'SELECT * FROM Users', duration: 120, timestamp: '2024-01-01T12:00:00Z', success: true }, { toolName: 'list_tables', query: 'SELECT * FROM INFORMATION_SCHEMA.TABLES', duration: 5500, timestamp: '2024-01-01T12:01:00Z', success: true } ] }; // CSV test data export const csvTestData = { sampleCsvOutput: 'id,name,email\n1,"John Doe","john@example.com"\n2,"Jane Smith","jane@example.com"', expectedCsvHeaders: ['id', 'name', 'email'], largeDataset: Array.from({ length: 1000 }, (_, i) => ({ id: i + 1, name: `User ${i + 1}`, data: `Sample data for user ${i + 1}` })) }; // Query test cases export const queryTestCases = { validQueries: { select: 'SELECT * FROM Users', selectWithWhere: 'SELECT id, name FROM Users WHERE active = 1', selectWithJoin: 'SELECT u.name, o.total FROM Users u JOIN Orders o ON u.id = o.user_id', cteQuery: 'WITH UserStats AS (SELECT COUNT(*) as total FROM Users) SELECT * FROM UserStats' }, destructiveQueries: { insert: "INSERT INTO Users (name, email) VALUES ('Test', 'test@example.com')", update: "UPDATE Users SET name = 'Updated' WHERE id = 1", delete: 'DELETE FROM Users WHERE id = 1', truncate: 'TRUNCATE TABLE Users' }, schemaQueries: { createTable: 'CREATE TABLE TestTable (id INT PRIMARY KEY, name VARCHAR(100))', dropTable: 'DROP TABLE TestTable', alterTable: 'ALTER TABLE Users ADD COLUMN phone VARCHAR(20)', createIndex: 'CREATE INDEX idx_name ON Users (name)' }, dangerousQueries: { exec: 'EXEC sp_configure', multiStatement: 'SELECT * FROM Users; DELETE FROM Users WHERE id = 1', sqlInjection: "'; DROP TABLE Users; --" } }; // Tool result helpers export const createToolResult = (success = true, data = {}, errorMessage = null) => { const baseResult = { content: [ { type: 'text', text: JSON.stringify({ success, ...data, ...(errorMessage && !success ? { error: { message: errorMessage } } : {}) }) } ] }; return baseResult; }; export const expectToolSuccess = (result, expectedData = {}) => { // Note: These functions require expect to be available in the calling test context const expect = globalThis.expect; expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); const data = JSON.parse(result.content[0].text); expect(data.success).toBe(true); if (Object.keys(expectedData).length > 0) { expect(data).toMatchObject(expectedData); } return data; }; export const expectToolError = (result, expectedErrorMessage = null) => { // Note: These functions require expect to be available in the calling test context const expect = globalThis.expect; expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); const data = JSON.parse(result.content[0].text); expect(data.success).toBe(false); expect(data.error).toBeDefined(); if (expectedErrorMessage) { expect(data.error.message).toContain(expectedErrorMessage); } return data; }; // Mock server instance factory export const createTestMcpServer = async (envOverrides = {}) => { setupTestEnvironment(envOverrides); // Import here to ensure mocks are in place const { SqlServerMCP } = await import('../../index.js'); // Mock the setupToolHandlers to prevent actual MCP server initialization vi.spyOn(SqlServerMCP.prototype, 'setupToolHandlers').mockImplementation(() => {}); const server = new SqlServerMCP(); // Add a compatibility layer for tests that still expect old methods server.connectToDatabase = async function() { return await this.connectionManager.connect(); }; // Expose connection manager's pool for tests that expect direct pool access Object.defineProperty(server, 'pool', { get: function() { return this.connectionManager.getPool(); }, set: function(value) { /* Ignore sets for now, most tests just need the getter */ } }); return server; }; // Common test setup function export const setupMcpTest = (envOverrides = {}) => { resetMocks(); setupTestEnvironment(envOverrides); setupDefaultMockResponses(); // Mock the new module imports vi.mock('../../lib/database/connection-manager.js', () => ({ ConnectionManager: vi.fn().mockImplementation(() => ({ connect: vi.fn().mockResolvedValue(mockPool), getPool: vi.fn().mockReturnValue(mockPool), isConnectionActive: vi.fn().mockReturnValue(true), close: vi.fn(), getConnectionHealth: vi.fn().mockReturnValue({ connected: true, status: 'Connected', pool: { size: 5, available: 3, pending: 0, borrowed: 2 } }) })) })); vi.mock('../../lib/config/server-config.js', () => ({ serverConfig: { getConnectionConfig: vi.fn().mockReturnValue({ connectionTimeout: 10000, requestTimeout: 30000, maxRetries: 3, retryDelay: 1000 }), getSecurityConfig: vi.fn().mockReturnValue({ readOnlyMode: false, allowDestructiveOperations: false, allowSchemaChanges: false, patterns: { readOnly: [/^\s*SELECT\s+/i], destructive: [/^\s*(DELETE|UPDATE|INSERT|TRUNCATE)\s+/i], schemaChanges: [/^\s*(CREATE|DROP|ALTER)\s+/i] } }), getPerformanceConfig: vi.fn().mockReturnValue({ enabled: true, maxMetricsHistory: 1000, slowQueryThreshold: 5000, trackPoolMetrics: true, samplingRate: 1.0 }), isDebugMode: vi.fn().mockReturnValue(false), logConfiguration: vi.fn() } })); vi.mock('../../lib/tools/tool-registry.js', () => ({ getAllTools: vi.fn().mockReturnValue([ { name: 'execute_query', description: 'Execute a SQL query' }, { name: 'list_databases', description: 'List all databases' }, { name: 'list_tables', description: 'List all tables' } ]), getTool: vi.fn().mockImplementation((name) => ({ name, description: `Mock tool: ${name}` })) })); }; // Performance monitoring mock setup export const setupPerformanceMonitoringMocks = () => { mockRequest.query.mockImplementation(query => { if (query.includes('performance statistics')) { return Promise.resolve({ recordset: [performanceTestData.samplePerformanceStats.summary] }); } if (query.includes('query metrics')) { return Promise.resolve({ recordset: performanceTestData.sampleQueryMetrics }); } if (query.includes('connection health')) { return Promise.resolve({ recordset: [performanceTestData.samplePerformanceStats.connectionHealth] }); } return Promise.resolve({ recordset: [], recordsets: [[]], rowsAffected: [0] }); }); }; // Export all commonly used imports to reduce duplication export { vi } from 'vitest'; export { SqlServerMCP } from '../../index.js'; export { default as sql } from 'mssql';

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/egarcia74/warp-sql-server-mcp'

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