blaze-performance-tester 3.0.1 → 3.0.4

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
@@ -22,6 +22,12 @@ var mathUtils = {
22
22
  if (arr.length === 0) return 0;
23
23
  const r = arr.reduce((p, c) => p + Math.pow(c - mean, 2), 0);
24
24
  return Math.sqrt(r / arr.length);
25
+ },
26
+ percentile: (arr, p) => {
27
+ if (arr.length === 0) return 0;
28
+ const sorted = [...arr].sort((a, b) => a - b);
29
+ const index = Math.ceil(p / 100 * sorted.length) - 1;
30
+ return sorted[Math.max(0, index)];
25
31
  }
26
32
  };
27
33
  async function main() {
@@ -95,6 +101,7 @@ async function runIntelligentAgenticStressTest(config) {
95
101
  let currentThreads = Math.max(5, Math.floor(config.threads * 0.5));
96
102
  let baseStep = config.stepSize;
97
103
  let isStable = true;
104
+ let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
98
105
  while (currentThreads <= config.maxThreads && isStable) {
99
106
  console.log(`
100
107
  \u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
@@ -102,6 +109,7 @@ async function runIntelligentAgenticStressTest(config) {
102
109
  const currentState = {
103
110
  threads: currentThreads,
104
111
  avgLatency: metrics.avgLatencyMs,
112
+ p95Latency: metrics.p95LatencyMs,
105
113
  errorRate: metrics.errorRate,
106
114
  apdex: metrics.apdexScore,
107
115
  throughput: metrics.requestsPerSecond
@@ -114,7 +122,8 @@ async function runIntelligentAgenticStressTest(config) {
114
122
  const dLatency_dThreads = (current.avgLatency - previous.avgLatency) / (current.threads - previous.threads);
115
123
  console.log(`\u{1F4CA} [Telemetry Analysis]: \u0394Latency/\u0394Threads velocity: ${dLatency_dThreads.toFixed(2)}ms/thread`);
116
124
  if (current.throughput <= previous.throughput * 1.02 && dLatency_dThreads > 4.5) {
117
- console.log("\u26A0\uFE0F [Agent Alert]: Infrastructure Saturation Detected. Throughput flattened but latency is spiking. Backing off.");
125
+ verdictReason = "The primary breakdown point was identified due to infrastructure saturation where throughput flattened while latency spiked rapidly.";
126
+ console.log(`\u26A0\uFE0F [Agent Alert]: ${verdictReason} Backing off.`);
118
127
  currentThreads = Math.floor(currentThreads * 0.8);
119
128
  isStable = false;
120
129
  break;
@@ -125,8 +134,15 @@ async function runIntelligentAgenticStressTest(config) {
125
134
  baseStep = Math.max(2, Math.floor(baseStep * 0.35));
126
135
  }
127
136
  }
128
- if (currentState.apdex < config.targetApdex || currentState.errorRate > config.targetErrorRate) {
129
- console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. Apdex: ${currentState.apdex.toFixed(2)}, Errors: ${(currentState.errorRate * 100).toFixed(2)}%`);
137
+ if (currentState.apdex < config.targetApdex) {
138
+ verdictReason = "The primary breakdown point was identified due to latency metrics breaching target limits and driving Apdex scores below thresholds.";
139
+ console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. Apdex: ${currentState.apdex.toFixed(2)}`);
140
+ isStable = false;
141
+ break;
142
+ }
143
+ if (currentState.errorRate > config.targetErrorRate) {
144
+ verdictReason = "The primary breakdown point was identified due to HTTP error rates spiking past target service level boundaries.";
145
+ console.log(`\u{1F6D1} [SLO Breach]: Critical thresholds violated. Errors: ${(currentState.errorRate * 100).toFixed(2)}%`);
130
146
  isStable = false;
131
147
  break;
132
148
  }
@@ -137,7 +153,7 @@ async function runIntelligentAgenticStressTest(config) {
137
153
  currentThreads += nextStep;
138
154
  }
139
155
  generateFinalAgentReport(history);
140
- exportHtmlDashboard(history, config);
156
+ exportHtmlDashboard(history, config, verdictReason);
141
157
  }
142
158
  async function runStandardStressTest(config) {
143
159
  console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
@@ -146,11 +162,14 @@ async function runStandardStressTest(config) {
146
162
  const history = [{
147
163
  threads: config.threads,
148
164
  avgLatency: metrics.avgLatencyMs,
165
+ p95Latency: metrics.p95LatencyMs,
149
166
  errorRate: metrics.errorRate,
150
167
  apdex: metrics.apdexScore,
151
168
  throughput: metrics.requestsPerSecond
152
169
  }];
153
- exportHtmlDashboard(history, config);
170
+ const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
171
+ const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
172
+ exportHtmlDashboard(history, config, verdictReason);
154
173
  }
155
174
  function generateSimulationData(threads, durationSec) {
156
175
  const data = [];
@@ -176,6 +195,7 @@ function processMetricsTelemetry(requests, durationSec) {
176
195
  const durations = requests.map((r) => r.durationMs);
177
196
  const avgLatencyMs = mathUtils.mean(durations);
178
197
  const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
198
+ const p95LatencyMs = mathUtils.percentile(durations, 95);
179
199
  const errorCount = requests.filter((r) => r.statusCode >= 400).length;
180
200
  const errorRate = errorCount / totalRequests;
181
201
  const T = 100;
@@ -187,6 +207,7 @@ function processMetricsTelemetry(requests, durationSec) {
187
207
  requestsPerSecond: totalRequests / durationSec,
188
208
  avgLatencyMs,
189
209
  stdDevMs,
210
+ p95LatencyMs,
190
211
  errorRate,
191
212
  apdexScore
192
213
  };
@@ -194,10 +215,10 @@ function processMetricsTelemetry(requests, durationSec) {
194
215
  function printMatrixDashboard(m, threads) {
195
216
  const pad = (val, size) => String(val).padEnd(size).substring(0, size);
196
217
  console.log(`-----------------------------------------------------------------------------------------`);
197
- console.log(`| Threads | Samples | Avg Latency | StdDev | Throughput | Error Rate | Apdex |`);
218
+ console.log(`| Threads | Samples | Avg Latency | P95 Latency | Throughput | Error Rate | Apdex |`);
198
219
  console.log(`-----------------------------------------------------------------------------------------`);
199
220
  console.log(
200
- `| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.stdDevMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`
221
+ `| ${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)} |`
201
222
  );
202
223
  console.log(`-----------------------------------------------------------------------------------------`);
203
224
  }
@@ -214,12 +235,14 @@ function generateFinalAgentReport(history) {
214
235
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
215
236
  console.log("=======================================================\n");
216
237
  }
217
- function exportHtmlDashboard(history, config) {
238
+ function exportHtmlDashboard(history, config, verdictReason) {
218
239
  const labels = history.map((h) => `${h.threads} Threads`);
219
240
  const throughputData = history.map((h) => h.throughput.toFixed(0));
220
241
  const latencyData = history.map((h) => h.avgLatency.toFixed(1));
221
242
  const errorData = history.map((h) => (h.errorRate * 100).toFixed(2));
222
- const apdexData = history.map((h) => h.apdex.toFixed(2));
243
+ const waveListItems = history.map((h) => {
244
+ return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
245
+ }).join("");
223
246
  const htmlContent = `<!DOCTYPE html>
224
247
  <html lang="en">
225
248
  <head>
@@ -229,9 +252,46 @@ function exportHtmlDashboard(history, config) {
229
252
  <style>
230
253
  body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0f172a; color: #f8fafc; margin: 0; padding: 2rem; }
231
254
  .container { max-width: 1200px; margin: 0 auto; }
232
- .header { border-bottom: 1px solid #334155; padding-bottom: 1rem; margin-bottom: 2rem; }
255
+ .header { border-bottom: 1px solid #334155; padding-bottom: 1rem; margin-bottom: 1.5rem; }
233
256
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
234
257
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
258
+
259
+ /* \u{1F916} AI VERDICT COMPONENT STYLING */
260
+ .verdict-box {
261
+ background: #111827;
262
+ border: 1px dashed #ea580c;
263
+ border-radius: 8px;
264
+ padding: 1.25rem;
265
+ margin-bottom: 2rem;
266
+ }
267
+ .verdict-title {
268
+ text-transform: uppercase;
269
+ font-size: 0.85rem;
270
+ font-weight: 700;
271
+ color: #f97316;
272
+ margin-bottom: 0.5rem;
273
+ display: flex;
274
+ align-items: center;
275
+ gap: 0.4rem;
276
+ }
277
+ .verdict-text {
278
+ font-size: 0.95rem;
279
+ line-height: 1.5;
280
+ color: #e2e8f0;
281
+ margin-bottom: 0.75rem;
282
+ }
283
+ .verdict-waves {
284
+ background: #1f2937;
285
+ border-radius: 6px;
286
+ padding: 0.75rem 1rem;
287
+ font-family: monospace;
288
+ color: #9ca3af;
289
+ font-size: 0.9rem;
290
+ }
291
+ .wave-item {
292
+ margin: 0.25rem 0;
293
+ }
294
+
235
295
  .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); gap: 2rem; margin-bottom: 2rem; }
236
296
  .card { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 1.5rem; }
237
297
  h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #334155; padding-bottom: 0.5rem; }
@@ -251,6 +311,18 @@ function exportHtmlDashboard(history, config) {
251
311
  <div class="meta">Target Script: <code>${config.targetScript}</code> | Mode: ${config.isAgentic ? "\u{1F9E0} Intelligent Agentic" : "\u{1F3CB}\uFE0F Fixed Workload"}</div>
252
312
  </div>
253
313
 
314
+ <!-- \u{1F916} AUTOMATED VERDICT CONTAINER -->
315
+ <div class="verdict-box">
316
+ <div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
317
+ <div class="verdict-text">
318
+ Blaze Agent auto-evaluated execution telemetry across <strong>${history.length} test waves</strong>.
319
+ ${verdictReason}
320
+ </div>
321
+ <div class="verdict-waves">
322
+ ${waveListItems}
323
+ </div>
324
+ </div>
325
+
254
326
  <div class="grid">
255
327
  <div class="card"><canvas id="throughputChart"></canvas></div>
256
328
  <div class="card"><canvas id="latencyChart"></canvas></div>
@@ -264,6 +336,7 @@ function exportHtmlDashboard(history, config) {
264
336
  <th>Concurrency (Threads)</th>
265
337
  <th>Throughput</th>
266
338
  <th>Avg Latency</th>
339
+ <th>P95 Latency</th>
267
340
  <th>Error Rate</th>
268
341
  <th>Apdex Score</th>
269
342
  <th>Status</th>
@@ -277,6 +350,7 @@ function exportHtmlDashboard(history, config) {
277
350
  <td><strong>${h.threads}</strong></td>
278
351
  <td>${h.throughput.toFixed(0)} req/sec</td>
279
352
  <td>${h.avgLatency.toFixed(1)} ms</td>
353
+ <td>${h.p95Latency.toFixed(1)} ms</td>
280
354
  <td>${(h.errorRate * 100).toFixed(2)}%</td>
281
355
  <td>${h.apdex.toFixed(2)}</td>
282
356
  <td>
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.0.1",
3
+ "version": "3.0.4",
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",