Skip to main content
Glama

1MCP Server

test-e2e-performance.js9.36 kB
#!/usr/bin/env node /** * Script to test E2E performance improvements * Tests both sequential and parallel execution with statistical analysis */ const { execSync } = require('child_process'); const fs = require('fs').promises; const fsSync = require('fs'); /** * Run a command and measure its execution time */ function runCommand(command, description) { console.log(`\n🚀 ${description}`); console.log(`Running: ${command}`); const startTime = Date.now(); try { const output = execSync(command, { encoding: 'utf8', stdio: 'inherit', maxBuffer: 10 * 1024 * 1024, // 10MB buffer }); const duration = Date.now() - startTime; console.log(`✅ Completed in ${duration}ms (${(duration / 1000).toFixed(2)}s)`); return { success: true, duration, output }; } catch (error) { const duration = Date.now() - startTime; console.log(`❌ Failed in ${duration}ms`); console.log(`Error: ${error.message}`); return { success: false, duration, error: error.message }; } } /** * Create a temporary vitest configuration with specified worker count */ async function createTemporaryConfig(workerCount) { const configPath = 'vitest.e2e.temp.config.ts'; const configContent = ` import { defineConfig } from 'vitest/config'; import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ plugins: [tsconfigPaths()], test: { environment: 'node', include: ['test/e2e/**/*.test.ts'], globals: true, testTimeout: 60000, hookTimeout: 30000, // Configure for ${workerCount} workers pool: 'forks', poolOptions: { forks: { maxForks: ${workerCount}, singleFork: false, }, }, fileParallelism: true, maxConcurrency: ${workerCount}, retry: 1, teardownTimeout: 15000, alias: { '^(\\.{1,2}/.*)\\.js$': '$1', }, coverage: { enabled: false }, env: { NODE_ENV: 'test', LOG_LEVEL: 'warn', }, reporters: ['basic'], globalSetup: ['test/e2e/setup/global-setup.ts'], }, }); `; await fs.writeFile(configPath, configContent, 'utf8'); return configPath; } /** * Clean up temporary configuration file */ async function cleanup(configPath) { try { // Check if file exists before attempting deletion if (fsSync.existsSync(configPath)) { await fs.unlink(configPath); } } catch (error) { // Ignore ENOENT errors (file doesn't exist) if (error.code !== 'ENOENT') { console.warn(`Warning: Failed to cleanup ${configPath}: ${error.message}`); } } } /** * Calculate statistical metrics for a set of durations */ function calculateStats(durations) { if (durations.length === 0) return null; const sum = durations.reduce((a, b) => a + b, 0); const mean = sum / durations.length; if (durations.length === 1) { return { mean, stddev: 0, min: mean, max: mean }; } const variance = durations.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0) / durations.length; const stddev = Math.sqrt(variance); const min = Math.min(...durations); const max = Math.max(...durations); return { mean, stddev, min, max }; } /** * Run performance tests with multiple iterations for statistical analysis */ async function main() { console.log('🔬 Testing E2E Performance Improvements'); console.log('====================================='); const results = []; const iterations = process.env.PERF_ITERATIONS ? parseInt(process.env.PERF_ITERATIONS, 10) : 1; console.log(`Running ${iterations} iteration(s) for each configuration\n`); // Test 1: Sequential (baseline) console.log('\n📊 Test 1: Sequential Execution (Baseline)'); let sequentialConfig; try { sequentialConfig = await createTemporaryConfig(1); const sequentialDurations = []; for (let i = 0; i < iterations; i++) { if (iterations > 1) console.log(`\nIteration ${i + 1}/${iterations}`); const result = runCommand( 'pnpm build && pnpm test:e2e --config vitest.e2e.temp.config.ts', 'Sequential execution (1 worker, baseline)', ); if (result.success) { sequentialDurations.push(result.duration); } } const stats = calculateStats(sequentialDurations); results.push({ name: 'Sequential', success: sequentialDurations.length > 0, stats, iterations: sequentialDurations.length, }); } finally { if (sequentialConfig) { await cleanup(sequentialConfig); } } // Test 2: Multi-worker (fast strategy) console.log('\n📊 Test 2: Multi-Worker Execution'); let multiWorkerConfig; try { multiWorkerConfig = await createTemporaryConfig(4); const multiWorkerDurations = []; for (let i = 0; i < iterations; i++) { if (iterations > 1) console.log(`\nIteration ${i + 1}/${iterations}`); const result = runCommand( 'pnpm test:e2e --config vitest.e2e.temp.config.ts', 'Multi-worker execution (4 workers, fast strategy)', ); if (result.success) { multiWorkerDurations.push(result.duration); } } const stats = calculateStats(multiWorkerDurations); results.push({ name: 'Multi-worker', success: multiWorkerDurations.length > 0, stats, iterations: multiWorkerDurations.length, }); } finally { if (multiWorkerConfig) { await cleanup(multiWorkerConfig); } } // Test 3: Sharding simulation (run 1/4 of tests) console.log('\n📊 Test 3: Sharded Execution (CI Simulation)'); let shardConfig; try { shardConfig = await createTemporaryConfig(2); const shardDurations = []; for (let i = 0; i < iterations; i++) { if (iterations > 1) console.log(`\nIteration ${i + 1}/${iterations}`); const result = runCommand( 'pnpm test:e2e --config vitest.e2e.temp.config.ts --shard=1/4', 'Sharded execution (1/4 of tests, simulated parallel strategy)', ); if (result.success) { shardDurations.push(result.duration); } } // Calculate estimated full time for sharding (multiply by 4 since we ran 1/4) if (shardDurations.length > 0) { const estimatedStats = calculateStats(shardDurations.map((d) => d * 4)); results.push({ name: 'Sharded (estimated)', success: true, stats: estimatedStats, iterations: shardDurations.length, note: '1/4 execution time × 4 (parallel shards)', }); } else { results.push({ name: 'Sharded (estimated)', success: false, stats: null, iterations: 0 }); } } finally { if (shardConfig) { await cleanup(shardConfig); } } // Summary console.log('\n📊 Performance Results Summary'); console.log('===============================\n'); const baseline = results.find((r) => r.name === 'Sequential'); if (baseline && baseline.success && baseline.stats) { // Print baseline console.log(`${baseline.name}:`); console.log(` Mean: ${baseline.stats.mean.toFixed(0)}ms (${(baseline.stats.mean / 1000).toFixed(2)}s)`); if (baseline.iterations > 1) { console.log(` Std Dev: ±${baseline.stats.stddev.toFixed(0)}ms`); console.log(` Range: ${baseline.stats.min.toFixed(0)}ms - ${baseline.stats.max.toFixed(0)}ms`); } console.log(` Iterations: ${baseline.iterations}\n`); // Print comparisons results.forEach((result) => { if (result.success && result.name !== 'Sequential' && result.stats) { const improvement = (((baseline.stats.mean - result.stats.mean) / baseline.stats.mean) * 100).toFixed(1); const speedup = (baseline.stats.mean / result.stats.mean).toFixed(2); console.log(`${result.name}:`); console.log(` Mean: ${result.stats.mean.toFixed(0)}ms (${(result.stats.mean / 1000).toFixed(2)}s)`); if (result.iterations > 1) { console.log(` Std Dev: ±${result.stats.stddev.toFixed(0)}ms`); console.log(` Range: ${result.stats.min.toFixed(0)}ms - ${result.stats.max.toFixed(0)}ms`); } console.log(` Speedup: ${speedup}x faster (${improvement}% improvement)`); if (result.note) { console.log(` Note: ${result.note}`); } console.log(` Iterations: ${result.iterations}\n`); } else if (!result.success) { console.log(`${result.name}: Failed ❌\n`); } }); } else { console.log('❌ Baseline sequential test failed, cannot calculate improvements\n'); results.forEach((result) => { if (result.success && result.stats) { console.log(`${result.name}: ${result.stats.mean.toFixed(0)}ms (${(result.stats.mean / 1000).toFixed(2)}s)`); } else { console.log(`${result.name}: Failed ❌`); } }); } console.log('\n💡 Recommendations:'); console.log('- Use "Multi-worker" strategy for local development (4 workers, single job)'); console.log('- Use "Sharded" strategy for CI/CD (4 parallel jobs, ~4min runtime)'); console.log('- Sequential execution provides baseline for comparison'); console.log('\n📝 To run multiple iterations for statistical analysis:'); console.log(' PERF_ITERATIONS=3 node scripts/test-e2e-performance.js'); } if (require.main === module) { main().catch((error) => { console.error('Fatal error:', error); process.exit(1); }); }

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