forked from wangziqi/gongxue-base
chore: add performance evidence summary tool
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { evaluateSummary, launchGateCheck, summarizeBenchmark } from './performance-summary.js';
|
||||
|
||||
const repoRoot = process.cwd();
|
||||
const scriptPath = path.join(repoRoot, 'scripts', 'performance-summary.js');
|
||||
|
||||
function sampleReport(overrides = {}) {
|
||||
return {
|
||||
startedAt: '2026-06-30T04:45:08.281Z',
|
||||
finishedAt: '2026-06-30T04:50:08.281Z',
|
||||
apiBase: 'https://api.example.com',
|
||||
config: {
|
||||
durationSeconds: 300,
|
||||
concurrency: 30,
|
||||
rampSeconds: 30,
|
||||
includeWrites: false,
|
||||
questionLimit: 20,
|
||||
timeoutMs: 15000,
|
||||
...(overrides.config || {}),
|
||||
},
|
||||
summary: {
|
||||
requests: 12000,
|
||||
ok: 12000,
|
||||
errors: 0,
|
||||
errorRate: 0,
|
||||
throughputRps: 40,
|
||||
okThroughputRps: 40,
|
||||
latencyOk: {
|
||||
p50Ms: 35,
|
||||
p90Ms: 120,
|
||||
p95Ms: 180,
|
||||
p99Ms: 430,
|
||||
maxMs: 700,
|
||||
...(overrides.latencyOk || {}),
|
||||
},
|
||||
...(overrides.summary || {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function runScript(report) {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tiku-perf-summary-'));
|
||||
const inputPath = path.join(tempDir, 'report.json');
|
||||
fs.writeFileSync(inputPath, JSON.stringify(report, null, 2), 'utf8');
|
||||
const result = spawnSync(process.execPath, [scriptPath, '--input', inputPath, '--json'], {
|
||||
cwd: repoRoot,
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const payload = JSON.parse(result.stdout || '{}');
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
return { ...result, payload };
|
||||
}
|
||||
|
||||
const summary = summarizeBenchmark(sampleReport());
|
||||
assert.deepEqual(
|
||||
{
|
||||
errors: summary.errors,
|
||||
errorRate: summary.errorRate,
|
||||
p95Ms: summary.p95Ms,
|
||||
p99Ms: summary.p99Ms,
|
||||
concurrency: summary.concurrency,
|
||||
durationSeconds: summary.durationSeconds,
|
||||
includeWrites: summary.includeWrites,
|
||||
},
|
||||
{
|
||||
errors: 0,
|
||||
errorRate: 0,
|
||||
p95Ms: 180,
|
||||
p99Ms: 430,
|
||||
concurrency: 30,
|
||||
durationSeconds: 300,
|
||||
includeWrites: false,
|
||||
},
|
||||
);
|
||||
|
||||
const passingEvaluation = evaluateSummary(summary);
|
||||
assert.equal(passingEvaluation.status, 'pass');
|
||||
assert.deepEqual(launchGateCheck(passingEvaluation).summary, {
|
||||
errors: 0,
|
||||
errorRate: 0,
|
||||
p95Ms: 180,
|
||||
p99Ms: 430,
|
||||
concurrency: 30,
|
||||
durationSeconds: 300,
|
||||
includeWrites: false,
|
||||
});
|
||||
|
||||
const okRun = runScript(sampleReport());
|
||||
assert.equal(okRun.status, 0, `passing report should exit 0: ${okRun.stdout} ${okRun.stderr}`);
|
||||
assert.equal(okRun.payload.evaluation?.status, 'pass');
|
||||
|
||||
const slowRun = runScript(sampleReport({ latencyOk: { p95Ms: 301 } }));
|
||||
assert.notEqual(slowRun.status, 0, 'slow report should fail');
|
||||
assert.equal(slowRun.payload.evaluation?.status, 'fail');
|
||||
assert.ok(
|
||||
slowRun.payload.evaluation?.failures?.some(item => item.includes('p95Ms expected <= 300')),
|
||||
'slow report should explain p95 threshold failure',
|
||||
);
|
||||
|
||||
const writeRun = runScript(sampleReport({ config: { includeWrites: true } }));
|
||||
assert.notEqual(writeRun.status, 0, 'write benchmark should fail read-path gate');
|
||||
assert.ok(
|
||||
writeRun.payload.evaluation?.failures?.some(item => item.includes('includeWrites expected false')),
|
||||
'write benchmark should explain includeWrites failure',
|
||||
);
|
||||
|
||||
console.log('[PASS] performance summary');
|
||||
Reference in New Issue
Block a user