blaze-performance-tester 3.1.13 → 3.1.15
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 +25 -239
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -85,9 +85,7 @@ 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
|
-
}
|
|
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])
|
|
88
|
+
}
|
|
91
89
|
};
|
|
92
90
|
async function executeTestPipeline(config) {
|
|
93
91
|
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
@@ -141,7 +139,6 @@ function parseArguments(args) {
|
|
|
141
139
|
const targetScript = path.resolve(args[0] || ".");
|
|
142
140
|
const isAgentic = args.includes("--agentic");
|
|
143
141
|
const isCorrelate = args.includes("--correlate");
|
|
144
|
-
const isSeoEnabled = args.includes("--seo");
|
|
145
142
|
const clusterLogs = !args.includes("--no-cluster-logs");
|
|
146
143
|
let threads = 10;
|
|
147
144
|
let durationSec = 10;
|
|
@@ -187,7 +184,6 @@ function parseArguments(args) {
|
|
|
187
184
|
targetScript,
|
|
188
185
|
isAgentic,
|
|
189
186
|
isCorrelate,
|
|
190
|
-
isSeoEnabled,
|
|
191
187
|
clusterLogs,
|
|
192
188
|
threads,
|
|
193
189
|
durationSec,
|
|
@@ -228,9 +224,6 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
228
224
|
let lastRawRequests = [];
|
|
229
225
|
let total1xx = 0, total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
|
|
230
226
|
let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [], lcp: [], fid: [], cls: [] };
|
|
231
|
-
let totalEconnreset = 0;
|
|
232
|
-
let totalEconnrefused = 0;
|
|
233
|
-
const globalCodeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
|
|
234
227
|
let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
|
|
235
228
|
let baseStep = config.stepSize;
|
|
236
229
|
let isStable = true;
|
|
@@ -248,11 +241,6 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
248
241
|
total3xx += metrics.c3xx;
|
|
249
242
|
total4xx += metrics.c4xx;
|
|
250
243
|
total5xx += metrics.c5xx;
|
|
251
|
-
totalEconnreset += metrics.econnresetCount;
|
|
252
|
-
totalEconnrefused += metrics.econnrefusedCount;
|
|
253
|
-
Object.keys(globalCodeCounts).forEach((code) => {
|
|
254
|
-
globalCodeCounts[code] += metrics.codeCounts[code] || 0;
|
|
255
|
-
});
|
|
256
244
|
globalMetricsAccumulator.dns.push(metrics.avgDnsMs);
|
|
257
245
|
globalMetricsAccumulator.tcp.push(metrics.avgTcpMs);
|
|
258
246
|
globalMetricsAccumulator.tls.push(metrics.avgTlsMs);
|
|
@@ -266,8 +254,6 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
266
254
|
threads: currentThreads,
|
|
267
255
|
avgLatency: metrics.avgLatencyMs,
|
|
268
256
|
p95Latency: metrics.p95LatencyMs,
|
|
269
|
-
minLatency: metrics.minLatencyMs,
|
|
270
|
-
maxLatency: metrics.maxLatencyMs,
|
|
271
257
|
errorRate: metrics.errorRate,
|
|
272
258
|
apdex: metrics.apdexScore,
|
|
273
259
|
throughput: metrics.requestsPerSecond,
|
|
@@ -275,10 +261,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
275
261
|
failedRequests: metrics.c4xx + metrics.c5xx,
|
|
276
262
|
lcp: metrics.lcpMs,
|
|
277
263
|
fid: metrics.fidMs,
|
|
278
|
-
cls: metrics.clsScore
|
|
279
|
-
econnresetRate: metrics.econnresetRate,
|
|
280
|
-
econnrefusedCount: metrics.econnrefusedCount,
|
|
281
|
-
codeRates: metrics.codeRates
|
|
264
|
+
cls: metrics.clsScore
|
|
282
265
|
};
|
|
283
266
|
printMatrixDashboard(metrics, currentThreads);
|
|
284
267
|
history.push(currentState);
|
|
@@ -322,11 +305,6 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
322
305
|
}
|
|
323
306
|
const totalTestSeconds = history.length * config.durationSec;
|
|
324
307
|
const vuRampUpVelocity = history.length > 1 && totalTestSeconds > 0 ? (history[history.length - 1].threads - history[0].threads) / totalTestSeconds : config.threads / config.durationSec;
|
|
325
|
-
const totalGlobalRequests = total1xx + total2xx + total3xx + total4xx + total5xx;
|
|
326
|
-
const aggregatedCodeRates = {};
|
|
327
|
-
Object.keys(globalCodeCounts).forEach((code) => {
|
|
328
|
-
aggregatedCodeRates[code] = totalGlobalRequests === 0 ? 0 : globalCodeCounts[code] / totalGlobalRequests * 100;
|
|
329
|
-
});
|
|
330
308
|
const finalSummaryCards = {
|
|
331
309
|
apdex: history[history.length - 1]?.apdex || 0,
|
|
332
310
|
throughput: history[history.length - 1]?.throughput || 0,
|
|
@@ -340,12 +318,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
340
318
|
vuRampUpVelocity,
|
|
341
319
|
lcp: mathUtils.mean(globalMetricsAccumulator.lcp),
|
|
342
320
|
fid: mathUtils.mean(globalMetricsAccumulator.fid),
|
|
343
|
-
cls: mathUtils.mean(globalMetricsAccumulator.cls)
|
|
344
|
-
econnresetRate: totalGlobalRequests === 0 ? 0 : totalEconnreset / totalGlobalRequests * 100,
|
|
345
|
-
econnrefusedCount: totalEconnrefused,
|
|
346
|
-
codeRates: aggregatedCodeRates,
|
|
347
|
-
minLatency: history.length === 0 ? 0 : Math.min(...history.map((h) => h.minLatency)),
|
|
348
|
-
maxLatency: history.length === 0 ? 0 : Math.max(...history.map((h) => h.maxLatency))
|
|
321
|
+
cls: mathUtils.mean(globalMetricsAccumulator.cls)
|
|
349
322
|
};
|
|
350
323
|
generateFinalAgentReport(history);
|
|
351
324
|
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx }, finalSummaryCards, lastRawRequests);
|
|
@@ -358,8 +331,6 @@ async function runStandardStressTest(config) {
|
|
|
358
331
|
threads: config.threads,
|
|
359
332
|
avgLatency: metrics.avgLatencyMs,
|
|
360
333
|
p95Latency: metrics.p95LatencyMs,
|
|
361
|
-
minLatency: metrics.minLatencyMs,
|
|
362
|
-
maxLatency: metrics.maxLatencyMs,
|
|
363
334
|
errorRate: metrics.errorRate,
|
|
364
335
|
apdex: metrics.apdexScore,
|
|
365
336
|
throughput: metrics.requestsPerSecond,
|
|
@@ -367,10 +338,7 @@ async function runStandardStressTest(config) {
|
|
|
367
338
|
failedRequests: metrics.c4xx + metrics.c5xx,
|
|
368
339
|
lcp: metrics.lcpMs,
|
|
369
340
|
fid: metrics.fidMs,
|
|
370
|
-
cls: metrics.clsScore
|
|
371
|
-
econnresetRate: metrics.econnresetRate,
|
|
372
|
-
econnrefusedCount: metrics.econnrefusedCount,
|
|
373
|
-
codeRates: metrics.codeRates
|
|
341
|
+
cls: metrics.clsScore
|
|
374
342
|
}];
|
|
375
343
|
let semanticReport = null;
|
|
376
344
|
if (config.clusterLogs) {
|
|
@@ -392,12 +360,7 @@ async function runStandardStressTest(config) {
|
|
|
392
360
|
vuRampUpVelocity: config.threads / config.durationSec,
|
|
393
361
|
lcp: metrics.lcpMs,
|
|
394
362
|
fid: metrics.fidMs,
|
|
395
|
-
cls: metrics.clsScore
|
|
396
|
-
econnresetRate: metrics.econnresetRate,
|
|
397
|
-
econnrefusedCount: metrics.econnrefusedCount,
|
|
398
|
-
codeRates: metrics.codeRates,
|
|
399
|
-
minLatency: metrics.minLatencyMs,
|
|
400
|
-
maxLatency: metrics.maxLatencyMs
|
|
363
|
+
cls: metrics.clsScore
|
|
401
364
|
};
|
|
402
365
|
const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
|
|
403
366
|
const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
|
|
@@ -429,29 +392,17 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
|
429
392
|
const isBreached = threads > targetThresholdBreak;
|
|
430
393
|
const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
|
|
431
394
|
const errorPool = getFallbackClusterLogs();
|
|
432
|
-
const targetFailureCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
|
|
433
395
|
for (let i = 0; i < totalRequests; i++) {
|
|
434
|
-
const isError = Math.random() < (isBreached ? 0.
|
|
396
|
+
const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
|
|
435
397
|
const baseLatency = (25 + Math.random() * 35) * stressFactor;
|
|
436
398
|
let errorMessage;
|
|
437
399
|
let statusCode = 200;
|
|
438
400
|
if (isError) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
statusCode = 502;
|
|
442
|
-
errorMessage = "Error: ECONNRESET socket hang up abruptly by remote host";
|
|
443
|
-
} else if (coin < 0.5) {
|
|
444
|
-
statusCode = 503;
|
|
445
|
-
errorMessage = "Error: ECONNREFUSED 127.0.0.1:8080 - connection rejected by destination target";
|
|
446
|
-
} else {
|
|
447
|
-
statusCode = targetFailureCodes[Math.floor(Math.random() * targetFailureCodes.length)];
|
|
448
|
-
errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
449
|
-
}
|
|
450
|
-
} else {
|
|
451
|
-
if (Math.random() < 0.03) statusCode = 302;
|
|
401
|
+
statusCode = Math.random() > 0.3 ? 500 : 404;
|
|
402
|
+
errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
452
403
|
}
|
|
453
404
|
data.push({
|
|
454
|
-
durationMs: isError ? baseLatency * 0.1
|
|
405
|
+
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
455
406
|
timestampMs: startTime + Math.random() * (durationSec * 1e3),
|
|
456
407
|
dnsTimeMs: 1 + Math.random() * 0.9,
|
|
457
408
|
tcpTimeMs: 4 + Math.random() * 1.5,
|
|
@@ -468,35 +419,18 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
468
419
|
const avgLatencyMs = mathUtils.mean(durations);
|
|
469
420
|
const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
|
|
470
421
|
const p95LatencyMs = mathUtils.percentile(durations, 95);
|
|
471
|
-
const minLatencyMs = mathUtils.min(durations);
|
|
472
|
-
const maxLatencyMs = mathUtils.max(durations);
|
|
473
422
|
const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
|
|
474
423
|
const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
|
|
475
424
|
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
476
425
|
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
477
426
|
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
478
|
-
let econnresetCount = 0;
|
|
479
|
-
let econnrefusedCount = 0;
|
|
480
|
-
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
|
|
481
427
|
requests.forEach((r) => {
|
|
482
428
|
if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
|
|
483
429
|
else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
|
|
484
430
|
else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
|
|
485
431
|
else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
|
|
486
432
|
else if (r.statusCode >= 500) c5xx++;
|
|
487
|
-
if (r.statusCode in codeCounts) {
|
|
488
|
-
codeCounts[r.statusCode]++;
|
|
489
|
-
}
|
|
490
|
-
if (r.errorMessage) {
|
|
491
|
-
if (r.errorMessage.includes("ECONNRESET")) econnresetCount++;
|
|
492
|
-
if (r.errorMessage.includes("ECONNREFUSED")) econnrefusedCount++;
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
const codeRates = {};
|
|
496
|
-
Object.keys(codeCounts).forEach((code) => {
|
|
497
|
-
codeRates[code] = totalRequests === 0 ? 0 : codeCounts[code] / totalRequests * 100;
|
|
498
433
|
});
|
|
499
|
-
const econnresetRate = totalRequests === 0 ? 0 : econnresetCount / totalRequests * 100;
|
|
500
434
|
const errorCount = c4xx + c5xx;
|
|
501
435
|
const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
|
|
502
436
|
const satisfied = requests.filter((r) => r.durationMs <= apdexT && r.statusCode < 400).length;
|
|
@@ -512,8 +446,6 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
512
446
|
avgLatencyMs,
|
|
513
447
|
stdDevMs,
|
|
514
448
|
p95LatencyMs,
|
|
515
|
-
minLatencyMs,
|
|
516
|
-
maxLatencyMs,
|
|
517
449
|
errorRate,
|
|
518
450
|
apdexScore,
|
|
519
451
|
c1xx,
|
|
@@ -528,12 +460,7 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
528
460
|
bandwidthMb,
|
|
529
461
|
lcpMs,
|
|
530
462
|
fidMs,
|
|
531
|
-
clsScore
|
|
532
|
-
econnresetCount,
|
|
533
|
-
econnresetRate,
|
|
534
|
-
econnrefusedCount,
|
|
535
|
-
codeCounts,
|
|
536
|
-
codeRates
|
|
463
|
+
clsScore
|
|
537
464
|
};
|
|
538
465
|
}
|
|
539
466
|
function printMatrixDashboard(m, threads) {
|
|
@@ -543,7 +470,7 @@ function printMatrixDashboard(m, threads) {
|
|
|
543
470
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
544
471
|
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)} |`);
|
|
545
472
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
546
|
-
console.log(`|
|
|
473
|
+
console.log(`| Simulated Web Vitals Equivalent -> LCP: ${m.lcpMs.toFixed(0)}ms | FID: ${m.fidMs.toFixed(1)}ms | CLS: ${m.clsScore.toFixed(3)} |`);
|
|
547
474
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
548
475
|
}
|
|
549
476
|
function generateFinalAgentReport(history) {
|
|
@@ -566,7 +493,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
566
493
|
const activeThreadsData = history.map((h) => h.threads);
|
|
567
494
|
const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
|
|
568
495
|
const waveListItems = history.map((h) => {
|
|
569
|
-
return `<div class="wave-item">Wave (${h.threads} VUs):
|
|
496
|
+
return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%, LCP ${h.lcp.toFixed(0)}ms</div>`;
|
|
570
497
|
}).join("");
|
|
571
498
|
const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
|
|
572
499
|
const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
|
|
@@ -582,136 +509,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
582
509
|
const lcpStat = getLcpStatus(cards.lcp);
|
|
583
510
|
const fidStat = getFidStatus(cards.fid);
|
|
584
511
|
const clsStat = getClsStatus(cards.cls);
|
|
585
|
-
let seoPredictorPanelHtml = "";
|
|
586
|
-
if (config.isSeoEnabled) {
|
|
587
|
-
const lcp = cards.lcp;
|
|
588
|
-
let seoScore = 100;
|
|
589
|
-
let visibilityLossPct = 0;
|
|
590
|
-
let trafficDropPct = 0;
|
|
591
|
-
let rankDropPositions = 0;
|
|
592
|
-
if (lcp > 2500) {
|
|
593
|
-
if (lcp <= 4e3) {
|
|
594
|
-
const ratio = (lcp - 2500) / 1500;
|
|
595
|
-
seoScore = 100 - ratio * 30;
|
|
596
|
-
visibilityLossPct = ratio * 14.5;
|
|
597
|
-
trafficDropPct = ratio * 18.2;
|
|
598
|
-
rankDropPositions = Number((ratio * 1.2).toFixed(1));
|
|
599
|
-
} else {
|
|
600
|
-
const ratio = Math.min(1, (lcp - 4e3) / 4e3);
|
|
601
|
-
seoScore = Math.max(8, 70 - ratio * 62);
|
|
602
|
-
visibilityLossPct = 14.5 + ratio * 52;
|
|
603
|
-
trafficDropPct = 18.2 + ratio * 58.5;
|
|
604
|
-
rankDropPositions = Number((1.2 + ratio * 4.6).toFixed(1));
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
let seoColor = "#10b981";
|
|
608
|
-
let seoStatus = "EXCELLENT";
|
|
609
|
-
if (seoScore < 90) {
|
|
610
|
-
seoColor = "#f59e0b";
|
|
611
|
-
seoStatus = "NEEDS IMPROVEMENT";
|
|
612
|
-
}
|
|
613
|
-
if (seoScore < 60) {
|
|
614
|
-
seoColor = "#ef4444";
|
|
615
|
-
seoStatus = "CRITICAL RISK / PENALIZED";
|
|
616
|
-
}
|
|
617
|
-
seoPredictorPanelHtml = `
|
|
618
|
-
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
619
|
-
<h3 style="color: #38bdf8; font-size: 1.2rem; border-bottom: none; margin-bottom: 0.25rem;">
|
|
620
|
-
\u{1F50D} Google SEO Rank & Search Visibility Impact Predictor
|
|
621
|
-
</h3>
|
|
622
|
-
<div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
623
|
-
Predictive algorithmic simulation modeling response degradation metrics directly against Google Core Web Vitals signal rules.
|
|
624
|
-
</div>
|
|
625
|
-
|
|
626
|
-
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; flex-wrap: wrap;">
|
|
627
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
|
|
628
|
-
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Core Web Vitals SEO Score</div>
|
|
629
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${seoColor}; margin-top: 0.25rem;">
|
|
630
|
-
${seoScore.toFixed(0)}<span style="font-size: 1rem; color: #94a3b8;">/100</span>
|
|
631
|
-
</div>
|
|
632
|
-
<div style="font-size: 0.75rem; font-weight: bold; margin-top: 0.4rem; color: ${seoColor};">${seoStatus}</div>
|
|
633
|
-
</div>
|
|
634
|
-
|
|
635
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
|
|
636
|
-
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Search Visibility Loss</div>
|
|
637
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${visibilityLossPct > 0 ? "#ef4444" : "#10b981"}; margin-top: 0.25rem;">
|
|
638
|
-
-${visibilityLossPct.toFixed(1)}%
|
|
639
|
-
</div>
|
|
640
|
-
<div style="font-size: 0.75rem; color: #64748b; margin-top: 0.4rem;">Projected SERP Impression Drop</div>
|
|
641
|
-
</div>
|
|
642
|
-
|
|
643
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
|
|
644
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${trafficDropPct > 0 ? "#ef4444" : "#10b981"}; margin-top: 0.25rem;">
|
|
645
|
-
-${trafficDropPct.toFixed(1)}%
|
|
646
|
-
</div>
|
|
647
|
-
<div style="font-size: 0.75rem; color: #64748b; margin-top: 0.4rem;">Expected funnel volume drop</div>
|
|
648
|
-
</div>
|
|
649
|
-
|
|
650
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
|
|
651
|
-
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Avg SERP Position Shift</div>
|
|
652
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${rankDropPositions > 0 ? "#f59e0b" : "#10b981"}; margin-top: 0.25rem;">
|
|
653
|
-
${rankDropPositions > 0 ? `+${rankDropPositions}` : "0.0"}
|
|
654
|
-
</div>
|
|
655
|
-
<div style="font-size: 0.75rem; color: #64748b; margin-top: 0.4rem;">Rank places dropped down standard index</div>
|
|
656
|
-
</div>
|
|
657
|
-
</div>
|
|
658
|
-
|
|
659
|
-
<div style="margin-top: 1rem; background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b; font-size: 0.85rem; line-height: 1.5; color: #cbd5e1;">
|
|
660
|
-
<strong>\u{1F52E} SEO Engine Intelligence Breakdown:</strong>
|
|
661
|
-
${lcp <= 2500 ? "Your simulated Largest Contentful Paint equivalent falls safely within Google's 'Good' range (≤ 2500ms). The parsing layer calculates zero performance-based rank penalties under current system load profiles." : lcp <= 4e3 ? `Warning: Current concurrency load has pushed LCP to ${lcp.toFixed(0)}ms, landing in Google's 'Needs Improvement' window. This response drag risks triggering index rank adjustments, potentially demoting listings down by ~${rankDropPositions} positions and sacrificing around ${trafficDropPct.toFixed(0)}% of organic top-of-funnel users.` : `Critical Signal Breach: Load performance has forced LCP to ${lcp.toFixed(0)}ms, severely violating Google's 'Poor' performance ceiling (> 4000ms). At this level, ranking algorithms actively de-weight domains. Expect structural search visibility erosion up to ${visibilityLossPct.toFixed(0)}% and immediate organic traffic redirection.`}
|
|
662
|
-
</div>
|
|
663
|
-
</div>`;
|
|
664
|
-
}
|
|
665
|
-
const connectionDiagnosticsPanelHtml = `
|
|
666
|
-
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
667
|
-
<h3 style="color: #38bdf8; font-size: 1.2rem; border-bottom: none; margin-bottom: 0.25rem;">
|
|
668
|
-
\u{1F50C} Network Connection & Detailed HTTP Status Diagnostics
|
|
669
|
-
</h3>
|
|
670
|
-
<div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
671
|
-
Real-time breakdown tracking transport anomalies and precise layer-7 distribution indicators.
|
|
672
|
-
</div>
|
|
673
|
-
|
|
674
|
-
<div style="display: grid; grid-template-columns: 1fr 2fr; gap: 2rem; flex-wrap: wrap;">
|
|
675
|
-
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
|
676
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
677
|
-
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNRESET Rate</div>
|
|
678
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnresetRate > 1 ? "#ef4444" : "#10b981"};">
|
|
679
|
-
${cards.econnresetRate.toFixed(2)}<span style="font-size: 1rem; color: #94a3b8;"> %</span>
|
|
680
|
-
</div>
|
|
681
|
-
<div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Abrupt remote host termination frequency</div>
|
|
682
|
-
</div>
|
|
683
|
-
|
|
684
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
685
|
-
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNREFUSED Count</div>
|
|
686
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnrefusedCount > 0 ? "#f59e0b" : "#10b981"};">
|
|
687
|
-
${cards.econnrefusedCount}<span style="font-size: 1rem; color: #94a3b8;"> drops</span>
|
|
688
|
-
</div>
|
|
689
|
-
<div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Total complete backend port link rejections</div>
|
|
690
|
-
</div>
|
|
691
|
-
</div>
|
|
692
|
-
|
|
693
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
694
|
-
<div style="font-size: 0.85rem; font-weight: bold; color: #f8fafc; margin-bottom: 1rem;">Target Response Code Matrix Rates</div>
|
|
695
|
-
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
|
|
696
|
-
${Object.keys(cards.codeRates).map((code) => {
|
|
697
|
-
const percentage = cards.codeRates[code];
|
|
698
|
-
let textColor = "#cbd5e1";
|
|
699
|
-
if (percentage > 0) {
|
|
700
|
-
textColor = code.startsWith("5") ? "#f87171" : "#fde047";
|
|
701
|
-
}
|
|
702
|
-
return `
|
|
703
|
-
<div style="background: #131c2e; padding: 0.75rem; border-radius: 4px; border: 1px solid #1e293b; text-align: center;">
|
|
704
|
-
<div style="font-size: 0.8rem; font-weight: bold; color: #94a3b8;">Code ${code}</div>
|
|
705
|
-
<div style="font-size: 1.1rem; font-weight: bold; margin-top: 0.25rem; color: ${textColor};">
|
|
706
|
-
${percentage.toFixed(2)}%
|
|
707
|
-
</div>
|
|
708
|
-
</div>
|
|
709
|
-
`;
|
|
710
|
-
}).join("")}
|
|
711
|
-
</div>
|
|
712
|
-
</div>
|
|
713
|
-
</div>
|
|
714
|
-
</div>`;
|
|
715
512
|
let logClustersHtml = "";
|
|
716
513
|
if (semanticReport) {
|
|
717
514
|
const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
|
|
@@ -722,7 +519,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
722
519
|
\u{1F9E0} Interactive Remediation Playbooks & Log Classification
|
|
723
520
|
</h3>
|
|
724
521
|
<div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
725
|
-
Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
|
|
522
|
+
Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns. Expand a playbook to inspect automated remediation strategies and diagnostics confidence ratings.
|
|
726
523
|
</div>
|
|
727
524
|
|
|
728
525
|
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
|
@@ -745,11 +542,11 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
745
542
|
let explanation = "High concurrency is causing socket/connection starvation. Increase connection limits or enable pooling keep-alives.";
|
|
746
543
|
if (c.category === "Authentication_Error") {
|
|
747
544
|
fixTitle = "Auth Token / JWT Strategy";
|
|
748
|
-
fixCode = "// Implement token caching and silent rotation
|
|
545
|
+
fixCode = "// Implement token caching and silent rotation\napp.use(authMiddleware({ cacheTtl: 300, allowGracePeriod: 60 }));";
|
|
749
546
|
explanation = "Frequent token verification failures under load. Extend signature verification cache or decouple auth validation check.";
|
|
750
547
|
} else if (c.category === "Product_Error") {
|
|
751
548
|
fixTitle = "Application Null-Safety / Guard Fix";
|
|
752
|
-
fixCode = "const configId = payload?.config_id ?? defaultConfigurationId
|
|
549
|
+
fixCode = "const configId = payload?.config_id ?? defaultConfigurationId;\nif (!configId) throw new ValidationError('Missing config_id');";
|
|
753
550
|
explanation = "Runtime reference error for undefined payload field under race condition spikes. Add optional chaining and input contracts.";
|
|
754
551
|
}
|
|
755
552
|
const snippetId = `snippet-${idx}`;
|
|
@@ -839,8 +636,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
839
636
|
.meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
|
|
840
637
|
|
|
841
638
|
.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; }
|
|
842
|
-
.cards-wrapper { display: grid; grid-template-columns: repeat(
|
|
843
|
-
@media (max-width: 1024px) { .cards-wrapper { grid-template-columns: repeat(2, 1fr); } }
|
|
639
|
+
.cards-wrapper { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
|
|
844
640
|
.metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
|
|
845
641
|
.card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
|
|
846
642
|
.card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
|
|
@@ -849,7 +645,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
849
645
|
.card-value.orange { color: #f97316; }
|
|
850
646
|
.card-value.purple { color: #a855f7; }
|
|
851
647
|
.card-value.cyan { color: #38bdf8; }
|
|
852
|
-
.card-value.red { color: #f87171; }
|
|
853
648
|
.card-subtext { font-size: 0.8rem; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
|
|
854
649
|
.card-subtext.green { color: #10b981; }
|
|
855
650
|
.card-subtext.orange { color: #f97316; }
|
|
@@ -897,20 +692,12 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
897
692
|
<div class="cards-wrapper">
|
|
898
693
|
<div class="metric-card">
|
|
899
694
|
<div class="card-title">Apdex Score (T: ${config.apdexT}ms)</div>
|
|
900
|
-
<div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(Verified)</span></div>
|
|
695
|
+
<div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(Target Verified)</span></div>
|
|
901
696
|
</div>
|
|
902
697
|
<div class="metric-card">
|
|
903
698
|
<div class="card-title">Throughput</div>
|
|
904
699
|
<div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
|
|
905
700
|
</div>
|
|
906
|
-
<div class="metric-card">
|
|
907
|
-
<div class="card-title">Minimum Response Time</div>
|
|
908
|
-
<div class="card-value green">${cards.minLatency.toFixed(1)}<span class="unit">ms</span></div>
|
|
909
|
-
</div>
|
|
910
|
-
<div class="metric-card">
|
|
911
|
-
<div class="card-title">Maximum Response Time</div>
|
|
912
|
-
<div class="card-value red">${cards.maxLatency.toFixed(1)}<span class="unit">ms</span></div>
|
|
913
|
-
</div>
|
|
914
701
|
<div class="metric-card">
|
|
915
702
|
<div class="card-title">Network Bandwidth</div>
|
|
916
703
|
<div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
|
|
@@ -1017,8 +804,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1017
804
|
<th>Throughput</th>
|
|
1018
805
|
<th>Avg Latency</th>
|
|
1019
806
|
<th>P95 Latency</th>
|
|
1020
|
-
<th>
|
|
1021
|
-
<th>
|
|
807
|
+
<th>Simulated LCP</th>
|
|
808
|
+
<th>Error Rate</th>
|
|
1022
809
|
<th>Status</th>
|
|
1023
810
|
</tr>
|
|
1024
811
|
</thead>
|
|
@@ -1030,16 +817,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1030
817
|
<td>${h.throughput.toFixed(0)} req/sec</td>
|
|
1031
818
|
<td>${h.avgLatency.toFixed(1)} ms</td>
|
|
1032
819
|
<td>${h.p95Latency.toFixed(1)} ms</td>
|
|
1033
|
-
<td
|
|
1034
|
-
<td>${h.
|
|
820
|
+
<td>${h.lcp.toFixed(0)} ms</td>
|
|
821
|
+
<td>${(h.errorRate * 100).toFixed(2)}%</td>
|
|
1035
822
|
<td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
|
|
1036
823
|
</tr>`;
|
|
1037
824
|
}).join("")}
|
|
1038
825
|
</tbody>
|
|
1039
826
|
</table>
|
|
1040
827
|
</div>
|
|
1041
|
-
${seoPredictorPanelHtml}
|
|
1042
|
-
${connectionDiagnosticsPanelHtml}
|
|
1043
828
|
${liveAdjusterHtml}
|
|
1044
829
|
${rightSizingHtml}
|
|
1045
830
|
${logClustersHtml}
|
|
@@ -1059,6 +844,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1059
844
|
const baseThroughput = ${cards.throughput};
|
|
1060
845
|
const baseLatency = ${cards.ttfb};
|
|
1061
846
|
|
|
847
|
+
// Live Concurrency Adjuster interactive client logic
|
|
1062
848
|
const slider = document.getElementById('concurrencySlider');
|
|
1063
849
|
const sliderVal = document.getElementById('sliderValue');
|
|
1064
850
|
const simThroughput = document.getElementById('simThroughput');
|
|
@@ -1143,8 +929,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1143
929
|
},
|
|
1144
930
|
scales: {
|
|
1145
931
|
x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
|
|
1146
|
-
|
|
1147
|
-
|
|
932
|
+
y_threads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
|
|
933
|
+
y_ttf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
|
|
1148
934
|
}
|
|
1149
935
|
}
|
|
1150
936
|
});
|
|
@@ -1204,6 +990,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1204
990
|
function printUsage() {
|
|
1205
991
|
console.log(`Blaze Core Performance Test CLI Engine
|
|
1206
992
|
Options:
|
|
1207
|
-
--threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --apdex-t <ms
|
|
993
|
+
--threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --apdex-t <ms>`);
|
|
1208
994
|
}
|
|
1209
995
|
main().catch(console.error);
|
|
Binary file
|