Skip to main content
Glama
test-database-helper.js8.61 kB
/** * Test Database Helper * Provides utilities for creating and managing test databases */ import crypto from 'crypto'; import dotenv from 'dotenv'; import { serverConfig } from '../../../lib/config/server-config.js'; export class TestDatabaseHelper { constructor(server) { this.server = server; this.testDatabases = []; this.isDockerMode = this.detectDockerMode(); // Load Docker-specific environment if in Docker mode if (this.isDockerMode) { this.loadDockerEnvironment(); } } /** * Detect if we're running in Docker testing mode */ detectDockerMode() { return process.env.MCP_TESTING_MODE === 'docker'; } /** * Load Docker-specific environment configuration */ loadDockerEnvironment() { console.log('🐳 Docker mode detected - loading Docker environment configuration...'); try { // Load Docker-specific environment variables const dockerEnvPath = './test/docker/.env.docker'; dotenv.config({ path: dockerEnvPath, override: true }); console.log('✅ Docker environment configuration loaded'); console.log(`🔧 Database: ${process.env.SQL_SERVER_HOST}:${process.env.SQL_SERVER_PORT}`); console.log(`👤 User: ${process.env.SQL_SERVER_USER}`); } catch (error) { console.warn('⚠️ Could not load Docker environment:', error.message); } } /** * Generate a unique test database name */ generateTestDatabaseName(testSuite = 'MCP') { const timestamp = Date.now(); const random = crypto.randomBytes(6).toString('hex'); // More entropy const processId = process.pid; const dbName = `${testSuite}_Test_${timestamp}_${processId}_${random}`; return dbName; } /** * Connect to a test database (assumes Docker has already initialized schema) */ async createTestDatabase(dbName = null, createTables = true) { // Note: createTables parameter is kept for backwards compatibility but ignored if (createTables !== true) { console.log( 'ℹ️ Note: createTables parameter is ignored - Docker init script handles all schema creation' ); } if (!dbName) { dbName = this.generateTestDatabaseName(); } console.log(`🔌 Connecting to database: ${dbName}${this.isDockerMode ? ' (Docker mode)' : ''}`); try { // In Docker mode, use predefined databases that are already initialized if (this.isDockerMode) { const predefinedDbs = [ 'WarpMcpTest', 'Phase1ReadOnly', 'Phase2DML', 'Phase3DDL', 'ProtocolTest' ]; if (predefinedDbs.includes(dbName)) { // Verify the database exists and has tables const verification = await this.server.executeQuery(` USE [${dbName}]; SELECT COUNT(*) as TableCount FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' `); if (verification && verification.content && verification.content[0]) { const content = verification.content[0].text; const tableCount = content.match(/\d+/)?.[0] || '0'; console.log(`✅ Connected to ${dbName} - found ${tableCount} tables`); return dbName; } } } // For non-Docker mode or custom database names, create empty database only // (No table creation - this should be handled by external initialization) console.log( `🏗️ Creating empty database: ${dbName} (schema should be initialized externally)` ); // Temporarily enable schema changes for database creation only const originalConfig = { readOnly: process.env.SQL_SERVER_READ_ONLY, allowDestructive: process.env.SQL_SERVER_ALLOW_DESTRUCTIVE_OPERATIONS, allowSchema: process.env.SQL_SERVER_ALLOW_SCHEMA_CHANGES }; // Enable minimal access for database creation only process.env.SQL_SERVER_READ_ONLY = 'false'; process.env.SQL_SERVER_ALLOW_DESTRUCTIVE_OPERATIONS = 'true'; process.env.SQL_SERVER_ALLOW_SCHEMA_CHANGES = 'true'; // Force the server to reload its configuration serverConfig.reload(); // Check if database already exists try { const existsResult = await this.server.executeQuery(` SELECT COUNT(*) as DbCount FROM sys.databases WHERE name = '${dbName}' `); if (existsResult && existsResult.content && existsResult.content[0]) { const content = existsResult.content[0].text; if (content && content.includes('1')) { console.log(`✅ Database ${dbName} already exists`); return dbName; } } } catch (error) { console.log(`ℹ️ Could not check database existence: ${error.message}`); } // Create empty database only await this.server.executeQuery(`CREATE DATABASE [${dbName}]`); this.testDatabases.push(dbName); // Restore original configuration process.env.SQL_SERVER_READ_ONLY = originalConfig.readOnly || 'true'; process.env.SQL_SERVER_ALLOW_DESTRUCTIVE_OPERATIONS = originalConfig.allowDestructive || 'false'; process.env.SQL_SERVER_ALLOW_SCHEMA_CHANGES = originalConfig.allowSchema || 'false'; // Force the server to reload its configuration back to test settings serverConfig.reload(); console.log( `✅ Empty database created: ${dbName} (tables should be created by external initialization)` ); return dbName; } catch (error) { console.error(`❌ Failed to connect to database ${dbName}:`, error.message); throw error; } } /** * Clean up a specific test database */ async cleanupDatabase(dbName) { try { console.log(`🧹 Cleaning up test database: ${dbName}`); // Temporarily enable all operations for cleanup const originalConfig = { readOnly: process.env.SQL_SERVER_READ_ONLY, allowDestructive: process.env.SQL_SERVER_ALLOW_DESTRUCTIVE_OPERATIONS, allowSchema: process.env.SQL_SERVER_ALLOW_SCHEMA_CHANGES }; // Enable full cleanup permissions temporarily process.env.SQL_SERVER_READ_ONLY = 'false'; process.env.SQL_SERVER_ALLOW_DESTRUCTIVE_OPERATIONS = 'true'; process.env.SQL_SERVER_ALLOW_SCHEMA_CHANGES = 'true'; // Force server to reload configuration for cleanup serverConfig.reload(); // Switch back to master before dropping await this.server.executeQuery('USE master'); // Force close connections and drop database await this.server.executeQuery(` IF EXISTS (SELECT name FROM sys.databases WHERE name = '${dbName}') BEGIN ALTER DATABASE [${dbName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [${dbName}]; END `); // Restore original configuration process.env.SQL_SERVER_READ_ONLY = originalConfig.readOnly || 'true'; process.env.SQL_SERVER_ALLOW_DESTRUCTIVE_OPERATIONS = originalConfig.allowDestructive || 'false'; process.env.SQL_SERVER_ALLOW_SCHEMA_CHANGES = originalConfig.allowSchema || 'false'; // Restore server configuration serverConfig.reload(); // Remove from our tracking list this.testDatabases = this.testDatabases.filter(db => db !== dbName); console.log(`✅ Test database cleaned up: ${dbName}`); } catch (error) { // Check if it's a security policy error - if so, provide helpful context if (error.message.includes('Query blocked by safety policy')) { // This is expected behavior in restricted security phases console.log(`ℹ️ Note: Database ${dbName} cleanup deferred - security restrictions active`); } else if (error.message.includes('database does not exist')) { // Database was already cleaned up console.log(`ℹ️ Note: Database ${dbName} already cleaned`); } else { console.warn(`⚠️ Warning: Could not clean up database ${dbName}:`, error.message); } } } /** * Clean up all test databases created by this helper */ async cleanupAllDatabases() { console.log(`🧹 Cleaning up ${this.testDatabases.length} test databases...`); for (const dbName of [...this.testDatabases]) { await this.cleanupDatabase(dbName); } console.log('✅ All test databases cleaned up'); } /** * Get list of test databases created by this helper */ getTestDatabases() { return [...this.testDatabases]; } }

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