blaze-performance-tester 3.0.36 → 3.0.38

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 CHANGED
@@ -209,7 +209,7 @@ async function runBlazeCoreEngine(config) {
209
209
  const steadyStateRequests = rawRequests.slice(warmUpCutoff);
210
210
  const metrics = processMetricsTelemetry(steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests, config.durationSec);
211
211
  const rawErrors = rawRequests.filter((r) => r.errorMessage).map((r) => r.errorMessage);
212
- resolve2({ metrics, rawErrors });
212
+ resolve2({ metrics, rawErrors, rawRequests: steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests });
213
213
  }, 1e3);
214
214
  });
215
215
  }
@@ -217,6 +217,7 @@ async function runIntelligentAgenticStressTest(config) {
217
217
  console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
218
218
  const history = [];
219
219
  let aggregateErrorLogs = [];
220
+ let lastRawRequests = [];
220
221
  let total1xx = 0, total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
221
222
  let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [] };
222
223
  let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
@@ -226,10 +227,11 @@ async function runIntelligentAgenticStressTest(config) {
226
227
  let saturationKneePoint = null;
227
228
  while (currentThreads <= config.maxThreads && isStable) {
228
229
  console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
229
- const { metrics, rawErrors } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
230
+ const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
230
231
  if (config.clusterLogs) {
231
232
  aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
232
233
  }
234
+ lastRawRequests = rawRequests;
233
235
  total1xx += metrics.c1xx;
234
236
  total2xx += metrics.c2xx;
235
237
  total3xx += metrics.c3xx;
@@ -291,6 +293,8 @@ async function runIntelligentAgenticStressTest(config) {
291
293
  const analyzer = new LogAnalyzer();
292
294
  semanticReport = analyzer.process(aggregateErrorLogs);
293
295
  }
296
+ const totalTestSeconds = history.length * config.durationSec;
297
+ const vuRampUpVelocity = history.length > 1 && totalTestSeconds > 0 ? (history[history.length - 1].threads - history[0].threads) / totalTestSeconds : config.threads / config.durationSec;
294
298
  const finalSummaryCards = {
295
299
  apdex: history[history.length - 1]?.apdex || 0,
296
300
  throughput: history[history.length - 1]?.throughput || 0,
@@ -300,14 +304,15 @@ async function runIntelligentAgenticStressTest(config) {
300
304
  tcp: mathUtils.mean(globalMetricsAccumulator.tcp),
301
305
  tls: mathUtils.mean(globalMetricsAccumulator.tls),
302
306
  stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies)),
303
- saturationKneePoint: saturationKneePoint || history[history.length - 1]?.threads || 50
307
+ saturationKneePoint: saturationKneePoint || history[history.length - 1]?.threads || 50,
308
+ vuRampUpVelocity
304
309
  };
305
310
  generateFinalAgentReport(history);
306
- exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx }, finalSummaryCards);
311
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx }, finalSummaryCards, lastRawRequests);
307
312
  }
308
313
  async function runStandardStressTest(config) {
309
314
  console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
310
- const { metrics, rawErrors } = await runBlazeCoreEngine(config);
315
+ const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine(config);
311
316
  printMatrixDashboard(metrics, config.threads);
312
317
  const history = [{
313
318
  threads: config.threads,
@@ -335,7 +340,8 @@ async function runStandardStressTest(config) {
335
340
  tcp: metrics.avgTcpMs,
336
341
  tls: metrics.avgTlsMs,
337
342
  stdDev: metrics.stdDevMs,
338
- saturationKneePoint: config.threads
343
+ saturationKneePoint: config.threads,
344
+ vuRampUpVelocity: config.threads / config.durationSec
339
345
  };
340
346
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
341
347
  const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
@@ -345,7 +351,7 @@ async function runStandardStressTest(config) {
345
351
  total3xx: metrics.c3xx,
346
352
  total4xx: metrics.c4xx,
347
353
  total5xx: metrics.c5xx
348
- }, finalSummaryCards);
354
+ }, finalSummaryCards, rawRequests);
349
355
  }
350
356
  function getFallbackClusterLogs() {
351
357
  const logs = [];
@@ -454,7 +460,7 @@ function generateFinalAgentReport(history) {
454
460
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
455
461
  console.log("=======================================================\n");
456
462
  }
457
- function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards) {
463
+ function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = []) {
458
464
  const labels = history.map((h, i) => `${i + 1}s`);
459
465
  const successRequestsData = history.map((h) => h.successRequests);
460
466
  const failedWorkloadsData = history.map((h) => h.failedRequests);
@@ -466,6 +472,11 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
466
472
  const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
467
473
  const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
468
474
  const recommendedRamGb = recommendedCpuCores * 2;
475
+ const firstTimestamp = rawRequests[0]?.timestampMs || Date.now();
476
+ const scatterPoints = rawRequests.slice(0, 180).map((r) => ({
477
+ x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
478
+ y: Number(((r.dnsTimeMs || 0) + (r.tcpTimeMs || 0) + (r.tlsTimeMs || 0) + r.durationMs * 0.3).toFixed(1))
479
+ }));
469
480
  let logClustersHtml = "";
470
481
  if (semanticReport) {
471
482
  const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
@@ -573,7 +584,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
573
584
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
574
585
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
575
586
 
576
- .cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
587
+ .cards-wrapper { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
577
588
  .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
578
589
  .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
579
590
  .card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
@@ -581,6 +592,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
581
592
  .card-value.green { color: #10b981; }
582
593
  .card-value.orange { color: #f97316; }
583
594
  .card-value.purple { color: #a855f7; }
595
+ .card-value.cyan { color: #38bdf8; }
584
596
  .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
585
597
 
586
598
  .top-layout-grid { display: grid; grid-template-columns: 1fr 400px; gap: 1.5rem; margin-bottom: 2rem; }
@@ -603,7 +615,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
603
615
  .badge-5xx { background: rgba(220, 38, 38, 0.2); color: #f87171; }
604
616
  .matrix-value { font-size: 1rem; font-weight: bold; color: #ffffff; }
605
617
 
606
- .charts-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; margin-bottom: 2rem; }
618
+ .charts-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; margin-bottom: 2rem; }
607
619
  .graph-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
608
620
  .graph-title { font-size: 1.1rem; font-weight: 700; color: #ffffff; margin-bottom: 1.2rem; display: flex; align-items: center; gap: 0.5rem; }
609
621
 
@@ -639,6 +651,10 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
639
651
  <div class="card-title">Avg Time To First Byte</div>
640
652
  <div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
641
653
  </div>
654
+ <div class="metric-card">
655
+ <div class="card-title">VU Ramp-up Velocity</div>
656
+ <div class="card-value cyan">${cards.vuRampUpVelocity.toFixed(1)}<span class="unit">VUs/s</span></div>
657
+ </div>
642
658
  <div class="metric-card">
643
659
  <div class="card-title">DNS Lookup</div>
644
660
  <div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
@@ -710,6 +726,13 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
710
726
  <canvas id="statusDonutChart"></canvas>
711
727
  </div>
712
728
  </div>
729
+
730
+ <div class="graph-card">
731
+ <div class="graph-title">\u{1F4C8} Time-to-First-Byte Scatter Plot</div>
732
+ <div style="height: 280px; position: relative;">
733
+ <canvas id="ttfbScatterChart"></canvas>
734
+ </div>
735
+ </div>
713
736
  </div>
714
737
 
715
738
  <div class="card">
@@ -861,6 +884,30 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
861
884
  cutout: '65%'
862
885
  }
863
886
  });
887
+
888
+ new Chart(document.getElementById('ttfbScatterChart'), {
889
+ type: 'scatter',
890
+ data: {
891
+ datasets: [{
892
+ label: 'Request Processing Delay',
893
+ data: ${JSON.stringify(scatterPoints)},
894
+ backgroundColor: '#a855f7',
895
+ pointRadius: 4,
896
+ pointHoverRadius: 6
897
+ }]
898
+ },
899
+ options: {
900
+ responsive: true,
901
+ maintainAspectRatio: false,
902
+ plugins: {
903
+ legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
904
+ },
905
+ scales: {
906
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'Time offset (s)', color: '#64748b' } },
907
+ y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'TTFB / Delay (ms)', color: '#64748b' } }
908
+ }
909
+ }
910
+ });
864
911
  </script></body></html>`;
865
912
  try {
866
913
  fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.0.36",
3
+ "version": "3.0.38",
4
4
  "description": "A high-performance, multi-threaded load testing engine built with Rust and QuickJS.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",