#!/usr/bin/env node /** * Master Test Runner - Phase 3 Integration & Validation * * Production-ready execution script that demonstrates the complete E2E test integration * for all 5 agent implementations with cross-functional workflows, performance monitoring, * and comprehensive error recovery. * * Usage: * node tests/integration/master-test-runner.js * node tests/integration/master-test-runner.js --environment=docker * node tests/integration/master-test-runner.js --agents=A,B,C --headed * node tests/integration/master-test-runner.js --performance-only * * @package HVAC_Community_Events * @version 3.0.0 * @created 2025-08-27 */ const path = require('path'); const fs = require('fs').promises; const { Command } = require('commander'); // Core integration components const MasterTestOrchestrator = require('./master-test-orchestrator'); const CrossFunctionalWorkflows = require('./cross-functional-workflows'); const PerformanceMonitor = require('../framework/monitoring/PerformanceMonitor'); const ErrorRecoveryManager = require('../framework/recovery/ErrorRecoveryManager'); // Configuration const DEFAULT_CONFIG = { environment: 'staging', baseUrl: process.env.BASE_URL || 'https://upskill-staging.measurequick.com', dockerUrl: process.env.DOCKER_BASE_URL || 'http://localhost:8080', headless: process.env.HEADLESS !== 'false', slowMo: parseInt(process.env.PLAYWRIGHT_SLOW_MO) || 0, timeout: 30 * 60 * 1000, // 30 minutes evidenceDir: path.join(__dirname, '../evidence/master-test-run'), reportDir: path.join(__dirname, '../reports'), display: process.env.DISPLAY || ':0', xAuthority: process.env.XAUTHORITY || '/run/user/1000/.mutter-Xwaylandauth.U8VEB3' }; class MasterTestRunner { constructor(options = {}) { this.config = { ...DEFAULT_CONFIG, ...options }; this.startTime = Date.now(); this.orchestrator = null; this.workflows = null; this.performanceMonitor = null; this.errorRecoveryManager = null; this.results = { orchestration: null, workflows: null, performance: null, recovery: null, summary: null }; } /** * Execute complete integration test suite */ async execute() { console.log('════════════════════════════════════════════════════════'); console.log(' HVAC E2E Integration & Validation - Master Test Runner'); console.log('════════════════════════════════════════════════════════'); console.log('Phase 3: Agent Implementation Coordination'); console.log(`Environment: ${this.config.environment}`); console.log(`Base URL: ${this.config.baseUrl}`); console.log(`Headless: ${this.config.headless}`); console.log(`Target Duration: <${this.config.timeout / 60000} minutes`); console.log(`Evidence Directory: ${this.config.evidenceDir}`); console.log('════════════════════════════════════════════════════════'); try { // Initialize all components await this.initializeComponents(); // Execute test phases if (this.config.skipOrchestration !== true) { await this.executeOrchestration(); } if (this.config.skipWorkflows !== true) { await this.executeWorkflows(); } if (this.config.skipPerformance !== true) { await this.executePerformanceTesting(); } if (this.config.skipRecovery !== true) { await this.executeRecoveryTesting(); } // Generate final report await this.generateFinalReport(); // Display results this.displayResults(); const success = this.evaluateOverallSuccess(); console.log(`\n🏁 Master Test Runner: ${success ? 'SUCCESS' : 'FAILED'}`); return success; } catch (error) { console.error('\n💥 Master Test Runner failed:', error.message); console.error('Stack trace:', error.stack); await this.handleExecutionError(error); return false; } finally { await this.cleanup(); } } /** * Initialize all test components */ async initializeComponents() { console.log('\n🔧 Initializing test components...'); // Create evidence directories await this.createDirectories(); // Initialize Master Test Orchestrator if (!this.config.skipOrchestration) { console.log(' Initializing Master Test Orchestrator...'); this.orchestrator = new MasterTestOrchestrator(); await this.orchestrator.setUp(); console.log(' ✓ Master Test Orchestrator ready'); } // Initialize Cross-Functional Workflows if (!this.config.skipWorkflows) { console.log(' Initializing Cross-Functional Workflows...'); this.workflows = new CrossFunctionalWorkflows(); await this.workflows.setUp(); console.log(' ✓ Cross-Functional Workflows ready'); } // Initialize Performance Monitor if (!this.config.skipPerformance) { console.log(' Initializing Performance Monitor...'); this.performanceMonitor = new PerformanceMonitor(); await this.performanceMonitor.initialize(); console.log(' ✓ Performance Monitor ready'); } // Initialize Error Recovery Manager if (!this.config.skipRecovery) { console.log(' Initializing Error Recovery Manager...'); this.errorRecoveryManager = new ErrorRecoveryManager(); const browserManager = this.orchestrator ? this.orchestrator.browserManager : null; await this.errorRecoveryManager.initialize(browserManager); console.log(' ✓ Error Recovery Manager ready'); } console.log('✓ All components initialized successfully'); } /** * Execute master test orchestration */ async executeOrchestration() { console.log('\n🎭 Executing Master Test Orchestration...'); const orchestrationStart = Date.now(); try { // Start performance monitoring for orchestration if (this.performanceMonitor) { await this.performanceMonitor.startAgentMonitoring('MasterOrchestration'); } // Execute orchestration const orchestrationResults = await this.orchestrator.executeIntegrationValidation(); // Stop performance monitoring if (this.performanceMonitor) { await this.performanceMonitor.stopAgentMonitoring('MasterOrchestration'); } const orchestrationDuration = Date.now() - orchestrationStart; this.results.orchestration = { status: 'completed', duration: orchestrationDuration, results: orchestrationResults, timestamp: new Date().toISOString() }; console.log(`✓ Master Test Orchestration completed in ${(orchestrationDuration / 1000).toFixed(1)}s`); } catch (error) { const orchestrationDuration = Date.now() - orchestrationStart; this.results.orchestration = { status: 'failed', duration: orchestrationDuration, error: error.message, timestamp: new Date().toISOString() }; console.error(`✗ Master Test Orchestration failed after ${(orchestrationDuration / 1000).toFixed(1)}s:`, error.message); if (this.config.continueOnFailure !== true) { throw error; } } } /** * Execute cross-functional workflows */ async executeWorkflows() { console.log('\n🔄 Executing Cross-Functional Workflows...'); const workflowsStart = Date.now(); try { // Start performance monitoring for workflows if (this.performanceMonitor) { await this.performanceMonitor.startAgentMonitoring('CrossFunctionalWorkflows'); } // Execute workflows const workflowResults = await this.workflows.executeAllWorkflows(); // Stop performance monitoring if (this.performanceMonitor) { await this.performanceMonitor.stopAgentMonitoring('CrossFunctionalWorkflows'); } const workflowsDuration = Date.now() - workflowsStart; this.results.workflows = { status: 'completed', duration: workflowsDuration, results: workflowResults, timestamp: new Date().toISOString() }; const passedWorkflows = workflowResults.filter(w => w.status === 'passed').length; console.log(`✓ Cross-Functional Workflows completed: ${passedWorkflows}/${workflowResults.length} passed in ${(workflowsDuration / 1000).toFixed(1)}s`); } catch (error) { const workflowsDuration = Date.now() - workflowsStart; this.results.workflows = { status: 'failed', duration: workflowsDuration, error: error.message, timestamp: new Date().toISOString() }; console.error(`✗ Cross-Functional Workflows failed after ${(workflowsDuration / 1000).toFixed(1)}s:`, error.message); if (this.config.continueOnFailure !== true) { throw error; } } } /** * Execute performance testing */ async executePerformanceTesting() { console.log('\n📊 Executing Performance Testing...'); const performanceStart = Date.now(); try { // Concurrent load testing const concurrentSessionConfigs = [ { sessionId: 'trainer_session_1', actions: [ { type: 'login', duration: 2000 }, { type: 'navigate', url: '/trainer/dashboard', duration: 1500 }, { type: 'create_event', duration: 5000 } ] }, { sessionId: 'trainer_session_2', actions: [ { type: 'login', duration: 2000 }, { type: 'navigate', url: '/trainer/my-events', duration: 1500 }, { type: 'form_submit', duration: 3000 } ] }, { sessionId: 'master_session_1', actions: [ { type: 'login', duration: 2000 }, { type: 'navigate', url: '/master-trainer/pending-approvals', duration: 2000 }, { type: 'form_submit', duration: 2000 } ] } ]; const loadTestResults = await this.performanceMonitor.executeConcurrentLoadTest(concurrentSessionConfigs); // Generate performance benchmark const performanceBenchmark = await this.performanceMonitor.generatePerformanceBenchmark(); const performanceDuration = Date.now() - performanceStart; this.results.performance = { status: 'completed', duration: performanceDuration, loadTest: loadTestResults, benchmark: performanceBenchmark, timestamp: new Date().toISOString() }; console.log(`✓ Performance Testing completed in ${(performanceDuration / 1000).toFixed(1)}s`); console.log(` Concurrent Sessions: ${loadTestResults.successfulSessions}/${loadTestResults.totalSessions} successful`); console.log(` Performance Rating: ${loadTestResults.concurrentPerformanceRating}`); } catch (error) { const performanceDuration = Date.now() - performanceStart; this.results.performance = { status: 'failed', duration: performanceDuration, error: error.message, timestamp: new Date().toISOString() }; console.error(`✗ Performance Testing failed after ${(performanceDuration / 1000).toFixed(1)}s:`, error.message); if (this.config.continueOnFailure !== true) { throw error; } } } /** * Execute error recovery testing */ async executeRecoveryTesting() { console.log('\n🛡️ Executing Error Recovery Testing...'); const recoveryStart = Date.now(); try { // Execute comprehensive recovery tests const recoveryResults = await this.errorRecoveryManager.executeComprehensiveRecoveryTests(this.config.baseUrl); // Get recovery statistics const recoveryStats = this.errorRecoveryManager.getRecoveryStatistics(); const recoveryDuration = Date.now() - recoveryStart; this.results.recovery = { status: 'completed', duration: recoveryDuration, results: recoveryResults, statistics: recoveryStats, timestamp: new Date().toISOString() }; console.log(`✓ Error Recovery Testing completed in ${(recoveryDuration / 1000).toFixed(1)}s`); console.log(` Recovery Tests: ${recoveryResults.summary.passed}/${recoveryResults.summary.total} passed`); console.log(` Success Rate: ${recoveryResults.summary.successRate.toFixed(1)}%`); } catch (error) { const recoveryDuration = Date.now() - recoveryStart; this.results.recovery = { status: 'failed', duration: recoveryDuration, error: error.message, timestamp: new Date().toISOString() }; console.error(`✗ Error Recovery Testing failed after ${(recoveryDuration / 1000).toFixed(1)}s:`, error.message); if (this.config.continueOnFailure !== true) { throw error; } } } /** * Generate final comprehensive report */ async generateFinalReport() { console.log('\n📋 Generating final integration report...'); const totalDuration = Date.now() - this.startTime; const finalReport = { meta: { version: '3.0.0', generatedAt: new Date().toISOString(), totalDuration: totalDuration, environment: this.config.environment, baseUrl: this.config.baseUrl, configuration: this.config }, summary: { overallStatus: this.evaluateOverallSuccess() ? 'SUCCESS' : 'FAILED', totalDuration: totalDuration, targetDuration: this.config.timeout, performanceMet: totalDuration <= this.config.timeout, phases: { orchestration: this.results.orchestration?.status || 'skipped', workflows: this.results.workflows?.status || 'skipped', performance: this.results.performance?.status || 'skipped', recovery: this.results.recovery?.status || 'skipped' } }, results: this.results, recommendations: this.generateRecommendations() }; // Export performance report if available if (this.performanceMonitor) { const performanceReportPath = path.join(this.config.reportDir, `performance-report-${Date.now()}.json`); await this.performanceMonitor.exportPerformanceReport(performanceReportPath); finalReport.performanceReportPath = performanceReportPath; } // Save final report const finalReportPath = path.join(this.config.reportDir, `master-test-report-${Date.now()}.json`); await fs.writeFile(finalReportPath, JSON.stringify(finalReport, null, 2)); // Generate executive summary const summaryPath = await this.generateExecutiveSummary(finalReport); console.log(`✓ Final integration report generated:`); console.log(` Report: ${finalReportPath}`); console.log(` Summary: ${summaryPath}`); this.results.summary = finalReport; return finalReport; } /** * Generate executive summary */ async generateExecutiveSummary(finalReport) { const summary = ` # HVAC E2E Integration & Validation - Executive Summary **Generated:** ${new Date().toISOString()} **Environment:** ${this.config.environment} **Base URL:** ${this.config.baseUrl} **Total Duration:** ${(finalReport.meta.totalDuration / 1000).toFixed(1)} seconds ## Overall Status: ${finalReport.summary.overallStatus} ### Phase Execution Results **Master Test Orchestration:** ${finalReport.summary.phases.orchestration.toUpperCase()} - Status: ${this.results.orchestration?.status || 'Not executed'} - Duration: ${this.results.orchestration ? (this.results.orchestration.duration / 1000).toFixed(1) + 's' : 'N/A'} **Cross-Functional Workflows:** ${finalReport.summary.phases.workflows.toUpperCase()} - Status: ${this.results.workflows?.status || 'Not executed'} - Duration: ${this.results.workflows ? (this.results.workflows.duration / 1000).toFixed(1) + 's' : 'N/A'} **Performance Testing:** ${finalReport.summary.phases.performance.toUpperCase()} - Status: ${this.results.performance?.status || 'Not executed'} - Duration: ${this.results.performance ? (this.results.performance.duration / 1000).toFixed(1) + 's' : 'N/A'} **Error Recovery Testing:** ${finalReport.summary.phases.recovery.toUpperCase()} - Status: ${this.results.recovery?.status || 'Not executed'} - Duration: ${this.results.recovery ? (this.results.recovery.duration / 1000).toFixed(1) + 's' : 'N/A'} ### Performance Summary **Total Execution Time:** ${(finalReport.meta.totalDuration / 1000).toFixed(1)}s **Target Execution Time:** ${(this.config.timeout / 1000).toFixed(1)}s **Performance Target Met:** ${finalReport.summary.performanceMet ? 'YES' : 'NO'} ### Key Findings ${this.generateKeyFindings()} ### Recommendations ${finalReport.recommendations.map(r => `- ${r}`).join('\n')} --- *Generated by Master Test Runner v3.0.0* `; const summaryPath = path.join(this.config.reportDir, `executive-summary-${Date.now()}.md`); await fs.writeFile(summaryPath, summary.trim()); return summaryPath; } /** * Generate key findings */ generateKeyFindings() { const findings = []; // Orchestration findings if (this.results.orchestration?.status === 'completed') { findings.push('✓ Master test orchestration successfully coordinated all agent implementations'); } else if (this.results.orchestration?.status === 'failed') { findings.push('✗ Master test orchestration encountered failures'); } // Workflow findings if (this.results.workflows?.results) { const passedWorkflows = this.results.workflows.results.filter(w => w.status === 'passed').length; const totalWorkflows = this.results.workflows.results.length; findings.push(`✓ Cross-functional workflows: ${passedWorkflows}/${totalWorkflows} passed`); } // Performance findings if (this.results.performance?.benchmark) { const benchmark = this.results.performance.benchmark; findings.push(`✓ Performance rating: ${benchmark.overall.rating}`); findings.push(`✓ Page load performance: ${benchmark.pageLoads.rating}`); } // Recovery findings if (this.results.recovery?.results) { const recovery = this.results.recovery.results; findings.push(`✓ Error recovery tests: ${recovery.summary.passed}/${recovery.summary.total} passed (${recovery.summary.successRate.toFixed(1)}%)`); } if (findings.length === 0) { findings.push('No significant findings to report'); } return findings.join('\n'); } /** * Generate recommendations */ generateRecommendations() { const recommendations = []; // Performance recommendations const totalDuration = Date.now() - this.startTime; if (totalDuration > this.config.timeout) { recommendations.push(`Optimize execution time - exceeded ${this.config.timeout / 60000} minute target by ${((totalDuration - this.config.timeout) / 1000).toFixed(1)}s`); } // Phase-specific recommendations if (this.results.orchestration?.status === 'failed') { recommendations.push('Review and fix master test orchestration failures'); } if (this.results.workflows?.results) { const failedWorkflows = this.results.workflows.results.filter(w => w.status === 'failed'); if (failedWorkflows.length > 0) { recommendations.push(`Address ${failedWorkflows.length} failed cross-functional workflow(s)`); } } if (this.results.performance?.benchmark) { const perfRecommendations = this.results.performance.benchmark.recommendations; if (perfRecommendations && perfRecommendations.length > 0) { recommendations.push(...perfRecommendations.map(r => r.message)); } } if (this.results.recovery?.results) { const recovery = this.results.recovery.results; if (recovery.summary.successRate < 90) { recommendations.push('Improve error recovery mechanisms - success rate below 90%'); } } if (recommendations.length === 0) { recommendations.push('All integration criteria met - continue with regular maintenance'); } return recommendations; } /** * Display results summary */ displayResults() { console.log('\n' + '═'.repeat(60)); console.log(' MASTER TEST RUNNER - EXECUTION SUMMARY'); console.log('═'.repeat(60)); const totalDuration = Date.now() - this.startTime; console.log(`Total Execution Time: ${(totalDuration / 1000).toFixed(1)}s`); console.log(`Performance Target: ${totalDuration <= this.config.timeout ? 'MET' : 'EXCEEDED'}`); console.log(''); // Phase results const phases = [ ['Master Orchestration', this.results.orchestration], ['Cross-Functional Workflows', this.results.workflows], ['Performance Testing', this.results.performance], ['Error Recovery Testing', this.results.recovery] ]; phases.forEach(([name, result]) => { if (result) { const status = result.status === 'completed' ? '✓' : result.status === 'failed' ? '✗' : '○'; const duration = result.duration ? `(${(result.duration / 1000).toFixed(1)}s)` : ''; console.log(`${status} ${name}: ${result.status.toUpperCase()} ${duration}`); if (result.error) { console.log(` Error: ${result.error}`); } } else { console.log(`○ ${name}: SKIPPED`); } }); console.log(''); // Success metrics const success = this.evaluateOverallSuccess(); console.log(`Overall Status: ${success ? '🎉 SUCCESS' : '❌ FAILED'}`); console.log(`Evidence Directory: ${this.config.evidenceDir}`); console.log(`Reports Directory: ${this.config.reportDir}`); } /** * Evaluate overall success */ evaluateOverallSuccess() { const phaseResults = [ this.results.orchestration, this.results.workflows, this.results.performance, this.results.recovery ].filter(Boolean); // Only include executed phases if (phaseResults.length === 0) { return false; // No phases executed } // All executed phases must be completed (not failed) const allPhasesSuccessful = phaseResults.every(result => result.status === 'completed'); // Performance target should be met const totalDuration = Date.now() - this.startTime; const performanceTargetMet = totalDuration <= this.config.timeout; return allPhasesSuccessful && (this.config.ignorePerformanceTarget || performanceTargetMet); } /** * Handle execution error */ async handleExecutionError(error) { const errorReport = { timestamp: new Date().toISOString(), error: error.message, stack: error.stack, config: this.config, results: this.results, executionDuration: Date.now() - this.startTime }; const errorReportPath = path.join(this.config.reportDir, `error-report-${Date.now()}.json`); await fs.writeFile(errorReportPath, JSON.stringify(errorReport, null, 2)); console.error(`\nError report saved: ${errorReportPath}`); } /** * Create necessary directories */ async createDirectories() { const dirs = [ this.config.evidenceDir, this.config.reportDir, path.join(this.config.evidenceDir, 'screenshots'), path.join(this.config.evidenceDir, 'videos'), path.join(this.config.evidenceDir, 'logs') ]; for (const dir of dirs) { try { await fs.mkdir(dir, { recursive: true }); } catch (error) { console.warn(`Failed to create directory ${dir}: ${error.message}`); } } } /** * Cleanup all components */ async cleanup() { console.log('\n🧹 Cleaning up components...'); try { if (this.orchestrator) { await this.orchestrator.tearDown(); console.log(' ✓ Master Test Orchestrator cleanup complete'); } if (this.workflows) { await this.workflows.tearDown(); console.log(' ✓ Cross-Functional Workflows cleanup complete'); } if (this.performanceMonitor) { await this.performanceMonitor.cleanup(); console.log(' ✓ Performance Monitor cleanup complete'); } if (this.errorRecoveryManager) { await this.errorRecoveryManager.cleanup(); console.log(' ✓ Error Recovery Manager cleanup complete'); } console.log('✓ All components cleaned up successfully'); } catch (error) { console.warn('Cleanup error:', error.message); } } } // CLI Configuration function setupCLI() { const program = new Command(); program .version('3.0.0') .description('HVAC E2E Integration & Validation - Master Test Runner') .option('-e, --environment ', 'Test environment (staging|docker|production)', 'staging') .option('-u, --base-url ', 'Base URL for testing') .option('-h, --headed', 'Run in headed mode (show browser)') .option('-s, --slow-mo ', 'Slow motion delay in milliseconds', parseInt) .option('-t, --timeout ', 'Total execution timeout in milliseconds', parseInt) .option('-c, --continue-on-failure', 'Continue execution even if phases fail') .option('--skip-orchestration', 'Skip master test orchestration phase') .option('--skip-workflows', 'Skip cross-functional workflows phase') .option('--skip-performance', 'Skip performance testing phase') .option('--skip-recovery', 'Skip error recovery testing phase') .option('--performance-only', 'Run only performance testing') .option('--recovery-only', 'Run only error recovery testing') .option('--ignore-performance-target', 'Ignore performance target for success evaluation') .parse(); return program.opts(); } // Main execution async function main() { const cliOptions = setupCLI(); // Configure based on CLI options const config = { ...DEFAULT_CONFIG, ...cliOptions }; // Handle environment-specific URLs if (config.environment === 'docker') { config.baseUrl = config.baseUrl || config.dockerUrl; } // Handle specialized execution modes if (config.performanceOnly) { config.skipOrchestration = true; config.skipWorkflows = true; config.skipRecovery = true; } if (config.recoveryOnly) { config.skipOrchestration = true; config.skipWorkflows = true; config.skipPerformance = true; } // Set display environment for headed mode if (!config.headless) { process.env.DISPLAY = config.display; process.env.XAUTHORITY = config.xAuthority; } // Initialize and execute const runner = new MasterTestRunner(config); const success = await runner.execute(); process.exit(success ? 0 : 1); } // Execute if run directly if (require.main === module) { main().catch(error => { console.error('Fatal error:', error); process.exit(1); }); } module.exports = MasterTestRunner;