Skip to main content
Glama
comprehensive-test-runner.jsโ€ข21.1 kB
#!/usr/bin/env node /** * Comprehensive Test Runner * Orchestrates all testing capabilities with intelligent execution strategies */ const { spawn } = require('child_process'); const { performance } = require('perf_hooks'); const { writeFile, mkdir, readFile } = require('fs/promises'); const path = require('path'); const os = require('os'); class ComprehensiveTestRunner { constructor() { this.startTime = performance.now(); this.results = { phases: [], summary: {}, performance: {}, recommendations: [] }; this.config = { outputDir: path.join(process.cwd(), 'test-results'), maxConcurrency: os.cpus().length, timeout: 30 * 60 * 1000, // 30 minutes retryAttempts: 2, memoryLimit: '4096MB', nodeOptions: '--max-old-space-size=4096 --expose-gc' }; } /** * Main test execution orchestrator */ async run(options = {}) { try { console.log('๐Ÿš€ Starting Comprehensive Test Suite'); console.log(`๐Ÿ“Š System: ${os.platform()} ${os.arch()}, CPUs: ${os.cpus().length}, Memory: ${Math.round(os.totalmem() / 1024 / 1024 / 1024)}GB`); await this.setupEnvironment(options); // Phase 1: Pre-flight checks await this.runPhase('pre-flight', 'Pre-flight Checks', async () => { await this.validateEnvironment(); await this.cleanupPreviousRuns(); await this.prepareMocks(); }); // Phase 2: Unit Tests (Core Foundation) await this.runPhase('unit', 'Unit Tests', async () => { await this.runUnitTests(); }); // Phase 3: Integration Tests (Service Chains) await this.runPhase('integration', 'Integration Tests', async () => { await this.runIntegrationTests(); }); // Phase 4: Contract Tests (API Validation) await this.runPhase('contract', 'Contract Tests', async () => { await this.runContractTests(); }); // Phase 5: Database Tests (Real DB Operations) await this.runPhase('database', 'Database Integration Tests', async () => { await this.runDatabaseTests(); }); // Phase 6: Performance Tests (Load & Stress) await this.runPhase('performance', 'Performance Tests', async () => { await this.runPerformanceTests(); }); // Phase 7: E2E Tests (Complete Workflows) await this.runPhase('e2e', 'End-to-End Tests', async () => { await this.runE2ETests(); }); // Phase 8: Security Tests (Vulnerability Scanning) await this.runPhase('security', 'Security Tests', async () => { await this.runSecurityTests(); }); // Phase 9: Comprehensive Analysis await this.runPhase('analysis', 'Comprehensive Analysis', async () => { await this.generateComprehensiveReport(); await this.analyzeResults(); }); // Phase 10: Quality Gates await this.runPhase('quality-gates', 'Quality Gates', async () => { await this.enforceQualityGates(); }); const totalTime = performance.now() - this.startTime; console.log(`\\nโœ… Comprehensive Test Suite completed in ${(totalTime / 1000).toFixed(2)}s`); return this.results; } catch (error) { console.error('โŒ Test suite failed:', error); process.exit(1); } } /** * Run a test phase with error handling and metrics */ async runPhase(phaseId, phaseName, phaseFunction) { const phaseStartTime = performance.now(); console.log(`\\n๐Ÿ“‹ Phase: ${phaseName}`); console.log('โ”€'.repeat(50)); try { const memoryBefore = process.memoryUsage(); await phaseFunction(); const memoryAfter = process.memoryUsage(); const phaseDuration = performance.now() - phaseStartTime; const phaseResult = { id: phaseId, name: phaseName, status: 'passed', duration: phaseDuration, memoryUsage: { before: memoryBefore, after: memoryAfter, delta: { heapUsed: memoryAfter.heapUsed - memoryBefore.heapUsed, heapTotal: memoryAfter.heapTotal - memoryBefore.heapTotal, external: memoryAfter.external - memoryBefore.external } } }; this.results.phases.push(phaseResult); console.log(`โœ… ${phaseName} completed in ${(phaseDuration / 1000).toFixed(2)}s`); // Force garbage collection if available if (global.gc) { global.gc(); } } catch (error) { const phaseDuration = performance.now() - phaseStartTime; const phaseResult = { id: phaseId, name: phaseName, status: 'failed', duration: phaseDuration, error: error.message }; this.results.phases.push(phaseResult); console.error(`โŒ ${phaseName} failed in ${(phaseDuration / 1000).toFixed(2)}s:`, error.message); if (options.failFast) { throw error; } } } /** * Setup test environment and configuration */ async setupEnvironment(options) { this.config = { ...this.config, ...options }; // Ensure output directory exists await mkdir(this.config.outputDir, { recursive: true }); // Set Node.js options for memory optimization process.env.NODE_OPTIONS = this.config.nodeOptions; process.env.JEST_MONITOR_MEMORY = 'true'; console.log(`๐Ÿ”ง Environment configured:`); console.log(` Output: ${this.config.outputDir}`); console.log(` Memory Limit: ${this.config.memoryLimit}`); console.log(` Max Concurrency: ${this.config.maxConcurrency}`); } /** * Validate environment prerequisites */ async validateEnvironment() { console.log('๐Ÿ” Validating environment...'); // Check Node.js version const nodeVersion = process.version; console.log(` Node.js: ${nodeVersion}`); // Check available memory const totalMemory = Math.round(os.totalmem() / 1024 / 1024 / 1024); const freeMemory = Math.round(os.freemem() / 1024 / 1024 / 1024); console.log(` Memory: ${freeMemory}GB free / ${totalMemory}GB total`); if (freeMemory < 2) { console.warn('โš ๏ธ Warning: Low available memory may affect test performance'); } // Check disk space try { const stats = require('fs').statSync(process.cwd()); console.log(' Disk: Available'); } catch (error) { throw new Error('Unable to access current directory'); } // Verify test dependencies try { require('jest'); require('@playwright/test'); console.log(' Dependencies: โœ… All testing dependencies found'); } catch (error) { throw new Error('Missing required testing dependencies'); } } /** * Clean up previous test runs */ async cleanupPreviousRuns() { console.log('๐Ÿงน Cleaning up previous runs...'); const cleanupPaths = [ path.join(process.cwd(), 'coverage'), path.join(process.cwd(), 'test-results'), path.join(process.cwd(), 'test-databases'), path.join(process.cwd(), 'test-reports') ]; for (const cleanupPath of cleanupPaths) { try { await this.removeDirectory(cleanupPath); await mkdir(cleanupPath, { recursive: true }); } catch (error) { // Directory might not exist, ignore } } console.log(' Previous runs cleaned up'); } /** * Prepare mock services and test data */ async prepareMocks() { console.log('๐ŸŽญ Preparing mocks and test data...'); // This would initialize comprehensive mock services // For now, we'll just ensure the mock directories exist const mockDirs = [ path.join(process.cwd(), 'src', '__tests__', 'helpers'), path.join(process.cwd(), 'src', '__tests__', '__mocks__') ]; for (const dir of mockDirs) { await mkdir(dir, { recursive: true }); } console.log(' Mocks and test data prepared'); } /** * Run unit tests with intelligent batching */ async runUnitTests() { console.log('๐Ÿงช Running unit tests...'); const unitTestCategories = [ { name: 'Services', pattern: 'src/__tests__/*Service.test.ts', priority: 'high' }, { name: 'Tools', pattern: 'src/__tests__/tools/*.test.ts', priority: 'high' }, { name: 'Utilities', pattern: 'src/__tests__/utils/*.test.ts', priority: 'medium' }, { name: 'Types', pattern: 'src/__tests__/types/*.test.ts', priority: 'low' } ]; for (const category of unitTestCategories) { console.log(` ๐Ÿ“ฆ Testing ${category.name}...`); const result = await this.runJestCommand([ '--testPathPattern', category.pattern, '--coverage', '--maxWorkers=1', '--verbose' ]); if (result.code !== 0) { throw new Error(`Unit tests failed for ${category.name}`); } } console.log(' โœ… Unit tests completed'); } /** * Run integration tests with service chain validation */ async runIntegrationTests() { console.log('๐Ÿ”— Running integration tests...'); const integrationTests = [ 'src/__tests__/integration/ServiceIntegrationChainTests.test.ts', 'src/__tests__/integration/DatabaseIntegrationTests.test.ts', 'src/__tests__/integration/ContractTests.test.ts', 'src/__tests__/integration/ErrorRecoveryTests.test.ts' ]; for (const testFile of integrationTests) { console.log(` ๐Ÿ”ง Running ${path.basename(testFile)}...`); const result = await this.runJestCommand([ testFile, '--verbose', '--detectOpenHandles', '--forceExit' ]); if (result.code !== 0) { throw new Error(`Integration test failed: ${testFile}`); } } console.log(' โœ… Integration tests completed'); } /** * Run contract tests for API validation */ async runContractTests() { console.log('๐Ÿ“‹ Running contract tests...'); const result = await this.runJestCommand([ 'src/__tests__/integration/ContractTests.test.ts', '--verbose', '--testTimeout=30000' ]); if (result.code !== 0) { throw new Error('Contract tests failed'); } console.log(' โœ… Contract tests completed'); } /** * Run database integration tests */ async runDatabaseTests() { console.log('๐Ÿ—„๏ธ Running database tests...'); const result = await this.runJestCommand([ 'src/__tests__/integration/DatabaseIntegrationTests.test.ts', '--verbose', '--testTimeout=60000' ]); if (result.code !== 0) { throw new Error('Database tests failed'); } console.log(' โœ… Database tests completed'); } /** * Run performance tests with load simulation */ async runPerformanceTests() { console.log('โšก Running performance tests...'); const result = await this.runJestCommand([ 'src/__tests__/performance/*.test.ts', '--verbose', '--testTimeout=120000' ]); if (result.code !== 0) { console.warn('โš ๏ธ Performance tests had issues (non-blocking)'); } else { console.log(' โœ… Performance tests completed'); } } /** * Run end-to-end tests with Playwright */ async runE2ETests() { console.log('๐ŸŽฏ Running E2E tests...'); const result = await this.runJestCommand([ 'src/__tests__/e2e/*.test.ts', '--verbose', '--testTimeout=180000' ]); if (result.code !== 0) { console.warn('โš ๏ธ E2E tests had issues (non-blocking)'); } else { console.log(' โœ… E2E tests completed'); } } /** * Run security tests and vulnerability scanning */ async runSecurityTests() { console.log('๐Ÿ›ก๏ธ Running security tests...'); // Run npm audit try { const auditResult = await this.runCommand('npm', ['audit', '--audit-level=moderate']); if (auditResult.code !== 0) { console.warn('โš ๏ธ Security vulnerabilities detected in dependencies'); } } catch (error) { console.warn('โš ๏ธ Could not run security audit:', error.message); } // Run security-focused tests const result = await this.runJestCommand([ '--testNamePattern="security|Security"', '--verbose' ]); console.log(' โœ… Security tests completed'); } /** * Generate comprehensive test report */ async generateComprehensiveReport() { console.log('๐Ÿ“Š Generating comprehensive report...'); try { // Run test reporting framework if available const reportingScript = path.join(__dirname, '..', 'src', '__tests__', 'helpers', 'TestReportingFramework.ts'); // Generate multiple report formats const reportFormats = ['html', 'markdown', 'json', 'junit']; console.log(` ๐Ÿ“„ Generating reports in ${reportFormats.length} formats...`); // This would integrate with the TestReportingFramework // For now, we'll create a summary report await this.createSummaryReport(); } catch (error) { console.warn('โš ๏ธ Report generation had issues:', error.message); } console.log(' โœ… Comprehensive report generated'); } /** * Analyze test results and generate insights */ async analyzeResults() { console.log('๐Ÿ” Analyzing results...'); // Analyze test execution patterns this.analyzeExecutionPatterns(); // Analyze performance metrics this.analyzePerformanceMetrics(); // Generate recommendations this.generateRecommendations(); console.log(' โœ… Results analysis completed'); } /** * Enforce quality gates and fail if thresholds not met */ async enforceQualityGates() { console.log('๐Ÿšช Enforcing quality gates...'); const qualityGates = { coverage: { threshold: 95, current: 0 }, testSuccess: { threshold: 95, current: 0 }, performance: { threshold: 5000, current: 0 }, // 5 seconds max security: { threshold: 0, current: 0 } // 0 high severity vulnerabilities }; // Check coverage try { const coverageData = await this.getCoverageData(); qualityGates.coverage.current = coverageData.statements?.percentage || 0; } catch (error) { console.warn('โš ๏ธ Could not read coverage data'); } // Check test success rate const passedPhases = this.results.phases.filter(p => p.status === 'passed').length; qualityGates.testSuccess.current = (passedPhases / this.results.phases.length) * 100; // Check performance const totalDuration = this.results.phases.reduce((sum, p) => sum + p.duration, 0); qualityGates.performance.current = totalDuration; // Evaluate quality gates let gatesPassed = true; for (const [gate, config] of Object.entries(qualityGates)) { const passed = gate === 'performance' ? config.current <= config.threshold : config.current >= config.threshold; console.log(` ${passed ? 'โœ…' : 'โŒ'} ${gate}: ${config.current}${gate === 'performance' ? 'ms' : '%'} (threshold: ${config.threshold}${gate === 'performance' ? 'ms' : '%'})`); if (!passed) { gatesPassed = false; } } if (!gatesPassed && !this.config.ignoreFailed) { throw new Error('Quality gates failed - see above for details'); } console.log(' โœ… Quality gates enforced'); } /** * Helper methods */ async runJestCommand(args) { return this.runCommand('npm', ['run', 'test', '--', ...args]); } async runCommand(command, args, options = {}) { return new Promise((resolve, reject) => { const process = spawn(command, args, { stdio: 'inherit', shell: true, ...options }); process.on('close', (code) => { resolve({ code }); }); process.on('error', (error) => { reject(error); }); }); } async removeDirectory(dirPath) { const fs = require('fs'); if (fs.existsSync(dirPath)) { const { rimraf } = require('rimraf'); await rimraf(dirPath); } } async getCoverageData() { try { const coveragePath = path.join(process.cwd(), 'coverage', 'coverage-summary.json'); const data = await readFile(coveragePath, 'utf8'); return JSON.parse(data).total; } catch (error) { return {}; } } analyzeExecutionPatterns() { console.log(' ๐Ÿ“ˆ Analyzing execution patterns...'); const phaseDurations = this.results.phases.map(p => ({ name: p.name, duration: p.duration, status: p.status })); // Find slowest phases const slowestPhases = phaseDurations .sort((a, b) => b.duration - a.duration) .slice(0, 3); this.results.analysis = { slowestPhases, totalPhases: this.results.phases.length, passedPhases: this.results.phases.filter(p => p.status === 'passed').length, failedPhases: this.results.phases.filter(p => p.status === 'failed').length }; } analyzePerformanceMetrics() { console.log(' โšก Analyzing performance metrics...'); const totalDuration = this.results.phases.reduce((sum, p) => sum + p.duration, 0); const avgPhaseDuration = totalDuration / this.results.phases.length; this.results.performance = { totalDuration, avgPhaseDuration, efficiency: this.calculateEfficiencyScore() }; } calculateEfficiencyScore() { // Simple efficiency calculation based on phase durations and success rates const totalTime = this.results.phases.reduce((sum, p) => sum + p.duration, 0); const successRate = this.results.phases.filter(p => p.status === 'passed').length / this.results.phases.length; // Efficiency = success rate / normalized time (higher is better) return (successRate / (totalTime / 60000)) * 100; // per minute } generateRecommendations() { console.log(' ๐Ÿ’ก Generating recommendations...'); const recommendations = []; // Performance recommendations if (this.results.performance.totalDuration > 300000) { // 5 minutes recommendations.push('Consider optimizing test execution time - current duration exceeds 5 minutes'); } // Coverage recommendations if (this.results.analysis.failedPhases > 0) { recommendations.push(`Fix ${this.results.analysis.failedPhases} failed test phases before deployment`); } // Memory recommendations const memoryIntensivePhases = this.results.phases.filter(p => p.memoryUsage && p.memoryUsage.delta.heapUsed > 100 * 1024 * 1024 ); if (memoryIntensivePhases.length > 0) { recommendations.push('Review memory usage in memory-intensive test phases'); } // General recommendations recommendations.push('Implement continuous performance monitoring'); recommendations.push('Set up automated quality gate enforcement'); recommendations.push('Consider parallel test execution for faster feedback'); this.results.recommendations = recommendations; } async createSummaryReport() { const summary = { timestamp: new Date().toISOString(), duration: performance.now() - this.startTime, phases: this.results.phases, analysis: this.results.analysis, performance: this.results.performance, recommendations: this.results.recommendations, environment: { platform: os.platform(), arch: os.arch(), nodeVersion: process.version, cpus: os.cpus().length, memory: Math.round(os.totalmem() / 1024 / 1024 / 1024) } }; const reportPath = path.join(this.config.outputDir, 'comprehensive-test-summary.json'); await writeFile(reportPath, JSON.stringify(summary, null, 2)); console.log(` ๐Ÿ“„ Summary report saved: ${reportPath}`); } } // CLI execution if (require.main === module) { const args = process.argv.slice(2); const options = {}; // Parse command line arguments for (let i = 0; i < args.length; i++) { const arg = args[i]; switch (arg) { case '--fail-fast': options.failFast = true; break; case '--ignore-failed': options.ignoreFailed = true; break; case '--memory-limit': options.memoryLimit = args[++i]; break; case '--timeout': options.timeout = parseInt(args[++i]) * 1000; break; case '--concurrency': options.maxConcurrency = parseInt(args[++i]); break; case '--output-dir': options.outputDir = args[++i]; break; } } const runner = new ComprehensiveTestRunner(); runner.run(options) .then((results) => { console.log('\\n๐ŸŽ‰ Test suite completed successfully!'); console.log(`๐Ÿ“Š Results: ${results.analysis?.passedPhases || 0}/${results.phases?.length || 0} phases passed`); process.exit(0); }) .catch((error) => { console.error('\\n๐Ÿ’ฅ Test suite failed:', error.message); process.exit(1); }); } module.exports = ComprehensiveTestRunner;

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/Ghostseller/CastPlan_mcp'

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