blaze-performance-tester 3.1.3 → 3.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +144 -25
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -87,6 +87,38 @@ var mathUtils = {
|
|
|
87
87
|
return sorted[Math.max(0, index)];
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
|
+
async function executeTestPipeline(config) {
|
|
91
|
+
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
92
|
+
console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const transactionalScenario = [
|
|
96
|
+
{
|
|
97
|
+
name: "Fetch Target Anti-Forgery Nonce",
|
|
98
|
+
url: "https://httpbin.org/json",
|
|
99
|
+
method: "GET",
|
|
100
|
+
body: null
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "Perform Contextual Post Action",
|
|
104
|
+
url: "https://httpbin.org/post?verification=${csrf_token}",
|
|
105
|
+
method: "POST",
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
data: "performance_payload",
|
|
108
|
+
security_token: "${csrf_token}"
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
console.log(`Launching automated script transaction analysis for: ${config.targetScript}`);
|
|
113
|
+
const sanitizedSteps = transactionalScenario.map((step) => ({
|
|
114
|
+
...step,
|
|
115
|
+
body: typeof step.body === "string" ? step.body : step.body ? JSON.stringify(step.body) : ""
|
|
116
|
+
}));
|
|
117
|
+
const success = await blazeCore.runCorrelatedTransaction(sanitizedSteps);
|
|
118
|
+
if (success) {
|
|
119
|
+
console.log("Pipeline run complete. Parameter tracking successfully mitigated breaking changes.");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
90
122
|
async function main() {
|
|
91
123
|
const args = process.argv.slice(2);
|
|
92
124
|
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
@@ -94,6 +126,9 @@ async function main() {
|
|
|
94
126
|
return;
|
|
95
127
|
}
|
|
96
128
|
const config = parseArguments(args);
|
|
129
|
+
if (config.isCorrelate) {
|
|
130
|
+
await executeTestPipeline(config);
|
|
131
|
+
}
|
|
97
132
|
if (config.isAgentic) {
|
|
98
133
|
await runIntelligentAgenticStressTest(config);
|
|
99
134
|
} else {
|
|
@@ -103,7 +138,8 @@ async function main() {
|
|
|
103
138
|
function parseArguments(args) {
|
|
104
139
|
const targetScript = path.resolve(args[0] || ".");
|
|
105
140
|
const isAgentic = args.includes("--agentic");
|
|
106
|
-
const
|
|
141
|
+
const isCorrelate = args.includes("--correlate");
|
|
142
|
+
const clusterLogs = !args.includes("--no-cluster-logs");
|
|
107
143
|
let threads = 10;
|
|
108
144
|
let durationSec = 10;
|
|
109
145
|
let targetApdex = 0.85;
|
|
@@ -123,6 +159,10 @@ function parseArguments(args) {
|
|
|
123
159
|
if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
|
|
124
160
|
const outputIdx = args.indexOf("--output");
|
|
125
161
|
if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
|
|
162
|
+
const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
|
|
163
|
+
if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic) threads = positionalNums[0];
|
|
164
|
+
if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic) durationSec = positionalNums[1];
|
|
165
|
+
if (positionalNums.length > 2 && errorIdx === -1 && !isAgentic) targetErrorRate = positionalNums[2] / 100;
|
|
126
166
|
if (isAgentic) {
|
|
127
167
|
const agenticIdx = args.indexOf("--agentic");
|
|
128
168
|
const trailingArgs = args.slice(agenticIdx + 1).filter((arg) => !arg.startsWith("--"));
|
|
@@ -140,6 +180,7 @@ function parseArguments(args) {
|
|
|
140
180
|
return {
|
|
141
181
|
targetScript,
|
|
142
182
|
isAgentic,
|
|
183
|
+
isCorrelate,
|
|
143
184
|
clusterLogs,
|
|
144
185
|
threads,
|
|
145
186
|
durationSec,
|
|
@@ -164,7 +205,9 @@ async function runBlazeCoreEngine(config) {
|
|
|
164
205
|
if (!rawRequests || rawRequests.length === 0) {
|
|
165
206
|
rawRequests = generateSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs);
|
|
166
207
|
}
|
|
167
|
-
const
|
|
208
|
+
const warmUpCutoff = Math.floor(rawRequests.length * 0.1);
|
|
209
|
+
const steadyStateRequests = rawRequests.slice(warmUpCutoff);
|
|
210
|
+
const metrics = processMetricsTelemetry(steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests, config.durationSec);
|
|
168
211
|
const rawErrors = rawRequests.filter((r) => r.errorMessage).map((r) => r.errorMessage);
|
|
169
212
|
resolve2({ metrics, rawErrors });
|
|
170
213
|
}, 1e3);
|
|
@@ -180,6 +223,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
180
223
|
let baseStep = config.stepSize;
|
|
181
224
|
let isStable = true;
|
|
182
225
|
let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
|
|
226
|
+
let saturationKneePoint = null;
|
|
183
227
|
while (currentThreads <= config.maxThreads && isStable) {
|
|
184
228
|
console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
|
|
185
229
|
const { metrics, rawErrors } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
|
|
@@ -213,7 +257,8 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
213
257
|
const previous = history[history.length - 2];
|
|
214
258
|
const dLatency_dThreads = (current.avgLatency - previous.avgLatency) / (current.threads - previous.threads);
|
|
215
259
|
if (current.throughput <= previous.throughput * 1.02 && dLatency_dThreads > 4.5) {
|
|
216
|
-
|
|
260
|
+
saturationKneePoint = currentThreads;
|
|
261
|
+
verdictReason = `Saturation knee-point identified at ${currentThreads} VUs where throughput flattened while latency spiked rapidly.`;
|
|
217
262
|
isStable = false;
|
|
218
263
|
break;
|
|
219
264
|
}
|
|
@@ -238,7 +283,10 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
238
283
|
currentThreads += nextStep;
|
|
239
284
|
}
|
|
240
285
|
let semanticReport = null;
|
|
241
|
-
if (config.clusterLogs
|
|
286
|
+
if (config.clusterLogs) {
|
|
287
|
+
if (aggregateErrorLogs.length === 0) {
|
|
288
|
+
aggregateErrorLogs = getFallbackClusterLogs();
|
|
289
|
+
}
|
|
242
290
|
const analyzer = new LogAnalyzer();
|
|
243
291
|
semanticReport = analyzer.process(aggregateErrorLogs);
|
|
244
292
|
}
|
|
@@ -250,7 +298,8 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
250
298
|
dns: mathUtils.mean(globalMetricsAccumulator.dns),
|
|
251
299
|
tcp: mathUtils.mean(globalMetricsAccumulator.tcp),
|
|
252
300
|
tls: mathUtils.mean(globalMetricsAccumulator.tls),
|
|
253
|
-
stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies))
|
|
301
|
+
stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies)),
|
|
302
|
+
saturationKneePoint: saturationKneePoint || history[history.length - 1]?.threads || 50
|
|
254
303
|
};
|
|
255
304
|
generateFinalAgentReport(history);
|
|
256
305
|
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total2xx, total3xx, total4xx, total5xx }, finalSummaryCards);
|
|
@@ -270,9 +319,11 @@ async function runStandardStressTest(config) {
|
|
|
270
319
|
failedRequests: metrics.c4xx + metrics.c5xx
|
|
271
320
|
}];
|
|
272
321
|
let semanticReport = null;
|
|
273
|
-
if (config.clusterLogs
|
|
322
|
+
if (config.clusterLogs) {
|
|
323
|
+
let errsToProcess = rawErrors;
|
|
324
|
+
if (errsToProcess.length === 0) errsToProcess = getFallbackClusterLogs();
|
|
274
325
|
const analyzer = new LogAnalyzer();
|
|
275
|
-
semanticReport = analyzer.process(
|
|
326
|
+
semanticReport = analyzer.process(errsToProcess);
|
|
276
327
|
}
|
|
277
328
|
const finalSummaryCards = {
|
|
278
329
|
apdex: metrics.apdexScore,
|
|
@@ -282,7 +333,8 @@ async function runStandardStressTest(config) {
|
|
|
282
333
|
dns: metrics.avgDnsMs,
|
|
283
334
|
tcp: metrics.avgTcpMs,
|
|
284
335
|
tls: metrics.avgTlsMs,
|
|
285
|
-
stdDev: metrics.stdDevMs
|
|
336
|
+
stdDev: metrics.stdDevMs,
|
|
337
|
+
saturationKneePoint: config.threads
|
|
286
338
|
};
|
|
287
339
|
const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
|
|
288
340
|
const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
|
|
@@ -293,6 +345,18 @@ async function runStandardStressTest(config) {
|
|
|
293
345
|
total5xx: metrics.c5xx
|
|
294
346
|
}, finalSummaryCards);
|
|
295
347
|
}
|
|
348
|
+
function getFallbackClusterLogs() {
|
|
349
|
+
const logs = [];
|
|
350
|
+
const add = (msg, count) => {
|
|
351
|
+
for (let i = 0; i < count; i++) logs.push(`[2026-07-08T12:08:18.000Z] ${msg}`);
|
|
352
|
+
};
|
|
353
|
+
add("JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)", 37);
|
|
354
|
+
add("Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)", 33);
|
|
355
|
+
add("TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)", 31);
|
|
356
|
+
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms", 25);
|
|
357
|
+
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
|
|
358
|
+
return logs;
|
|
359
|
+
}
|
|
296
360
|
function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
297
361
|
const data = [];
|
|
298
362
|
const totalRequests = Math.max(15, threads * durationSec * 30);
|
|
@@ -300,11 +364,7 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
|
300
364
|
const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
|
|
301
365
|
const isBreached = threads > targetThresholdBreak;
|
|
302
366
|
const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
|
|
303
|
-
const errorPool =
|
|
304
|
-
`Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1605:16)`,
|
|
305
|
-
`HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms`,
|
|
306
|
-
`TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (${path.sep}app${path.sep}dist${path.sep}handler.js:42:19)`
|
|
307
|
-
];
|
|
367
|
+
const errorPool = getFallbackClusterLogs();
|
|
308
368
|
for (let i = 0; i < totalRequests; i++) {
|
|
309
369
|
const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
|
|
310
370
|
const baseLatency = (25 + Math.random() * 35) * stressFactor;
|
|
@@ -312,7 +372,7 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
|
312
372
|
let statusCode = 200;
|
|
313
373
|
if (isError) {
|
|
314
374
|
statusCode = Math.random() > 0.3 ? 500 : 404;
|
|
315
|
-
errorMessage =
|
|
375
|
+
errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
316
376
|
}
|
|
317
377
|
data.push({
|
|
318
378
|
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
@@ -399,6 +459,9 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
399
459
|
const waveListItems = history.map((h) => {
|
|
400
460
|
return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
|
|
401
461
|
}).join("");
|
|
462
|
+
const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
|
|
463
|
+
const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
|
|
464
|
+
const recommendedRamGb = recommendedCpuCores * 2;
|
|
402
465
|
let logClustersHtml = "";
|
|
403
466
|
if (semanticReport) {
|
|
404
467
|
const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
|
|
@@ -451,6 +514,50 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
451
514
|
</table>
|
|
452
515
|
</div>`;
|
|
453
516
|
}
|
|
517
|
+
const liveAdjusterHtml = `
|
|
518
|
+
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
519
|
+
<h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem; margin-bottom: 1rem;">
|
|
520
|
+
\u{1F39B}\uFE0F Live Concurrency Adjuster (Mid-Test Simulation)
|
|
521
|
+
</h3>
|
|
522
|
+
<div style="display: flex; gap: 2rem; align-items: center; flex-wrap: wrap;">
|
|
523
|
+
<div style="flex: 1; min-width: 280px;">
|
|
524
|
+
<label for="concurrencySlider" style="display: block; font-size: 0.85rem; color: #94a3b8; margin-bottom: 0.5rem;">
|
|
525
|
+
Scale Virtual Users (VUs): <strong id="sliderValue" style="color: #38bdf8;">${config.threads}</strong> threads
|
|
526
|
+
</label>
|
|
527
|
+
<input type="range" id="concurrencySlider" min="5" max="${config.maxThreads || 300}" step="5" value="${config.threads}" style="width: 100%; accent-color: #38bdf8; cursor: pointer;">
|
|
528
|
+
</div>
|
|
529
|
+
<div style="display: flex; gap: 1.5rem; text-align: center;">
|
|
530
|
+
<div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
531
|
+
<div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Throughput</div>
|
|
532
|
+
<div id="simThroughput" style="font-size: 1.2rem; font-weight: bold; color: #f97316;">${cards.throughput.toFixed(0)}/s</div>
|
|
533
|
+
</div>
|
|
534
|
+
<div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
535
|
+
<div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Latency</div>
|
|
536
|
+
<div id="simLatency" style="font-size: 1.2rem; font-weight: bold; color: #10b981;">${cards.ttfb.toFixed(1)}ms</div>
|
|
537
|
+
</div>
|
|
538
|
+
</div>
|
|
539
|
+
</div>
|
|
540
|
+
</div>`;
|
|
541
|
+
const rightSizingHtml = `
|
|
542
|
+
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
543
|
+
<h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem;">
|
|
544
|
+
\u2601\uFE0F Infrastructure Right-Sizing & Cloud Cost Projections
|
|
545
|
+
</h3>
|
|
546
|
+
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-top: 1rem;">
|
|
547
|
+
<div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
548
|
+
<div class="card-title">Est. Monthly Cloud Cost</div>
|
|
549
|
+
<div class="card-value green">$${estimatedMonthlyCost} <span class="unit">/mo</span></div>
|
|
550
|
+
</div>
|
|
551
|
+
<div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
552
|
+
<div class="card-title">Recommended CPU Right-Sizing</div>
|
|
553
|
+
<div class="card-value purple">${recommendedCpuCores} <span class="unit">vCores</span></div>
|
|
554
|
+
</div>
|
|
555
|
+
<div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
556
|
+
<div class="card-title">Recommended Memory Allocation</div>
|
|
557
|
+
<div class="card-value orange">${recommendedRamGb} <span class="unit">GB RAM</span></div>
|
|
558
|
+
</div>
|
|
559
|
+
</div>
|
|
560
|
+
</div>`;
|
|
454
561
|
const htmlContent = `<!DOCTYPE html><html lang="en"><head>
|
|
455
562
|
<meta charset="UTF-8">
|
|
456
563
|
<title>Blaze Core Performance Report</title>
|
|
@@ -481,8 +588,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
481
588
|
|
|
482
589
|
.matrix-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; }
|
|
483
590
|
.matrix-title { font-size: 1.1rem; font-weight: bold; color: #ffffff; margin-bottom: 1rem; }
|
|
484
|
-
.matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
|
|
485
|
-
.matrix-row:last-child { border-bottom: none; }
|
|
591
|
+
.matrix-box .matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
|
|
592
|
+
.matrix-box .matrix-row:last-child { border-bottom: none; }
|
|
486
593
|
.matrix-label { font-size: 0.9rem; color: #f8fafc; display: flex; align-items: center; gap: 0.5rem; }
|
|
487
594
|
.matrix-badge { font-size: 0.7rem; font-weight: bold; padding: 0.1rem 0.3rem; border-radius: 4px; }
|
|
488
595
|
.badge-2xx { background: rgba(22, 163, 74, 0.2); color: #4ade80; }
|
|
@@ -491,7 +598,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
491
598
|
.badge-5xx { background: rgba(220, 38, 38, 0.2); color: #f87171; }
|
|
492
599
|
.matrix-value { font-size: 1rem; font-weight: bold; color: #ffffff; }
|
|
493
600
|
|
|
494
|
-
/* \u{1F4CA} TWO-COLUMN CHART GRID */
|
|
495
601
|
.charts-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom: 2rem; }
|
|
496
602
|
.graph-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
|
|
497
603
|
.graph-title { font-size: 1.1rem; font-weight: 700; color: #ffffff; margin-bottom: 1.2rem; display: flex; align-items: center; gap: 0.5rem; }
|
|
@@ -574,9 +680,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
574
680
|
</div>
|
|
575
681
|
</div>
|
|
576
682
|
|
|
577
|
-
<!-- \u{1F4C9} SPLIT CHARTS SYSTEM -->
|
|
578
683
|
<div class="charts-grid">
|
|
579
|
-
<!-- Chart 1: Volume Streams -->
|
|
580
684
|
<div class="graph-card">
|
|
581
685
|
<div class="graph-title">\u{1F4CA} Throughput Velocity & Workload Volume</div>
|
|
582
686
|
<div style="height: 280px; position: relative;">
|
|
@@ -584,7 +688,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
584
688
|
</div>
|
|
585
689
|
</div>
|
|
586
690
|
|
|
587
|
-
<!-- Chart 2: Concurrency & Health Trends -->
|
|
588
691
|
<div class="graph-card">
|
|
589
692
|
<div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. Time-to-Failure (TTF) Trend</div>
|
|
590
693
|
<div style="height: 280px; position: relative;">
|
|
@@ -621,13 +724,30 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
621
724
|
</tbody>
|
|
622
725
|
</table>
|
|
623
726
|
</div>
|
|
727
|
+
${liveAdjusterHtml}
|
|
728
|
+
${rightSizingHtml}
|
|
624
729
|
${logClustersHtml}
|
|
625
730
|
</div>
|
|
626
731
|
|
|
627
732
|
<script>
|
|
628
733
|
const labels = ${JSON.stringify(labels)};
|
|
734
|
+
const baseThroughput = ${cards.throughput};
|
|
735
|
+
const baseLatency = ${cards.ttfb};
|
|
736
|
+
|
|
737
|
+
// Live Concurrency Adjuster interactive client logic
|
|
738
|
+
const slider = document.getElementById('concurrencySlider');
|
|
739
|
+
const sliderVal = document.getElementById('sliderValue');
|
|
740
|
+
const simThroughput = document.getElementById('simThroughput');
|
|
741
|
+
const simLatency = document.getElementById('simLatency');
|
|
742
|
+
|
|
743
|
+
slider.addEventListener('input', (e) => {
|
|
744
|
+
const val = parseInt(e.target.value, 10);
|
|
745
|
+
sliderVal.textContent = val;
|
|
746
|
+
const factor = val / ${config.threads || 10};
|
|
747
|
+
simThroughput.textContent = (baseThroughput * Math.min(factor, 2.5)).toFixed(0) + '/s';
|
|
748
|
+
simLatency.textContent = (baseLatency * Math.pow(factor, 0.8)).toFixed(1) + 'ms';
|
|
749
|
+
});
|
|
629
750
|
|
|
630
|
-
// \u{1F6E0}\uFE0F Chart 1 Setup: Throughput Bar Streams
|
|
631
751
|
new Chart(document.getElementById('volumeChart'), {
|
|
632
752
|
type: 'bar',
|
|
633
753
|
data: {
|
|
@@ -664,7 +784,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
664
784
|
}
|
|
665
785
|
});
|
|
666
786
|
|
|
667
|
-
// \u{1F6E0}\uFE0F Chart 2 Setup: Concurrency & TTF Correlated Slopes
|
|
668
787
|
new Chart(document.getElementById('concurrencyChart'), {
|
|
669
788
|
type: 'line',
|
|
670
789
|
data: {
|
|
@@ -700,8 +819,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
700
819
|
},
|
|
701
820
|
scales: {
|
|
702
821
|
x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
|
|
703
|
-
|
|
704
|
-
|
|
822
|
+
y_threads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
|
|
823
|
+
y_ttf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
|
|
705
824
|
}
|
|
706
825
|
}
|
|
707
826
|
});
|
|
@@ -716,6 +835,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
716
835
|
function printUsage() {
|
|
717
836
|
console.log(`Blaze Core Performance Test CLI Engine
|
|
718
837
|
Options:
|
|
719
|
-
--threads <count> | --duration <seconds> | --agentic | --cluster-logs`);
|
|
838
|
+
--threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate`);
|
|
720
839
|
}
|
|
721
840
|
main().catch(console.error);
|
|
Binary file
|