blaze-performance-tester 3.1.12 → 3.1.14
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 +13 -115
- 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") {
|
|
@@ -264,8 +262,6 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
264
262
|
threads: currentThreads,
|
|
265
263
|
avgLatency: metrics.avgLatencyMs,
|
|
266
264
|
p95Latency: metrics.p95LatencyMs,
|
|
267
|
-
minLatency: metrics.minLatencyMs,
|
|
268
|
-
maxLatency: metrics.maxLatencyMs,
|
|
269
265
|
errorRate: metrics.errorRate,
|
|
270
266
|
apdex: metrics.apdexScore,
|
|
271
267
|
throughput: metrics.requestsPerSecond,
|
|
@@ -341,9 +337,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
341
337
|
cls: mathUtils.mean(globalMetricsAccumulator.cls),
|
|
342
338
|
econnresetRate: totalGlobalRequests === 0 ? 0 : totalEconnreset / totalGlobalRequests * 100,
|
|
343
339
|
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))
|
|
340
|
+
codeRates: aggregatedCodeRates
|
|
347
341
|
};
|
|
348
342
|
generateFinalAgentReport(history);
|
|
349
343
|
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx }, finalSummaryCards, lastRawRequests);
|
|
@@ -356,8 +350,6 @@ async function runStandardStressTest(config) {
|
|
|
356
350
|
threads: config.threads,
|
|
357
351
|
avgLatency: metrics.avgLatencyMs,
|
|
358
352
|
p95Latency: metrics.p95LatencyMs,
|
|
359
|
-
minLatency: metrics.minLatencyMs,
|
|
360
|
-
maxLatency: metrics.maxLatencyMs,
|
|
361
353
|
errorRate: metrics.errorRate,
|
|
362
354
|
apdex: metrics.apdexScore,
|
|
363
355
|
throughput: metrics.requestsPerSecond,
|
|
@@ -393,9 +385,7 @@ async function runStandardStressTest(config) {
|
|
|
393
385
|
cls: metrics.clsScore,
|
|
394
386
|
econnresetRate: metrics.econnresetRate,
|
|
395
387
|
econnrefusedCount: metrics.econnrefusedCount,
|
|
396
|
-
codeRates: metrics.codeRates
|
|
397
|
-
minLatency: metrics.minLatencyMs,
|
|
398
|
-
maxLatency: metrics.maxLatencyMs
|
|
388
|
+
codeRates: metrics.codeRates
|
|
399
389
|
};
|
|
400
390
|
const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
|
|
401
391
|
const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
|
|
@@ -449,7 +439,7 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
|
449
439
|
if (Math.random() < 0.03) statusCode = 302;
|
|
450
440
|
}
|
|
451
441
|
data.push({
|
|
452
|
-
durationMs: isError ? baseLatency * 0.1
|
|
442
|
+
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
453
443
|
timestampMs: startTime + Math.random() * (durationSec * 1e3),
|
|
454
444
|
dnsTimeMs: 1 + Math.random() * 0.9,
|
|
455
445
|
tcpTimeMs: 4 + Math.random() * 1.5,
|
|
@@ -466,8 +456,6 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
466
456
|
const avgLatencyMs = mathUtils.mean(durations);
|
|
467
457
|
const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
|
|
468
458
|
const p95LatencyMs = mathUtils.percentile(durations, 95);
|
|
469
|
-
const minLatencyMs = mathUtils.min(durations);
|
|
470
|
-
const maxLatencyMs = mathUtils.max(durations);
|
|
471
459
|
const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
|
|
472
460
|
const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
|
|
473
461
|
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
@@ -475,6 +463,7 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
475
463
|
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
476
464
|
let econnresetCount = 0;
|
|
477
465
|
let econnrefusedCount = 0;
|
|
466
|
+
const targetCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
|
|
478
467
|
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
|
|
479
468
|
requests.forEach((r) => {
|
|
480
469
|
if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
|
|
@@ -491,7 +480,7 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
491
480
|
}
|
|
492
481
|
});
|
|
493
482
|
const codeRates = {};
|
|
494
|
-
|
|
483
|
+
targetCodes.forEach((code) => {
|
|
495
484
|
codeRates[code] = totalRequests === 0 ? 0 : codeCounts[code] / totalRequests * 100;
|
|
496
485
|
});
|
|
497
486
|
const econnresetRate = totalRequests === 0 ? 0 : econnresetCount / totalRequests * 100;
|
|
@@ -510,8 +499,6 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
|
|
|
510
499
|
avgLatencyMs,
|
|
511
500
|
stdDevMs,
|
|
512
501
|
p95LatencyMs,
|
|
513
|
-
minLatencyMs,
|
|
514
|
-
maxLatencyMs,
|
|
515
502
|
errorRate,
|
|
516
503
|
apdexScore,
|
|
517
504
|
c1xx,
|
|
@@ -541,7 +528,7 @@ function printMatrixDashboard(m, threads) {
|
|
|
541
528
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
542
529
|
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)} |`);
|
|
543
530
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
544
|
-
console.log(`| Network
|
|
531
|
+
console.log(`| Network State -> RESET Rate: ${m.econnresetRate.toFixed(2)}% | REFUSED Count: ${m.econnrefusedCount} | Web Vitals -> LCP: ${m.lcpMs.toFixed(0)}ms |`);
|
|
545
532
|
console.log(`-----------------------------------------------------------------------------------------`);
|
|
546
533
|
}
|
|
547
534
|
function generateFinalAgentReport(history) {
|
|
@@ -564,7 +551,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
564
551
|
const activeThreadsData = history.map((h) => h.threads);
|
|
565
552
|
const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
|
|
566
553
|
const waveListItems = history.map((h) => {
|
|
567
|
-
return `<div class="wave-item">Wave (${h.threads} VUs):
|
|
554
|
+
return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, ECONNRESET Rate: ${h.econnresetRate.toFixed(1)}%, REFUSED: ${h.econnrefusedCount}</div>`;
|
|
568
555
|
}).join("");
|
|
569
556
|
const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
|
|
570
557
|
const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
|
|
@@ -580,84 +567,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
580
567
|
const lcpStat = getLcpStatus(cards.lcp);
|
|
581
568
|
const fidStat = getFidStatus(cards.fid);
|
|
582
569
|
const clsStat = getClsStatus(cards.cls);
|
|
583
|
-
const lcp = cards.lcp;
|
|
584
|
-
let seoScore = 100;
|
|
585
|
-
let visibilityLossPct = 0;
|
|
586
|
-
let trafficDropPct = 0;
|
|
587
|
-
let rankDropPositions = 0;
|
|
588
|
-
if (lcp > 2500) {
|
|
589
|
-
if (lcp <= 4e3) {
|
|
590
|
-
const ratio = (lcp - 2500) / 1500;
|
|
591
|
-
seoScore = 100 - ratio * 30;
|
|
592
|
-
visibilityLossPct = ratio * 14.5;
|
|
593
|
-
trafficDropPct = ratio * 18.2;
|
|
594
|
-
rankDropPositions = Number((ratio * 1.2).toFixed(1));
|
|
595
|
-
} else {
|
|
596
|
-
const ratio = Math.min(1, (lcp - 4e3) / 4e3);
|
|
597
|
-
seoScore = Math.max(8, 70 - ratio * 62);
|
|
598
|
-
visibilityLossPct = 14.5 + ratio * 52;
|
|
599
|
-
trafficDropPct = 18.2 + ratio * 58.5;
|
|
600
|
-
rankDropPositions = Number((1.2 + ratio * 4.6).toFixed(1));
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
let seoColor = "#10b981";
|
|
604
|
-
let seoStatus = "EXCELLENT";
|
|
605
|
-
if (seoScore < 90) {
|
|
606
|
-
seoColor = "#f59e0b";
|
|
607
|
-
seoStatus = "NEEDS IMPROVEMENT";
|
|
608
|
-
}
|
|
609
|
-
if (seoScore < 60) {
|
|
610
|
-
seoColor = "#ef4444";
|
|
611
|
-
seoStatus = "CRITICAL RISK / PENALIZED";
|
|
612
|
-
}
|
|
613
|
-
const seoPredictorPanelHtml = `
|
|
614
|
-
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
615
|
-
<h3 style="color: #38bdf8; font-size: 1.2rem; border-bottom: none; margin-bottom: 0.25rem;">
|
|
616
|
-
\u{1F50D} Google SEO Rank & Search Visibility Impact Predictor
|
|
617
|
-
</h3>
|
|
618
|
-
<div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
619
|
-
Predictive algorithmic simulation modeling response degradation metrics directly against Google Core Web Vitals signal rules.
|
|
620
|
-
</div>
|
|
621
|
-
|
|
622
|
-
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; flex-wrap: wrap;">
|
|
623
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
|
|
624
|
-
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Core Web Vitals SEO Score</div>
|
|
625
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${seoColor}; margin-top: 0.25rem;">
|
|
626
|
-
${seoScore.toFixed(0)}<span style="font-size: 1rem; color: #94a3b8;">/100</span>
|
|
627
|
-
</div>
|
|
628
|
-
<div style="font-size: 0.75rem; font-weight: bold; margin-top: 0.4rem; color: ${seoColor};">${seoStatus}</div>
|
|
629
|
-
</div>
|
|
630
|
-
|
|
631
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
|
|
632
|
-
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Search Visibility Loss</div>
|
|
633
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${visibilityLossPct > 0 ? "#ef4444" : "#10b981"}; margin-top: 0.25rem;">
|
|
634
|
-
-${visibilityLossPct.toFixed(1)}%
|
|
635
|
-
</div>
|
|
636
|
-
<div style="font-size: 0.75rem; color: #64748b; margin-top: 0.4rem;">Projected SERP Impression Drop</div>
|
|
637
|
-
</div>
|
|
638
|
-
|
|
639
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
|
|
640
|
-
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Est. Organic Traffic Loss</div>
|
|
641
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${trafficDropPct > 0 ? "#ef4444" : "#10b981"}; margin-top: 0.25rem;">
|
|
642
|
-
-${trafficDropPct.toFixed(1)}%
|
|
643
|
-
</div>
|
|
644
|
-
<div style="font-size: 0.75rem; color: #64748b; margin-top: 0.4rem;">Expected funnel volume drop</div>
|
|
645
|
-
</div>
|
|
646
|
-
|
|
647
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
|
|
648
|
-
<div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Avg SERP Position Shift</div>
|
|
649
|
-
<div style="font-size: 1.8rem; font-weight: bold; color: ${rankDropPositions > 0 ? "#f59e0b" : "#10b981"}; margin-top: 0.25rem;">
|
|
650
|
-
${rankDropPositions > 0 ? `+${rankDropPositions}` : "0.0"}
|
|
651
|
-
</div>
|
|
652
|
-
<div style="font-size: 0.75rem; color: #64748b; margin-top: 0.4rem;">Rank places dropped down standard index</div>
|
|
653
|
-
</div>
|
|
654
|
-
</div>
|
|
655
|
-
|
|
656
|
-
<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;">
|
|
657
|
-
<strong>\u{1F52E} SEO Engine Intelligence Breakdown:</strong>
|
|
658
|
-
${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 traffic redirection.`}
|
|
659
|
-
</div>
|
|
660
|
-
</div>`;
|
|
661
570
|
const connectionDiagnosticsPanelHtml = `
|
|
662
571
|
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
663
572
|
<h3 style="color: #38bdf8; font-size: 1.2rem; border-bottom: none; margin-bottom: 0.25rem;">
|
|
@@ -835,8 +744,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
835
744
|
.meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
|
|
836
745
|
|
|
837
746
|
.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; }
|
|
838
|
-
.cards-wrapper { display: grid; grid-template-columns: repeat(
|
|
839
|
-
@media (max-width: 1024px) { .cards-wrapper { grid-template-columns: repeat(2, 1fr); } }
|
|
747
|
+
.cards-wrapper { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
|
|
840
748
|
.metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
|
|
841
749
|
.card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
|
|
842
750
|
.card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
|
|
@@ -845,7 +753,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
845
753
|
.card-value.orange { color: #f97316; }
|
|
846
754
|
.card-value.purple { color: #a855f7; }
|
|
847
755
|
.card-value.cyan { color: #38bdf8; }
|
|
848
|
-
.card-value.red { color: #f87171; }
|
|
849
756
|
.card-subtext { font-size: 0.8rem; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
|
|
850
757
|
.card-subtext.green { color: #10b981; }
|
|
851
758
|
.card-subtext.orange { color: #f97316; }
|
|
@@ -893,20 +800,12 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
893
800
|
<div class="cards-wrapper">
|
|
894
801
|
<div class="metric-card">
|
|
895
802
|
<div class="card-title">Apdex Score (T: ${config.apdexT}ms)</div>
|
|
896
|
-
<div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(Verified)</span></div>
|
|
803
|
+
<div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(Target Verified)</span></div>
|
|
897
804
|
</div>
|
|
898
805
|
<div class="metric-card">
|
|
899
806
|
<div class="card-title">Throughput</div>
|
|
900
807
|
<div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
|
|
901
808
|
</div>
|
|
902
|
-
<div class="metric-card">
|
|
903
|
-
<div class="card-title">Minimum Response Time</div>
|
|
904
|
-
<div class="card-value green">${cards.minLatency.toFixed(1)}<span class="unit">ms</span></div>
|
|
905
|
-
</div>
|
|
906
|
-
<div class="metric-card">
|
|
907
|
-
<div class="card-title">Maximum Response Time</div>
|
|
908
|
-
<div class="card-value red">${cards.maxLatency.toFixed(1)}<span class="unit">ms</span></div>
|
|
909
|
-
</div>
|
|
910
809
|
<div class="metric-card">
|
|
911
810
|
<div class="card-title">Network Bandwidth</div>
|
|
912
811
|
<div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
|
|
@@ -1013,8 +912,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1013
912
|
<th>Throughput</th>
|
|
1014
913
|
<th>Avg Latency</th>
|
|
1015
914
|
<th>P95 Latency</th>
|
|
1016
|
-
<th>
|
|
1017
|
-
<th>
|
|
915
|
+
<th>ECONNRESET Rate</th>
|
|
916
|
+
<th>ECONNREFUSED</th>
|
|
1018
917
|
<th>Status</th>
|
|
1019
918
|
</tr>
|
|
1020
919
|
</thead>
|
|
@@ -1026,15 +925,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1026
925
|
<td>${h.throughput.toFixed(0)} req/sec</td>
|
|
1027
926
|
<td>${h.avgLatency.toFixed(1)} ms</td>
|
|
1028
927
|
<td>${h.p95Latency.toFixed(1)} ms</td>
|
|
1029
|
-
<td><small>${h.minLatency.toFixed(1)}ms / ${h.maxLatency.toFixed(1)}ms</small></td>
|
|
1030
928
|
<td>${h.econnresetRate.toFixed(2)}%</td>
|
|
929
|
+
<td>${h.econnrefusedCount} nodes</td>
|
|
1031
930
|
<td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
|
|
1032
931
|
</tr>`;
|
|
1033
932
|
}).join("")}
|
|
1034
933
|
</tbody>
|
|
1035
934
|
</table>
|
|
1036
935
|
</div>
|
|
1037
|
-
${seoPredictorPanelHtml}
|
|
1038
936
|
${connectionDiagnosticsPanelHtml}
|
|
1039
937
|
${liveAdjusterHtml}
|
|
1040
938
|
${rightSizingHtml}
|
|
Binary file
|