124 أسطر
3.3 KiB
JavaScript
124 أسطر
3.3 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { execFile } = require('node:child_process');
|
|
const { createServer } = require('node:http');
|
|
const path = require('node:path');
|
|
const { test } = require('node:test');
|
|
|
|
const { evaluatePerformanceGates, parseArgs } = require('./load-test');
|
|
|
|
const passingSummary = {
|
|
requestsPerSecond: 250.25,
|
|
successRate: 99.95,
|
|
latencyMs: { p95: 120.5, p99: 180.75 },
|
|
};
|
|
|
|
test('parseArgs reads all optional performance gates', () => {
|
|
const options = parseArgs([
|
|
'--min-success-rate',
|
|
'99.9',
|
|
'--min-rps',
|
|
'200',
|
|
'--max-p95',
|
|
'150',
|
|
'--max-p99',
|
|
'250',
|
|
]);
|
|
|
|
assert.deepEqual(options.gates, {
|
|
minSuccessRate: 99.9,
|
|
minRequestsPerSecond: 200,
|
|
maxP95Ms: 150,
|
|
maxP99Ms: 250,
|
|
});
|
|
});
|
|
|
|
test('parseArgs rejects invalid gate values', () => {
|
|
assert.throws(
|
|
() => parseArgs(['--min-success-rate', '100.1']),
|
|
/min-success-rate must be a finite number between 0 and 100/,
|
|
);
|
|
assert.throws(() => parseArgs(['--min-rps', '-1']), /min-rps must be a finite number/);
|
|
assert.throws(() => parseArgs(['--max-p95']), /--max-p95 requires a value/);
|
|
});
|
|
|
|
test('evaluatePerformanceGates passes when every threshold is satisfied', () => {
|
|
const result = evaluatePerformanceGates(passingSummary, {
|
|
minSuccessRate: 99.9,
|
|
minRequestsPerSecond: 200,
|
|
maxP95Ms: 150,
|
|
maxP99Ms: 200,
|
|
});
|
|
|
|
assert.equal(result.configured, true);
|
|
assert.equal(result.passed, true);
|
|
assert.equal(result.checks.length, 4);
|
|
assert.ok(result.checks.every((check) => check.passed));
|
|
});
|
|
|
|
test('evaluatePerformanceGates reports every failed threshold', () => {
|
|
const result = evaluatePerformanceGates(passingSummary, {
|
|
minSuccessRate: 100,
|
|
minRequestsPerSecond: 300,
|
|
maxP95Ms: 100,
|
|
maxP99Ms: 150,
|
|
});
|
|
|
|
assert.equal(result.passed, false);
|
|
assert.deepEqual(
|
|
result.checks.filter((check) => !check.passed).map((check) => check.metric),
|
|
['successRate', 'requestsPerSecond', 'latencyMs.p95', 'latencyMs.p99'],
|
|
);
|
|
});
|
|
|
|
test('load-test process exits with code 2 and a clear report when a gate fails', async () => {
|
|
const server = createServer((_request, response) => {
|
|
response.statusCode = 503;
|
|
response.end('unavailable');
|
|
});
|
|
|
|
await new Promise((resolve, reject) => {
|
|
server.once('error', reject);
|
|
server.listen(0, '127.0.0.1', resolve);
|
|
});
|
|
|
|
try {
|
|
const address = server.address();
|
|
assert.ok(address && typeof address === 'object');
|
|
|
|
const result = await runLoadTest([
|
|
'--url',
|
|
`http://127.0.0.1:${address.port}`,
|
|
'--duration',
|
|
'0.05',
|
|
'--concurrency',
|
|
'1',
|
|
'--warmup',
|
|
'0',
|
|
'--min-success-rate',
|
|
'100',
|
|
]);
|
|
|
|
assert.equal(result.exitCode, 2);
|
|
assert.match(result.stdout, /Performance gates: FAILED \(0\/1\)/);
|
|
assert.match(result.stdout, /\[FAIL\] Success rate:/);
|
|
} finally {
|
|
await new Promise((resolve, reject) => {
|
|
server.close((error) => (error ? reject(error) : resolve()));
|
|
});
|
|
}
|
|
});
|
|
|
|
function runLoadTest(args) {
|
|
const scriptPath = path.join(__dirname, 'load-test.js');
|
|
|
|
return new Promise((resolve) => {
|
|
execFile(process.execPath, [scriptPath, ...args], (error, stdout, stderr) => {
|
|
resolve({
|
|
exitCode: error && typeof error.code === 'number' ? error.code : 0,
|
|
stdout,
|
|
stderr,
|
|
});
|
|
});
|
|
});
|
|
}
|