@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,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BasicPattern = void 0;
4
+ const time_1 = require("../utils/time");
5
+ const logger_1 = require("../utils/logger");
6
+ class BasicPattern {
7
+ async execute(config, vuFactory) {
8
+ const virtualUsers = config.users || config.virtual_users || 1;
9
+ const rampUpMs = (0, time_1.parseTime)(config.ramp_up || '0s');
10
+ const durationMs = config.duration ? (0, time_1.parseTime)(config.duration) : null;
11
+ if (durationMs) {
12
+ logger_1.logger.info(`đŸŽ¯ Basic load: ${virtualUsers} VUs, ramp-up: ${(rampUpMs / 1000).toFixed(1)}s, duration: ${(durationMs / 1000).toFixed(1)}s`);
13
+ }
14
+ else {
15
+ logger_1.logger.info(`đŸŽ¯ Basic load: ${virtualUsers} VUs, ramp-up: ${(rampUpMs / 1000).toFixed(1)}s, run once`);
16
+ }
17
+ const intervalMs = rampUpMs > 0 ? rampUpMs / virtualUsers : 0;
18
+ const vuPromises = [];
19
+ // Start virtual users with ramp-up
20
+ for (let i = 0; i < virtualUsers; i++) {
21
+ const vuPromise = new Promise((resolve) => {
22
+ setTimeout(async () => {
23
+ const vuStartTime = Date.now();
24
+ try {
25
+ // CRITICAL: Await VU creation to ensure CSV initialization
26
+ logger_1.logger.debug(`Creating VU ${i + 1}...`);
27
+ const vu = await this.createVU(vuFactory, i + 1);
28
+ logger_1.logger.debug(`VU ${i + 1} ready`);
29
+ // Record VU start for metrics and reporting
30
+ const metrics = vuFactory.getMetrics();
31
+ metrics.recordVUStart(vu.getId());
32
+ logger_1.logger.debug(`👤 Started VU ${vu.getId()}`);
33
+ if (durationMs) {
34
+ // Run for specified duration
35
+ await this.runVUForDuration(vu, durationMs, vuStartTime);
36
+ }
37
+ else {
38
+ // Run scenarios once
39
+ await this.runVUOnce(vu);
40
+ }
41
+ }
42
+ catch (error) {
43
+ const errorMessage = error instanceof Error ? error.message : String(error);
44
+ if (errorMessage.includes('terminated due to CSV data exhaustion')) {
45
+ logger_1.logger.info(`âšī¸ VU ${i + 1} terminated due to CSV data exhaustion`);
46
+ }
47
+ else {
48
+ logger_1.logger.error(`❌ VU ${i + 1} error:`, error);
49
+ }
50
+ }
51
+ finally {
52
+ logger_1.logger.debug(`🏁 VU ${i + 1} completed`);
53
+ }
54
+ resolve();
55
+ }, i * intervalMs);
56
+ });
57
+ vuPromises.push(vuPromise);
58
+ }
59
+ // Wait for all VUs to complete (some may terminate early due to CSV exhaustion)
60
+ await Promise.all(vuPromises);
61
+ logger_1.logger.debug(`✅ Basic pattern completed`);
62
+ }
63
+ async createVU(vuFactory, id) {
64
+ const vu = vuFactory.create(id);
65
+ if (vu instanceof Promise) {
66
+ return await vu;
67
+ }
68
+ return vu;
69
+ }
70
+ async runVUOnce(vu) {
71
+ try {
72
+ if (vu.isRunning()) {
73
+ await vu.executeScenarios();
74
+ logger_1.logger.debug(`🏁 VU ${vu.getId()} completed scenario execution`);
75
+ }
76
+ }
77
+ catch (error) {
78
+ const errorMessage = error instanceof Error ? error.message : String(error);
79
+ if (errorMessage.includes('terminated due to CSV data exhaustion')) {
80
+ // This is expected - VU stopped due to CSV exhaustion
81
+ throw error; // Re-throw to be caught by the calling code
82
+ }
83
+ logger_1.logger.error(`⚠ VU ${vu.getId()} error during execution:`, error);
84
+ }
85
+ finally {
86
+ vu.stop();
87
+ logger_1.logger.debug(`🏁 VU ${vu.getId()} completed and stopped`);
88
+ }
89
+ }
90
+ async runVUForDuration(vu, durationMs, startTime) {
91
+ const endTime = startTime + durationMs;
92
+ while (Date.now() < endTime && vu.isRunning()) {
93
+ try {
94
+ // Execute all scenarios for this VU
95
+ await vu.executeScenarios();
96
+ // Check if we still have time for another iteration
97
+ if (Date.now() >= endTime) {
98
+ break;
99
+ }
100
+ // Small pause between full scenario loops
101
+ await (0, time_1.sleep)(100);
102
+ }
103
+ catch (error) {
104
+ const errorMessage = error instanceof Error ? error.message : String(error);
105
+ if (errorMessage.includes('terminated due to CSV data exhaustion')) {
106
+ logger_1.logger.info(`âšī¸ VU ${vu.getId()} terminated due to CSV data exhaustion`);
107
+ break; // Exit the loop, but don't propagate the error
108
+ }
109
+ logger_1.logger.error(`❌ VU ${vu.getId()} error during execution:`, error);
110
+ break;
111
+ }
112
+ }
113
+ vu.stop();
114
+ logger_1.logger.debug(`🏁 VU ${vu.getId()} completed after ${((Date.now() - startTime) / 1000).toFixed(1)}s`);
115
+ }
116
+ }
117
+ exports.BasicPattern = BasicPattern;
@@ -0,0 +1,6 @@
1
+ import { LoadPattern, VUFactory } from './base';
2
+ export declare class SteppingPattern implements LoadPattern {
3
+ execute(config: any, vuFactory: VUFactory): Promise<void>;
4
+ private createVU;
5
+ private runVUForDuration;
6
+ }
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SteppingPattern = void 0;
4
+ const time_1 = require("../utils/time");
5
+ const logger_1 = require("../utils/logger");
6
+ class SteppingPattern {
7
+ async execute(config, vuFactory) {
8
+ const steps = config.steps;
9
+ if (!steps || steps.length === 0) {
10
+ throw new Error('Stepping pattern requires steps configuration');
11
+ }
12
+ let currentVUs = 0;
13
+ const activeVUs = [];
14
+ logger_1.logger.info(`📊 Stepping load: ${steps.length} steps`);
15
+ for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) {
16
+ const step = steps[stepIndex];
17
+ const targetUsers = step.users;
18
+ const stepDuration = (0, time_1.parseTime)(step.duration);
19
+ const rampUp = (0, time_1.parseTime)(step.ramp_up || '0s');
20
+ logger_1.logger.info(`📈 Step ${stepIndex + 1}/${steps.length}: ${currentVUs} → ${targetUsers} users over ${(stepDuration / 1000).toFixed(1)}s`);
21
+ if (targetUsers > currentVUs) {
22
+ // Scale up: Add new users
23
+ const usersToAdd = targetUsers - currentVUs;
24
+ const rampUpInterval = rampUp > 0 ? rampUp / usersToAdd : 0;
25
+ for (let i = 0; i < usersToAdd; i++) {
26
+ setTimeout(async () => {
27
+ const vuStartTime = Date.now();
28
+ const vuEndTime = vuStartTime + stepDuration;
29
+ const vuId = currentVUs + i + 1;
30
+ try {
31
+ // CRITICAL: Await VU creation to ensure CSV initialization
32
+ logger_1.logger.debug(`Creating VU ${vuId}...`);
33
+ const vu = await this.createVU(vuFactory, vuId);
34
+ logger_1.logger.debug(`VU ${vuId} ready`);
35
+ // Record VU start for metrics and reporting
36
+ const metrics = vuFactory.getMetrics();
37
+ metrics.recordVUStart(vu.getId());
38
+ activeVUs.push({ vu, startTime: vuStartTime, endTime: vuEndTime });
39
+ logger_1.logger.debug(`👤 Started VU ${vu.getId()}`);
40
+ // Run VU for this step's duration
41
+ await this.runVUForDuration(vu, stepDuration, vuStartTime);
42
+ }
43
+ catch (error) {
44
+ const errorMessage = error instanceof Error ? error.message : String(error);
45
+ if (errorMessage.includes('terminated due to CSV data exhaustion')) {
46
+ logger_1.logger.info(`âšī¸ VU ${vuId} terminated due to CSV data exhaustion`);
47
+ }
48
+ else {
49
+ logger_1.logger.error(`❌ Failed to create/run VU ${vuId}:`, error);
50
+ }
51
+ }
52
+ }, i * rampUpInterval);
53
+ }
54
+ // Wait for ramp-up
55
+ if (rampUp > 0) {
56
+ await (0, time_1.sleep)(rampUp);
57
+ }
58
+ }
59
+ else if (targetUsers < currentVUs) {
60
+ // Scale down: Stop some users
61
+ logger_1.logger.debug(`📉 Scaling down from ${currentVUs} to ${targetUsers} users`);
62
+ const usersToStop = currentVUs - targetUsers;
63
+ // Stop the most recently started VUs
64
+ const vusToStop = activeVUs
65
+ .filter(vuInfo => vuInfo.vu.isRunning())
66
+ .slice(-usersToStop);
67
+ vusToStop.forEach(vuInfo => {
68
+ vuInfo.vu.stop();
69
+ logger_1.logger.debug(`âšī¸ Stopped VU ${vuInfo.vu.getId()}`);
70
+ });
71
+ }
72
+ currentVUs = targetUsers;
73
+ // Wait for step duration (minus ramp-up time)
74
+ const waitTime = Math.max(stepDuration - rampUp, 0);
75
+ if (waitTime > 0) {
76
+ await (0, time_1.sleep)(waitTime);
77
+ }
78
+ }
79
+ // Wait for all remaining VUs to complete
80
+ const remainingVUs = activeVUs.filter(vuInfo => vuInfo.vu.isRunning());
81
+ if (remainingVUs.length > 0) {
82
+ logger_1.logger.debug(`âŗ Waiting for ${remainingVUs.length} VUs to complete...`);
83
+ const maxEndTime = Math.max(...remainingVUs.map(vuInfo => vuInfo.endTime));
84
+ const waitTime = Math.max(maxEndTime - Date.now(), 0);
85
+ if (waitTime > 0) {
86
+ await (0, time_1.sleep)(waitTime);
87
+ }
88
+ }
89
+ logger_1.logger.debug('✅ Stepping pattern completed');
90
+ }
91
+ async createVU(vuFactory, id) {
92
+ // Handle both sync and async factories
93
+ const vu = vuFactory.create(id);
94
+ if (vu instanceof Promise) {
95
+ return await vu;
96
+ }
97
+ return vu;
98
+ }
99
+ async runVUForDuration(vu, durationMs, startTime) {
100
+ const endTime = startTime + durationMs;
101
+ while (Date.now() < endTime && vu.isRunning()) {
102
+ try {
103
+ await vu.executeScenarios();
104
+ if (Date.now() >= endTime) {
105
+ break;
106
+ }
107
+ await (0, time_1.sleep)(100); // Small pause between iterations
108
+ }
109
+ catch (error) {
110
+ const errorMessage = error instanceof Error ? error.message : String(error);
111
+ if (errorMessage.includes('terminated due to CSV data exhaustion')) {
112
+ logger_1.logger.info(`âšī¸ VU ${vu.getId()} terminated due to CSV data exhaustion`);
113
+ break; // Exit the loop, VU is already stopped
114
+ }
115
+ logger_1.logger.error(`❌ VU ${vu.getId()} error:`, error);
116
+ break;
117
+ }
118
+ }
119
+ logger_1.logger.debug(`🏁 VU ${vu.getId()} completed step execution`);
120
+ }
121
+ }
122
+ exports.SteppingPattern = SteppingPattern;
@@ -0,0 +1,72 @@
1
+ import { MetricsSummary, TestResult } from './types';
2
+ import { EventEmitter } from 'events';
3
+ export interface RealtimeConfig {
4
+ enabled: boolean;
5
+ batch_size?: number;
6
+ interval_ms?: number;
7
+ endpoints?: RealtimeEndpoint[];
8
+ file_output?: {
9
+ enabled: boolean;
10
+ path: string;
11
+ format: 'jsonl' | 'csv';
12
+ };
13
+ incremental_files?: {
14
+ enabled: boolean;
15
+ json_path?: string;
16
+ csv_path?: string;
17
+ update_summary?: boolean;
18
+ };
19
+ }
20
+ export interface RealtimeEndpoint {
21
+ type: 'graphite' | 'webhook' | 'influxdb' | 'websocket';
22
+ url?: string;
23
+ host?: string;
24
+ port?: number;
25
+ database?: string;
26
+ token?: string;
27
+ headers?: Record<string, string>;
28
+ }
29
+ export declare class MetricsCollector extends EventEmitter {
30
+ private results;
31
+ private startTime;
32
+ private errorDetails;
33
+ private vuStartEvents;
34
+ private loadPatternType;
35
+ private realtimeConfig;
36
+ private batchBuffer;
37
+ private batchTimer;
38
+ private batchCounter;
39
+ private csvHeaderWritten;
40
+ private defaultJsonPath;
41
+ private defaultCsvPath;
42
+ constructor(realtimeConfig?: RealtimeConfig);
43
+ private initializeRealtime;
44
+ private startBatchTimer;
45
+ private initializeIncrementalFiles;
46
+ start(): void;
47
+ recordVUStart(vuId: number): void;
48
+ recordResult(result: TestResult): void;
49
+ recordError(vuId: number, scenario: string, action: string, error: Error): void;
50
+ private flushBatch;
51
+ private writeBatchToFile;
52
+ private formatBatchAsCSV;
53
+ private updateIncrementalFiles;
54
+ private updateIncrementalJSON;
55
+ private updateIncrementalCSV;
56
+ private updateIncrementalSummary;
57
+ private generateSimpleHTMLSummary;
58
+ private sendToRealTimeEndpoints;
59
+ private sendToEndpoint;
60
+ private sendToGraphite;
61
+ private sendToWebhook;
62
+ private sendToInfluxDB;
63
+ private sendToWebSocket;
64
+ finalize(): Promise<void>;
65
+ private trackErrorDetail;
66
+ getResults(): TestResult[];
67
+ getSummary(): MetricsSummary;
68
+ private calculateStepStatistics;
69
+ private calculateTimelineData;
70
+ private calculatePercentiles;
71
+ clear(): void;
72
+ }