UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

924 lines (919 loc) 35.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.LoadTesting = void 0; exports.createLoadScenario = createLoadScenario; exports.createLoadProfile = createLoadProfile; exports.runLoadTests = runLoadTests; const events_1 = require("events"); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const os = __importStar(require("os")); const perf_hooks_1 = require("perf_hooks"); class LoadTesting extends events_1.EventEmitter { constructor(config) { super(); this.results = []; this.resourceMonitor = null; this.resourceSamples = new Map(); this.config = { maxApps: 1000, maxFiles: 10000, maxDependencies: 5000, concurrent: true, workers: os.cpus().length, generateReport: true, captureMetrics: true, simulateRealWorld: true, progressiveLoad: false, ...config }; this.workspaceDir = path.join(os.tmpdir(), 're-shell-load-test'); } async run() { this.emit('loadtest:start', { scenarios: this.config.scenarios.length }); const startTime = perf_hooks_1.performance.now(); try { // Prepare test workspace await this.prepareWorkspace(); // Start resource monitoring if (this.config.captureMetrics) { this.startResourceMonitoring(); } // Run load scenarios for (const scenario of this.config.scenarios) { const result = await this.runScenario(scenario); this.results.push(result); } // Stop monitoring this.stopResourceMonitoring(); // Generate report const report = this.generateReport(perf_hooks_1.performance.now() - startTime); if (this.config.generateReport) { await this.saveReport(report); } this.emit('loadtest:complete', report); return report; } catch (error) { this.emit('loadtest:error', error); throw error; } finally { await this.cleanup(); } } async runScenario(scenario) { this.emit('scenario:start', scenario); const result = { scenario: scenario.name, success: false, metrics: this.createEmptyMetrics(), operations: [], violations: [], profile: this.createEmptyProfile(), timestamp: new Date() }; const startTime = perf_hooks_1.performance.now(); try { // Setup phase const setupStart = perf_hooks_1.performance.now(); await this.setupScenario(scenario); result.metrics.setupTime = perf_hooks_1.performance.now() - setupStart; // Execution phase const execStart = perf_hooks_1.performance.now(); const operationResults = await this.executeOperations(scenario); result.operations = operationResults; result.metrics.executionTime = perf_hooks_1.performance.now() - execStart; // Collect metrics result.metrics = this.collectMetrics(result.metrics, operationResults); result.profile = this.collectResourceProfile(scenario.name); // Check expectations if (scenario.expectations) { result.violations = this.checkExpectations(scenario.expectations, result); } result.success = result.violations.length === 0; result.metrics.totalTime = perf_hooks_1.performance.now() - startTime; this.emit('scenario:complete', result); return result; } catch (error) { result.metrics.errors++; result.metrics.totalTime = perf_hooks_1.performance.now() - startTime; this.emit('scenario:error', { scenario, error }); return result; } finally { // Teardown phase const teardownStart = perf_hooks_1.performance.now(); await this.teardownScenario(scenario); result.metrics.teardownTime = perf_hooks_1.performance.now() - teardownStart; } } async setupScenario(scenario) { const profile = scenario.load; // Create workspace structure if (profile.apps > 0) { await this.createApps(profile); } // Add dependencies if specified if (profile.dependenciesPerApp || profile.sharedDependencies) { await this.createDependencies(profile); } // Apply growth pattern if specified if (profile.growth) { await this.applyGrowthPattern(profile.growth); } } async createApps(profile) { const appPromises = []; for (let i = 0; i < profile.apps; i++) { const appName = `app-${i}`; const appPath = path.join(this.workspaceDir, 'apps', appName); const promise = this.createApp(appPath, profile, i); appPromises.push(promise); // Batch processing to avoid overwhelming the system if (appPromises.length >= 100) { await Promise.all(appPromises); appPromises.length = 0; } } await Promise.all(appPromises); } async createApp(appPath, profile, index) { await fs.ensureDir(appPath); // Create package.json const packageJson = { name: `app-${index}`, version: '1.0.0', dependencies: this.generateDependencies(profile, index), devDependencies: {}, scripts: { build: 'echo "Building..."', test: 'echo "Testing..."' } }; await fs.writeJson(path.join(appPath, 'package.json'), packageJson); // Create source files if (profile.filesPerApp) { await this.createFiles(appPath, profile); } } async createFiles(appPath, profile) { const srcPath = path.join(appPath, 'src'); await fs.ensureDir(srcPath); const filePromises = []; for (let i = 0; i < (profile.filesPerApp || 10); i++) { const fileName = `file-${i}.ts`; const filePath = path.join(srcPath, fileName); const content = this.generateFileContent(profile, i); filePromises.push(fs.writeFile(filePath, content)); } await Promise.all(filePromises); } generateDependencies(profile, appIndex) { const deps = {}; // Add app-specific dependencies if (profile.dependenciesPerApp) { for (let i = 0; i < profile.dependenciesPerApp; i++) { deps[`dep-${appIndex}-${i}`] = '1.0.0'; } } // Add shared dependencies if (profile.sharedDependencies) { for (let i = 0; i < profile.sharedDependencies; i++) { deps[`shared-dep-${i}`] = '1.0.0'; } } return deps; } generateFileContent(profile, index) { const size = this.getFileSize(profile.fileSize); const complexity = profile.complexity || 'medium'; let content = `// File ${index} - Size: ${size} bytes\n\n`; if (complexity === 'low') { content += this.generateSimpleContent(size); } else if (complexity === 'medium') { content += this.generateMediumContent(size); } else { content += this.generateComplexContent(size); } return content; } getFileSize(distribution) { if (!distribution) { return 1024; // Default 1KB } const random = Math.random() * 100; const cumulative = { small: distribution.small, medium: distribution.small + distribution.medium, large: distribution.small + distribution.medium + distribution.large, xlarge: 100 }; if (random < cumulative.small) return Math.random() * 1024; // < 1KB if (random < cumulative.medium) return 1024 + Math.random() * 100 * 1024; // 1KB - 100KB if (random < cumulative.large) return 100 * 1024 + Math.random() * 900 * 1024; // 100KB - 1MB return 1024 * 1024 + Math.random() * 5 * 1024 * 1024; // > 1MB } generateSimpleContent(size) { const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; let content = ''; while (content.length < size) { content += chars.charAt(Math.floor(Math.random() * chars.length)); } return content.substring(0, size); } generateMediumContent(size) { let content = ''; const functions = ['function', 'const', 'class', 'interface', 'export']; while (content.length < size) { const func = functions[Math.floor(Math.random() * functions.length)]; const name = `item${Math.floor(Math.random() * 1000)}`; content += `${func} ${name} {\n // Implementation\n}\n\n`; } return content.substring(0, size); } generateComplexContent(size) { let content = ''; while (content.length < size) { content += ` import { Module${Math.floor(Math.random() * 100)} } from './module'; export class Component${Math.floor(Math.random() * 1000)} { private data: any[] = []; constructor(private readonly service: Service) {} async process(input: string): Promise<void> { const result = await this.service.execute(input); this.data.push(result); } get results(): any[] { return [...this.data]; } } `; } return content.substring(0, size); } async createDependencies(profile) { const modulesPath = path.join(this.workspaceDir, 'node_modules'); await fs.ensureDir(modulesPath); // Create mock dependency modules const totalDeps = (profile.apps * (profile.dependenciesPerApp || 0)) + (profile.sharedDependencies || 0); const depPromises = []; for (let i = 0; i < Math.min(totalDeps, 1000); i++) { const depName = `dep-${i}`; const depPath = path.join(modulesPath, depName); depPromises.push(this.createMockDependency(depPath, depName)); if (depPromises.length >= 100) { await Promise.all(depPromises); depPromises.length = 0; } } await Promise.all(depPromises); } async createMockDependency(depPath, name) { await fs.ensureDir(depPath); const packageJson = { name, version: '1.0.0', main: 'index.js' }; await fs.writeJson(path.join(depPath, 'package.json'), packageJson); await fs.writeFile(path.join(depPath, 'index.js'), `module.exports = { name: '${name}' };`); } async applyGrowthPattern(growth) { // Growth pattern simulation would be implemented here // For now, just log the pattern this.emit('growth:applied', growth); } async executeOperations(scenario) { const results = []; for (const operation of scenario.operations) { const result = await this.executeOperation(operation, scenario.load); results.push(result); } return results; } async executeOperation(operation, profile) { const result = { operation: `${operation.type}:${operation.target}`, count: operation.count || 1, totalTime: 0, avgTime: 0, minTime: Infinity, maxTime: 0, errors: 0, throughput: 0 }; const times = []; for (let i = 0; i < result.count; i++) { try { const start = perf_hooks_1.performance.now(); switch (operation.type) { case 'create': await this.performCreate(operation, profile, i); break; case 'read': await this.performRead(operation, profile, i); break; case 'update': await this.performUpdate(operation, profile, i); break; case 'delete': await this.performDelete(operation, profile, i); break; case 'build': await this.performBuild(operation, profile, i); break; case 'analyze': await this.performAnalyze(operation, profile, i); break; case 'search': await this.performSearch(operation, profile, i); break; } const time = perf_hooks_1.performance.now() - start; times.push(time); result.minTime = Math.min(result.minTime, time); result.maxTime = Math.max(result.maxTime, time); } catch (error) { result.errors++; } } result.totalTime = times.reduce((a, b) => a + b, 0); result.avgTime = result.totalTime / result.count; result.throughput = result.count / (result.totalTime / 1000); return result; } async performCreate(operation, profile, index) { switch (operation.target) { case 'app': const appPath = path.join(this.workspaceDir, 'apps', `new-app-${index}`); await this.createApp(appPath, profile, index); break; case 'file': const filePath = path.join(this.workspaceDir, `file-${index}.ts`); await fs.writeFile(filePath, this.generateFileContent(profile, index)); break; } } async performRead(operation, profile, index) { switch (operation.target) { case 'workspace': await fs.readdir(this.workspaceDir, { recursive: true }); break; case 'app': const appPath = path.join(this.workspaceDir, 'apps', `app-${index % profile.apps}`); if (await fs.pathExists(appPath)) { await fs.readdir(appPath); } break; } } async performUpdate(operation, profile, index) { switch (operation.target) { case 'file': const filePath = path.join(this.workspaceDir, 'apps', `app-${index % profile.apps}`, 'src', 'file-0.ts'); if (await fs.pathExists(filePath)) { await fs.appendFile(filePath, '\n// Updated'); } break; } } async performDelete(operation, profile, index) { // Implement delete operations } async performBuild(operation, profile, index) { // Simulate build operation await this.wait(10 + Math.random() * 50); } async performAnalyze(operation, profile, index) { // Simulate analysis operation switch (operation.target) { case 'workspace': const files = await fs.readdir(this.workspaceDir, { recursive: true }); // Count files, calculate sizes, etc. break; } } async performSearch(operation, profile, index) { // Simulate search operation const pattern = operation.pattern || 'test'; // Would implement actual search logic here } startResourceMonitoring() { const samples = []; this.resourceSamples.set('global', samples); this.resourceMonitor = setInterval(() => { const memoryUsage = process.memoryUsage(); const cpuUsage = process.cpuUsage(); samples.push({ timestamp: Date.now(), value: memoryUsage.heapUsed / 1024 / 1024 // MB }); // Keep only last 1000 samples if (samples.length > 1000) { samples.shift(); } }, 100); } stopResourceMonitoring() { if (this.resourceMonitor) { clearInterval(this.resourceMonitor); this.resourceMonitor = null; } } collectMetrics(metrics, operations) { metrics.throughput = operations.reduce((sum, op) => sum + op.throughput, 0); metrics.errors = operations.reduce((sum, op) => sum + op.errors, 0); metrics.ioOperations = operations.reduce((sum, op) => sum + op.count, 0); const samples = this.resourceSamples.get('global') || []; if (samples.length > 0) { metrics.peakMemory = Math.max(...samples.map(s => s.value)); metrics.avgMemory = samples.reduce((sum, s) => sum + s.value, 0) / samples.length; } return metrics; } collectResourceProfile(scenarioName) { const samples = this.resourceSamples.get('global') || []; const memoryTimeline = { samples: samples.slice(-100), // Last 100 samples peak: samples.length > 0 ? Math.max(...samples.map(s => s.value)) : 0, average: samples.length > 0 ? samples.reduce((sum, s) => sum + s.value, 0) / samples.length : 0, trend: this.analyzeTrend(samples) }; return { memory: memoryTimeline, cpu: this.createEmptyTimeline(), io: this.createEmptyTimeline(), handles: this.createEmptyTimeline() }; } analyzeTrend(samples) { if (samples.length < 10) return 'stable'; const recent = samples.slice(-10); const older = samples.slice(-20, -10); const recentAvg = recent.reduce((sum, s) => sum + s.value, 0) / recent.length; const olderAvg = older.reduce((sum, s) => sum + s.value, 0) / older.length; const change = (recentAvg - olderAvg) / olderAvg; if (change > 0.1) return 'increasing'; if (change < -0.1) return 'decreasing'; return 'stable'; } checkExpectations(expectations, result) { const violations = []; for (const expectation of expectations) { let actual = 0; switch (expectation.metric) { case 'time': actual = this.getTimeMetric(expectation, result); break; case 'memory': actual = this.getMemoryMetric(expectation, result); break; case 'cpu': actual = this.getCpuMetric(expectation, result); break; case 'io': actual = this.getIoMetric(expectation, result); break; } if (actual > expectation.threshold) { const difference = actual - expectation.threshold; violations.push({ expectation: `${expectation.metric} ${expectation.operation || 'overall'}`, actual, expected: expectation.threshold, difference, percentage: (difference / expectation.threshold) * 100 }); } } return violations; } getTimeMetric(expectation, result) { if (expectation.operation) { const op = result.operations.find(o => o.operation === expectation.operation); return op ? op.avgTime : 0; } return result.metrics.totalTime; } getMemoryMetric(expectation, result) { return expectation.unit === 'gb' ? result.metrics.peakMemory / 1024 : result.metrics.peakMemory; } getCpuMetric(expectation, result) { return result.metrics.peakCpu || 0; } getIoMetric(expectation, result) { return result.metrics.ioOperations; } async prepareWorkspace() { await fs.ensureDir(this.workspaceDir); await fs.emptyDir(this.workspaceDir); } async teardownScenario(scenario) { // Cleanup scenario-specific resources } async cleanup() { try { await fs.remove(this.workspaceDir); } catch { // Ignore cleanup errors } } generateReport(duration) { const summary = this.generateSummary(duration); const analysis = this.analyzeResults(); const recommendations = this.generateRecommendations(analysis); const charts = this.config.captureMetrics ? this.generateCharts() : undefined; return { summary, results: this.results, analysis, recommendations, charts, timestamp: new Date() }; } generateSummary(duration) { const maxLoad = this.calculateMaxLoad(); const performance = this.analyzePerformance(); return { totalScenarios: this.results.length, passed: this.results.filter(r => r.success).length, failed: this.results.filter(r => !r.success).length, totalOperations: this.results.reduce((sum, r) => sum + r.operations.reduce((s, o) => s + o.count, 0), 0), totalDuration: duration / 1000, maxLoad, performance }; } calculateMaxLoad() { let maxApps = 0; let maxFiles = 0; let maxDependencies = 0; for (const scenario of this.config.scenarios) { maxApps = Math.max(maxApps, scenario.load.apps); maxFiles = Math.max(maxFiles, scenario.load.apps * (scenario.load.filesPerApp || 0)); maxDependencies = Math.max(maxDependencies, scenario.load.apps * (scenario.load.dependenciesPerApp || 0) + (scenario.load.sharedDependencies || 0)); } return { apps: maxApps, files: maxFiles, dependencies: maxDependencies }; } analyzePerformance() { let fastest = ''; let slowest = ''; let mostMemory = ''; let mostCpu = ''; let minTime = Infinity; let maxTime = 0; let maxMemory = 0; let maxCpu = 0; for (const result of this.results) { if (result.metrics.totalTime < minTime) { minTime = result.metrics.totalTime; fastest = result.scenario; } if (result.metrics.totalTime > maxTime) { maxTime = result.metrics.totalTime; slowest = result.scenario; } if (result.metrics.peakMemory > maxMemory) { maxMemory = result.metrics.peakMemory; mostMemory = result.scenario; } if (result.metrics.peakCpu > maxCpu) { maxCpu = result.metrics.peakCpu; mostCpu = result.scenario; } } return { fastest, slowest, mostMemory, mostCpu }; } analyzeResults() { const scalability = this.analyzeScalability(); const bottlenecks = this.identifyBottlenecks(); const trends = this.analyzeTrends(); const limits = this.calculateLimits(); return { scalability, bottlenecks, trends, limits }; } analyzeScalability() { // Analyze how performance scales with load const loadPoints = []; for (const result of this.results) { const scenario = this.config.scenarios.find(s => s.name === result.scenario); if (scenario) { loadPoints.push({ load: scenario.load.apps, time: result.metrics.totalTime }); } } loadPoints.sort((a, b) => a.load - b.load); // Simple linear regression to detect scaling pattern const isLinear = this.checkLinearScaling(loadPoints); const breakpoint = this.findBreakpoint(loadPoints); return { linear: isLinear, breakpoint, recommendation: isLinear ? 'System scales linearly with load' : `Performance degrades significantly after ${breakpoint} apps` }; } checkLinearScaling(points) { if (points.length < 3) return true; // Calculate correlation coefficient const n = points.length; const sumX = points.reduce((sum, p) => sum + p.load, 0); const sumY = points.reduce((sum, p) => sum + p.time, 0); const sumXY = points.reduce((sum, p) => sum + p.load * p.time, 0); const sumX2 = points.reduce((sum, p) => sum + p.load * p.load, 0); const sumY2 = points.reduce((sum, p) => sum + p.time * p.time, 0); const r = (n * sumXY - sumX * sumY) / Math.sqrt((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY)); return r > 0.9; // Strong linear correlation } findBreakpoint(points) { if (points.length < 3) return this.config.maxApps; for (let i = 1; i < points.length - 1; i++) { const slope1 = (points[i].time - points[i - 1].time) / (points[i].load - points[i - 1].load); const slope2 = (points[i + 1].time - points[i].time) / (points[i + 1].load - points[i].load); if (slope2 > slope1 * 2) { return points[i].load; } } return this.config.maxApps; } identifyBottlenecks() { const bottlenecks = []; // Analyze operation times const slowOps = new Map(); for (const result of this.results) { for (const op of result.operations) { if (op.avgTime > 1000) { // Operations taking > 1s slowOps.set(op.operation, op.avgTime); } } } for (const [op, time] of slowOps) { bottlenecks.push({ operation: op, resource: 'time', impact: time > 5000 ? 'high' : time > 2000 ? 'medium' : 'low', description: `Operation takes ${time.toFixed(0)}ms on average`, suggestion: 'Consider optimizing or parallelizing this operation' }); } // Analyze memory usage for (const result of this.results) { if (result.metrics.peakMemory > 1024) { // > 1GB bottlenecks.push({ operation: result.scenario, resource: 'memory', impact: 'high', description: `Peak memory usage: ${result.metrics.peakMemory.toFixed(0)}MB`, suggestion: 'Implement memory-efficient algorithms or streaming' }); } } return bottlenecks; } analyzeTrends() { // Analyze resource usage trends across scenarios let memoryTrend = 'stable'; const cpuTrend = 'stable'; const performanceTrend = 'stable'; // Simple trend analysis based on load vs resource usage const memoryByLoad = []; for (const result of this.results) { const scenario = this.config.scenarios.find(s => s.name === result.scenario); if (scenario) { memoryByLoad.push({ load: scenario.load.apps, memory: result.metrics.peakMemory }); } } if (memoryByLoad.length >= 3) { memoryByLoad.sort((a, b) => a.load - b.load); // Check growth rate const growthRates = []; for (let i = 1; i < memoryByLoad.length; i++) { const rate = (memoryByLoad[i].memory - memoryByLoad[i - 1].memory) / (memoryByLoad[i].load - memoryByLoad[i - 1].load); growthRates.push(rate); } const avgGrowth = growthRates.reduce((a, b) => a + b, 0) / growthRates.length; const increasing = growthRates.every((r, i) => i === 0 || r > growthRates[i - 1]); if (increasing) { memoryTrend = 'exponential'; } else if (avgGrowth > 0.1) { memoryTrend = 'linear'; } } return { memoryTrend, cpuTrend, performanceTrend }; } calculateLimits() { const limits = { maxApps: this.config.maxApps, maxFiles: this.config.maxFiles, maxDependencies: this.config.maxDependencies, maxConcurrent: this.config.workers, constraints: [] }; // Analyze actual limits based on test results for (const result of this.results) { if (!result.success) { const scenario = this.config.scenarios.find(s => s.name === result.scenario); if (scenario) { if (scenario.load.apps < limits.maxApps) { limits.maxApps = scenario.load.apps; limits.constraints.push(`App limit: ${scenario.load.apps} (${result.scenario})`); } } } } return limits; } generateRecommendations(analysis) { const recommendations = []; // Scalability recommendations if (!analysis.scalability.linear) { recommendations.push(`Performance degrades non-linearly. Consider horizontal scaling after ${analysis.scalability.breakpoint} apps`); } // Bottleneck recommendations const highImpact = analysis.bottlenecks.filter(b => b.impact === 'high'); if (highImpact.length > 0) { recommendations.push(`Address high-impact bottlenecks: ${highImpact.map(b => b.operation).join(', ')}`); } // Memory recommendations if (analysis.trends.memoryTrend === 'exponential') { recommendations.push('Memory usage grows exponentially. Implement memory pooling or caching strategies'); } // Limit recommendations if (analysis.limits.maxApps < this.config.maxApps) { recommendations.push(`System limit reached at ${analysis.limits.maxApps} apps. Consider optimization or infrastructure upgrades`); } return recommendations; } generateCharts() { // Generate chart data for visualization return { performanceOverTime: [], resourceUsage: [], throughput: [], scalability: [] }; } async saveReport(report) { const reportPath = path.join(process.cwd(), 'load-test-report.json'); await fs.writeJson(reportPath, report, { spaces: 2 }); const summaryPath = reportPath.replace('.json', '-summary.txt'); await fs.writeFile(summaryPath, this.formatSummary(report)); this.emit('report:saved', { json: reportPath, summary: summaryPath }); } formatSummary(report) { const lines = [ 'Load Test Report', '================', '', `Date: ${report.timestamp.toISOString()}`, '', 'Summary:', ` Total Scenarios: ${report.summary.totalScenarios}`, ` Passed: ${report.summary.passed}`, ` Failed: ${report.summary.failed}`, ` Total Operations: ${report.summary.totalOperations}`, ` Total Duration: ${report.summary.totalDuration.toFixed(2)}s`, '', 'Maximum Load:', ` Apps: ${report.summary.maxLoad.apps}`, ` Files: ${report.summary.maxLoad.files}`, ` Dependencies: ${report.summary.maxLoad.dependencies}`, '', 'Performance:', ` Fastest: ${report.summary.performance.fastest}`, ` Slowest: ${report.summary.performance.slowest}`, ` Most Memory: ${report.summary.performance.mostMemory}`, '', 'Analysis:', ` Scalability: ${report.analysis.scalability.linear ? 'Linear' : 'Non-linear'}`, ` Bottlenecks: ${report.analysis.bottlenecks.length}`, ` Memory Trend: ${report.analysis.trends.memoryTrend}`, '', 'Recommendations:' ]; report.recommendations.forEach(rec => { lines.push(` - ${rec}`); }); return lines.join('\n'); } createEmptyMetrics() { return { totalTime: 0, setupTime: 0, executionTime: 0, teardownTime: 0, throughput: 0, peakMemory: 0, avgMemory: 0, peakCpu: 0, avgCpu: 0, ioOperations: 0, errors: 0 }; } createEmptyProfile() { return { memory: this.createEmptyTimeline(), cpu: this.createEmptyTimeline(), io: this.createEmptyTimeline(), handles: this.createEmptyTimeline() }; } createEmptyTimeline() { return { samples: [], peak: 0, average: 0, trend: 'stable' }; } wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } exports.LoadTesting = LoadTesting; // Export utility functions function createLoadScenario(name, type, load, operations) { return { name, type, load, operations }; } function createLoadProfile(apps, options) { return { apps, filesPerApp: 10, dependenciesPerApp: 5, sharedDependencies: 10, complexity: 'medium', ...options }; } async function runLoadTests(scenarios, config) { const tester = new LoadTesting({ scenarios, ...config }); return tester.run(); }