usezap-cli
Version:
Zap CLI - Command-line interface for Zap API client
216 lines (189 loc) ⢠7.03 kB
JavaScript
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
};