blaze-performance-tester 3.1.30 → 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 +56 -7
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -133,6 +133,41 @@ ${errorSummary}`
|
|
|
133
133
|
return `<span style="color: #ef4444;">Failed to resolve external cloud model: ${error.message}</span>`;
|
|
134
134
|
}
|
|
135
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
|
+
}
|
|
136
171
|
async function executeTestPipeline(config) {
|
|
137
172
|
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
138
173
|
console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
|
|
@@ -141,13 +176,13 @@ async function executeTestPipeline(config) {
|
|
|
141
176
|
const transactionalScenario = [
|
|
142
177
|
{
|
|
143
178
|
name: "Fetch Target Anti-Forgery Nonce",
|
|
144
|
-
url: "https://httpbin.org/json",
|
|
179
|
+
url: "[https://httpbin.org/json](https://httpbin.org/json)",
|
|
145
180
|
method: "GET",
|
|
146
181
|
body: null
|
|
147
182
|
},
|
|
148
183
|
{
|
|
149
184
|
name: "Perform Contextual Post Action",
|
|
150
|
-
url: "https://httpbin.org/post?verification
|
|
185
|
+
url: "[https://httpbin.org/post?verification=](https://httpbin.org/post?verification=)${csrf_token}",
|
|
151
186
|
method: "POST",
|
|
152
187
|
body: JSON.stringify({
|
|
153
188
|
data: "performance_payload",
|
|
@@ -378,6 +413,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
378
413
|
}
|
|
379
414
|
let semanticReport = null;
|
|
380
415
|
let rcaInsights = "";
|
|
416
|
+
let incidentNarrative = "";
|
|
381
417
|
if (config.clusterLogs) {
|
|
382
418
|
if (aggregateErrorLogs.length === 0) {
|
|
383
419
|
aggregateErrorLogs = getFallbackClusterLogs();
|
|
@@ -387,6 +423,8 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
387
423
|
if (config.aiKey) {
|
|
388
424
|
console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
|
|
389
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);
|
|
390
428
|
}
|
|
391
429
|
}
|
|
392
430
|
const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0 };
|
|
@@ -413,7 +451,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
413
451
|
Object.keys(globalCodeCounts).forEach((code) => {
|
|
414
452
|
breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
|
|
415
453
|
});
|
|
416
|
-
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);
|
|
417
455
|
}
|
|
418
456
|
async function runStandardStressTest(config) {
|
|
419
457
|
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
|
|
@@ -433,6 +471,7 @@ async function runStandardStressTest(config) {
|
|
|
433
471
|
}];
|
|
434
472
|
let semanticReport = null;
|
|
435
473
|
let rcaInsights = "";
|
|
474
|
+
let incidentNarrative = "";
|
|
436
475
|
if (config.clusterLogs) {
|
|
437
476
|
let errsToProcess = rawErrors;
|
|
438
477
|
if (errsToProcess.length === 0) errsToProcess = getFallbackClusterLogs();
|
|
@@ -441,6 +480,8 @@ async function runStandardStressTest(config) {
|
|
|
441
480
|
if (config.aiKey) {
|
|
442
481
|
console.log("\u{1F9E0} Fetching autonomous root-cause analysis from model...");
|
|
443
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);
|
|
444
485
|
}
|
|
445
486
|
}
|
|
446
487
|
const finalSummaryCards = {
|
|
@@ -472,7 +513,7 @@ async function runStandardStressTest(config) {
|
|
|
472
513
|
total4xx: metrics.c4xx,
|
|
473
514
|
total5xx: metrics.c5xx,
|
|
474
515
|
breakdownRates
|
|
475
|
-
}, finalSummaryCards, rawRequests, rcaInsights);
|
|
516
|
+
}, finalSummaryCards, rawRequests, rcaInsights, incidentNarrative);
|
|
476
517
|
}
|
|
477
518
|
function getFallbackClusterLogs() {
|
|
478
519
|
const logs = [];
|
|
@@ -527,7 +568,7 @@ function processMetricsTelemetry(requests, durationSec) {
|
|
|
527
568
|
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
528
569
|
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
529
570
|
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
530
|
-
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 };
|
|
531
572
|
requests.forEach((r) => {
|
|
532
573
|
if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
|
|
533
574
|
else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
|
|
@@ -588,7 +629,7 @@ function generateFinalAgentReport(history, score) {
|
|
|
588
629
|
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
589
630
|
console.log("=======================================================\n");
|
|
590
631
|
}
|
|
591
|
-
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], rcaInsights = "") {
|
|
632
|
+
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], rcaInsights = "", incidentNarrative = "") {
|
|
592
633
|
const labels = history.map((h, i) => `${i + 1}s`);
|
|
593
634
|
const successRequestsData = history.map((h) => h.successRequests);
|
|
594
635
|
const failedWorkloadsData = history.map((h) => h.failedRequests);
|
|
@@ -768,7 +809,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
768
809
|
const htmlContent = `<!DOCTYPE html><html lang="en"><head>
|
|
769
810
|
<meta charset="UTF-8">
|
|
770
811
|
<title>Blaze Core Performance Report</title>
|
|
771
|
-
<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>
|
|
772
813
|
<style>
|
|
773
814
|
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
|
|
774
815
|
.container { max-width: 1300px; margin: 0 auto; }
|
|
@@ -869,6 +910,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
869
910
|
<div class="verdict-box">
|
|
870
911
|
<div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
|
|
871
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
|
+
|
|
872
921
|
<div class="verdict-waves">${waveListItems}</div>
|
|
873
922
|
</div>
|
|
874
923
|
|
|
Binary file
|