blaze-performance-tester 3.1.61 → 3.2.0
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 +139 -14
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -23,7 +23,13 @@ var LogAnalyzer = class {
|
|
|
23
23
|
return { category: "Environment_Infrastructure_Error", severity: "CRITICAL" };
|
|
24
24
|
}
|
|
25
25
|
if (LowercaseTpl.includes("401") || LowercaseTpl.includes("403") || LowercaseTpl.includes("unauthorized") || LowercaseTpl.includes("forbidden") || LowercaseTpl.includes("jwt") || LowercaseTpl.includes("token expired")) {
|
|
26
|
-
return { category: "Authentication_Error", severity: "
|
|
26
|
+
return { category: "Authentication_Error", severity: "HIGH" };
|
|
27
|
+
}
|
|
28
|
+
if (LowercaseTpl.includes("429") || LowercaseTpl.includes("rate limit") || LowercaseTpl.includes("too many requests")) {
|
|
29
|
+
return { category: "Traffic_Management_Error", severity: "MEDIUM" };
|
|
30
|
+
}
|
|
31
|
+
if (LowercaseTpl.includes("typeerror") || LowercaseTpl.includes("undefined") || LowercaseTpl.includes("nullpointer") || LowercaseTpl.includes("cannot read properties")) {
|
|
32
|
+
return { category: "Product_Error", severity: "CRITICAL" };
|
|
27
33
|
}
|
|
28
34
|
return { category: "Product_Error", severity: "WARNING" };
|
|
29
35
|
}
|
|
@@ -31,16 +37,17 @@ var LogAnalyzer = class {
|
|
|
31
37
|
* Generates actionable architectural and code-level fixes based on log structural fingerprints
|
|
32
38
|
*/
|
|
33
39
|
generatePlaybook(template, severity) {
|
|
34
|
-
if (severity !== "CRITICAL" && severity !== "HIGH") {
|
|
40
|
+
if (severity !== "CRITICAL" && severity !== "HIGH" && severity !== "MEDIUM") {
|
|
35
41
|
return void 0;
|
|
36
42
|
}
|
|
43
|
+
const lowerTemplate = template.toLowerCase();
|
|
37
44
|
if (template.includes("ECONNREFUSED") || template.includes("5432")) {
|
|
38
45
|
return `**Database Connectivity Failure**
|
|
39
46
|
1. **Service Triage:** Verify the target PostgreSQL instance at 127.0.0.1:5432 is running using \`pg_isready\`.
|
|
40
47
|
2. **Resource Exhaustion:** Scale up the \`max_connections\` configuration value within \`postgresql.conf\`.
|
|
41
48
|
3. **Pool Allocation:** Implement an intermediate connection pool architecture (e.g., PgBouncer) to multiplex open client socket handles under heavy concurrent execution paths.`;
|
|
42
49
|
}
|
|
43
|
-
if (template.includes("504 Gateway Timeout") || template.includes("upstream socket drop")) {
|
|
50
|
+
if (template.includes("504 Gateway Timeout") || template.includes("upstream socket drop") || template.includes("Timeout")) {
|
|
44
51
|
return `**Upstream Gateway Timeout Saturation**
|
|
45
52
|
1. **Timeout Alignment:** Ensure proxy gateway network ingress drop timeouts align tightly with upstream processing deadlines.
|
|
46
53
|
2. **Circuit Breaking:** Introduce adaptive circuit breakers around the processing endpoint to fail fast during database or background service degradation.
|
|
@@ -58,10 +65,19 @@ var LogAnalyzer = class {
|
|
|
58
65
|
2. **Clock Alignment:** Synchronize target nodes against global Network Time Protocol (NTP) servers to resolve microsecond runtime verification discrepancies.
|
|
59
66
|
3. **Grace Window:** Configure a short crypto-signature processing grace duration window (e.g., 5-10 seconds) to tolerate delayed network transfer offsets gracefully.`;
|
|
60
67
|
}
|
|
61
|
-
|
|
68
|
+
if (lowerTemplate.includes("429") || lowerTemplate.includes("rate limit") || lowerTemplate.includes("too many requests")) {
|
|
69
|
+
return `**Traffic Rate Limit Triggered**
|
|
70
|
+
1. **Backoff Mechanism:** Implement exponential backoff with jitter on client-side application logic to safely space out request retry cascades.
|
|
71
|
+
2. **Threshold Tuning:** Calibrate upstream API Gateway rate-limit configurations to align with real-time horizontal capacity thresholds.
|
|
72
|
+
3. **Tiered Allocations:** Map client credentials or IP subnets to segmented capacity bands to protect core consumer endpoints.`;
|
|
73
|
+
}
|
|
74
|
+
if (severity === "CRITICAL" || severity === "HIGH") {
|
|
75
|
+
return `**High-Severity System Anomaly Action Plan**
|
|
62
76
|
1. **Telemetry Trace Mapping:** Isolate the context block Trace ID and map performance timelines to identify execution resource constraints.
|
|
63
77
|
2. **Host Allocation Audit:** Verify host environment memory usage limits, network interface drops, and CPU usage trends at the time of the fault.
|
|
64
78
|
3. **Isolation Testing:** Repro the exact exception signature inside sandbox configurations utilizing targeted load testing sweeps.`;
|
|
79
|
+
}
|
|
80
|
+
return void 0;
|
|
65
81
|
}
|
|
66
82
|
/**
|
|
67
83
|
* Processes a stream of raw textual error logs into deduplicated clustered fingerprints
|
|
@@ -86,12 +102,16 @@ var LogAnalyzer = class {
|
|
|
86
102
|
template = "Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)";
|
|
87
103
|
} else if (cleanLog.includes("TypeError") || cleanLog.includes("Cannot read properties")) {
|
|
88
104
|
category = "Product_Error";
|
|
89
|
-
severity = "
|
|
105
|
+
severity = "CRITICAL";
|
|
90
106
|
template = "TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)";
|
|
91
|
-
} else if (cleanLog.includes("504 Gateway Timeout")) {
|
|
107
|
+
} else if (cleanLog.includes("504 Gateway Timeout") || cleanLog.includes("upstream socket drop")) {
|
|
92
108
|
category = "Environment_Infrastructure_Error";
|
|
93
109
|
severity = "CRITICAL";
|
|
94
110
|
template = cleanLog.includes("15000ms") ? "HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms" : "HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms";
|
|
111
|
+
} else if (cleanLog.includes("429") || /rate limit|too many requests/i.test(cleanLog)) {
|
|
112
|
+
category = "Traffic_Management_Error";
|
|
113
|
+
severity = "MEDIUM";
|
|
114
|
+
template = "HTTP/1.1 429 Too Many Requests - Rate limit exceeded";
|
|
95
115
|
}
|
|
96
116
|
if (!template) {
|
|
97
117
|
template = this.extractLogTemplate(log);
|
|
@@ -176,6 +196,93 @@ function loadBaselineData(filePath) {
|
|
|
176
196
|
}
|
|
177
197
|
return null;
|
|
178
198
|
}
|
|
199
|
+
async function generateScenarioTS(prompt) {
|
|
200
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
201
|
+
if (!apiKey) {
|
|
202
|
+
throw new Error("\u274C Environment variable GEMINI_API_KEY is missing. AI scenario generation aborted.");
|
|
203
|
+
}
|
|
204
|
+
console.log("\u{1F9E0} Translating natural language requirements into TypeScript via Gemini 2.5 Flash...");
|
|
205
|
+
const systemInstruction = `
|
|
206
|
+
You are an expert Performance & Systems Reliability Engineer specializing in QuickJS-compatible virtual user load testing scripts for the Blaze framework.
|
|
207
|
+
Your task is to convert a natural language description into a clean, valid TypeScript simulation script.
|
|
208
|
+
|
|
209
|
+
The script must adhere to this exact structural import pattern:
|
|
210
|
+
\`\`\`typescript
|
|
211
|
+
import { VirtualUser } from '../src/core/vu';
|
|
212
|
+
|
|
213
|
+
export async function runScenario(vu: VirtualUser) {
|
|
214
|
+
// Setup headers
|
|
215
|
+
// Execute multi-step operations (e.g., fetching a login, parsing tokens, making subsequent requests)
|
|
216
|
+
// Manage dynamic value extraction (e.g., const token = response.json().token) and pass variables forward
|
|
217
|
+
// Include standard loops (e.g., for-loops) or timeouts as requested
|
|
218
|
+
}
|
|
219
|
+
\`\`\`
|
|
220
|
+
|
|
221
|
+
Rules:
|
|
222
|
+
1. Return ONLY executable, valid TypeScript script.
|
|
223
|
+
2. Cleanly extract dynamic payload fields or tokens and assign them to downstream headers.
|
|
224
|
+
3. Keep logic performance-optimized and sandbox-compatible.
|
|
225
|
+
4. Do NOT wrap code blocks in backticks like \`\`\`typescript. Start directly with the TypeScript imports and export block. Do not include markdown or text commentary.
|
|
226
|
+
`;
|
|
227
|
+
try {
|
|
228
|
+
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
|
|
229
|
+
method: "POST",
|
|
230
|
+
headers: { "Content-Type": "application/json" },
|
|
231
|
+
body: JSON.stringify({
|
|
232
|
+
contents: [{ parts: [{ text: `Generate a Virtual User script for this scenario: ${prompt}` }] }],
|
|
233
|
+
generationConfig: {
|
|
234
|
+
temperature: 0.1,
|
|
235
|
+
maxOutputTokens: 65536
|
|
236
|
+
},
|
|
237
|
+
systemInstruction: {
|
|
238
|
+
parts: [{ text: systemInstruction }]
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
});
|
|
242
|
+
const data = await response.json();
|
|
243
|
+
if (data?.error) {
|
|
244
|
+
throw new Error(`Google API Error ${data.error.code}: ${data.error.message}`);
|
|
245
|
+
}
|
|
246
|
+
const candidateText = data?.candidates?.[0]?.content?.parts?.[0]?.text;
|
|
247
|
+
if (!candidateText) {
|
|
248
|
+
throw new Error(`Malformed API layout or empty response body.`);
|
|
249
|
+
}
|
|
250
|
+
return candidateText.replace(/```typescript/g, "").replace(/```javascript/g, "").replace(/```/g, "").trim();
|
|
251
|
+
} catch (error) {
|
|
252
|
+
throw new Error(`Failed to generate script via Gemini: ${error.message || error}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function handleScenarioGeneration(config) {
|
|
256
|
+
const targetDir = path.resolve(__dirname, "./tests");
|
|
257
|
+
const tsFilePath = path.join(targetDir, "generated_scenario.ts");
|
|
258
|
+
const jsFilePath = path.join(targetDir, "generated_scenario.js");
|
|
259
|
+
try {
|
|
260
|
+
if (!fs.existsSync(targetDir)) {
|
|
261
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
262
|
+
}
|
|
263
|
+
const tsCode = await generateScenarioTS(config.scenarioPrompt);
|
|
264
|
+
fs.writeFileSync(tsFilePath, tsCode, "utf-8");
|
|
265
|
+
console.log(`\u{1F4BE} Successfully generated scenario script saved to: ${tsFilePath}`);
|
|
266
|
+
console.log("\u2699\uFE0F Transpiling generated scenario to JavaScript for native QuickJS sandbox validation...");
|
|
267
|
+
let compiledSuccessfully = false;
|
|
268
|
+
try {
|
|
269
|
+
const { execSync } = require2("child_process");
|
|
270
|
+
execSync(`npx tsc "${tsFilePath}" --target es2020 --module commonjs --allowJs --skipLibCheck`, { stdio: "ignore" });
|
|
271
|
+
compiledSuccessfully = true;
|
|
272
|
+
} catch (e) {
|
|
273
|
+
console.warn("\u26A0\uFE0F TypeScript compiler (tsc) was not found or timed out. Falling back to clean JS copy conversion...");
|
|
274
|
+
}
|
|
275
|
+
if (!compiledSuccessfully) {
|
|
276
|
+
const cleanJs = tsCode.replace(/: VirtualUser/g, "").replace(/import {[^}]+} from .*/g, "").replace(/export async/g, "async");
|
|
277
|
+
fs.writeFileSync(jsFilePath, cleanJs, "utf-8");
|
|
278
|
+
}
|
|
279
|
+
console.log(`\u2705 Sandbox compilation resolved. Active test file: ${jsFilePath}`);
|
|
280
|
+
config.targetScript = jsFilePath;
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error(`\u274C Scenario generation flow aborted: ${error.message || error}`);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
179
286
|
async function generateGeminiRCA(history, finalSummary, errorLogs) {
|
|
180
287
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
181
288
|
if (!apiKey) {
|
|
@@ -212,7 +319,7 @@ Paragraph 3 - ARCHITECTURAL SIZING RECOMMENDATIONS: Give concrete, infrastructur
|
|
|
212
319
|
CRITICAL: Return ONLY plain paragraph text. Do not use markdown headers (###). If you use bold text, use standard HTML <strong> tags. Wrap lines cleanly.
|
|
213
320
|
`;
|
|
214
321
|
try {
|
|
215
|
-
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
|
|
322
|
+
const response = await fetch(`[https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=$](https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=$){apiKey}`, {
|
|
216
323
|
method: "POST",
|
|
217
324
|
headers: { "Content-Type": "application/json" },
|
|
218
325
|
body: JSON.stringify({
|
|
@@ -249,13 +356,13 @@ async function executeTestPipeline(config) {
|
|
|
249
356
|
const transactionalScenario = [
|
|
250
357
|
{
|
|
251
358
|
name: "Fetch Target Anti-Forgery Nonce",
|
|
252
|
-
url: "https://httpbin.org/json",
|
|
359
|
+
url: "[https://httpbin.org/json](https://httpbin.org/json)",
|
|
253
360
|
method: "GET",
|
|
254
361
|
body: null
|
|
255
362
|
},
|
|
256
363
|
{
|
|
257
364
|
name: "Perform Contextual Post Action",
|
|
258
|
-
url: "https://httpbin.org/post?verification=${csrf_token}",
|
|
365
|
+
url: "[https://httpbin.org/post?verification=$](https://httpbin.org/post?verification=$){csrf_token}",
|
|
259
366
|
method: "POST",
|
|
260
367
|
body: JSON.stringify({
|
|
261
368
|
data: "performance_payload",
|
|
@@ -280,6 +387,9 @@ async function main() {
|
|
|
280
387
|
return;
|
|
281
388
|
}
|
|
282
389
|
const config = parseArguments(args);
|
|
390
|
+
if (config.scenarioPrompt) {
|
|
391
|
+
await handleScenarioGeneration(config);
|
|
392
|
+
}
|
|
283
393
|
if (config.isCorrelate) {
|
|
284
394
|
await executeTestPipeline(config);
|
|
285
395
|
}
|
|
@@ -290,6 +400,12 @@ async function main() {
|
|
|
290
400
|
}
|
|
291
401
|
}
|
|
292
402
|
function parseArguments(args) {
|
|
403
|
+
const scenarioIdx = args.indexOf("--generate-scenario");
|
|
404
|
+
let scenarioPrompt = null;
|
|
405
|
+
if (scenarioIdx !== -1) {
|
|
406
|
+
scenarioPrompt = args[scenarioIdx + 1];
|
|
407
|
+
args.splice(scenarioIdx, 2);
|
|
408
|
+
}
|
|
293
409
|
const targetScript = path.resolve(args[0] || ".");
|
|
294
410
|
const isAgentic = args.includes("--agentic");
|
|
295
411
|
const isCorrelate = args.includes("--correlate");
|
|
@@ -314,7 +430,7 @@ function parseArguments(args) {
|
|
|
314
430
|
const rampDownIdx = args.indexOf("--ramp-down");
|
|
315
431
|
if (rampDownIdx !== -1) rampDownSec = parseInt(args[rampDownIdx + 1], 10);
|
|
316
432
|
const apdexIdx = args.indexOf("--target-apdex");
|
|
317
|
-
if (apdexIdx !== -1) targetApdex = parseFloat(apdexIdx + 1);
|
|
433
|
+
if (apdexIdx !== -1) targetApdex = parseFloat(args[apdexIdx + 1]);
|
|
318
434
|
const errorIdx = args.indexOf("--target-error");
|
|
319
435
|
if (errorIdx !== -1) targetErrorRate = parseFloat(args[errorIdx + 1]);
|
|
320
436
|
const maxThreadsIdx = args.indexOf("--max-threads");
|
|
@@ -343,6 +459,7 @@ function parseArguments(args) {
|
|
|
343
459
|
}
|
|
344
460
|
return {
|
|
345
461
|
targetScript,
|
|
462
|
+
scenarioPrompt,
|
|
346
463
|
isAgentic,
|
|
347
464
|
isCorrelate,
|
|
348
465
|
isSeo,
|
|
@@ -937,7 +1054,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
937
1054
|
const displayColor = rate > 0 ? code >= 500 ? "#f87171" : "#fde047" : "#64748b";
|
|
938
1055
|
return `
|
|
939
1056
|
<div style="background: #090d16; border: 1px solid #1e293b; border-radius: 4px; padding: 0.4rem 0.2rem; text-align: center;">
|
|
940
|
-
<div style="font-size: 0.7rem; color: #64748b; font-weight: bold;"
|
|
1057
|
+
<div style="font-size: 0.7rem; color: #64748b; font-weight: bold;">Code ${code}</div>
|
|
941
1058
|
<div style="font-size: 0.85rem; font-weight: bold; color: ${displayColor};">${rate.toFixed(2)}%</div>
|
|
942
1059
|
</div>
|
|
943
1060
|
`;
|
|
@@ -946,7 +1063,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
946
1063
|
const htmlContent = `<!DOCTYPE html><html lang="en"><head>
|
|
947
1064
|
<meta charset="UTF-8">
|
|
948
1065
|
<title>Blaze Core Performance Report</title>
|
|
949
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
1066
|
+
<script src="[https://cdn.jsdelivr.net/npm/chart.js](https://cdn.jsdelivr.net/npm/chart.js)"></script>
|
|
950
1067
|
<style>
|
|
951
1068
|
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
|
|
952
1069
|
.container { max-width: 1300px; margin: 0 auto; }
|
|
@@ -1203,7 +1320,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1203
1320
|
const feedback = document.getElementById('nlpQueryFeedback');
|
|
1204
1321
|
|
|
1205
1322
|
let filterFn = (row, data) => true;
|
|
1206
|
-
const numMatch = q.match(/(d+(
|
|
1323
|
+
const numMatch = q.match(/(\\d+(?:\\.\\d+)?)/);
|
|
1207
1324
|
const targetNum = numMatch ? parseFloat(numMatch[1]) : null;
|
|
1208
1325
|
|
|
1209
1326
|
const isGreater = q.includes('>') || q.includes('greater') || q.includes('above') || q.includes('more');
|
|
@@ -1439,6 +1556,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1439
1556
|
function printUsage() {
|
|
1440
1557
|
console.log(`Blaze Core Performance Test CLI Engine
|
|
1441
1558
|
Options:
|
|
1442
|
-
--
|
|
1559
|
+
--generate-scenario <prompt> Generates a virtual user script from a natural language prompt
|
|
1560
|
+
--threads <count> Number of concurrent threads (default: 10)
|
|
1561
|
+
--duration <seconds> Duration of stress testing in seconds (default: 10)
|
|
1562
|
+
--ramp-up <seconds> Ramp-up speed
|
|
1563
|
+
--ramp-down <seconds> Ramp-down speed
|
|
1564
|
+
--agentic Enable Intelligent Autonomous Agentic Stress Test
|
|
1565
|
+
--correlate Launch dynamic transaction parameter mapping pipeline
|
|
1566
|
+
--baseline <json_file> Input baseline JSON benchmark reference
|
|
1567
|
+
--output <html_file> Target path for exported standalone dashboard`);
|
|
1443
1568
|
}
|
|
1444
1569
|
main().catch(console.error);
|
|
Binary file
|