UNPKG

usezap-cli

Version:

Zap CLI - Command-line interface for Zap API client

216 lines (189 loc) • 7.03 kB
const chalk = require('chalk'); const fs = require('fs'); const path = require('path'); /** * Performance testing command for Zap CLI * * Usage: * zap perf collection.zap --users 50 --duration 5m --output report.html * zap perf collection.zap --load-pattern ramp --ramp-time 30s */ const command = 'perf <collection>'; const desc = 'Run performance tests on a collection'; const builder = (yargs) => { return yargs .positional('collection', { describe: 'Path to the Zap collection file', type: 'string' }) .option('users', { alias: 'u', describe: 'Number of virtual users', type: 'number', default: 10 }) .option('duration', { alias: 'd', describe: 'Test duration (e.g., 30s, 5m, 1h)', type: 'string', default: '1m' }) .option('load-pattern', { describe: 'Load pattern: constant, ramp, spike', type: 'string', choices: ['constant', 'ramp', 'spike'], default: 'constant' }) .option('ramp-time', { describe: 'Ramp-up time for ramp pattern', type: 'string', default: '30s' }) .option('think-time', { describe: 'Think time between requests (ms)', type: 'number', default: 0 }) .option('output', { alias: 'o', describe: 'Output file path', type: 'string' }) .option('format', { describe: 'Report format: html, json, junit', type: 'string', choices: ['html', 'json', 'junit'], default: 'html' }) .option('env', { alias: 'e', describe: 'Environment file path', type: 'string' }) .option('threshold-avg-response', { describe: 'Average response time threshold (ms)', type: 'number', default: 1000 }) .option('threshold-p95-response', { describe: 'P95 response time threshold (ms)', type: 'number', default: 2000 }) .option('threshold-error-rate', { describe: 'Error rate threshold (%)', type: 'number', default: 1 }) .option('verbose', { alias: 'v', describe: 'Verbose output', type: 'boolean', default: false }) .example('$0 perf api-tests.zap --users 50 --duration 5m', 'Run 50 virtual users for 5 minutes') .example('$0 perf api-tests.zap --load-pattern ramp --users 100', 'Ramp up to 100 users') .example('$0 perf api-tests.zap --output results.html --format html', 'Generate HTML report'); }; const handler = async (argv) => { try { console.log(chalk.cyan('šŸš€ Starting Zap Performance Test')); console.log(chalk.gray(`Collection: ${argv.collection}`)); console.log(chalk.gray(`Users: ${argv.users}, Duration: ${argv.duration}, Pattern: ${argv.loadPattern}`)); // Validate collection file if (!fs.existsSync(argv.collection)) { throw new Error(`Collection file not found: ${argv.collection}`); } // Load collection const collectionData = JSON.parse(fs.readFileSync(argv.collection, 'utf8')); // Load environment if provided let environment = {}; if (argv.env && fs.existsSync(argv.env)) { environment = JSON.parse(fs.readFileSync(argv.env, 'utf8')); } // Initialize performance runner const PerformanceRunner = require('@usezap/performance/src/performance-runner'); const PerformanceReporter = require('@usezap/performance/src/performance-reporter'); const runner = new PerformanceRunner({ users: argv.users, duration: argv.duration, loadPattern: argv.loadPattern, rampUpTime: argv.rampTime, thinkTime: argv.thinkTime }); // Set up real-time progress reporting if (argv.verbose) { runner.on('testStart', (data) => { console.log(chalk.green('āœ… Test started')); }); runner.on('userAdded', (data) => { console.log(chalk.blue(`šŸ‘¤ User ${data.userId + 1}/${argv.users} started`)); }); runner.on('metrics', (metrics) => { // Show progress every 10 seconds if (Math.floor(metrics.timestamp / 10000) % 1 === 0) { console.log(chalk.yellow(`šŸ“Š ${metrics.requestCount} requests, ${metrics.errorCount} errors`)); } }); runner.on('error', (error) => { console.log(chalk.red(`āŒ Error: ${error.error}`)); }); } // Run performance test console.log(chalk.yellow('ā³ Running performance test...')); const results = await runner.executePerformanceTest(collectionData, environment); // Generate report const reporter = new PerformanceReporter(); let outputPath = argv.output; if (!outputPath) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); outputPath = `performance-report-${timestamp}.${argv.format}`; } switch (argv.format) { case 'html': reporter.generateHtmlReport(results, outputPath); break; case 'json': reporter.generateJsonReport(results, outputPath); break; case 'junit': reporter.generateJUnitReport(results, outputPath); break; } // Display summary console.log(chalk.green('\nāœ… Performance test completed!')); console.log(chalk.cyan('\nšŸ“Š Summary:')); console.log(chalk.white(` Total Requests: ${results.summary.totalRequests}`)); console.log(chalk.white(` Requests/Second: ${Math.round(results.summary.requestsPerSecond * 100) / 100}`)); console.log(chalk.white(` Average Response Time: ${Math.round(results.responseTime.avg)}ms`)); console.log(chalk.white(` P95 Response Time: ${Math.round(results.responseTime.p95)}ms`)); console.log(chalk.white(` Error Rate: ${Math.round(results.summary.errorRate * 100) / 100}%`)); console.log(chalk.white(` Duration: ${Math.round(results.summary.totalDuration / 1000)}s`)); // Check thresholds const thresholds = reporter.evaluateThresholds(results); let allPassed = true; console.log(chalk.cyan('\nšŸŽÆ Threshold Validation:')); Object.entries(thresholds).forEach(([metric, threshold]) => { const status = threshold.passed ? chalk.green('āœ… PASS') : chalk.red('āŒ FAIL'); console.log(` ${metric}: ${Math.round(threshold.actual * 100) / 100} / ${threshold.threshold} ${status}`); if (!threshold.passed) allPassed = false; }); console.log(chalk.cyan(`\nšŸ“„ Report saved: ${outputPath}`)); if (!allPassed) { console.log(chalk.yellow('\nāš ļø Some thresholds were not met')); process.exit(1); } } catch (error) { console.error(chalk.red('āŒ Performance test failed:'), error.message); if (argv.verbose) { console.error(error.stack); } process.exit(1); } }; module.exports = { command, desc, builder, handler };