blaze-performance-tester 3.2.4 → 3.2.5
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 +97 -14
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -261,31 +261,108 @@ CRITICAL: Return ONLY plain paragraph text. Do not use markdown headers (###). I
|
|
|
261
261
|
return `<span style="color: #f87171;">Failed to generate AI diagnosis framework: ${error.message || error}</span>`;
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
|
-
async function
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
264
|
+
async function generateNlpToTsScenario(promptText) {
|
|
265
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
266
|
+
if (!apiKey) {
|
|
267
|
+
console.warn("\u26A0\uFE0F GEMINI_API_KEY missing. Falling back to default transactional scenario.");
|
|
268
|
+
return getDefaultTransactionalScenario();
|
|
268
269
|
}
|
|
269
|
-
|
|
270
|
+
console.log(`\u{1F916} [NLP Scenario Generator]: Translating natural language prompt into TS simulation scenario via Gemini 2.5 Flash...`);
|
|
271
|
+
console.log(`Prompt: "${promptText}"`);
|
|
272
|
+
const systemPrompt = `
|
|
273
|
+
You are an expert Performance & Load Testing Architect. Your task is to convert the following natural language description of a user journey into a strict JSON array of HTTP transaction steps for a TypeScript load testing script.
|
|
274
|
+
|
|
275
|
+
Each step in the array must be an object with the following fields:
|
|
276
|
+
- "name": string (descriptive name of the step)
|
|
277
|
+
- "url": string (target URL, supporting dynamic token interpolation like \${token_name})
|
|
278
|
+
- "method": string ("GET", "POST", "PUT", "DELETE", etc.)
|
|
279
|
+
- "headers": object (optional key-value pairs of HTTP headers, e.g. { "Authorization": "Bearer \${auth_token}", "Content-Type": "application/json" })
|
|
280
|
+
- "body": string or null (stringified JSON or payload, supporting dynamic tokens)
|
|
281
|
+
- "extractors": object (optional key-value pairs to extract dynamic tokens from response headers/body)
|
|
282
|
+
|
|
283
|
+
Natural Language Journey Description:
|
|
284
|
+
"${promptText}"
|
|
285
|
+
|
|
286
|
+
CRITICAL REQUIREMENTS:
|
|
287
|
+
1. Return ONLY a valid JSON array. No markdown code blocks, no preamble, no explanatory text.
|
|
288
|
+
2. Ensure proper handling of headers, dynamic tokens, and multi-step transaction loops.
|
|
289
|
+
3. If the prompt implies multiple iterations or steps, sequence them logically in the array.
|
|
290
|
+
`;
|
|
291
|
+
try {
|
|
292
|
+
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
|
|
293
|
+
method: "POST",
|
|
294
|
+
headers: { "Content-Type": "application/json" },
|
|
295
|
+
body: JSON.stringify({
|
|
296
|
+
contents: [{ parts: [{ text: systemPrompt }] }],
|
|
297
|
+
generationConfig: {
|
|
298
|
+
temperature: 0.1,
|
|
299
|
+
maxOutputTokens: 8192
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
});
|
|
303
|
+
const data = await response.json();
|
|
304
|
+
if (data?.error) {
|
|
305
|
+
throw new Error(`Google API Error ${data.error.code}: ${data.error.message}`);
|
|
306
|
+
}
|
|
307
|
+
const candidateText = data?.candidates?.[0]?.content?.parts?.[0]?.text;
|
|
308
|
+
if (!candidateText) {
|
|
309
|
+
throw new Error("Empty response body from Gemini API.");
|
|
310
|
+
}
|
|
311
|
+
const cleanedJson = candidateText.replace(/```json/g, "").replace(/```/g, "").trim();
|
|
312
|
+
const parsedScenario = JSON.parse(cleanedJson);
|
|
313
|
+
if (!Array.isArray(parsedScenario) || parsedScenario.length === 0) {
|
|
314
|
+
throw new Error("Generated scenario is not a valid non-empty array.");
|
|
315
|
+
}
|
|
316
|
+
console.log(`\u2705 Successfully generated ${parsedScenario.length} transaction steps from natural language.`);
|
|
317
|
+
return parsedScenario;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error(`\u274C NLP Scenario Generation failed: ${error.message || error}. Falling back to default scenario.`);
|
|
320
|
+
return getDefaultTransactionalScenario();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function getDefaultTransactionalScenario() {
|
|
324
|
+
return [
|
|
270
325
|
{
|
|
271
326
|
name: "Fetch Target Anti-Forgery Nonce",
|
|
272
327
|
url: "https://httpbin.org/json",
|
|
273
328
|
method: "GET",
|
|
329
|
+
headers: { "Accept": "application/json" },
|
|
274
330
|
body: null
|
|
275
331
|
},
|
|
276
332
|
{
|
|
277
333
|
name: "Perform Contextual Post Action",
|
|
278
334
|
url: "https://httpbin.org/post?verification=${csrf_token}",
|
|
279
335
|
method: "POST",
|
|
336
|
+
headers: { "Content-Type": "application/json", "Authorization": "Bearer ${bearer_token}" },
|
|
280
337
|
body: JSON.stringify({
|
|
281
338
|
data: "performance_payload",
|
|
282
339
|
security_token: "${csrf_token}"
|
|
283
340
|
})
|
|
284
341
|
}
|
|
285
342
|
];
|
|
343
|
+
}
|
|
344
|
+
async function executeTestPipeline(config) {
|
|
345
|
+
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
346
|
+
console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
let transactionalScenario;
|
|
350
|
+
if (config.nlpPrompt) {
|
|
351
|
+
transactionalScenario = await generateNlpToTsScenario(config.nlpPrompt);
|
|
352
|
+
} else if (config.targetScript && fs.existsSync(config.targetScript) && !fs.lstatSync(config.targetScript).isDirectory()) {
|
|
353
|
+
const fileContent = fs.readFileSync(config.targetScript, "utf-8");
|
|
354
|
+
if (fileContent.length < 1e3 && !fileContent.includes("import ")) {
|
|
355
|
+
transactionalScenario = await generateNlpToTsScenario(fileContent);
|
|
356
|
+
} else {
|
|
357
|
+
transactionalScenario = getDefaultTransactionalScenario();
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
transactionalScenario = getDefaultTransactionalScenario();
|
|
361
|
+
}
|
|
286
362
|
console.log(`Launching automated script transaction analysis for: ${config.targetScript}`);
|
|
287
363
|
const sanitizedSteps = transactionalScenario.map((step) => ({
|
|
288
364
|
...step,
|
|
365
|
+
headers: step.headers || { "Content-Type": "application/json" },
|
|
289
366
|
body: typeof step.body === "string" ? step.body : step.body ? JSON.stringify(step.body) : ""
|
|
290
367
|
}));
|
|
291
368
|
const success = await blazeCore.runCorrelatedTransaction(sanitizedSteps);
|
|
@@ -300,7 +377,7 @@ async function main() {
|
|
|
300
377
|
return;
|
|
301
378
|
}
|
|
302
379
|
const config = parseArguments(args);
|
|
303
|
-
if (config.isCorrelate) {
|
|
380
|
+
if (config.isCorrelate || config.nlpPrompt) {
|
|
304
381
|
await executeTestPipeline(config);
|
|
305
382
|
}
|
|
306
383
|
if (config.isAgentic) {
|
|
@@ -315,6 +392,11 @@ function parseArguments(args) {
|
|
|
315
392
|
const isCorrelate = args.includes("--correlate");
|
|
316
393
|
const isSeo = args.includes("--seo");
|
|
317
394
|
const clusterLogs = !args.includes("--no-cluster-logs");
|
|
395
|
+
let nlpPrompt = null;
|
|
396
|
+
const promptIdx = args.indexOf("--prompt");
|
|
397
|
+
if (promptIdx !== -1) nlpPrompt = args[promptIdx + 1];
|
|
398
|
+
const nlpScenarioIdx = args.indexOf("--nlp-scenario");
|
|
399
|
+
if (nlpScenarioIdx !== -1) nlpPrompt = args[nlpScenarioIdx + 1];
|
|
318
400
|
let threads = 10;
|
|
319
401
|
let durationSec = 10;
|
|
320
402
|
let rampUpSec = 0;
|
|
@@ -367,6 +449,7 @@ function parseArguments(args) {
|
|
|
367
449
|
isCorrelate,
|
|
368
450
|
isSeo,
|
|
369
451
|
clusterLogs,
|
|
452
|
+
nlpPrompt,
|
|
370
453
|
threads,
|
|
371
454
|
durationSec,
|
|
372
455
|
rampUpSec,
|
|
@@ -815,10 +898,10 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
815
898
|
<table style="width: 100%; border-collapse: collapse;">
|
|
816
899
|
<thead>
|
|
817
900
|
<tr style="border-bottom: 1px solid #1e293b;">
|
|
818
|
-
<th style="width: 8%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold;
|
|
819
|
-
<th style="width: 22%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold;
|
|
820
|
-
<th style="width: 10%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold;
|
|
821
|
-
<th style="width: 60%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold;
|
|
901
|
+
<th style="width: 8%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold;">Count</th>
|
|
902
|
+
<th style="width: 22%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold;">Classification Category</th>
|
|
903
|
+
<th style="width: 10%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold;">Severity</th>
|
|
904
|
+
<th style="width: 60%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold;">Structural Abstract Blueprint / Dynamic Log Fingerprint</th>
|
|
822
905
|
</tr>
|
|
823
906
|
</thead>
|
|
824
907
|
<tbody>
|
|
@@ -938,12 +1021,12 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
938
1021
|
<div style="display: flex; gap: 0.75rem; margin-bottom: 0.75rem;">
|
|
939
1022
|
<input type="text" id="nlpQueryInput" placeholder="e.g., show me tiers where latency > 40ms or error rate > 0%"
|
|
940
1023
|
style="flex: 1; background: #090d16; border: 1px solid #1e293b; border-radius: 6px; padding: 0.75rem 1rem; color: #ffffff; font-size: 0.95rem; outline: none; transition: border-color 0.2s;">
|
|
941
|
-
<button id="nlpQueryBtn" style="background: #0ea5e9; color: #ffffff; border: none; border-radius: 6px; padding: 0.75rem 1.5rem; font-weight: 600; cursor: pointer;
|
|
1024
|
+
<button id="nlpQueryBtn" style="background: #0ea5e9; color: #ffffff; border: none; border-radius: 6px; padding: 0.75rem 1.5rem; font-weight: 600; cursor: pointer;">
|
|
942
1025
|
Query Matrix
|
|
943
1026
|
</button>
|
|
944
1027
|
</div>
|
|
945
1028
|
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;">
|
|
946
|
-
<span style="font-size: 0.8rem; color: #64748b; font-weight: bold; text-transform: uppercase;
|
|
1029
|
+
<span style="font-size: 0.8rem; color: #64748b; font-weight: bold; text-transform: uppercase;">Suggestions:</span>
|
|
947
1030
|
<button onclick="setNlpQuery('Show P95 latency > 50ms')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">P95 Latency > 50ms</button>
|
|
948
1031
|
<button onclick="setNlpQuery('Find tiers with errors > 0%')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Errors > 0%</button>
|
|
949
1032
|
<button onclick="setNlpQuery('High concurrency > 25 VUs')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Concurrency > 25 VUs</button>
|
|
@@ -1223,7 +1306,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1223
1306
|
const feedback = document.getElementById('nlpQueryFeedback');
|
|
1224
1307
|
|
|
1225
1308
|
let filterFn = (row, data) => true;
|
|
1226
|
-
const numMatch = q.match(/(
|
|
1309
|
+
const numMatch = q.match(/(d+(?:.d+)?)/);
|
|
1227
1310
|
const targetNum = numMatch ? parseFloat(numMatch[1]) : null;
|
|
1228
1311
|
|
|
1229
1312
|
const isGreater = q.includes('>') || q.includes('greater') || q.includes('above') || q.includes('more');
|
|
@@ -1459,6 +1542,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1459
1542
|
function printUsage() {
|
|
1460
1543
|
console.log(`Blaze Core Performance Test CLI Engine
|
|
1461
1544
|
Options:
|
|
1462
|
-
--threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file
|
|
1545
|
+
--threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file> | --prompt "<natural language description>" | --correlate`);
|
|
1463
1546
|
}
|
|
1464
1547
|
main().catch(console.error);
|
|
Binary file
|