blaze-performance-tester 3.1.7 → 3.1.10
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 +157 -27
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -85,7 +85,9 @@ var mathUtils = {
|
|
|
85
85
|
const sorted = [...arr].sort((a, b) => a - b);
|
|
86
86
|
const index = Math.ceil(p / 100 * sorted.length) - 1;
|
|
87
87
|
return sorted[Math.max(0, index)];
|
|
88
|
-
}
|
|
88
|
+
},
|
|
89
|
+
min: (arr) => arr.length === 0 ? 0 : arr.reduce((min, val) => val < min ? val : min, arr[0]),
|
|
90
|
+
max: (arr) => arr.length === 0 ? 0 : arr.reduce((max, val) => val > max ? val : max, arr[0])
|
|
89
91
|
};
|
|
90
92
|
async function executeTestPipeline(config) {
|
|
91
93
|
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
@@ -224,6 +226,9 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
224
226
|
let lastRawRequests = [];
|
|
225
227
|
let total1xx = 0, total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
|
|
226
228
|
let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [], lcp: [], fid: [], cls: [] };
|
|
229
|
+
let totalEconnreset = 0;
|
|
230
|
+
let totalEconnrefused = 0;
|
|
231
|
+
const globalCodeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
|
|
227
232
|
let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
|
|
228
233
|
let baseStep = config.stepSize;
|
|
229
234
|
let isStable = true;
|
|
@@ -241,6 +246,11 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
241
246
|
total3xx += metrics.c3xx;
|
|
242
247
|
total4xx += metrics.c4xx;
|
|
243
248
|
total5xx += metrics.c5xx;
|
|
249
|
+
totalEconnreset += metrics.econnresetCount;
|
|
250
|
+
totalEconnrefused += metrics.econnrefusedCount;
|
|
251
|
+
Object.keys(globalCodeCounts).forEach((code) => {
|
|
252
|
+
globalCodeCounts[code] += metrics.codeCounts[code] || 0;
|
|
253
|
+
});
|
|
244
254
|
globalMetricsAccumulator.dns.push(metrics.avgDnsMs);
|
|
245
255
|
globalMetricsAccumulator.tcp.push(metrics.avgTcpMs);
|
|
246
256
|
globalMetricsAccumulator.tls.push(metrics.avgTlsMs);
|
|
@@ -254,6 +264,8 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
254
264
|
threads: currentThreads,
|
|
255
265
|
avgLatency: metrics.avgLatencyMs,
|
|
256
266
|
p95Latency: metrics.p95LatencyMs,
|
|
267
|
+
minLatency: metrics.minLatencyMs,
|
|
268
|
+
maxLatency: metrics.maxLatencyMs,
|
|
257
269
|
errorRate: metrics.errorRate,
|
|
258
270
|
apdex: metrics.apdexScore,
|
|
259
271
|
throughput: metrics.requestsPerSecond,
|
|
@@ -261,7 +273,10 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
261
273
|
failedRequests: metrics.c4xx + metrics.c5xx,
|
|
262
274
|
lcp: metrics.lcpMs,
|
|
263
275
|
fid: metrics.fidMs,
|
|
264
|
-
cls: metrics.clsScore
|
|
276
|
+
cls: metrics.clsScore,
|
|
277
|
+
econnresetRate: metrics.econnresetRate,
|
|
278
|
+
econnrefusedCount: metrics.econnrefusedCount,
|
|
279
|
+
codeRates: metrics.codeRates
|
|
265
280
|
};
|
|
266
281
|
printMatrixDashboard(metrics, currentThreads);
|
|
267
282
|
history.push(currentState);
|
|
@@ -305,6 +320,11 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
305
320
|
}
|
|
306
321
|
const totalTestSeconds = history.length * config.durationSec;
|
|
307
322
|
const vuRampUpVelocity = history.length > 1 && totalTestSeconds > 0 ? (history[history.length - 1].threads - history[0].threads) / totalTestSeconds : config.threads / config.durationSec;
|
|
323
|
+
const totalGlobalRequests = total1xx + total2xx + total3xx + total4xx + total5xx;
|
|
324
|
+
const aggregatedCodeRates = {};
|
|
325
|
+
Object.keys(globalCodeCounts).forEach((code) => {
|
|
326
|
+
aggregatedCodeRates[code] = totalGlobalRequests === 0 ? 0 : globalCodeCounts[code] / totalGlobalRequests * 100;
|
|
327
|
+
});
|
|
308
328
|
const finalSummaryCards = {
|
|
309
329
|
apdex: history[history.length - 1]?.apdex || 0,
|
|
310
330
|
throughput: history[history.length - 1]?.throughput || 0,
|
|
@@ -318,7 +338,12 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
318
338
|
vuRampUpVelocity,
|
|
319
339
|
lcp: mathUtils.mean(globalMetricsAccumulator.lcp),
|
|
320
340
|
fid: mathUtils.mean(globalMetricsAccumulator.fid),
|
|
321
|
-
cls: mathUtils.mean(globalMetricsAccumulator.cls)
|
|
341
|
+
cls: mathUtils.mean(globalMetricsAccumulator.cls),
|
|
342
|
+
econnresetRate: totalGlobalRequests === 0 ? 0 : totalEconnreset / totalGlobalRequests * 100,
|
|
343
|
+
econnrefusedCount: totalEconnrefused,
|
|
344
|
+
codeRates: aggregatedCodeRates,
|
|
345
|
+
minLatency: history.length === 0 ? 0 : Math.min(...history.map((h) => h.minLatency)),
|
|
346
|
+
maxLatency: history.length === 0 ? 0 : Math.max(...history.map((h) => h.maxLatency))
|
|
322
347
|
};
|
|
323
348
|
generateFinalAgentReport(history);
|
|
324
349
|
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx }, finalSummaryCards, lastRawRequests);
|
|
@@ -331,6 +356,8 @@ async function runStandardStressTest(config) {
|
|
|
331
356
|
threads: config.threads,
|
|
332
357
|
avgLatency: metrics.avgLatencyMs,
|
|
333
358
|
p95Latency: metrics.p95LatencyMs,
|
|
359
|
+
minLatency: metrics.minLatencyMs,
|
|
360
|
+
maxLatency: metrics.maxLatencyMs,
|
|
334
361
|
errorRate: metrics.errorRate,
|
|
335
362
|
apdex: metrics.apdexScore,
|
|
336
363
|
throughput: metrics.requestsPerSecond,
|
|
@@ -338,7 +365,10 @@ async function runStandardStressTest(config) {
|
|
|
338
365
|
failedRequests: metrics.c4xx + metrics.c5xx,
|
|
339
366
|
lcp: metrics.lcpMs,
|
|
340
367
|
fid: metrics.fidMs,
|
|
341
|
-
cls: metrics.clsScore
|
|
368
|
+
cls: metrics.clsScore,
|
|
369
|
+
econnresetRate: metrics.econnresetRate,
|
|
370
|
+
econnrefusedCount: metrics.econnrefusedCount,
|
|
371
|
+
codeRates: metrics.codeRates
|
|
342
372
|
}];
|
|
343
373
|
let semanticReport = null;
|
|
344
374
|
if (config.clusterLogs) {
|
|
@@ -360,7 +390,12 @@ async function runStandardStressTest(config) {
|
|
|
360
390
|
vuRampUpVelocity: config.threads / config.durationSec,
|
|
361
391
|
lcp: metrics.lcpMs,
|
|
362
392
|
fid: metrics.fidMs,
|
|
363
|
-
cls: metrics.clsScore
|
|
393
|
+
cls: metrics.clsScore,
|
|
394
|
+
econnresetRate: metrics.econnresetRate,
|
|
395
|
+
econnrefusedCount: metrics.econnrefusedCount,
|
|
396
|
+
codeRates: metrics.codeRates,
|
|
397
|
+
minLatency: metrics.minLatencyMs,
|
|
398
|
+
maxLatency: metrics.maxLatencyMs
|
|
364
399
|
};
|
|
365
400
|
const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
|
|
366
401
|
const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
|
|
@@ -392,17 +427,29 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
|
392
427
|
const isBreached = threads > targetThresholdBreak;
|
|
393
428
|
const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
|
|
394
429
|
const errorPool = getFallbackClusterLogs();
|
|
430
|
+
const targetFailureCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
|
|
395
431
|
for (let i = 0; i < totalRequests; i++) {
|
|
396
|
-
const isError = Math.random() < (isBreached ? 0.
|
|
432
|
+
const isError = Math.random() < (isBreached ? 0.08 : 5e-3);
|
|
397
433
|
const baseLatency = (25 + Math.random() * 35) * stressFactor;
|
|
398
434
|
let errorMessage;
|
|
399
435
|
let statusCode = 200;
|
|
400
436
|
if (isError) {
|
|
401
|
-
|
|
402
|
-
|
|
437
|
+
const coin = Math.random();
|
|
438
|
+
if (coin < 0.25) {
|
|
439
|
+
statusCode = 502;
|
|
440
|
+
errorMessage = "Error: ECONNRESET socket hang up abruptly by remote host";
|
|
441
|
+
} else if (coin < 0.5) {
|
|
442
|
+
statusCode = 503;
|
|
443
|
+
errorMessage = "Error: ECONNREFUSED 127.0.0.1:8080 - connection rejected by destination target";
|
|
444
|
+
} else {
|
|
445
|
+
statusCode = targetFailureCodes[Math.floor(Math.random() * targetFailureCodes.length)];
|
|
446
|
+
errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
447
|
+
}
|
|
448
|
+
} else {
|
|
449
|
+
if (Math.random() < 0.03) statusCode = 302;
|
|
403
450
|
}
|
|
404
451
|
data.push({
|
|
405
|
-
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
452
|
+
durationMs: isError ? baseLatency * 0.1 + 2 : baseLatency + Math.random() * 20,
|
|
406
453
|
timestampMs: startTime + Math.random() * (durationSec * 1e3),
|
|
407
454
|
dnsTimeMs: 1 + Math.random() * 0.9,
|
|
408
455
|
tcpTimeMs: 4 + Math.random() * 1.5,
|
|
@@ -419,18 +466,36 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
419
466
|
const avgLatencyMs = mathUtils.mean(durations);
|
|
420
467
|
const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
|
|
421
468
|
const p95LatencyMs = mathUtils.percentile(durations, 95);
|
|
469
|
+
const minLatencyMs = mathUtils.min(durations);
|
|
470
|
+
const maxLatencyMs = mathUtils.max(durations);
|
|
422
471
|
const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
|
|
423
472
|
const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
|
|
424
473
|
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
425
474
|
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
426
475
|
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
476
|
+
let econnresetCount = 0;
|
|
477
|
+
let econnrefusedCount = 0;
|
|
478
|
+
const targetCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
|
|
479
|
+
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
|
|
427
480
|
requests.forEach((r) => {
|
|
428
481
|
if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
|
|
429
482
|
else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
|
|
430
483
|
else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
|
|
431
484
|
else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
|
|
432
485
|
else if (r.statusCode >= 500) c5xx++;
|
|
486
|
+
if (r.statusCode in codeCounts) {
|
|
487
|
+
codeCounts[r.statusCode]++;
|
|
488
|
+
}
|
|
489
|
+
if (r.errorMessage) {
|
|
490
|
+
if (r.errorMessage.includes("ECONNRESET")) econnresetCount++;
|
|
491
|
+
if (r.errorMessage.includes("ECONNREFUSED")) econnrefusedCount++;
|
|
492
|
+
}
|
|
433
493
|
});
|
|
494
|
+
const codeRates = {};
|
|
495
|
+
targetCodes.forEach((code) => {
|
|
496
|
+
codeRates[code] = totalRequests === 0 ? 0 : codeCounts[code] / totalRequests * 100;
|
|
497
|
+
});
|
|
498
|
+
const econnresetRate = totalRequests === 0 ? 0 : econnresetCount / totalRequests * 100;
|
|
434
499
|
const errorCount = c4xx + c5xx;
|
|
435
500
|
const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
|
|
436
501
|
const satisfied = requests.filter((r) => r.durationMs <= apdexT && r.statusCode < 400).length;
|
|
@@ -446,6 +511,8 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
446
511
|
avgLatencyMs,
|
|
447
512
|
stdDevMs,
|
|
448
513
|
p95LatencyMs,
|
|
514
|
+
minLatencyMs,
|
|
515
|
+
maxLatencyMs,
|
|
449
516
|
errorRate,
|
|
450
517
|
apdexScore,
|
|
451
518
|
c1xx,
|
|
@@ -460,7 +527,12 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
460
527
|
bandwidthMb,
|
|
461
528
|
lcpMs,
|
|
462
529
|
fidMs,
|
|
463
|
-
clsScore
|
|
530
|
+
clsScore,
|
|
531
|
+
econnresetCount,
|
|
532
|
+
econnresetRate,
|
|
533
|
+
econnrefusedCount,
|
|
534
|
+
codeCounts,
|
|
535
|
+
codeRates
|
|
464
536
|
};
|
|
465
537
|
}
|
|
466
538
|
function printMatrixDashboard(m, threads) {
|
|
@@ -470,7 +542,7 @@ function printMatrixDashboard(m, threads) {
|
|
|
470
542
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
471
543
|
console.log(`| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.p95LatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`);
|
|
472
544
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
473
|
-
console.log(`|
|
|
545
|
+
console.log(`| Network & Floor -> RESET: ${m.econnresetRate.toFixed(1)}% | Min/Max Boundary: ${m.minLatencyMs.toFixed(1)}ms / ${m.maxLatencyMs.toFixed(1)}ms`);
|
|
474
546
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
475
547
|
}
|
|
476
548
|
function generateFinalAgentReport(history) {
|
|
@@ -493,7 +565,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
493
565
|
const activeThreadsData = history.map((h) => h.threads);
|
|
494
566
|
const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
|
|
495
567
|
const waveListItems = history.map((h) => {
|
|
496
|
-
return `<div class="wave-item">Wave (${h.threads} VUs):
|
|
568
|
+
return `<div class="wave-item">Wave (${h.threads} VUs): Floor ${h.minLatency.toFixed(1)}ms | Ceil ${h.maxLatency.toFixed(1)}ms | P95 ${h.p95Latency.toFixed(1)}ms</div>`;
|
|
497
569
|
}).join("");
|
|
498
570
|
const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
|
|
499
571
|
const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
|
|
@@ -509,6 +581,56 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
509
581
|
const lcpStat = getLcpStatus(cards.lcp);
|
|
510
582
|
const fidStat = getFidStatus(cards.fid);
|
|
511
583
|
const clsStat = getClsStatus(cards.cls);
|
|
584
|
+
const connectionDiagnosticsPanelHtml = `
|
|
585
|
+
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
586
|
+
<h3 style="color: #38bdf8; font-size: 1.2rem; border-bottom: none; margin-bottom: 0.25rem;">
|
|
587
|
+
\u{1F50C} Network Connection & Detailed HTTP Status Diagnostics
|
|
588
|
+
</h3>
|
|
589
|
+
<div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
590
|
+
Real-time breakdown tracking transport anomalies and precise layer-7 distribution indicators.
|
|
591
|
+
</div>
|
|
592
|
+
|
|
593
|
+
<div style="display: grid; grid-template-columns: 1fr 2fr; gap: 2rem; flex-wrap: wrap;">
|
|
594
|
+
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
|
595
|
+
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
596
|
+
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNRESET Rate</div>
|
|
597
|
+
<div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnresetRate > 1 ? "#ef4444" : "#10b981"};">
|
|
598
|
+
${cards.econnresetRate.toFixed(2)}<span style="font-size: 1rem; color: #94a3b8;"> %</span>
|
|
599
|
+
</div>
|
|
600
|
+
<div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Abrupt remote host termination frequency</div>
|
|
601
|
+
</div>
|
|
602
|
+
|
|
603
|
+
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
604
|
+
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNREFUSED Count</div>
|
|
605
|
+
<div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnrefusedCount > 0 ? "#f59e0b" : "#10b981"};">
|
|
606
|
+
${cards.econnrefusedCount}<span style="font-size: 1rem; color: #94a3b8;"> drops</span>
|
|
607
|
+
</div>
|
|
608
|
+
<div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Total complete backend port link rejections</div>
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
|
|
612
|
+
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
613
|
+
<div style="font-size: 0.85rem; font-weight: bold; color: #f8fafc; margin-bottom: 1rem;">Target Response Code Matrix Rates</div>
|
|
614
|
+
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
|
|
615
|
+
${Object.keys(cards.codeRates).map((code) => {
|
|
616
|
+
const percentage = cards.codeRates[code];
|
|
617
|
+
let textColor = "#cbd5e1";
|
|
618
|
+
if (percentage > 0) {
|
|
619
|
+
textColor = code.startsWith("5") ? "#f87171" : "#fde047";
|
|
620
|
+
}
|
|
621
|
+
return `
|
|
622
|
+
<div style="background: #131c2e; padding: 0.75rem; border-radius: 4px; border: 1px solid #1e293b; text-align: center;">
|
|
623
|
+
<div style="font-size: 0.8rem; font-weight: bold; color: #94a3b8;">Code ${code}</div>
|
|
624
|
+
<div style="font-size: 1.1rem; font-weight: bold; margin-top: 0.25rem; color: ${textColor};">
|
|
625
|
+
${percentage.toFixed(2)}%
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
`;
|
|
629
|
+
}).join("")}
|
|
630
|
+
</div>
|
|
631
|
+
</div>
|
|
632
|
+
</div>
|
|
633
|
+
</div>`;
|
|
512
634
|
let logClustersHtml = "";
|
|
513
635
|
if (semanticReport) {
|
|
514
636
|
const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
|
|
@@ -519,7 +641,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
519
641
|
\u{1F9E0} Interactive Remediation Playbooks & Log Classification
|
|
520
642
|
</h3>
|
|
521
643
|
<div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
522
|
-
Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
|
|
644
|
+
Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
|
|
523
645
|
</div>
|
|
524
646
|
|
|
525
647
|
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
|
@@ -542,11 +664,11 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
542
664
|
let explanation = "High concurrency is causing socket/connection starvation. Increase connection limits or enable pooling keep-alives.";
|
|
543
665
|
if (c.category === "Authentication_Error") {
|
|
544
666
|
fixTitle = "Auth Token / JWT Strategy";
|
|
545
|
-
fixCode = "// Implement token caching and silent rotation
|
|
667
|
+
fixCode = "// Implement token caching and silent rotation\\napp.use(authMiddleware({ cacheTtl: 300, allowGracePeriod: 60 }));";
|
|
546
668
|
explanation = "Frequent token verification failures under load. Extend signature verification cache or decouple auth validation check.";
|
|
547
669
|
} else if (c.category === "Product_Error") {
|
|
548
670
|
fixTitle = "Application Null-Safety / Guard Fix";
|
|
549
|
-
fixCode = "const configId = payload?.config_id ?? defaultConfigurationId
|
|
671
|
+
fixCode = "const configId = payload?.config_id ?? defaultConfigurationId;\\nif (!configId) throw new ValidationError('Missing config_id');";
|
|
550
672
|
explanation = "Runtime reference error for undefined payload field under race condition spikes. Add optional chaining and input contracts.";
|
|
551
673
|
}
|
|
552
674
|
const snippetId = `snippet-${idx}`;
|
|
@@ -636,7 +758,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
636
758
|
.meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
|
|
637
759
|
|
|
638
760
|
.section-title { font-size: 1.2rem; color: #cbd5e1; font-weight: bold; margin: 2rem 0 1rem 0; padding-bottom: 0.4rem; border-bottom: 1px solid #1e293b; }
|
|
639
|
-
.cards-wrapper { display: grid; grid-template-columns: repeat(
|
|
761
|
+
.cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
|
|
762
|
+
@media (max-width: 1024px) { .cards-wrapper { grid-template-columns: repeat(2, 1fr); } }
|
|
640
763
|
.metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
|
|
641
764
|
.card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
|
|
642
765
|
.card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
|
|
@@ -645,6 +768,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
645
768
|
.card-value.orange { color: #f97316; }
|
|
646
769
|
.card-value.purple { color: #a855f7; }
|
|
647
770
|
.card-value.cyan { color: #38bdf8; }
|
|
771
|
+
.card-value.red { color: #f87171; }
|
|
648
772
|
.card-subtext { font-size: 0.8rem; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
|
|
649
773
|
.card-subtext.green { color: #10b981; }
|
|
650
774
|
.card-subtext.orange { color: #f97316; }
|
|
@@ -692,12 +816,20 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
692
816
|
<div class="cards-wrapper">
|
|
693
817
|
<div class="metric-card">
|
|
694
818
|
<div class="card-title">Apdex Score (T: ${config.apdexT}ms)</div>
|
|
695
|
-
<div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(
|
|
819
|
+
<div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(Verified)</span></div>
|
|
696
820
|
</div>
|
|
697
821
|
<div class="metric-card">
|
|
698
822
|
<div class="card-title">Throughput</div>
|
|
699
823
|
<div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
|
|
700
824
|
</div>
|
|
825
|
+
<div class="metric-card">
|
|
826
|
+
<div class="card-title">Minimum Response Time</div>
|
|
827
|
+
<div class="card-value green">${cards.minLatency.toFixed(1)}<span class="unit">ms</span></div>
|
|
828
|
+
</div>
|
|
829
|
+
<div class="metric-card">
|
|
830
|
+
<div class="card-title">Maximum Response Time</div>
|
|
831
|
+
<div class="card-value red">${cards.maxLatency.toFixed(1)}<span class="unit">ms</span></div>
|
|
832
|
+
</div>
|
|
701
833
|
<div class="metric-card">
|
|
702
834
|
<div class="card-title">Network Bandwidth</div>
|
|
703
835
|
<div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
|
|
@@ -804,8 +936,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
804
936
|
<th>Throughput</th>
|
|
805
937
|
<th>Avg Latency</th>
|
|
806
938
|
<th>P95 Latency</th>
|
|
807
|
-
<th>
|
|
808
|
-
<th>
|
|
939
|
+
<th>Min / Max Floor</th>
|
|
940
|
+
<th>RESET Rate</th>
|
|
809
941
|
<th>Status</th>
|
|
810
942
|
</tr>
|
|
811
943
|
</thead>
|
|
@@ -817,14 +949,15 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
817
949
|
<td>${h.throughput.toFixed(0)} req/sec</td>
|
|
818
950
|
<td>${h.avgLatency.toFixed(1)} ms</td>
|
|
819
951
|
<td>${h.p95Latency.toFixed(1)} ms</td>
|
|
820
|
-
<td>${h.
|
|
821
|
-
<td>${
|
|
952
|
+
<td><small>${h.minLatency.toFixed(1)}ms / ${h.maxLatency.toFixed(1)}ms</small></td>
|
|
953
|
+
<td>${h.econnresetRate.toFixed(2)}%</td>
|
|
822
954
|
<td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
|
|
823
955
|
</tr>`;
|
|
824
956
|
}).join("")}
|
|
825
957
|
</tbody>
|
|
826
958
|
</table>
|
|
827
959
|
</div>
|
|
960
|
+
${connectionDiagnosticsPanelHtml}
|
|
828
961
|
${liveAdjusterHtml}
|
|
829
962
|
${rightSizingHtml}
|
|
830
963
|
${logClustersHtml}
|
|
@@ -844,7 +977,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
844
977
|
const baseThroughput = ${cards.throughput};
|
|
845
978
|
const baseLatency = ${cards.ttfb};
|
|
846
979
|
|
|
847
|
-
// Live Concurrency Adjuster interactive client logic
|
|
848
980
|
const slider = document.getElementById('concurrencySlider');
|
|
849
981
|
const sliderVal = document.getElementById('sliderValue');
|
|
850
982
|
const simThroughput = document.getElementById('simThroughput');
|
|
@@ -929,8 +1061,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
929
1061
|
},
|
|
930
1062
|
scales: {
|
|
931
1063
|
x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
|
|
932
|
-
|
|
933
|
-
|
|
1064
|
+
yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
|
|
1065
|
+
yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
|
|
934
1066
|
}
|
|
935
1067
|
}
|
|
936
1068
|
});
|
|
@@ -988,8 +1120,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
988
1120
|
}
|
|
989
1121
|
}
|
|
990
1122
|
function printUsage() {
|
|
991
|
-
console.log(`Blaze Core Performance Test CLI Engine
|
|
992
|
-
Options:
|
|
993
|
-
--threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --apdex-t <ms>`);
|
|
1123
|
+
console.log(`Blaze Core Performance Test CLI Engine\\nOptions:\\n --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --apdex-t <ms>`);
|
|
994
1124
|
}
|
|
995
1125
|
main().catch(console.error);
|
|
Binary file
|