@testsmith/perfornium 0.1.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.
Files changed (164) hide show
  1. package/README.md +360 -0
  2. package/dist/cli/cli.d.ts +2 -0
  3. package/dist/cli/cli.js +192 -0
  4. package/dist/cli/commands/distributed.d.ts +11 -0
  5. package/dist/cli/commands/distributed.js +179 -0
  6. package/dist/cli/commands/import.d.ts +23 -0
  7. package/dist/cli/commands/import.js +461 -0
  8. package/dist/cli/commands/init.d.ts +7 -0
  9. package/dist/cli/commands/init.js +923 -0
  10. package/dist/cli/commands/mock.d.ts +7 -0
  11. package/dist/cli/commands/mock.js +281 -0
  12. package/dist/cli/commands/report.d.ts +5 -0
  13. package/dist/cli/commands/report.js +70 -0
  14. package/dist/cli/commands/run.d.ts +12 -0
  15. package/dist/cli/commands/run.js +260 -0
  16. package/dist/cli/commands/validate.d.ts +3 -0
  17. package/dist/cli/commands/validate.js +35 -0
  18. package/dist/cli/commands/worker.d.ts +27 -0
  19. package/dist/cli/commands/worker.js +320 -0
  20. package/dist/config/index.d.ts +2 -0
  21. package/dist/config/index.js +20 -0
  22. package/dist/config/parser.d.ts +19 -0
  23. package/dist/config/parser.js +330 -0
  24. package/dist/config/types/global-config.d.ts +74 -0
  25. package/dist/config/types/global-config.js +2 -0
  26. package/dist/config/types/hooks.d.ts +58 -0
  27. package/dist/config/types/hooks.js +3 -0
  28. package/dist/config/types/import-types.d.ts +33 -0
  29. package/dist/config/types/import-types.js +2 -0
  30. package/dist/config/types/index.d.ts +11 -0
  31. package/dist/config/types/index.js +27 -0
  32. package/dist/config/types/load-config.d.ts +32 -0
  33. package/dist/config/types/load-config.js +9 -0
  34. package/dist/config/types/output-config.d.ts +10 -0
  35. package/dist/config/types/output-config.js +2 -0
  36. package/dist/config/types/report-config.d.ts +10 -0
  37. package/dist/config/types/report-config.js +2 -0
  38. package/dist/config/types/runtime-types.d.ts +6 -0
  39. package/dist/config/types/runtime-types.js +2 -0
  40. package/dist/config/types/scenario-config.d.ts +30 -0
  41. package/dist/config/types/scenario-config.js +2 -0
  42. package/dist/config/types/step-types.d.ts +139 -0
  43. package/dist/config/types/step-types.js +2 -0
  44. package/dist/config/types/test-configuration.d.ts +18 -0
  45. package/dist/config/types/test-configuration.js +2 -0
  46. package/dist/config/types/worker-config.d.ts +12 -0
  47. package/dist/config/types/worker-config.js +2 -0
  48. package/dist/config/validator.d.ts +19 -0
  49. package/dist/config/validator.js +198 -0
  50. package/dist/core/csv-data-provider.d.ts +47 -0
  51. package/dist/core/csv-data-provider.js +265 -0
  52. package/dist/core/hooks-manager.d.ts +33 -0
  53. package/dist/core/hooks-manager.js +129 -0
  54. package/dist/core/index.d.ts +5 -0
  55. package/dist/core/index.js +11 -0
  56. package/dist/core/script-executor.d.ts +14 -0
  57. package/dist/core/script-executor.js +290 -0
  58. package/dist/core/step-executor.d.ts +41 -0
  59. package/dist/core/step-executor.js +680 -0
  60. package/dist/core/test-runner.d.ts +34 -0
  61. package/dist/core/test-runner.js +465 -0
  62. package/dist/core/threshold-evaluator.d.ts +43 -0
  63. package/dist/core/threshold-evaluator.js +170 -0
  64. package/dist/core/virtual-user-pool.d.ts +42 -0
  65. package/dist/core/virtual-user-pool.js +136 -0
  66. package/dist/core/virtual-user.d.ts +51 -0
  67. package/dist/core/virtual-user.js +488 -0
  68. package/dist/distributed/coordinator.d.ts +34 -0
  69. package/dist/distributed/coordinator.js +158 -0
  70. package/dist/distributed/health-monitor.d.ts +18 -0
  71. package/dist/distributed/health-monitor.js +72 -0
  72. package/dist/distributed/load-distributor.d.ts +17 -0
  73. package/dist/distributed/load-distributor.js +106 -0
  74. package/dist/distributed/remote-worker.d.ts +37 -0
  75. package/dist/distributed/remote-worker.js +241 -0
  76. package/dist/distributed/result-aggregator.d.ts +43 -0
  77. package/dist/distributed/result-aggregator.js +146 -0
  78. package/dist/dsl/index.d.ts +3 -0
  79. package/dist/dsl/index.js +11 -0
  80. package/dist/dsl/test-builder.d.ts +111 -0
  81. package/dist/dsl/test-builder.js +514 -0
  82. package/dist/importers/har-importer.d.ts +17 -0
  83. package/dist/importers/har-importer.js +172 -0
  84. package/dist/importers/open-api-importer.d.ts +23 -0
  85. package/dist/importers/open-api-importer.js +181 -0
  86. package/dist/importers/wsdl-importer.d.ts +42 -0
  87. package/dist/importers/wsdl-importer.js +440 -0
  88. package/dist/index.d.ts +5 -0
  89. package/dist/index.js +17 -0
  90. package/dist/load-patterns/arrivals.d.ts +7 -0
  91. package/dist/load-patterns/arrivals.js +118 -0
  92. package/dist/load-patterns/base.d.ts +9 -0
  93. package/dist/load-patterns/base.js +2 -0
  94. package/dist/load-patterns/basic.d.ts +7 -0
  95. package/dist/load-patterns/basic.js +117 -0
  96. package/dist/load-patterns/stepping.d.ts +6 -0
  97. package/dist/load-patterns/stepping.js +122 -0
  98. package/dist/metrics/collector.d.ts +72 -0
  99. package/dist/metrics/collector.js +662 -0
  100. package/dist/metrics/types.d.ts +135 -0
  101. package/dist/metrics/types.js +2 -0
  102. package/dist/outputs/base.d.ts +7 -0
  103. package/dist/outputs/base.js +2 -0
  104. package/dist/outputs/csv.d.ts +13 -0
  105. package/dist/outputs/csv.js +163 -0
  106. package/dist/outputs/graphite.d.ts +13 -0
  107. package/dist/outputs/graphite.js +126 -0
  108. package/dist/outputs/influxdb.d.ts +12 -0
  109. package/dist/outputs/influxdb.js +82 -0
  110. package/dist/outputs/json.d.ts +14 -0
  111. package/dist/outputs/json.js +107 -0
  112. package/dist/outputs/streaming-csv.d.ts +37 -0
  113. package/dist/outputs/streaming-csv.js +254 -0
  114. package/dist/outputs/streaming-json.d.ts +43 -0
  115. package/dist/outputs/streaming-json.js +353 -0
  116. package/dist/outputs/webhook.d.ts +16 -0
  117. package/dist/outputs/webhook.js +96 -0
  118. package/dist/protocols/base.d.ts +33 -0
  119. package/dist/protocols/base.js +2 -0
  120. package/dist/protocols/rest/handler.d.ts +67 -0
  121. package/dist/protocols/rest/handler.js +776 -0
  122. package/dist/protocols/soap/handler.d.ts +12 -0
  123. package/dist/protocols/soap/handler.js +165 -0
  124. package/dist/protocols/web/core-web-vitals.d.ts +121 -0
  125. package/dist/protocols/web/core-web-vitals.js +373 -0
  126. package/dist/protocols/web/handler.d.ts +50 -0
  127. package/dist/protocols/web/handler.js +706 -0
  128. package/dist/recorder/native-recorder.d.ts +14 -0
  129. package/dist/recorder/native-recorder.js +533 -0
  130. package/dist/recorder/scenario-recorder.d.ts +55 -0
  131. package/dist/recorder/scenario-recorder.js +296 -0
  132. package/dist/reporting/constants.d.ts +94 -0
  133. package/dist/reporting/constants.js +82 -0
  134. package/dist/reporting/enhanced-html-generator.d.ts +55 -0
  135. package/dist/reporting/enhanced-html-generator.js +965 -0
  136. package/dist/reporting/generator.d.ts +42 -0
  137. package/dist/reporting/generator.js +1217 -0
  138. package/dist/reporting/statistics.d.ts +144 -0
  139. package/dist/reporting/statistics.js +742 -0
  140. package/dist/reporting/templates/enhanced-report.hbs +2812 -0
  141. package/dist/reporting/templates/html.hbs +2453 -0
  142. package/dist/utils/faker-manager.d.ts +55 -0
  143. package/dist/utils/faker-manager.js +166 -0
  144. package/dist/utils/file-manager.d.ts +33 -0
  145. package/dist/utils/file-manager.js +154 -0
  146. package/dist/utils/handlebars-manager.d.ts +42 -0
  147. package/dist/utils/handlebars-manager.js +172 -0
  148. package/dist/utils/logger.d.ts +16 -0
  149. package/dist/utils/logger.js +46 -0
  150. package/dist/utils/template.d.ts +80 -0
  151. package/dist/utils/template.js +513 -0
  152. package/dist/utils/test-output-writer.d.ts +56 -0
  153. package/dist/utils/test-output-writer.js +643 -0
  154. package/dist/utils/time.d.ts +3 -0
  155. package/dist/utils/time.js +23 -0
  156. package/dist/utils/timestamp-helper.d.ts +17 -0
  157. package/dist/utils/timestamp-helper.js +53 -0
  158. package/dist/workers/manager.d.ts +18 -0
  159. package/dist/workers/manager.js +95 -0
  160. package/dist/workers/server.d.ts +21 -0
  161. package/dist/workers/server.js +205 -0
  162. package/dist/workers/worker.d.ts +19 -0
  163. package/dist/workers/worker.js +147 -0
  164. package/package.json +102 -0
@@ -0,0 +1,290 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ScriptExecutor = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const logger_1 = require("../utils/logger");
41
+ class ScriptExecutor {
42
+ static setStepExecutor(stepExecutor) {
43
+ ScriptExecutor.stepExecutor = stepExecutor;
44
+ }
45
+ // Add the createContext static method
46
+ static createContext(testName, vuId, variables, extractedData, csvData, additionalContext) {
47
+ return {
48
+ test_name: testName,
49
+ vu_id: vuId,
50
+ variables: { ...variables },
51
+ extracted_data: { ...extractedData },
52
+ csv_data: csvData ? { ...csvData } : undefined,
53
+ ...additionalContext
54
+ };
55
+ }
56
+ static async executeHookScript(script, context, hookName) {
57
+ const startTime = Date.now();
58
+ try {
59
+ logger_1.logger.debug(`Executing ${hookName} hook for VU${context.vu_id}`);
60
+ let result;
61
+ switch (script.type) {
62
+ case 'inline':
63
+ if (!script.content) {
64
+ throw new Error('Inline script content is required');
65
+ }
66
+ result = await ScriptExecutor.executeInlineScript(script.content, context);
67
+ break;
68
+ case 'file':
69
+ if (!script.file) {
70
+ throw new Error('File script path is required');
71
+ }
72
+ result = await ScriptExecutor.executeFileScript(script.file, context, hookName);
73
+ break;
74
+ case 'steps':
75
+ if (!script.steps || script.steps.length === 0) {
76
+ throw new Error('Steps script requires at least one step');
77
+ }
78
+ result = await ScriptExecutor.executeStepsScript(script.steps, context, hookName);
79
+ break;
80
+ default:
81
+ throw new Error(`Invalid script type: ${script.type}`);
82
+ }
83
+ const duration = Date.now() - startTime;
84
+ logger_1.logger.debug(`${hookName} hook completed in ${duration}ms for VU${context.vu_id}`);
85
+ return {
86
+ success: true,
87
+ result: result.value || result,
88
+ duration,
89
+ variables: result.variables || {}
90
+ };
91
+ }
92
+ catch (error) {
93
+ const duration = Date.now() - startTime;
94
+ logger_1.logger.error(`${hookName} hook failed for VU${context.vu_id}:`, error);
95
+ const shouldContinue = script.continueOnError !== false;
96
+ if (!shouldContinue) {
97
+ throw error;
98
+ }
99
+ return {
100
+ success: false,
101
+ error: error,
102
+ duration
103
+ };
104
+ }
105
+ }
106
+ static async executeInlineScript(content, context) {
107
+ const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
108
+ const wrappedContent = `
109
+ let returnValue = undefined;
110
+ let variables = {};
111
+
112
+ function setVariable(key, value) {
113
+ variables[key] = value;
114
+ context.variables[key] = value;
115
+ }
116
+
117
+ function getVariable(key) {
118
+ return context.variables[key] || context.extracted_data[key] || context.csv_data?.[key];
119
+ }
120
+
121
+ const utils = {
122
+ randomInt: (min = 1, max = 100) => Math.floor(Math.random() * (max - min + 1)) + min,
123
+ randomChoice: (...choices) => choices[Math.floor(Math.random() * choices.length)],
124
+ uuid: () => require('crypto').randomUUID(),
125
+ sleep: (ms) => new Promise(resolve => setTimeout(resolve, ms)),
126
+ timestamp: () => Date.now(),
127
+ isoDate: (days = 0) => {
128
+ const date = new Date();
129
+ date.setDate(date.getDate() + days);
130
+ return date.toISOString();
131
+ }
132
+ };
133
+
134
+ try {
135
+ ${content}
136
+ } catch (error) {
137
+ throw new Error(\`Script execution failed: \${error.message}\`);
138
+ }
139
+
140
+ return { value: returnValue, variables };
141
+ `;
142
+ const compiledFunction = new AsyncFunction('context', 'require', 'console', 'logger', wrappedContent);
143
+ const timeout = 30000;
144
+ const timeoutPromise = new Promise((_, reject) => {
145
+ setTimeout(() => reject(new Error(`Hook script timeout after ${timeout}ms`)), timeout);
146
+ });
147
+ return Promise.race([
148
+ compiledFunction(context, require, console, logger_1.logger),
149
+ timeoutPromise
150
+ ]);
151
+ }
152
+ static async executeFileScript(filePath, context, hookName) {
153
+ const fullPath = path.resolve(ScriptExecutor.baseDir, filePath);
154
+ if (!fs.existsSync(fullPath)) {
155
+ throw new Error(`Script file not found: ${fullPath}`);
156
+ }
157
+ let content = fs.readFileSync(fullPath, 'utf8');
158
+ // Handle TypeScript files (basic transpilation)
159
+ if (filePath.endsWith('.ts')) {
160
+ content = ScriptExecutor.transpileTypeScript(content, filePath);
161
+ }
162
+ // Determine if it's a module export or direct script
163
+ const isModule = content.includes('module.exports') || content.includes('export');
164
+ if (isModule) {
165
+ // Handle as module
166
+ const tempFilePath = path.join(os.tmpdir(), `hook-${Date.now()}-${Math.random().toString(36).substring(7)}.js`);
167
+ fs.writeFileSync(tempFilePath, content);
168
+ try {
169
+ delete require.cache[tempFilePath];
170
+ const moduleExports = require(tempFilePath);
171
+ let hookFunction;
172
+ if (typeof moduleExports === 'function') {
173
+ hookFunction = moduleExports;
174
+ }
175
+ else if (moduleExports.default && typeof moduleExports.default === 'function') {
176
+ hookFunction = moduleExports.default;
177
+ }
178
+ else {
179
+ throw new Error('Module must export a function');
180
+ }
181
+ // Execute the hook function with helper utilities
182
+ const helpers = {
183
+ setVariable: (key, value) => {
184
+ context.variables[key] = value;
185
+ },
186
+ getVariable: (key) => context.variables[key] || context.extracted_data[key] || context.csv_data?.[key],
187
+ utils: {
188
+ randomInt: (min = 1, max = 100) => Math.floor(Math.random() * (max - min + 1)) + min,
189
+ randomChoice: (...choices) => choices[Math.floor(Math.random() * choices.length)],
190
+ uuid: () => require('crypto').randomUUID(),
191
+ sleep: (ms) => new Promise(resolve => setTimeout(resolve, ms)),
192
+ timestamp: () => Date.now(),
193
+ isoDate: (days = 0) => {
194
+ const date = new Date();
195
+ date.setDate(date.getDate() + days);
196
+ return date.toISOString();
197
+ }
198
+ }
199
+ };
200
+ const result = await hookFunction(context, helpers);
201
+ return {
202
+ value: result,
203
+ variables: {} // Variables are set directly on context through helpers
204
+ };
205
+ }
206
+ finally {
207
+ // Cleanup temp file
208
+ try {
209
+ fs.unlinkSync(tempFilePath);
210
+ }
211
+ catch (e) {
212
+ // Ignore cleanup errors
213
+ }
214
+ }
215
+ }
216
+ else {
217
+ // Handle as direct script (same as inline)
218
+ return ScriptExecutor.executeInlineScript(content, context);
219
+ }
220
+ }
221
+ static async executeStepsScript(steps, context, hookName) {
222
+ if (!ScriptExecutor.stepExecutor) {
223
+ throw new Error('StepExecutor not configured for hook step execution');
224
+ }
225
+ const results = [];
226
+ // Create a temporary VU context from script context
227
+ const vuContext = {
228
+ vu_id: context.vu_id,
229
+ iteration: context.loop_iteration || 0,
230
+ variables: { ...context.variables },
231
+ extracted_data: { ...context.extracted_data }
232
+ };
233
+ for (const step of steps) {
234
+ try {
235
+ logger_1.logger.debug(`Executing hook step: ${step.name || step.type}`);
236
+ const result = await ScriptExecutor.stepExecutor.executeStepInternal(step, vuContext, context.scenario_name || 'hook', Date.now());
237
+ results.push({
238
+ stepName: step.name || step.type,
239
+ success: result.success,
240
+ duration: result.duration,
241
+ error: result.error
242
+ });
243
+ // CRITICAL FIX: Update both context objects with extracted data
244
+ Object.assign(context.variables, vuContext.variables);
245
+ Object.assign(context.extracted_data, vuContext.extracted_data);
246
+ // Log what was extracted for debugging
247
+ if (Object.keys(vuContext.extracted_data).length > 0) {
248
+ logger_1.logger.debug(`VU${context.vu_id}: Hook step extracted data: ${Object.keys(vuContext.extracted_data).join(', ')}`);
249
+ logger_1.logger.debug(`VU${context.vu_id}: Extracted values: ${JSON.stringify(vuContext.extracted_data)}`);
250
+ }
251
+ }
252
+ catch (error) {
253
+ results.push({
254
+ stepName: step.name || step.type,
255
+ success: false,
256
+ error: error.message
257
+ });
258
+ // Continue with next step unless continueOnError is false
259
+ if (step.continueOnError === false) {
260
+ throw error;
261
+ }
262
+ }
263
+ }
264
+ return {
265
+ value: results,
266
+ variables: {
267
+ ...vuContext.variables,
268
+ ...vuContext.extracted_data // CRITICAL: Include extracted data in returned variables
269
+ }
270
+ };
271
+ }
272
+ static transpileTypeScript(content, filePath) {
273
+ // Basic TypeScript to JavaScript transpilation
274
+ return content
275
+ .replace(/:\s*\w+(\[\])?(\s*\|\s*\w+(\[\])?)*\s*[=;,)]/g, (match) => {
276
+ return match.slice(-1);
277
+ })
278
+ .replace(/interface\s+\w+\s*{[^}]*}/g, '')
279
+ .replace(/type\s+\w+\s*=\s*[^;]+;/g, '')
280
+ .replace(/import\s+.*?from\s+['"][^'"]+['"];?/g, '')
281
+ .replace(/export\s+/g, '');
282
+ }
283
+ static clearCache() {
284
+ ScriptExecutor.scriptCache.clear();
285
+ logger_1.logger.info('Script cache cleared');
286
+ }
287
+ }
288
+ exports.ScriptExecutor = ScriptExecutor;
289
+ ScriptExecutor.baseDir = process.cwd();
290
+ ScriptExecutor.scriptCache = new Map();
@@ -0,0 +1,41 @@
1
+ import { Step, VUContext } from '../config';
2
+ import { ProtocolHandler } from '../protocols/base';
3
+ import { TestResult } from '../metrics/types';
4
+ export declare class StepExecutor {
5
+ private handlers;
6
+ private templateProcessor;
7
+ private testName;
8
+ constructor(handlers: Map<string, ProtocolHandler>, testName?: string);
9
+ executeStep(step: Step, context: VUContext, scenarioName: string): Promise<TestResult>;
10
+ executeStepInternal(step: Step, context: VUContext, scenarioName: string, startTime: number): Promise<TestResult>;
11
+ private shouldRecordStep;
12
+ private executeRESTStep;
13
+ /**
14
+ * Load JSON payload from file and apply overrides
15
+ * Supports dot notation for nested paths in overrides
16
+ */
17
+ private processJsonFile;
18
+ /**
19
+ * Apply overrides to a JSON object using dot notation for nested paths
20
+ * Override values are processed through the template processor
21
+ */
22
+ private applyOverrides;
23
+ /**
24
+ * Set a value at a nested path using dot notation
25
+ * Example: setNestedValue(obj, 'user.profile.name', 'John')
26
+ */
27
+ private setNestedValue;
28
+ private executeSOAPStep;
29
+ private executeWebStep;
30
+ private executeCustomStep;
31
+ private executeWaitStep;
32
+ private executeScriptStep;
33
+ private executeScript;
34
+ private evaluateCondition;
35
+ private processTemplate;
36
+ private runChecks;
37
+ private checkCustom;
38
+ private extractData;
39
+ private extractCustom;
40
+ private getJsonPath;
41
+ }