blaze-performance-tester 3.1.29 → 3.1.31
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 +78 -22
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -93,39 +93,81 @@ async function fetchRcaInsights(semanticReport, apiKey) {
|
|
|
93
93
|
}
|
|
94
94
|
const errorSummary = semanticReport.clusters.map((c) => `[Count: ${c.count}] Category: ${c.category} -> Pattern: ${c.template}`).join("\n");
|
|
95
95
|
try {
|
|
96
|
-
const response = await fetch(
|
|
96
|
+
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
|
|
97
97
|
method: "POST",
|
|
98
98
|
headers: {
|
|
99
|
-
"Content-Type": "application/json"
|
|
100
|
-
"Authorization": `Bearer ${apiKey}`
|
|
99
|
+
"Content-Type": "application/json"
|
|
101
100
|
},
|
|
102
101
|
body: JSON.stringify({
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
systemInstruction: {
|
|
103
|
+
parts: [
|
|
104
|
+
{
|
|
105
|
+
text: "You are a principal systems architect and site reliability engineer. Return clean, direct HTML list items (using <li> tags, no raw markdown blocks) summarizing diagnostics."
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
contents: [
|
|
109
110
|
{
|
|
110
111
|
role: "user",
|
|
111
|
-
|
|
112
|
+
parts: [
|
|
113
|
+
{
|
|
114
|
+
text: `Analyze these performance test errors and provide 3 bullet points detailing the likely backend bottleneck (e.g., database connection pool exhaustion) and practical configuration fixes.
|
|
112
115
|
|
|
113
116
|
Error Log Patterns:
|
|
114
117
|
${errorSummary}`
|
|
118
|
+
}
|
|
119
|
+
]
|
|
115
120
|
}
|
|
116
121
|
],
|
|
117
|
-
|
|
122
|
+
generationConfig: {
|
|
123
|
+
temperature: 0.2
|
|
124
|
+
}
|
|
118
125
|
})
|
|
119
126
|
});
|
|
120
127
|
if (!response.ok) {
|
|
121
128
|
return `<span style="color: #ef4444;">AI Completion API Error: ${response.status} ${response.statusText}</span>`;
|
|
122
129
|
}
|
|
123
130
|
const data = await response.json();
|
|
124
|
-
return data.
|
|
131
|
+
return data.candidates?.[0]?.content?.parts?.[0]?.text || "<li>Failed to aggregate autonomous analysis parameters.</li>";
|
|
125
132
|
} catch (error) {
|
|
126
133
|
return `<span style="color: #ef4444;">Failed to resolve external cloud model: ${error.message}</span>`;
|
|
127
134
|
}
|
|
128
135
|
}
|
|
136
|
+
async function fetchIncidentNarrative(history, semanticReport, apiKey) {
|
|
137
|
+
const timelineSummary = history.map((h) => `Wave Tier [${h.threads} VUs] -> Throughput: ${h.throughput.toFixed(0)} req/s, Avg Latency: ${h.avgLatency.toFixed(1)}ms, Error Rate: ${(h.errorRate * 100).toFixed(2)}%`).join("\n");
|
|
138
|
+
const logSummary = semanticReport && semanticReport.clusters ? semanticReport.clusters.map((c) => `[Error Count: ${c.count}] Pattern Blueprint: ${c.template}`).join("\n") : "No major software errors logged.";
|
|
139
|
+
try {
|
|
140
|
+
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers: { "Content-Type": "application/json" },
|
|
143
|
+
body: JSON.stringify({
|
|
144
|
+
systemInstruction: {
|
|
145
|
+
parts: [{
|
|
146
|
+
text: "You are an expert technical writer and lead SRE. Write a fluid, highly professional, plain-English narrative story explaining the timeline of how the system degraded during this load test. Synthesize the raw timeline curves and matching log errors together. Return clean, elegant paragraphs (<p> text </p>) using standard HTML inline formatting elements like <strong> where needed. Do not wrap the response in markdown block quotes (like ```html)."
|
|
147
|
+
}]
|
|
148
|
+
},
|
|
149
|
+
contents: [{
|
|
150
|
+
role: "user",
|
|
151
|
+
parts: [{
|
|
152
|
+
text: `Analyze this historical load pipeline timeline and corresponding logs. Write a clear narrative detailing the failure journey as concurrency grew:
|
|
153
|
+
|
|
154
|
+
=== Chronological Testing Waves ===
|
|
155
|
+
${timelineSummary}
|
|
156
|
+
|
|
157
|
+
=== Captured Structural Error Blueprints ===
|
|
158
|
+
${logSummary}`
|
|
159
|
+
}]
|
|
160
|
+
}],
|
|
161
|
+
generationConfig: { temperature: 0.4 }
|
|
162
|
+
})
|
|
163
|
+
});
|
|
164
|
+
if (!response.ok) return `<em>Narrative generator unavailable: ${response.statusText}</em>`;
|
|
165
|
+
const data = await response.json();
|
|
166
|
+
return data.candidates?.[0]?.content?.parts?.[0]?.text || "<em>Failed to construct incident storyline parameters.</em>";
|
|
167
|
+
} catch (e) {
|
|
168
|
+
return `<em>Failed to compile conversational telemetry narrative: ${e.message}</em>`;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
129
171
|
async function executeTestPipeline(config) {
|
|
130
172
|
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
131
173
|
console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
|
|
@@ -134,13 +176,13 @@ async function executeTestPipeline(config) {
|
|
|
134
176
|
const transactionalScenario = [
|
|
135
177
|
{
|
|
136
178
|
name: "Fetch Target Anti-Forgery Nonce",
|
|
137
|
-
url: "https://httpbin.org/json",
|
|
179
|
+
url: "[https://httpbin.org/json](https://httpbin.org/json)",
|
|
138
180
|
method: "GET",
|
|
139
181
|
body: null
|
|
140
182
|
},
|
|
141
183
|
{
|
|
142
184
|
name: "Perform Contextual Post Action",
|
|
143
|
-
url: "https://httpbin.org/post?verification
|
|
185
|
+
url: "[https://httpbin.org/post?verification=](https://httpbin.org/post?verification=)${csrf_token}",
|
|
144
186
|
method: "POST",
|
|
145
187
|
body: JSON.stringify({
|
|
146
188
|
data: "performance_payload",
|
|
@@ -187,7 +229,7 @@ function parseArguments(args) {
|
|
|
187
229
|
let maxThreads = 300;
|
|
188
230
|
let maxAllowedLatencyMs = 800;
|
|
189
231
|
let outputHtml = "blaze-dashboard.html";
|
|
190
|
-
let aiKey = "";
|
|
232
|
+
let aiKey = process.env.GEMINI_API_KEY || "";
|
|
191
233
|
const threadsIdx = args.indexOf("--threads");
|
|
192
234
|
if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
|
|
193
235
|
const durationIdx = args.indexOf("--duration");
|
|
@@ -371,6 +413,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
371
413
|
}
|
|
372
414
|
let semanticReport = null;
|
|
373
415
|
let rcaInsights = "";
|
|
416
|
+
let incidentNarrative = "";
|
|
374
417
|
if (config.clusterLogs) {
|
|
375
418
|
if (aggregateErrorLogs.length === 0) {
|
|
376
419
|
aggregateErrorLogs = getFallbackClusterLogs();
|
|
@@ -380,6 +423,8 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
380
423
|
if (config.aiKey) {
|
|
381
424
|
console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
|
|
382
425
|
rcaInsights = await fetchRcaInsights(semanticReport, config.aiKey);
|
|
426
|
+
console.log("\u{1F4D6} Generating plain-English incident narrative...");
|
|
427
|
+
incidentNarrative = await fetchIncidentNarrative(history, semanticReport, config.aiKey);
|
|
383
428
|
}
|
|
384
429
|
}
|
|
385
430
|
const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0 };
|
|
@@ -406,7 +451,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
406
451
|
Object.keys(globalCodeCounts).forEach((code) => {
|
|
407
452
|
breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
|
|
408
453
|
});
|
|
409
|
-
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, rcaInsights);
|
|
454
|
+
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, rcaInsights, incidentNarrative);
|
|
410
455
|
}
|
|
411
456
|
async function runStandardStressTest(config) {
|
|
412
457
|
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
|
|
@@ -426,6 +471,7 @@ async function runStandardStressTest(config) {
|
|
|
426
471
|
}];
|
|
427
472
|
let semanticReport = null;
|
|
428
473
|
let rcaInsights = "";
|
|
474
|
+
let incidentNarrative = "";
|
|
429
475
|
if (config.clusterLogs) {
|
|
430
476
|
let errsToProcess = rawErrors;
|
|
431
477
|
if (errsToProcess.length === 0) errsToProcess = getFallbackClusterLogs();
|
|
@@ -434,6 +480,8 @@ async function runStandardStressTest(config) {
|
|
|
434
480
|
if (config.aiKey) {
|
|
435
481
|
console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
|
|
436
482
|
rcaInsights = await fetchRcaInsights(semanticReport, config.aiKey);
|
|
483
|
+
console.log("\u{1F4D6} Generating plain-English incident narrative...");
|
|
484
|
+
incidentNarrative = await fetchIncidentNarrative(history, semanticReport, config.aiKey);
|
|
437
485
|
}
|
|
438
486
|
}
|
|
439
487
|
const finalSummaryCards = {
|
|
@@ -465,7 +513,7 @@ async function runStandardStressTest(config) {
|
|
|
465
513
|
total4xx: metrics.c4xx,
|
|
466
514
|
total5xx: metrics.c5xx,
|
|
467
515
|
breakdownRates
|
|
468
|
-
}, finalSummaryCards, rawRequests, rcaInsights);
|
|
516
|
+
}, finalSummaryCards, rawRequests, rcaInsights, incidentNarrative);
|
|
469
517
|
}
|
|
470
518
|
function getFallbackClusterLogs() {
|
|
471
519
|
const logs = [];
|
|
@@ -520,7 +568,7 @@ function processMetricsTelemetry(requests, durationSec) {
|
|
|
520
568
|
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
521
569
|
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
522
570
|
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
523
|
-
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500:
|
|
571
|
+
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 502, 503: 0, 504: 0 };
|
|
524
572
|
requests.forEach((r) => {
|
|
525
573
|
if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
|
|
526
574
|
else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
|
|
@@ -581,7 +629,7 @@ function generateFinalAgentReport(history, score) {
|
|
|
581
629
|
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
582
630
|
console.log("=======================================================\n");
|
|
583
631
|
}
|
|
584
|
-
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], rcaInsights = "") {
|
|
632
|
+
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], rcaInsights = "", incidentNarrative = "") {
|
|
585
633
|
const labels = history.map((h, i) => `${i + 1}s`);
|
|
586
634
|
const successRequestsData = history.map((h) => h.successRequests);
|
|
587
635
|
const failedWorkloadsData = history.map((h) => h.failedRequests);
|
|
@@ -602,7 +650,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
602
650
|
if (config.clusterLogs) {
|
|
603
651
|
const content = rcaInsights ? rcaInsights : `
|
|
604
652
|
<div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px dashed #475569; color: #94a3b8;">
|
|
605
|
-
\u{1F4A1} <strong>Autonomous RCA Available:</strong> Pass the <code>--ai-key <your-key></code> flag
|
|
653
|
+
\u{1F4A1} <strong>Autonomous RCA Available:</strong> Pass the <code>--ai-key <your-key></code> flag, or configure a <code>GEMINI_API_KEY</code> environment variable in your PowerShell runtime session to dynamically structure error logs here.
|
|
606
654
|
</div>`;
|
|
607
655
|
rcaAccordionHtml = `
|
|
608
656
|
<details class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #8b5cf6; padding: 0; overflow: hidden;" open>
|
|
@@ -761,7 +809,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
761
809
|
const htmlContent = `<!DOCTYPE html><html lang="en"><head>
|
|
762
810
|
<meta charset="UTF-8">
|
|
763
811
|
<title>Blaze Core Performance Report</title>
|
|
764
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
812
|
+
<script src="[https://cdn.jsdelivr.net/npm/chart.js](https://cdn.jsdelivr.net/npm/chart.js)"></script>
|
|
765
813
|
<style>
|
|
766
814
|
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
|
|
767
815
|
.container { max-width: 1300px; margin: 0 auto; }
|
|
@@ -819,7 +867,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
819
867
|
</style></head><body>
|
|
820
868
|
<div class="container">
|
|
821
869
|
<div class="header">
|
|
822
|
-
<h1>\u{1F525} Blaze Core Performance
|
|
870
|
+
<h1>\u{1F525} Blaze Core Performance Report</h1>
|
|
823
871
|
<div class="meta">Target Script: <code>${config.targetScript}</code></div>
|
|
824
872
|
</div>
|
|
825
873
|
|
|
@@ -862,6 +910,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
862
910
|
<div class="verdict-box">
|
|
863
911
|
<div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
|
|
864
912
|
<div class="verdict-text">${verdictReason}</div>
|
|
913
|
+
|
|
914
|
+
<!-- GENERATIVE INCIDENT NARRATIVE INJECTION PANEL -->
|
|
915
|
+
${incidentNarrative ? `
|
|
916
|
+
<div style="margin-top: 1rem; margin-bottom: 1rem; padding: 1rem; background: rgba(56, 189, 248, 0.06); border-left: 4px solid #38bdf8; border-radius: 4px; font-size: 0.95rem; line-height: 1.6; color: #cbd5e1;">
|
|
917
|
+
<strong style="color: #38bdf8; display: flex; align-items: center; gap: 0.4rem; margin-bottom: 0.5rem; text-transform: uppercase; font-size: 0.8rem; letter-spacing: 0.05em;">\u{1F4D6} Generative Incident Narrative:</strong>
|
|
918
|
+
${incidentNarrative}
|
|
919
|
+
</div>` : ""}
|
|
920
|
+
|
|
865
921
|
<div class="verdict-waves">${waveListItems}</div>
|
|
866
922
|
</div>
|
|
867
923
|
|
|
Binary file
|