31 lines
1.5 KiB
JavaScript
31 lines
1.5 KiB
JavaScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
const [baselineDir, candidateDir] = process.argv.slice(2);
|
|
if (!baselineDir || !candidateDir) {
|
|
throw new Error('usage: node compare-baseline.mjs <baseline-dir> <candidate-dir>');
|
|
}
|
|
|
|
function median(values) {
|
|
const sorted = [...values].sort((left, right) => left - right);
|
|
return sorted[Math.floor(sorted.length / 2)];
|
|
}
|
|
|
|
function read(directory) {
|
|
const summaries = [1, 2, 3].map((run) => JSON.parse(fs.readFileSync(path.join(directory, `summary-run-${run}.json`), 'utf8')));
|
|
return {
|
|
throughput: median(summaries.map((summary) => summary.metrics.http_reqs.values.rate)),
|
|
p95: median(summaries.map((summary) => summary.metrics.http_req_duration.values['p(95)'])),
|
|
p99: median(summaries.map((summary) => summary.metrics.http_req_duration.values['p(99)'])),
|
|
failures: summaries.reduce((total, summary) => total + (summary.metrics.http_req_failed?.values?.passes || 0), 0),
|
|
dropped: summaries.reduce((total, summary) => total + (summary.metrics.dropped_iterations?.values?.count || 0), 0),
|
|
};
|
|
}
|
|
|
|
const baseline = read(baselineDir);
|
|
const candidate = read(candidateDir);
|
|
const throughputChange = (candidate.throughput - baseline.throughput) / baseline.throughput;
|
|
const p95Change = (candidate.p95 - baseline.p95) / baseline.p95;
|
|
console.log(JSON.stringify({ baseline, candidate, throughputChange, p95Change }, null, 2));
|
|
if (candidate.failures > 0 || candidate.dropped > 0 || throughputChange < -0.05 || p95Change > 0.05) process.exitCode = 1;
|