@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
JavaScript
"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();
}