blaze-performance-tester 3.1.55 → 3.1.56

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
@@ -163,6 +163,19 @@ function getLoadMultiplier(elapsedMs, rampUpMs, steadyMs, rampDownMs) {
163
163
  }
164
164
  return elapsedMs >= totalDurationMs ? 0 : 1;
165
165
  }
166
+ function loadBaselineData(filePath) {
167
+ if (!filePath) return null;
168
+ try {
169
+ const resolved = path.resolve(filePath);
170
+ if (fs.existsSync(resolved)) {
171
+ console.log(`\u{1F4C1} Loaded baseline comparison telemetry from: ${resolved}`);
172
+ return JSON.parse(fs.readFileSync(resolved, "utf-8"));
173
+ }
174
+ } catch (e) {
175
+ console.error(`\u274C Failed to parse baseline telemetry file: ${e}`);
176
+ }
177
+ return null;
178
+ }
166
179
  async function generateGeminiRCA(history, finalSummary, errorLogs) {
167
180
  const apiKey = process.env.GEMINI_API_KEY;
168
181
  if (!apiKey) {
@@ -291,6 +304,7 @@ function parseArguments(args) {
291
304
  let maxThreads = 300;
292
305
  let maxAllowedLatencyMs = 800;
293
306
  let outputHtml = "blaze-dashboard.html";
307
+ let baselinePath = null;
294
308
  const threadsIdx = args.indexOf("--threads");
295
309
  if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
296
310
  const durationIdx = args.indexOf("--duration");
@@ -300,13 +314,15 @@ function parseArguments(args) {
300
314
  const rampDownIdx = args.indexOf("--ramp-down");
301
315
  if (rampDownIdx !== -1) rampDownSec = parseInt(args[rampDownIdx + 1], 10);
302
316
  const apdexIdx = args.indexOf("--target-apdex");
303
- if (apdexIdx !== -1) targetApdex = parseFloat(args[apdexIdx + 1]);
317
+ if (apdexIdx !== -1) targetApdex = parseFloat(apdexIdx + 1);
304
318
  const errorIdx = args.indexOf("--target-error");
305
319
  if (errorIdx !== -1) targetErrorRate = parseFloat(args[errorIdx + 1]);
306
320
  const maxThreadsIdx = args.indexOf("--max-threads");
307
321
  if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
308
322
  const outputIdx = args.indexOf("--output");
309
323
  if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
324
+ const baselineIdx = args.indexOf("--baseline");
325
+ if (baselineIdx !== -1) baselinePath = args[baselineIdx + 1];
310
326
  const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
311
327
  if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic) threads = positionalNums[0];
312
328
  if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic) durationSec = positionalNums[1];
@@ -340,7 +356,8 @@ function parseArguments(args) {
340
356
  maxThreads,
341
357
  stepSize: 15,
342
358
  maxAllowedLatencyMs,
343
- outputHtml
359
+ outputHtml,
360
+ baselinePath
344
361
  };
345
362
  }
346
363
  async function runBlazeCoreEngine(config) {
@@ -495,7 +512,9 @@ async function runIntelligentAgenticStressTest(config) {
495
512
  tls: mathUtils.mean(globalMetricsAccumulator.tls),
496
513
  stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies)),
497
514
  saturationKneePoint: saturationKneePoint || finalState.threads || 50,
498
- healthScore
515
+ healthScore,
516
+ errorRate: finalState.errorRate,
517
+ p95Latency: finalState.p95Latency
499
518
  };
500
519
  generateFinalAgentReport(history, healthScore);
501
520
  const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, aggregateErrorLogs);
@@ -509,7 +528,8 @@ async function runIntelligentAgenticStressTest(config) {
509
528
  const code = Number(codeStr);
510
529
  breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
511
530
  });
512
- exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml);
531
+ const baselineData = loadBaselineData(config.baselinePath);
532
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml, baselineData);
513
533
  }
514
534
  async function runStandardStressTest(config) {
515
535
  console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s | RampUp: ${config.rampUpSec}s | RampDown: ${config.rampDownSec}s]`);
@@ -544,7 +564,9 @@ async function runStandardStressTest(config) {
544
564
  tls: metrics.avgTlsMs,
545
565
  stdDev: metrics.stdDevMs,
546
566
  saturationKneePoint: config.threads,
547
- healthScore
567
+ healthScore,
568
+ errorRate: metrics.errorRate,
569
+ p95Latency: metrics.p95LatencyMs
548
570
  };
549
571
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
550
572
  const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
@@ -558,6 +580,7 @@ async function runStandardStressTest(config) {
558
580
  const code = Number(codeStr);
559
581
  breakdownRates[code] = metrics.totalRequests === 0 ? 0 : metrics.codeCounts[code] / metrics.totalRequests * 100;
560
582
  });
583
+ const baselineData = loadBaselineData(config.baselinePath);
561
584
  exportHtmlDashboard(history, config, verdictReason, semanticReport, {
562
585
  total1xx: metrics.c1xx,
563
586
  total2xx: metrics.c2xx,
@@ -565,7 +588,7 @@ async function runStandardStressTest(config) {
565
588
  total4xx: metrics.c4xx,
566
589
  total5xx: metrics.c5xx,
567
590
  breakdownRates
568
- }, finalSummaryCards, rawRequests, geminiAnalysisHtml);
591
+ }, finalSummaryCards, rawRequests, geminiAnalysisHtml, baselineData);
569
592
  }
570
593
  function getFallbackClusterLogs() {
571
594
  const logs = [];
@@ -690,7 +713,12 @@ function generateFinalAgentReport(history, score) {
690
713
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
691
714
  console.log("=======================================================\n");
692
715
  }
693
- function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "") {
716
+ function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", baselineData = null) {
717
+ const summaryArtifactPath = config.outputHtml.replace(/\.html$/, "-summary.json");
718
+ try {
719
+ fs.writeFileSync(summaryArtifactPath, JSON.stringify(cards, null, 2), "utf-8");
720
+ } catch (e) {
721
+ }
694
722
  const labels = history.map((h, i) => `${i + 1}s`);
695
723
  const successRequestsData = history.map((h) => h.successRequests);
696
724
  const failedWorkloadsData = history.map((h) => h.failedRequests);
@@ -707,6 +735,50 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
707
735
  x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
708
736
  y: Number(((r.dnsTimeMs || 0) + (r.tcpTimeMs || 0) + (r.tlsTimeMs || 0) + r.durationMs * 0.3).toFixed(1))
709
737
  }));
738
+ let baselineComparisonHtml = "";
739
+ if (baselineData) {
740
+ const diffApdex = cards.apdex - baselineData.apdex;
741
+ const diffThroughput = (cards.throughput - baselineData.throughput) / (baselineData.throughput || 1) * 100;
742
+ const diffTtfb = (cards.ttfb - baselineData.ttfb) / (baselineData.ttfb || 1) * 100;
743
+ const diffHealth = cards.healthScore - baselineData.healthScore;
744
+ const formatDelta = (val, isInverse = false) => {
745
+ const good = isInverse ? val <= 0 : val >= 0;
746
+ const color = good ? "#10b981" : "#ef4444";
747
+ const sign = val > 0 ? "+" : "";
748
+ return `<span style="color: ${color}; font-weight: bold;">${sign}${val.toFixed(1)}%</span>`;
749
+ };
750
+ baselineComparisonHtml = `
751
+ <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #7c3aed;">
752
+ <h3 style="color: #a78bfa; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
753
+ \u{1F4CA} Baseline Regression Comparison (Delta Engine)
754
+ </h3>
755
+ <p style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
756
+ Comparing current execution metrics directly against reference payload from baseline source.
757
+ </p>
758
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem;">
759
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
760
+ <div class="card-title">Apdex Shift</div>
761
+ <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.apdex.toFixed(2)}</div>
762
+ <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: <span style="color: ${diffApdex >= 0 ? "#10b981" : "#ef4444"}; font-weight: bold;">${diffApdex >= 0 ? "+" : ""}${diffApdex.toFixed(2)}</span></div>
763
+ </div>
764
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
765
+ <div class="card-title">Throughput Delta</div>
766
+ <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.throughput.toFixed(0)}/s</div>
767
+ <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: ${formatDelta(diffThroughput, false)}</div>
768
+ </div>
769
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
770
+ <div class="card-title">TTFB Latency Shift</div>
771
+ <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.ttfb.toFixed(1)}ms</div>
772
+ <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: ${formatDelta(diffTtfb, true)}</div>
773
+ </div>
774
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
775
+ <div class="card-title">Health Score Diff</div>
776
+ <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.healthScore}/100</div>
777
+ <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: <span style="color: ${diffHealth >= 0 ? "#10b981" : "#ef4444"}; font-weight: bold;">${diffHealth >= 0 ? "+" : ""}${diffHealth} pts</span></div>
778
+ </div>
779
+ </div>
780
+ </div>`;
781
+ }
710
782
  let logClustersHtml = "";
711
783
  if (semanticReport) {
712
784
  const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
@@ -969,6 +1041,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
969
1041
  </div>
970
1042
  </div>
971
1043
 
1044
+ ${baselineComparisonHtml}
1045
+
972
1046
  <div class="top-layout-grid">
973
1047
  <div class="verdict-box">
974
1048
  <div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
@@ -1365,6 +1439,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1365
1439
  function printUsage() {
1366
1440
  console.log(`Blaze Core Performance Test CLI Engine
1367
1441
  Options:
1368
- --threads <count> | --duration <seconds> | --ramp-up <seconds> | --ramp-down <seconds> | --agentic | --cluster-logs | --correlate | --seo`);
1442
+ --threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file>`);
1369
1443
  }
1370
1444
  main().catch(console.error);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.1.55",
3
+ "version": "3.1.56",
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",