@loadstrike/loadstrike-sdk 1.0.10101 → 1.0.10801
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/README.md +8 -8
- package/dist/cjs/cluster.js +18 -1
- package/dist/cjs/correlation.js +69 -11
- package/dist/cjs/index.js +2 -14
- package/dist/cjs/local.js +88 -18
- package/dist/cjs/reporting.js +19 -5
- package/dist/cjs/runtime.js +526 -264
- package/dist/cjs/sinks.js +200 -106
- package/dist/cjs/transports.js +104 -18
- package/dist/esm/cluster.js +17 -0
- package/dist/esm/correlation.js +68 -10
- package/dist/esm/index.js +2 -4
- package/dist/esm/local.js +86 -16
- package/dist/esm/reporting.js +18 -2
- package/dist/esm/runtime.js +527 -264
- package/dist/esm/sinks.js +199 -105
- package/dist/esm/transports.js +103 -17
- package/dist/types/cluster.d.ts +30 -0
- package/dist/types/contracts.d.ts +15 -15
- package/dist/types/correlation.d.ts +41 -1
- package/dist/types/index.d.ts +4 -8
- package/dist/types/local.d.ts +146 -2
- package/dist/types/reporting.d.ts +33 -0
- package/dist/types/runtime.d.ts +464 -77
- package/dist/types/sinks.d.ts +212 -21
- package/dist/types/transports.d.ts +142 -0
- package/package.json +8 -19
package/dist/cjs/runtime.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.LoadStrikeRunner = exports.LoadStrikeScenario = exports.LoadStrikeContext = exports.ScenarioTrackingExtensions = exports.CrossPlatformScenarioConfigurator = exports.LoadStrikeThreshold = exports.LoadStrikeMetric = exports.LoadStrikeGauge = exports.LoadStrikeCounter = exports.LoadStrikeSimulation = exports.LoadStrikeStep = exports.LoadStrikeResponse = exports.
|
|
6
|
+
exports.__loadstrikeTestExports = exports.LoadStrikeRunner = exports.LoadStrikeScenario = exports.LoadStrikeContext = exports.ScenarioTrackingExtensions = exports.CrossPlatformScenarioConfigurator = exports.LoadStrikeThreshold = exports.LoadStrikeMetric = exports.LoadStrikeGauge = exports.LoadStrikeCounter = exports.LoadStrikeSimulation = exports.LoadStrikeStep = exports.LoadStrikeResponse = exports.LoadStrikePluginData = exports.LoadStrikePluginDataTable = exports.LoadStrikeOperationType = exports.LoadStrikeScenarioOperation = exports.LoadStrikeLogLevel = exports.LoadStrikeReportFormat = exports.LoadStrikeNodeType = void 0;
|
|
7
7
|
const node_fs_1 = require("node:fs");
|
|
8
8
|
const node_crypto_1 = require("node:crypto");
|
|
9
9
|
const node_os_1 = __importDefault(require("node:os"));
|
|
@@ -93,43 +93,6 @@ class LoadStrikePluginData {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
exports.LoadStrikePluginData = LoadStrikePluginData;
|
|
96
|
-
class LoadStrikeReportData {
|
|
97
|
-
constructor(scenarioStats = []) {
|
|
98
|
-
if (!Array.isArray(scenarioStats) || scenarioStats.some((value) => !value || typeof value !== "object")) {
|
|
99
|
-
throw new TypeError("Scenario stats cannot contain null values.");
|
|
100
|
-
}
|
|
101
|
-
this.scenarioStats = scenarioStats.map((value, index) => attachScenarioStatsAliases(normalizeScenarioStatsValue(value, index)));
|
|
102
|
-
}
|
|
103
|
-
static create(...scenarioStats) {
|
|
104
|
-
return new LoadStrikeReportData(scenarioStats);
|
|
105
|
-
}
|
|
106
|
-
static Create(...scenarioStats) {
|
|
107
|
-
return LoadStrikeReportData.create(...scenarioStats);
|
|
108
|
-
}
|
|
109
|
-
static fromRunResult(result) {
|
|
110
|
-
return new LoadStrikeReportData((result?.scenarioStats ?? result?.ScenarioStats ?? []));
|
|
111
|
-
}
|
|
112
|
-
findScenarioStats(scenarioName) {
|
|
113
|
-
return this.scenarioStats.find((value) => value.scenarioName === scenarioName);
|
|
114
|
-
}
|
|
115
|
-
getScenarioStats(scenarioName) {
|
|
116
|
-
const value = this.findScenarioStats(scenarioName);
|
|
117
|
-
if (!value) {
|
|
118
|
-
throw new Error(`Scenario stats not found: ${scenarioName}`);
|
|
119
|
-
}
|
|
120
|
-
return value;
|
|
121
|
-
}
|
|
122
|
-
FindScenarioStats(scenarioName) {
|
|
123
|
-
return this.findScenarioStats(scenarioName);
|
|
124
|
-
}
|
|
125
|
-
GetScenarioStats(scenarioName) {
|
|
126
|
-
return this.getScenarioStats(scenarioName);
|
|
127
|
-
}
|
|
128
|
-
get ScenarioStats() {
|
|
129
|
-
return this.scenarioStats;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
exports.LoadStrikeReportData = LoadStrikeReportData;
|
|
133
96
|
class MeasurementAccumulator {
|
|
134
97
|
constructor() {
|
|
135
98
|
this.count = 0;
|
|
@@ -749,28 +712,10 @@ class LoadStrikeContext {
|
|
|
749
712
|
.configureContext(this);
|
|
750
713
|
return runner.run();
|
|
751
714
|
}
|
|
752
|
-
async runDetailed(...argsOrSingleArray) {
|
|
753
|
-
const args = normalizeRunArgsInput(argsOrSingleArray);
|
|
754
|
-
if (!this.registeredScenarios.length) {
|
|
755
|
-
throw new Error("At least one scenario must be added before running the context.");
|
|
756
|
-
}
|
|
757
|
-
if (args.length) {
|
|
758
|
-
this.mergeValues(extractContextOverridesFromArgs(args), args);
|
|
759
|
-
}
|
|
760
|
-
const runner = LoadStrikeRunner
|
|
761
|
-
.create()
|
|
762
|
-
.addScenarios(...this.registeredScenarios)
|
|
763
|
-
.configureContext(this);
|
|
764
|
-
return runner.runDetailed();
|
|
765
|
-
}
|
|
766
715
|
async Run(...argsOrSingleArray) {
|
|
767
716
|
const args = normalizeRunArgsInput(argsOrSingleArray);
|
|
768
717
|
return this.run(args);
|
|
769
718
|
}
|
|
770
|
-
async RunDetailed(...argsOrSingleArray) {
|
|
771
|
-
const args = normalizeRunArgsInput(argsOrSingleArray);
|
|
772
|
-
return this.runDetailed(args);
|
|
773
|
-
}
|
|
774
719
|
toRunnerOptions() {
|
|
775
720
|
const normalizedValues = normalizeRunContextCollectionShapes(this.values);
|
|
776
721
|
return {
|
|
@@ -784,7 +729,6 @@ class LoadStrikeContext {
|
|
|
784
729
|
coordinatorTargetScenarios: normalizedValues.CoordinatorTargetScenarios,
|
|
785
730
|
natsServerUrl: normalizedValues.NatsServerUrl,
|
|
786
731
|
runnerKey: normalizedValues.RunnerKey,
|
|
787
|
-
licenseValidationServerUrl: normalizedValues.LicenseValidationServerUrl,
|
|
788
732
|
licenseValidationTimeoutSeconds: normalizedValues.LicenseValidationTimeoutSeconds,
|
|
789
733
|
configPath: normalizedValues.ConfigPath,
|
|
790
734
|
infraConfigPath: normalizedValues.InfraConfigPath,
|
|
@@ -797,8 +741,6 @@ class LoadStrikeContext {
|
|
|
797
741
|
reportFileName: normalizedValues.ReportFileName,
|
|
798
742
|
reportFolderPath: normalizedValues.ReportFolderPath,
|
|
799
743
|
reportFormats: normalizedValues.ReportFormats,
|
|
800
|
-
reportFinalizer: normalizedValues.ReportFinalizer,
|
|
801
|
-
detailedReportFinalizer: normalizedValues.DetailedReportFinalizer,
|
|
802
744
|
reportingIntervalSeconds: normalizedValues.ReportingIntervalSeconds,
|
|
803
745
|
minimumLogLevel: normalizedValues.MinimumLogLevel,
|
|
804
746
|
loggerConfig: normalizedValues.LoggerConfig,
|
|
@@ -806,6 +748,7 @@ class LoadStrikeContext {
|
|
|
806
748
|
sinkRetryCount: normalizedValues.SinkRetryCount,
|
|
807
749
|
sinkRetryBackoffMs: normalizedValues.SinkRetryBackoffMs,
|
|
808
750
|
runtimePolicies: normalizedValues.RuntimePolicies,
|
|
751
|
+
runtimePolicyErrorMode: normalizedRuntimePolicyErrorMode(normalizedValues.RuntimePolicyErrorMode),
|
|
809
752
|
scenarioCompletionTimeoutSeconds: normalizedValues.ScenarioCompletionTimeoutSeconds,
|
|
810
753
|
clusterCommandTimeoutSeconds: normalizedValues.ClusterCommandTimeoutSeconds,
|
|
811
754
|
restartIterationMaxAttempts: normalizedValues.RestartIterationMaxAttempts,
|
|
@@ -851,28 +794,6 @@ class LoadStrikeContext {
|
|
|
851
794
|
ConfigureContext(context) {
|
|
852
795
|
return this.configureContext(context);
|
|
853
796
|
}
|
|
854
|
-
buildLicenseValidationPayload() {
|
|
855
|
-
const scenarios = this.registeredScenarios.map((scenario) => ({
|
|
856
|
-
Name: scenario.name,
|
|
857
|
-
MaxFailCount: scenario.getMaxFailCount(),
|
|
858
|
-
RestartIterationOnFail: scenario.shouldRestartIterationOnFail(),
|
|
859
|
-
WithoutWarmUp: scenario.isWithoutWarmUp(),
|
|
860
|
-
WarmUpDurationSeconds: scenario.getWarmUpDurationSeconds(),
|
|
861
|
-
Weight: scenario.getWeight(),
|
|
862
|
-
LoadSimulations: [...scenario.getSimulations()],
|
|
863
|
-
Thresholds: [...scenario.getThresholds()],
|
|
864
|
-
Tracking: scenario.getTrackingConfiguration() ?? {},
|
|
865
|
-
LicenseFeatures: scenario.getLicenseFeatures()
|
|
866
|
-
}));
|
|
867
|
-
return {
|
|
868
|
-
Context: this.toObject(),
|
|
869
|
-
Scenarios: scenarios,
|
|
870
|
-
RunArgs: [...this.runArgs]
|
|
871
|
-
};
|
|
872
|
-
}
|
|
873
|
-
BuildLicenseValidationPayload() {
|
|
874
|
-
return this.buildLicenseValidationPayload();
|
|
875
|
-
}
|
|
876
797
|
displayConsoleMetrics(enable) {
|
|
877
798
|
return this.DisplayConsoleMetrics(enable);
|
|
878
799
|
}
|
|
@@ -946,12 +867,6 @@ class LoadStrikeContext {
|
|
|
946
867
|
WithRunnerKey(runnerKey) {
|
|
947
868
|
return this.mergeValues({ RunnerKey: requireNonEmpty(runnerKey, "Runner key must be provided.") });
|
|
948
869
|
}
|
|
949
|
-
withLicenseValidationServerUrl(serverUrl) {
|
|
950
|
-
return this.WithLicenseValidationServerUrl(serverUrl);
|
|
951
|
-
}
|
|
952
|
-
WithLicenseValidationServerUrl(serverUrl) {
|
|
953
|
-
return this.mergeValues({ LicenseValidationServerUrl: requireNonEmpty(serverUrl, "License validation server URL must be provided.") });
|
|
954
|
-
}
|
|
955
870
|
withLicenseValidationTimeout(timeoutSeconds) {
|
|
956
871
|
return this.WithLicenseValidationTimeout(timeoutSeconds);
|
|
957
872
|
}
|
|
@@ -1004,24 +919,6 @@ class LoadStrikeContext {
|
|
|
1004
919
|
}
|
|
1005
920
|
return this.mergeValues({ ReportFormats: normalized });
|
|
1006
921
|
}
|
|
1007
|
-
withReportFinalizer(handler) {
|
|
1008
|
-
return this.WithReportFinalizer(handler);
|
|
1009
|
-
}
|
|
1010
|
-
WithReportFinalizer(handler) {
|
|
1011
|
-
if (typeof handler !== "function") {
|
|
1012
|
-
throw new TypeError("Report finalizer must be provided.");
|
|
1013
|
-
}
|
|
1014
|
-
return this.mergeValues({ ReportFinalizer: handler });
|
|
1015
|
-
}
|
|
1016
|
-
withDetailedReportFinalizer(handler) {
|
|
1017
|
-
return this.WithDetailedReportFinalizer(handler);
|
|
1018
|
-
}
|
|
1019
|
-
WithDetailedReportFinalizer(handler) {
|
|
1020
|
-
if (typeof handler !== "function") {
|
|
1021
|
-
throw new TypeError("Detailed report finalizer must be provided.");
|
|
1022
|
-
}
|
|
1023
|
-
return this.mergeValues({ DetailedReportFinalizer: handler });
|
|
1024
|
-
}
|
|
1025
922
|
withReportingInterval(intervalSeconds) {
|
|
1026
923
|
return this.WithReportingInterval(intervalSeconds);
|
|
1027
924
|
}
|
|
@@ -1044,6 +941,26 @@ class LoadStrikeContext {
|
|
|
1044
941
|
validateNamedReportingSinks(sinks);
|
|
1045
942
|
return this.mergeValues({ ReportingSinks: sinks });
|
|
1046
943
|
}
|
|
944
|
+
withRuntimePolicies(...policies) {
|
|
945
|
+
return this.WithRuntimePolicies(...policies);
|
|
946
|
+
}
|
|
947
|
+
WithRuntimePolicies(...policies) {
|
|
948
|
+
if (!policies.length) {
|
|
949
|
+
throw new Error("At least one runtime policy should be provided.");
|
|
950
|
+
}
|
|
951
|
+
if (policies.some((policy) => policy == null)) {
|
|
952
|
+
throw new Error("Runtime policy collection cannot contain null values.");
|
|
953
|
+
}
|
|
954
|
+
return this.mergeValues({ RuntimePolicies: policies });
|
|
955
|
+
}
|
|
956
|
+
withRuntimePolicyErrorMode(mode) {
|
|
957
|
+
return this.WithRuntimePolicyErrorMode(mode);
|
|
958
|
+
}
|
|
959
|
+
WithRuntimePolicyErrorMode(mode) {
|
|
960
|
+
return this.mergeValues({
|
|
961
|
+
RuntimePolicyErrorMode: normalizedRuntimePolicyErrorMode(mode)
|
|
962
|
+
});
|
|
963
|
+
}
|
|
1047
964
|
withWorkerPlugins(...plugins) {
|
|
1048
965
|
return this.WithWorkerPlugins(...plugins);
|
|
1049
966
|
}
|
|
@@ -1140,7 +1057,7 @@ class LoadStrikeContext {
|
|
|
1140
1057
|
}
|
|
1141
1058
|
exports.LoadStrikeContext = LoadStrikeContext;
|
|
1142
1059
|
class LoadStrikeScenario {
|
|
1143
|
-
constructor(name, runHandler, initHandler, cleanHandler, loadSimulations, thresholds, trackingConfiguration, maxFailCount, withoutWarmUpValue, warmUpDurationSeconds, weight, restartIterationOnFail
|
|
1060
|
+
constructor(name, runHandler, initHandler, cleanHandler, loadSimulations, thresholds, trackingConfiguration, maxFailCount, withoutWarmUpValue, warmUpDurationSeconds, weight, restartIterationOnFail) {
|
|
1144
1061
|
this.name = name;
|
|
1145
1062
|
this.runHandler = runHandler;
|
|
1146
1063
|
this.initHandler = initHandler;
|
|
@@ -1153,18 +1070,23 @@ class LoadStrikeScenario {
|
|
|
1153
1070
|
this.warmUpDurationSeconds = warmUpDurationSeconds;
|
|
1154
1071
|
this.weight = weight;
|
|
1155
1072
|
this.restartIterationOnFail = restartIterationOnFail;
|
|
1156
|
-
this.licenseFeatures = [...licenseFeatures];
|
|
1157
1073
|
}
|
|
1158
1074
|
static create(name, runHandler) {
|
|
1159
1075
|
const scenarioName = requireNonEmpty(name, "Scenario name must be provided.");
|
|
1160
1076
|
if (typeof runHandler !== "function") {
|
|
1161
1077
|
throw new TypeError("Scenario run handler must be provided.");
|
|
1162
1078
|
}
|
|
1163
|
-
return new LoadStrikeScenario(scenarioName, runHandler, undefined, undefined, [], [], undefined, 0, false, 0, 1, false
|
|
1079
|
+
return new LoadStrikeScenario(scenarioName, runHandler, undefined, undefined, [], [], undefined, 0, false, 0, 1, false);
|
|
1164
1080
|
}
|
|
1165
1081
|
static Create(name, runHandler) {
|
|
1166
1082
|
return LoadStrikeScenario.create(name, runHandler);
|
|
1167
1083
|
}
|
|
1084
|
+
static createAsync(name, runHandler) {
|
|
1085
|
+
return LoadStrikeScenario.create(name, runHandler);
|
|
1086
|
+
}
|
|
1087
|
+
static CreateAsync(name, runHandler) {
|
|
1088
|
+
return LoadStrikeScenario.createAsync(name, runHandler);
|
|
1089
|
+
}
|
|
1168
1090
|
static empty(name) {
|
|
1169
1091
|
return LoadStrikeScenario.create(name, () => LoadStrikeResponse.ok());
|
|
1170
1092
|
}
|
|
@@ -1178,37 +1100,43 @@ class LoadStrikeScenario {
|
|
|
1178
1100
|
if (typeof handler !== "function") {
|
|
1179
1101
|
throw new TypeError("Init handler must be provided.");
|
|
1180
1102
|
}
|
|
1181
|
-
return new LoadStrikeScenario(this.name, this.runHandler, handler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail
|
|
1103
|
+
return new LoadStrikeScenario(this.name, this.runHandler, handler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail);
|
|
1104
|
+
}
|
|
1105
|
+
withInitAsync(handler) {
|
|
1106
|
+
return this.withInit(handler);
|
|
1182
1107
|
}
|
|
1183
1108
|
withClean(handler) {
|
|
1184
1109
|
if (typeof handler !== "function") {
|
|
1185
1110
|
throw new TypeError("Clean handler must be provided.");
|
|
1186
1111
|
}
|
|
1187
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, handler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail
|
|
1112
|
+
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, handler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail);
|
|
1113
|
+
}
|
|
1114
|
+
withCleanAsync(handler) {
|
|
1115
|
+
return this.withClean(handler);
|
|
1188
1116
|
}
|
|
1189
1117
|
withMaxFailCount(maxFailCount) {
|
|
1190
1118
|
if (!Number.isFinite(maxFailCount)) {
|
|
1191
1119
|
throw new RangeError("maxFailCount should be a finite number.");
|
|
1192
1120
|
}
|
|
1193
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, Math.trunc(maxFailCount), this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail
|
|
1121
|
+
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, Math.trunc(maxFailCount), this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail);
|
|
1194
1122
|
}
|
|
1195
1123
|
withoutWarmUp() {
|
|
1196
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, true, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail
|
|
1124
|
+
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, true, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail);
|
|
1197
1125
|
}
|
|
1198
1126
|
withWarmUpDuration(durationSeconds) {
|
|
1199
1127
|
if (!Number.isFinite(durationSeconds)) {
|
|
1200
1128
|
throw new RangeError("Warmup duration should be a finite number.");
|
|
1201
1129
|
}
|
|
1202
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, durationSeconds, this.weight, this.restartIterationOnFail
|
|
1130
|
+
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, durationSeconds, this.weight, this.restartIterationOnFail);
|
|
1203
1131
|
}
|
|
1204
1132
|
withWeight(weight) {
|
|
1205
1133
|
if (!Number.isFinite(weight)) {
|
|
1206
1134
|
throw new RangeError("Weight should be a finite number.");
|
|
1207
1135
|
}
|
|
1208
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, Math.trunc(weight), this.restartIterationOnFail
|
|
1136
|
+
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, Math.trunc(weight), this.restartIterationOnFail);
|
|
1209
1137
|
}
|
|
1210
1138
|
withRestartIterationOnFail(shouldRestart) {
|
|
1211
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, Boolean(shouldRestart)
|
|
1139
|
+
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, Boolean(shouldRestart));
|
|
1212
1140
|
}
|
|
1213
1141
|
withCrossPlatformTracking(configuration) {
|
|
1214
1142
|
if (!configuration || typeof configuration !== "object" || Array.isArray(configuration)) {
|
|
@@ -1225,25 +1153,19 @@ class LoadStrikeScenario {
|
|
|
1225
1153
|
? mapRuntimeTrackingEndpointSpec(destinationSpec)
|
|
1226
1154
|
: null;
|
|
1227
1155
|
validateRuntimeTrackingConfiguration(copied, sourceEndpoint, destinationEndpoint);
|
|
1228
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, copied, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail
|
|
1156
|
+
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, copied, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail);
|
|
1229
1157
|
}
|
|
1230
1158
|
withLoadSimulations(...simulations) {
|
|
1231
1159
|
if (!simulations.length) {
|
|
1232
1160
|
throw new Error("At least one load simulation should be provided.");
|
|
1233
1161
|
}
|
|
1234
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, simulations.map((simulation) => attachLoadSimulationProjection({ ...simulation })), this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail
|
|
1162
|
+
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, simulations.map((simulation) => attachLoadSimulationProjection({ ...simulation })), this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail);
|
|
1235
1163
|
}
|
|
1236
1164
|
withThresholds(...thresholds) {
|
|
1237
1165
|
if (!thresholds.length) {
|
|
1238
1166
|
throw new Error("At least one threshold should be provided.");
|
|
1239
1167
|
}
|
|
1240
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, thresholds.map((threshold) => ({ ...threshold })), this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail
|
|
1241
|
-
}
|
|
1242
|
-
withLicenseFeatures(...features) {
|
|
1243
|
-
const normalized = features
|
|
1244
|
-
.map((value) => String(value ?? "").trim())
|
|
1245
|
-
.filter((value) => value.length > 0);
|
|
1246
|
-
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, this.thresholds, this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail, [...this.licenseFeatures, ...normalized]);
|
|
1168
|
+
return new LoadStrikeScenario(this.name, this.runHandler, this.initHandler, this.cleanHandler, this.loadSimulations, thresholds.map((threshold) => ({ ...threshold })), this.trackingConfiguration, this.maxFailCount, this.withoutWarmUpValue, this.warmUpDurationSeconds, this.weight, this.restartIterationOnFail);
|
|
1247
1169
|
}
|
|
1248
1170
|
getSimulations() {
|
|
1249
1171
|
return this.loadSimulations.map((simulation) => attachLoadSimulationProjection({ ...simulation }));
|
|
@@ -1269,9 +1191,6 @@ class LoadStrikeScenario {
|
|
|
1269
1191
|
shouldRestartIterationOnFail() {
|
|
1270
1192
|
return this.restartIterationOnFail;
|
|
1271
1193
|
}
|
|
1272
|
-
getLicenseFeatures() {
|
|
1273
|
-
return [...this.licenseFeatures];
|
|
1274
|
-
}
|
|
1275
1194
|
async invokeInit(context) {
|
|
1276
1195
|
if (this.initHandler) {
|
|
1277
1196
|
await this.initHandler(context);
|
|
@@ -1300,9 +1219,15 @@ class LoadStrikeScenario {
|
|
|
1300
1219
|
WithInit(handler) {
|
|
1301
1220
|
return this.withInit(handler);
|
|
1302
1221
|
}
|
|
1222
|
+
WithInitAsync(handler) {
|
|
1223
|
+
return this.withInitAsync(handler);
|
|
1224
|
+
}
|
|
1303
1225
|
WithClean(handler) {
|
|
1304
1226
|
return this.withClean(handler);
|
|
1305
1227
|
}
|
|
1228
|
+
WithCleanAsync(handler) {
|
|
1229
|
+
return this.withCleanAsync(handler);
|
|
1230
|
+
}
|
|
1306
1231
|
WithLoadSimulations(...simulations) {
|
|
1307
1232
|
return this.withLoadSimulations(...simulations);
|
|
1308
1233
|
}
|
|
@@ -1315,9 +1240,6 @@ class LoadStrikeScenario {
|
|
|
1315
1240
|
WithRestartIterationOnFail(shouldRestart) {
|
|
1316
1241
|
return this.withRestartIterationOnFail(shouldRestart);
|
|
1317
1242
|
}
|
|
1318
|
-
WithLicenseFeatures(...features) {
|
|
1319
|
-
return this.withLicenseFeatures(...features);
|
|
1320
|
-
}
|
|
1321
1243
|
WithThresholds(...thresholds) {
|
|
1322
1244
|
return this.withThresholds(...thresholds);
|
|
1323
1245
|
}
|
|
@@ -1381,9 +1303,6 @@ class LoadStrikeRunner {
|
|
|
1381
1303
|
static WithRunnerKey(context, runnerKey) {
|
|
1382
1304
|
return context.WithRunnerKey(runnerKey);
|
|
1383
1305
|
}
|
|
1384
|
-
static WithLicenseValidationServerUrl(context, serverUrl) {
|
|
1385
|
-
return context.WithLicenseValidationServerUrl(serverUrl);
|
|
1386
|
-
}
|
|
1387
1306
|
static WithLicenseValidationTimeout(context, timeoutSeconds) {
|
|
1388
1307
|
return context.WithLicenseValidationTimeout(timeoutSeconds);
|
|
1389
1308
|
}
|
|
@@ -1405,12 +1324,6 @@ class LoadStrikeRunner {
|
|
|
1405
1324
|
static WithReportFileName(context, reportFileName) {
|
|
1406
1325
|
return context.WithReportFileName(reportFileName);
|
|
1407
1326
|
}
|
|
1408
|
-
static WithReportFinalizer(context, handler) {
|
|
1409
|
-
return context.WithReportFinalizer(handler);
|
|
1410
|
-
}
|
|
1411
|
-
static WithDetailedReportFinalizer(context, handler) {
|
|
1412
|
-
return context.WithDetailedReportFinalizer(handler);
|
|
1413
|
-
}
|
|
1414
1327
|
static WithReportFolder(context, reportFolderPath) {
|
|
1415
1328
|
return context.WithReportFolder(reportFolderPath);
|
|
1416
1329
|
}
|
|
@@ -1423,6 +1336,12 @@ class LoadStrikeRunner {
|
|
|
1423
1336
|
static WithReportingSinks(context, ...sinks) {
|
|
1424
1337
|
return context.WithReportingSinks(...sinks);
|
|
1425
1338
|
}
|
|
1339
|
+
static WithRuntimePolicies(context, ...policies) {
|
|
1340
|
+
return context.WithRuntimePolicies(...policies);
|
|
1341
|
+
}
|
|
1342
|
+
static WithRuntimePolicyErrorMode(context, mode) {
|
|
1343
|
+
return context.WithRuntimePolicyErrorMode(mode);
|
|
1344
|
+
}
|
|
1426
1345
|
static WithScenarioCompletionTimeout(context, timeoutSeconds) {
|
|
1427
1346
|
return context.WithScenarioCompletionTimeout(timeoutSeconds);
|
|
1428
1347
|
}
|
|
@@ -1583,15 +1502,11 @@ class LoadStrikeRunner {
|
|
|
1583
1502
|
return this.buildContext();
|
|
1584
1503
|
}
|
|
1585
1504
|
async run(args = []) {
|
|
1586
|
-
const detailed = await this.runDetailed(args);
|
|
1587
|
-
return detailedToNodeStats(detailed);
|
|
1588
|
-
}
|
|
1589
|
-
async runDetailed(args = []) {
|
|
1590
1505
|
if (this.contextConfigurators.length) {
|
|
1591
|
-
return new LoadStrikeRunner(this.scenarios, this.buildContext().toRunnerOptions()).
|
|
1506
|
+
return new LoadStrikeRunner(this.scenarios, this.buildContext().toRunnerOptions()).run(args);
|
|
1592
1507
|
}
|
|
1593
1508
|
if (args.length) {
|
|
1594
|
-
return this.buildContext().
|
|
1509
|
+
return this.buildContext().run(args);
|
|
1595
1510
|
}
|
|
1596
1511
|
const started = new Date();
|
|
1597
1512
|
const scenarioStats = new Map();
|
|
@@ -1605,14 +1520,15 @@ class LoadStrikeRunner {
|
|
|
1605
1520
|
name: resolveSinkName(sink, index)
|
|
1606
1521
|
}));
|
|
1607
1522
|
const sinkErrors = [];
|
|
1523
|
+
const policyErrors = [];
|
|
1608
1524
|
const sinkRetryCount = Math.max(this.options.sinkRetryCount ?? 2, 0);
|
|
1609
1525
|
const sinkRetryBackoffMs = Math.max(this.options.sinkRetryBackoffMs ?? 25, 0);
|
|
1610
1526
|
const policies = this.options.runtimePolicies ?? [];
|
|
1527
|
+
const runtimePolicyErrorMode = normalizedRuntimePolicyErrorMode(this.options.runtimePolicyErrorMode);
|
|
1611
1528
|
const plugins = this.options.reportingSinks === undefined && this.options.workerPlugins === undefined &&
|
|
1612
1529
|
this.options.runtimePolicies === undefined && this.options.customSettings === undefined &&
|
|
1613
1530
|
this.options.globalCustomSettings === undefined && this.options.configPath === undefined &&
|
|
1614
1531
|
this.options.infraConfigPath === undefined && this.options.infraConfig === undefined &&
|
|
1615
|
-
this.options.reportFinalizer === undefined && this.options.detailedReportFinalizer === undefined &&
|
|
1616
1532
|
this.options.loggerConfig === undefined && this.options.minimumLogLevel === undefined &&
|
|
1617
1533
|
this.options.sinkRetryCount === undefined && this.options.sinkRetryBackoffMs === undefined &&
|
|
1618
1534
|
this.options.reportsEnabled === false && this.options.displayConsoleMetrics === false &&
|
|
@@ -1627,7 +1543,6 @@ class LoadStrikeRunner {
|
|
|
1627
1543
|
const createdUtc = started.toISOString();
|
|
1628
1544
|
const testInfo = buildTestInfo(this.options, createdUtc);
|
|
1629
1545
|
const nodeInfo = buildNodeInfo(this.options);
|
|
1630
|
-
const runLogger = createLogger(this.options.loggerConfig, this.options.minimumLogLevel);
|
|
1631
1546
|
let pluginsStopped = false;
|
|
1632
1547
|
let sinksStopped = false;
|
|
1633
1548
|
let realtimeTimer = null;
|
|
@@ -1637,18 +1552,18 @@ class LoadStrikeRunner {
|
|
|
1637
1552
|
let licenseSession = null;
|
|
1638
1553
|
const clusterMode = resolveClusterExecutionMode(this.options);
|
|
1639
1554
|
const selectedScenarios = clusterMode === "local-coordinator" || clusterMode === "nats-coordinator"
|
|
1640
|
-
? await this.filterScenariosWithPolicies(this.scenarios, policies)
|
|
1641
|
-
: await this.selectScenarios(policies);
|
|
1642
|
-
|
|
1643
|
-
licensePayload = this.buildLicenseValidationPayload();
|
|
1555
|
+
? await this.filterScenariosWithPolicies(this.scenarios, policies, policyErrors, runtimePolicyErrorMode)
|
|
1556
|
+
: await this.selectScenarios(policies, policyErrors, runtimePolicyErrorMode);
|
|
1557
|
+
licensePayload = buildLicenseValidationPayload(this.options, this.scenarios);
|
|
1644
1558
|
licenseClient = new local_js_1.LoadStrikeLocalClient({
|
|
1645
|
-
licensingApiBaseUrl: licenseValidationServerUrl,
|
|
1646
1559
|
licenseValidationTimeoutMs: Math.max((this.options.licenseValidationTimeoutSeconds ?? 10) * 1000, 1)
|
|
1647
1560
|
});
|
|
1648
1561
|
licenseSession = await licenseClient.acquireLicenseLease(licensePayload);
|
|
1649
1562
|
if (clusterMode === "nats-agent") {
|
|
1650
1563
|
return this.runAgentWithNats(createdUtc, testInfo, nodeInfo);
|
|
1651
1564
|
}
|
|
1565
|
+
const loggerSetup = createLoggerSetup(this.options.loggerConfig, this.options.minimumLogLevel, this.options, testInfo, nodeInfo);
|
|
1566
|
+
const runLogger = loggerSetup.logger;
|
|
1652
1567
|
const scenarioStartInfos = selectedScenarios.map((scenario, index) => {
|
|
1653
1568
|
const startInfo = {
|
|
1654
1569
|
scenarioName: scenario.name,
|
|
@@ -1715,7 +1630,7 @@ class LoadStrikeRunner {
|
|
|
1715
1630
|
const okCount = snapshot.reduce((sum, value) => sum + value.allOkCount, 0);
|
|
1716
1631
|
const failCount = snapshot.reduce((sum, value) => sum + value.allFailCount, 0);
|
|
1717
1632
|
const stamp = new Date().toTimeString().slice(0, 8);
|
|
1718
|
-
|
|
1633
|
+
console.log(`[${stamp}] requests=${requestCount} ok=${okCount} fail=${failCount}`);
|
|
1719
1634
|
}
|
|
1720
1635
|
}
|
|
1721
1636
|
finally {
|
|
@@ -1723,7 +1638,7 @@ class LoadStrikeRunner {
|
|
|
1723
1638
|
}
|
|
1724
1639
|
};
|
|
1725
1640
|
const reportingIntervalMs = Math.max(Math.trunc((this.options.reportingIntervalSeconds ?? 5) * 1000), 1);
|
|
1726
|
-
if (sinkStates.length > 0) {
|
|
1641
|
+
if (sinkStates.length > 0 || toBoolean(this.options.displayConsoleMetrics, true)) {
|
|
1727
1642
|
realtimeTimer = setInterval(() => {
|
|
1728
1643
|
void emitRealtimeSnapshot();
|
|
1729
1644
|
}, reportingIntervalMs);
|
|
@@ -1738,12 +1653,12 @@ class LoadStrikeRunner {
|
|
|
1738
1653
|
if (clusterMode === "local-coordinator") {
|
|
1739
1654
|
const aggregated = await this.runCoordinatorWithLocalAgents(selectedScenarios, testInfo, nodeInfo);
|
|
1740
1655
|
metricStats = aggregated.metrics;
|
|
1741
|
-
result = toDetailedRunResultFromNodeStats(aggregated, started.toISOString(), sinkErrors);
|
|
1656
|
+
result = toDetailedRunResultFromNodeStats(aggregated, started.toISOString(), sinkErrors, policyErrors);
|
|
1742
1657
|
}
|
|
1743
1658
|
else if (clusterMode === "nats-coordinator") {
|
|
1744
1659
|
const aggregated = await this.runCoordinatorWithNats(selectedScenarios, testInfo, nodeInfo);
|
|
1745
1660
|
metricStats = aggregated.metrics;
|
|
1746
|
-
result = toDetailedRunResultFromNodeStats(aggregated, started.toISOString(), sinkErrors);
|
|
1661
|
+
result = toDetailedRunResultFromNodeStats(aggregated, started.toISOString(), sinkErrors, policyErrors);
|
|
1747
1662
|
}
|
|
1748
1663
|
else {
|
|
1749
1664
|
const testAbortController = new AbortController();
|
|
@@ -1766,21 +1681,15 @@ class LoadStrikeRunner {
|
|
|
1766
1681
|
stopTestState,
|
|
1767
1682
|
testAbortController,
|
|
1768
1683
|
executeScenarioInvocation: (targetScenario, context, operation) => this.executeScenarioInvocation(targetScenario, context, operation),
|
|
1769
|
-
invokeBeforeScenario: (runtimePolicies, scenarioName) => this.invokeBeforeScenario(runtimePolicies, scenarioName),
|
|
1770
|
-
invokeAfterScenario: (runtimePolicies, scenarioName, stats) => this.invokeAfterScenario(runtimePolicies, scenarioName, stats)
|
|
1684
|
+
invokeBeforeScenario: (runtimePolicies, scenarioName) => this.invokeBeforeScenario(runtimePolicies, scenarioName, policyErrors, runtimePolicyErrorMode),
|
|
1685
|
+
invokeAfterScenario: (runtimePolicies, scenarioName, stats) => this.invokeAfterScenario(runtimePolicies, scenarioName, stats, policyErrors, runtimePolicyErrorMode),
|
|
1686
|
+
invokeBeforeStep: (runtimePolicies, scenarioName, stepName) => this.invokeBeforeStep(runtimePolicies, scenarioName, stepName, policyErrors, runtimePolicyErrorMode),
|
|
1687
|
+
invokeAfterStep: (runtimePolicies, scenarioName, stepName, reply) => this.invokeAfterStep(runtimePolicies, scenarioName, stepName, reply, policyErrors, runtimePolicyErrorMode)
|
|
1771
1688
|
})));
|
|
1772
1689
|
nodeInfo.currentOperation = stopTestState.value ? "Stop" : "Complete";
|
|
1773
|
-
|
|
1690
|
+
const scenarioStatList = Array.from(scenarioAccumulators.values())
|
|
1774
1691
|
.map((value) => value.build(scenarioDurationsMs.get(value.scenarioName) ?? 0))
|
|
1775
1692
|
.sort((left, right) => left.sortIndex - right.sortIndex);
|
|
1776
|
-
const reportFinalizer = this.options.reportFinalizer;
|
|
1777
|
-
if (typeof reportFinalizer === "function") {
|
|
1778
|
-
const finalized = reportFinalizer(LoadStrikeReportData.create(...scenarioStatList));
|
|
1779
|
-
if (!(finalized instanceof LoadStrikeReportData)) {
|
|
1780
|
-
throw new TypeError("Report finalizer must return LoadStrikeReportData.");
|
|
1781
|
-
}
|
|
1782
|
-
scenarioStatList = [...finalized.scenarioStats];
|
|
1783
|
-
}
|
|
1784
1693
|
const metricValues = collectMetricValues(allRegisteredMetrics);
|
|
1785
1694
|
metricStats = collectMetricStats(allRegisteredMetrics, Date.now() - started.getTime());
|
|
1786
1695
|
const metricsByName = metricValues.reduce((accumulator, metric) => {
|
|
@@ -1794,35 +1703,42 @@ class LoadStrikeRunner {
|
|
|
1794
1703
|
result = {
|
|
1795
1704
|
startedUtc: started.toISOString(),
|
|
1796
1705
|
completedUtc: new Date().toISOString(),
|
|
1706
|
+
allBytes: builtScenarioStats.reduce((sum, x) => sum + x.allBytes, 0),
|
|
1797
1707
|
allRequestCount: scenarioStatList.reduce((sum, x) => sum + x.allRequestCount, 0),
|
|
1798
1708
|
allOkCount: scenarioStatList.reduce((sum, x) => sum + x.allOkCount, 0),
|
|
1799
1709
|
allFailCount: scenarioStatList.reduce((sum, x) => sum + x.allFailCount, 0),
|
|
1800
1710
|
failedThresholds: thresholdEvaluation.failedCount,
|
|
1711
|
+
durationMs: Math.max(Date.now() - started.getTime(), 0),
|
|
1801
1712
|
nodeInfo,
|
|
1802
1713
|
testInfo,
|
|
1714
|
+
thresholds: thresholdEvaluation.results.map((value) => ({ ...value })),
|
|
1803
1715
|
thresholdResults: thresholdEvaluation.results,
|
|
1716
|
+
metricStats,
|
|
1804
1717
|
metrics: metricValues,
|
|
1805
1718
|
scenarioStats: builtScenarioStats,
|
|
1806
1719
|
stepStats: builtStepStats,
|
|
1720
|
+
scenarioDurationsMs: Object.fromEntries(scenarioDurationsMs.entries()),
|
|
1807
1721
|
pluginsData: [],
|
|
1808
1722
|
disabledSinks: [],
|
|
1809
1723
|
sinkErrors,
|
|
1810
|
-
|
|
1724
|
+
policyErrors,
|
|
1725
|
+
reportFiles: [],
|
|
1726
|
+
logFiles: [...loggerSetup.logFiles],
|
|
1727
|
+
correlationRows: buildDetailedCorrelationRows(),
|
|
1728
|
+
failedCorrelationRows: buildDetailedFailedCorrelationRows()
|
|
1811
1729
|
};
|
|
1812
1730
|
}
|
|
1813
|
-
result.pluginsData = mergePluginData(result.pluginsData, await this.collectPluginData(plugins,
|
|
1814
|
-
const
|
|
1815
|
-
|
|
1816
|
-
const finalizedResult = attachRunResultAliases(typeof detailedReportFinalizer === "function"
|
|
1817
|
-
? requireDetailedReportFinalizerResult(detailedReportFinalizer(detailedResult))
|
|
1818
|
-
: detailedResult);
|
|
1819
|
-
await this.emitFinalStats(sinkStates, detailedToNodeStats(finalizedResult, metricStats), sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
1731
|
+
result.pluginsData = mergePluginData(result.pluginsData, await this.collectPluginData(plugins, attachRunResultAliases(result), pluginLifecycleErrors));
|
|
1732
|
+
const finalizedResult = attachRunResultAliases(result);
|
|
1733
|
+
finalizedResult.logFiles = mergeStringArrays(finalizedResult.logFiles, loggerSetup.logFiles);
|
|
1820
1734
|
finalizedResult.reportFiles = this.writeReports(finalizedResult);
|
|
1821
1735
|
finalizedResult.disabledSinks = sinkStates.filter((x) => x.disabled).map((x) => x.name);
|
|
1822
1736
|
await this.emitRunResult(sinkStates, finalizedResult, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
1823
1737
|
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
1824
1738
|
sinksStopped = true;
|
|
1825
1739
|
finalizedResult.disabledSinks = sinkStates.filter((x) => x.disabled).map((x) => x.name);
|
|
1740
|
+
finalizedResult.sinkErrors = sinkErrors
|
|
1741
|
+
.map((value) => attachSinkErrorAliases(normalizeSinkErrorValue(value)));
|
|
1826
1742
|
return finalizedResult;
|
|
1827
1743
|
}
|
|
1828
1744
|
finally {
|
|
@@ -1833,7 +1749,7 @@ class LoadStrikeRunner {
|
|
|
1833
1749
|
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
1834
1750
|
}
|
|
1835
1751
|
if (!pluginsStopped) {
|
|
1836
|
-
await this.stopPlugins(plugins, pluginLifecycleErrors);
|
|
1752
|
+
await this.stopPlugins(plugins, pluginLifecycleErrors, runLogger);
|
|
1837
1753
|
}
|
|
1838
1754
|
if (licenseClient && licenseSession && licensePayload) {
|
|
1839
1755
|
await licenseClient.releaseLicenseLease(licenseSession, licensePayload);
|
|
@@ -1843,10 +1759,7 @@ class LoadStrikeRunner {
|
|
|
1843
1759
|
async Run(args = []) {
|
|
1844
1760
|
return this.run(args);
|
|
1845
1761
|
}
|
|
1846
|
-
async
|
|
1847
|
-
return this.runDetailed(args);
|
|
1848
|
-
}
|
|
1849
|
-
async filterScenariosWithPolicies(scenarios, policies) {
|
|
1762
|
+
async filterScenariosWithPolicies(scenarios, policies, policyErrors, mode) {
|
|
1850
1763
|
if (!policies.length) {
|
|
1851
1764
|
return [...scenarios];
|
|
1852
1765
|
}
|
|
@@ -1855,7 +1768,7 @@ class LoadStrikeRunner {
|
|
|
1855
1768
|
let allowed = true;
|
|
1856
1769
|
for (const policy of policies) {
|
|
1857
1770
|
if (policy.shouldRunScenario) {
|
|
1858
|
-
const shouldRun = await policy.shouldRunScenario(scenario.name);
|
|
1771
|
+
const shouldRun = await this.invokePolicyCallback(policy, "shouldRunScenario", scenario.name, "", policyErrors, mode, () => policy.shouldRunScenario(scenario.name), true);
|
|
1859
1772
|
if (!shouldRun) {
|
|
1860
1773
|
allowed = false;
|
|
1861
1774
|
break;
|
|
@@ -1888,6 +1801,7 @@ class LoadStrikeRunner {
|
|
|
1888
1801
|
localDevClusterEnabled: false,
|
|
1889
1802
|
nodeType,
|
|
1890
1803
|
natsServerUrl: undefined,
|
|
1804
|
+
reportFolderPath: (0, node_path_1.resolve)(this.options.reportFolderPath ?? "./reports", sanitizeReportFileName(machineName)),
|
|
1891
1805
|
targetScenarios,
|
|
1892
1806
|
agentTargetScenarios: targetScenarios,
|
|
1893
1807
|
coordinatorTargetScenarios: [],
|
|
@@ -1896,14 +1810,16 @@ class LoadStrikeRunner {
|
|
|
1896
1810
|
reportingSinks: includeWorkerExtensions ? this.options.reportingSinks : [],
|
|
1897
1811
|
workerPlugins: includeWorkerExtensions ? this.options.workerPlugins : []
|
|
1898
1812
|
});
|
|
1899
|
-
const
|
|
1813
|
+
const childResult = await childRunner.run();
|
|
1814
|
+
const childStats = detailedToNodeStats(childResult);
|
|
1900
1815
|
return {
|
|
1901
1816
|
...childStats,
|
|
1902
1817
|
nodeInfo: {
|
|
1903
1818
|
...childStats.nodeInfo,
|
|
1904
1819
|
machineName,
|
|
1905
1820
|
nodeType
|
|
1906
|
-
}
|
|
1821
|
+
},
|
|
1822
|
+
logFiles: [...(childResult.logFiles ?? [])]
|
|
1907
1823
|
};
|
|
1908
1824
|
}
|
|
1909
1825
|
async runCoordinatorWithLocalAgents(scenarios, testInfo, nodeInfo) {
|
|
@@ -1971,7 +1887,7 @@ class LoadStrikeRunner {
|
|
|
1971
1887
|
};
|
|
1972
1888
|
});
|
|
1973
1889
|
if (handled && handledStats) {
|
|
1974
|
-
return toDetailedRunResultFromNodeStats(handledStats, startedUtc, []);
|
|
1890
|
+
return toDetailedRunResultFromNodeStats(handledStats, startedUtc, [], []);
|
|
1975
1891
|
}
|
|
1976
1892
|
await sleep(5);
|
|
1977
1893
|
}
|
|
@@ -1981,15 +1897,17 @@ class LoadStrikeRunner {
|
|
|
1981
1897
|
await agent.dispose().catch(() => { });
|
|
1982
1898
|
}
|
|
1983
1899
|
}
|
|
1984
|
-
async stopPlugins(plugins, pluginLifecycleErrors) {
|
|
1985
|
-
void pluginLifecycleErrors;
|
|
1900
|
+
async stopPlugins(plugins, pluginLifecycleErrors, logger) {
|
|
1986
1901
|
for (const plugin of plugins) {
|
|
1902
|
+
const pluginName = normalizePluginName(resolveWorkerPluginName(plugin));
|
|
1987
1903
|
const stop = resolveWorkerPluginStop(plugin);
|
|
1988
1904
|
if (stop) {
|
|
1989
1905
|
try {
|
|
1990
1906
|
await stop();
|
|
1991
1907
|
}
|
|
1992
|
-
catch {
|
|
1908
|
+
catch (error) {
|
|
1909
|
+
recordPluginLifecycleError(pluginLifecycleErrors, pluginName, "stop", error);
|
|
1910
|
+
logger?.warn?.(`Worker plugin ${pluginName} stop failed during shutdown: ${String(error ?? "unknown error")}`);
|
|
1993
1911
|
}
|
|
1994
1912
|
}
|
|
1995
1913
|
const dispose = resolveWorkerPluginDispose(plugin);
|
|
@@ -1997,7 +1915,9 @@ class LoadStrikeRunner {
|
|
|
1997
1915
|
try {
|
|
1998
1916
|
await dispose();
|
|
1999
1917
|
}
|
|
2000
|
-
catch {
|
|
1918
|
+
catch (error) {
|
|
1919
|
+
recordPluginLifecycleError(pluginLifecycleErrors, pluginName, "dispose", error);
|
|
1920
|
+
logger?.warn?.(`Worker plugin ${pluginName} dispose failed during shutdown: ${String(error ?? "unknown error")}`);
|
|
2001
1921
|
}
|
|
2002
1922
|
}
|
|
2003
1923
|
}
|
|
@@ -2009,7 +1929,7 @@ class LoadStrikeRunner {
|
|
|
2009
1929
|
}
|
|
2010
1930
|
return executeTrackedScenarioInvocation(tracking, context, () => scenario.execute(context), operation);
|
|
2011
1931
|
}
|
|
2012
|
-
async selectScenarios(policies) {
|
|
1932
|
+
async selectScenarios(policies, policyErrors, mode) {
|
|
2013
1933
|
const scenarioMap = new Map(this.scenarios.map((x) => [x.name, x]));
|
|
2014
1934
|
const nodeType = (this.options.nodeType ?? "SingleNode").toLowerCase();
|
|
2015
1935
|
let selectedNames = this.options.targetScenarios;
|
|
@@ -2032,7 +1952,7 @@ class LoadStrikeRunner {
|
|
|
2032
1952
|
let allowed = true;
|
|
2033
1953
|
for (const policy of policies) {
|
|
2034
1954
|
if (policy.shouldRunScenario) {
|
|
2035
|
-
const shouldRun = await policy.shouldRunScenario(scenario.name);
|
|
1955
|
+
const shouldRun = await this.invokePolicyCallback(policy, "shouldRunScenario", scenario.name, "", policyErrors, mode, () => policy.shouldRunScenario(scenario.name), true);
|
|
2036
1956
|
if (!shouldRun) {
|
|
2037
1957
|
allowed = false;
|
|
2038
1958
|
break;
|
|
@@ -2045,9 +1965,6 @@ class LoadStrikeRunner {
|
|
|
2045
1965
|
}
|
|
2046
1966
|
return filtered;
|
|
2047
1967
|
}
|
|
2048
|
-
buildLicenseValidationPayload() {
|
|
2049
|
-
return buildLicenseValidationPayload(this.options, this.scenarios);
|
|
2050
|
-
}
|
|
2051
1968
|
async initializeSinks(sinkStates, context, infraConfig, retryCount, retryBackoffMs, sinkErrors) {
|
|
2052
1969
|
for (const state of sinkStates) {
|
|
2053
1970
|
await this.invokeSinkAction(state, "init", retryCount, retryBackoffMs, sinkErrors, false, true, async () => {
|
|
@@ -2082,19 +1999,10 @@ class LoadStrikeRunner {
|
|
|
2082
1999
|
});
|
|
2083
2000
|
}
|
|
2084
2001
|
}
|
|
2085
|
-
async emitFinalStats(sinkStates, result, retryCount, retryBackoffMs, sinkErrors) {
|
|
2086
|
-
for (const state of sinkStates) {
|
|
2087
|
-
await this.invokeSinkAction(state, "final", retryCount, retryBackoffMs, sinkErrors, false, false, async () => {
|
|
2088
|
-
const saveFinalStats = resolveSinkSaveFinalStats(state.sink);
|
|
2089
|
-
if (saveFinalStats) {
|
|
2090
|
-
await saveFinalStats(result);
|
|
2091
|
-
}
|
|
2092
|
-
});
|
|
2093
|
-
}
|
|
2094
|
-
}
|
|
2095
2002
|
async stopSinks(sinkStates, retryCount, retryBackoffMs, sinkErrors) {
|
|
2003
|
+
const shutdownRetryCount = 0;
|
|
2096
2004
|
for (const state of sinkStates) {
|
|
2097
|
-
await this.invokeSinkAction(state, "stop",
|
|
2005
|
+
await this.invokeSinkAction(state, "stop", shutdownRetryCount, retryBackoffMs, sinkErrors, true, true, async () => {
|
|
2098
2006
|
const stop = resolveSinkStop(state.sink);
|
|
2099
2007
|
if (stop) {
|
|
2100
2008
|
await stop();
|
|
@@ -2102,11 +2010,9 @@ class LoadStrikeRunner {
|
|
|
2102
2010
|
});
|
|
2103
2011
|
const dispose = resolveSinkDispose(state.sink);
|
|
2104
2012
|
if (dispose) {
|
|
2105
|
-
|
|
2013
|
+
await this.invokeSinkAction(state, "dispose", shutdownRetryCount, retryBackoffMs, sinkErrors, true, true, async () => {
|
|
2106
2014
|
await dispose();
|
|
2107
|
-
}
|
|
2108
|
-
catch {
|
|
2109
|
-
}
|
|
2015
|
+
});
|
|
2110
2016
|
}
|
|
2111
2017
|
}
|
|
2112
2018
|
}
|
|
@@ -2150,20 +2056,56 @@ class LoadStrikeRunner {
|
|
|
2150
2056
|
}
|
|
2151
2057
|
}
|
|
2152
2058
|
}
|
|
2153
|
-
async invokeBeforeScenario(policies, scenarioName) {
|
|
2059
|
+
async invokeBeforeScenario(policies, scenarioName, policyErrors, mode) {
|
|
2154
2060
|
for (const policy of policies) {
|
|
2155
2061
|
if (policy.beforeScenario) {
|
|
2156
|
-
await policy.beforeScenario(scenarioName);
|
|
2062
|
+
await this.invokePolicyCallback(policy, "beforeScenario", scenarioName, "", policyErrors, mode, () => policy.beforeScenario(scenarioName));
|
|
2157
2063
|
}
|
|
2158
2064
|
}
|
|
2159
2065
|
}
|
|
2160
|
-
async invokeAfterScenario(policies, scenarioName, stats) {
|
|
2066
|
+
async invokeAfterScenario(policies, scenarioName, stats, policyErrors, mode) {
|
|
2161
2067
|
for (const policy of policies) {
|
|
2162
2068
|
if (policy.afterScenario) {
|
|
2163
|
-
await policy.afterScenario(scenarioName, stats);
|
|
2069
|
+
await this.invokePolicyCallback(policy, "afterScenario", scenarioName, "", policyErrors, mode, () => policy.afterScenario(scenarioName, stats));
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
async invokeBeforeStep(policies, scenarioName, stepName, policyErrors, mode) {
|
|
2074
|
+
for (const policy of policies) {
|
|
2075
|
+
if (policy.beforeStep) {
|
|
2076
|
+
await this.invokePolicyCallback(policy, "beforeStep", scenarioName, stepName, policyErrors, mode, () => policy.beforeStep(scenarioName, stepName));
|
|
2164
2077
|
}
|
|
2165
2078
|
}
|
|
2166
2079
|
}
|
|
2080
|
+
async invokeAfterStep(policies, scenarioName, stepName, reply, policyErrors, mode) {
|
|
2081
|
+
for (const policy of policies) {
|
|
2082
|
+
if (policy.afterStep) {
|
|
2083
|
+
await this.invokePolicyCallback(policy, "afterStep", scenarioName, stepName, policyErrors, mode, () => policy.afterStep(scenarioName, stepName, reply));
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
async invokePolicyCallback(policy, callbackName, scenarioName, stepName, policyErrors, mode, callback, continueFallback) {
|
|
2088
|
+
try {
|
|
2089
|
+
return await callback();
|
|
2090
|
+
}
|
|
2091
|
+
catch (error) {
|
|
2092
|
+
const entry = this.buildRuntimePolicyError(policy, callbackName, scenarioName, stepName, error);
|
|
2093
|
+
policyErrors.push(entry);
|
|
2094
|
+
if (mode === "fail") {
|
|
2095
|
+
throw new RuntimePolicyCallbackError(`Runtime policy '${entry.policyName}' failed during ${callbackName}: ${entry.message}`);
|
|
2096
|
+
}
|
|
2097
|
+
return continueFallback;
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
buildRuntimePolicyError(policy, callbackName, scenarioName, stepName, error) {
|
|
2101
|
+
return attachRuntimePolicyErrorAliases({
|
|
2102
|
+
policyName: resolveRuntimePolicyName(policy),
|
|
2103
|
+
callbackName,
|
|
2104
|
+
scenarioName,
|
|
2105
|
+
stepName,
|
|
2106
|
+
message: String(error ?? "runtime policy callback failed")
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2167
2109
|
writeReports(result) {
|
|
2168
2110
|
const reportsEnabled = this.options.reportsEnabled ?? true;
|
|
2169
2111
|
if (!reportsEnabled) {
|
|
@@ -2172,7 +2114,7 @@ class LoadStrikeRunner {
|
|
|
2172
2114
|
const reportFolder = (0, node_path_1.resolve)(this.options.reportFolderPath ?? "./reports");
|
|
2173
2115
|
const reportFile = sanitizeReportFileName(this.options.reportFileName?.trim()
|
|
2174
2116
|
? this.options.reportFileName
|
|
2175
|
-
: `${result.testInfo.testSuite}_${result.testInfo.testName}_${
|
|
2117
|
+
: `${result.testInfo.testSuite}_${result.testInfo.testName}_${resolveDefaultReportTimestamp(result)}`);
|
|
2176
2118
|
const reportFormats = normalizeReportFormats(this.options.reportFormats ?? ["html", "txt", "csv", "md"]);
|
|
2177
2119
|
(0, node_fs_1.mkdirSync)(reportFolder, { recursive: true });
|
|
2178
2120
|
const nodeStats = detailedToNodeStats(result);
|
|
@@ -2224,7 +2166,7 @@ function hasPluginRows(value) {
|
|
|
2224
2166
|
return value.tables.some((table) => Array.isArray(table.rows) && table.rows.length > 0);
|
|
2225
2167
|
}
|
|
2226
2168
|
async function executeScenarioRuntime(args) {
|
|
2227
|
-
const { scenario, scenarioIndex, scenarioCount, options, logger, nodeInfo, testInfo, policies, restartIterationMaxAttempts, allRegisteredMetrics, scenarioRuntimes, stepRuntimes, scenarioAccumulators, scenarioDurationsMs, stopTestState, testAbortController, executeScenarioInvocation, invokeBeforeScenario, invokeAfterScenario } = args;
|
|
2169
|
+
const { scenario, scenarioIndex, scenarioCount, options, logger, nodeInfo, testInfo, policies, restartIterationMaxAttempts, allRegisteredMetrics, scenarioRuntimes, stepRuntimes, scenarioAccumulators, scenarioDurationsMs, stopTestState, testAbortController, executeScenarioInvocation, invokeBeforeScenario, invokeAfterScenario, invokeBeforeStep, invokeAfterStep } = args;
|
|
2228
2170
|
const scenarioStartedMs = Date.now();
|
|
2229
2171
|
const scenarioContextData = {};
|
|
2230
2172
|
const registeredMetrics = [];
|
|
@@ -2337,20 +2279,8 @@ async function executeScenarioRuntime(args) {
|
|
|
2337
2279
|
},
|
|
2338
2280
|
shouldStopScenario: () => stopScenario || scenarioCancellationToken.aborted,
|
|
2339
2281
|
shouldStopTest: () => stopTestState.value || scenarioCancellationToken.aborted,
|
|
2340
|
-
invokeBeforeStep: async (stepName) =>
|
|
2341
|
-
|
|
2342
|
-
if (policy.beforeStep) {
|
|
2343
|
-
await policy.beforeStep(scenario.name, stepName);
|
|
2344
|
-
}
|
|
2345
|
-
}
|
|
2346
|
-
},
|
|
2347
|
-
invokeAfterStep: async (stepName, reply) => {
|
|
2348
|
-
for (const policy of policies) {
|
|
2349
|
-
if (policy.afterStep) {
|
|
2350
|
-
await policy.afterStep(scenario.name, stepName, reply);
|
|
2351
|
-
}
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2282
|
+
invokeBeforeStep: async (stepName) => invokeBeforeStep(policies, scenario.name, stepName),
|
|
2283
|
+
invokeAfterStep: async (stepName, reply) => invokeAfterStep(policies, scenario.name, stepName, reply)
|
|
2354
2284
|
};
|
|
2355
2285
|
attachScenarioContextAliases(context);
|
|
2356
2286
|
const startedAt = Date.now();
|
|
@@ -2589,6 +2519,10 @@ async function executeScenarioRuntime(args) {
|
|
|
2589
2519
|
await invokeAfterScenario(policies, scenario.name, runtime);
|
|
2590
2520
|
}
|
|
2591
2521
|
catch (error) {
|
|
2522
|
+
if (error instanceof RuntimePolicyCallbackError) {
|
|
2523
|
+
accumulator.setCurrentOperation("Error");
|
|
2524
|
+
throw error;
|
|
2525
|
+
}
|
|
2592
2526
|
if (scenarioCancellationToken.aborted || testAbortController.signal.aborted) {
|
|
2593
2527
|
accumulator.setCurrentOperation("Stop");
|
|
2594
2528
|
}
|
|
@@ -2986,6 +2920,16 @@ function normalizeSinkErrorValue(value) {
|
|
|
2986
2920
|
attempts: pickAliasNumber(source, "attempts", "Attempts")
|
|
2987
2921
|
};
|
|
2988
2922
|
}
|
|
2923
|
+
function normalizeRuntimePolicyErrorValue(value) {
|
|
2924
|
+
const source = asAliasRecord(value);
|
|
2925
|
+
return {
|
|
2926
|
+
policyName: pickAliasString(source, "policyName", "PolicyName"),
|
|
2927
|
+
callbackName: pickAliasString(source, "callbackName", "CallbackName"),
|
|
2928
|
+
scenarioName: pickAliasString(source, "scenarioName", "ScenarioName"),
|
|
2929
|
+
stepName: pickAliasString(source, "stepName", "StepName"),
|
|
2930
|
+
message: pickAliasString(source, "message", "Message")
|
|
2931
|
+
};
|
|
2932
|
+
}
|
|
2989
2933
|
function normalizeMetricValueProjection(value) {
|
|
2990
2934
|
const source = asAliasRecord(value);
|
|
2991
2935
|
const scenarioName = pickAliasString(source, "scenarioName", "ScenarioName");
|
|
@@ -3328,6 +3272,16 @@ function attachSinkErrorAliases(error) {
|
|
|
3328
3272
|
Attempts: "attempts"
|
|
3329
3273
|
});
|
|
3330
3274
|
}
|
|
3275
|
+
function attachRuntimePolicyErrorAliases(error) {
|
|
3276
|
+
const projected = normalizeRuntimePolicyErrorValue(error);
|
|
3277
|
+
return attachAliasMap(projected, {
|
|
3278
|
+
PolicyName: "policyName",
|
|
3279
|
+
CallbackName: "callbackName",
|
|
3280
|
+
ScenarioName: "scenarioName",
|
|
3281
|
+
StepName: "stepName",
|
|
3282
|
+
Message: "message"
|
|
3283
|
+
});
|
|
3284
|
+
}
|
|
3331
3285
|
function attachMetricStatsAliases(stats) {
|
|
3332
3286
|
const projected = normalizeMetricStatsValue(stats);
|
|
3333
3287
|
projected.counters = projected.counters.map((value) => attachCounterStatsAliases(value));
|
|
@@ -3486,7 +3440,8 @@ function attachNodeStatsAliases(stats) {
|
|
|
3486
3440
|
PluginsData: "pluginsData",
|
|
3487
3441
|
DisabledSinks: "disabledSinks",
|
|
3488
3442
|
SinkErrors: "sinkErrors",
|
|
3489
|
-
ReportFiles: "reportFiles"
|
|
3443
|
+
ReportFiles: "reportFiles",
|
|
3444
|
+
LogFiles: "logFiles"
|
|
3490
3445
|
});
|
|
3491
3446
|
defineAliasProperty(projected, "StartedUtc", () => parseAliasDate(stats.startedUtc));
|
|
3492
3447
|
defineAliasProperty(projected, "CompletedUtc", () => parseAliasDate(stats.completedUtc));
|
|
@@ -3497,17 +3452,30 @@ function attachRunResultAliases(result) {
|
|
|
3497
3452
|
const source = asAliasRecord(result);
|
|
3498
3453
|
const scenarioStats = pickAliasArray(source, "scenarioStats", "ScenarioStats")
|
|
3499
3454
|
.map((value, index) => attachScenarioStatsAliases(normalizeScenarioStatsValue(value, index)));
|
|
3455
|
+
const findScenarioStats = (scenarioName) => scenarioStats.find((value) => value.scenarioName === scenarioName);
|
|
3456
|
+
const getScenarioStats = (scenarioName) => {
|
|
3457
|
+
const value = findScenarioStats(scenarioName);
|
|
3458
|
+
if (!value) {
|
|
3459
|
+
throw new Error(`Scenario stats not found: ${scenarioName}`);
|
|
3460
|
+
}
|
|
3461
|
+
return value;
|
|
3462
|
+
};
|
|
3500
3463
|
const projected = {
|
|
3501
3464
|
startedUtc: pickAliasDateToken(source, "startedUtc", "StartedUtc"),
|
|
3502
3465
|
completedUtc: pickAliasDateToken(source, "completedUtc", "CompletedUtc"),
|
|
3466
|
+
allBytes: pickAliasNumber(source, "allBytes", "AllBytes"),
|
|
3503
3467
|
allRequestCount: pickAliasNumber(source, "allRequestCount", "AllRequestCount"),
|
|
3504
3468
|
allOkCount: pickAliasNumber(source, "allOkCount", "AllOkCount"),
|
|
3505
3469
|
allFailCount: pickAliasNumber(source, "allFailCount", "AllFailCount"),
|
|
3506
3470
|
failedThresholds: pickAliasNumber(source, "failedThresholds", "FailedThresholds"),
|
|
3471
|
+
durationMs: pickAliasNumber(source, "durationMs", "DurationMs", "Duration"),
|
|
3507
3472
|
nodeInfo: attachNodeInfoAliases(pickAliasValue(source, "nodeInfo", "NodeInfo")),
|
|
3508
3473
|
testInfo: attachTestInfoAliases(pickAliasValue(source, "testInfo", "TestInfo")),
|
|
3474
|
+
thresholds: pickAliasArray(source, "thresholds", "Thresholds")
|
|
3475
|
+
.map((value) => attachThresholdResultAliases(normalizeThresholdResultValue(value))),
|
|
3509
3476
|
thresholdResults: pickAliasArray(source, "thresholdResults", "ThresholdResults")
|
|
3510
3477
|
.map((value) => attachThresholdResultAliases(normalizeThresholdResultValue(value))),
|
|
3478
|
+
metricStats: attachMetricStatsAliases(normalizeMetricStatsValue(pickAliasValue(source, "metricStats", "MetricStats"), pickAliasNumber(source, "durationMs", "DurationMs", "Duration"))),
|
|
3511
3479
|
metrics: pickAliasArray(source, "metrics", "Metrics")
|
|
3512
3480
|
.map((value) => attachMetricValueAliases(normalizeMetricValueProjection(value))),
|
|
3513
3481
|
scenarioStats,
|
|
@@ -3517,37 +3485,53 @@ function attachRunResultAliases(result) {
|
|
|
3517
3485
|
: scenarioStats.flatMap((value) => value.stepStats)),
|
|
3518
3486
|
pluginsData: pickAliasArray(source, "pluginsData", "PluginsData")
|
|
3519
3487
|
.map((value) => normalizePluginData(pickAliasString(asAliasRecord(value), "pluginName", "PluginName"), value)),
|
|
3488
|
+
scenarioDurationsMs: pickAliasValue(source, "scenarioDurationsMs", "ScenarioDurationsMs") ?? {},
|
|
3520
3489
|
disabledSinks: normalizeAliasStringArray(pickAliasValue(source, "disabledSinks", "DisabledSinks")),
|
|
3521
3490
|
sinkErrors: pickAliasArray(source, "sinkErrors", "SinkErrors")
|
|
3522
3491
|
.map((value) => attachSinkErrorAliases(normalizeSinkErrorValue(value))),
|
|
3523
|
-
|
|
3492
|
+
policyErrors: pickAliasArray(source, "policyErrors", "PolicyErrors")
|
|
3493
|
+
.map((value) => attachRuntimePolicyErrorAliases(normalizeRuntimePolicyErrorValue(value))),
|
|
3494
|
+
reportFiles: normalizeAliasStringArray(pickAliasValue(source, "reportFiles", "ReportFiles")),
|
|
3495
|
+
logFiles: normalizeAliasStringArray(pickAliasValue(source, "logFiles", "LogFiles")),
|
|
3496
|
+
correlationRows: pickAliasArray(source, "correlationRows", "CorrelationRows")
|
|
3497
|
+
.map((value) => ({ ...asAliasRecord(value) })),
|
|
3498
|
+
failedCorrelationRows: pickAliasArray(source, "failedCorrelationRows", "FailedCorrelationRows")
|
|
3499
|
+
.map((value) => ({ ...asAliasRecord(value) })),
|
|
3500
|
+
findScenarioStats,
|
|
3501
|
+
getScenarioStats,
|
|
3502
|
+
FindScenarioStats: findScenarioStats,
|
|
3503
|
+
GetScenarioStats: getScenarioStats
|
|
3524
3504
|
};
|
|
3525
3505
|
attachAliasMap(projected, {
|
|
3506
|
+
AllBytes: "allBytes",
|
|
3526
3507
|
AllRequestCount: "allRequestCount",
|
|
3527
3508
|
AllOkCount: "allOkCount",
|
|
3528
3509
|
AllFailCount: "allFailCount",
|
|
3529
3510
|
FailedThresholds: "failedThresholds",
|
|
3511
|
+
DurationMs: "durationMs",
|
|
3530
3512
|
NodeInfo: "nodeInfo",
|
|
3531
3513
|
TestInfo: "testInfo",
|
|
3514
|
+
Thresholds: "thresholds",
|
|
3532
3515
|
ThresholdResults: "thresholdResults",
|
|
3516
|
+
MetricStats: "metricStats",
|
|
3533
3517
|
Metrics: "metrics",
|
|
3534
3518
|
ScenarioStats: "scenarioStats",
|
|
3535
3519
|
StepStats: "stepStats",
|
|
3520
|
+
ScenarioDurationsMs: "scenarioDurationsMs",
|
|
3536
3521
|
PluginsData: "pluginsData",
|
|
3537
3522
|
DisabledSinks: "disabledSinks",
|
|
3538
3523
|
SinkErrors: "sinkErrors",
|
|
3539
|
-
|
|
3524
|
+
PolicyErrors: "policyErrors",
|
|
3525
|
+
ReportFiles: "reportFiles",
|
|
3526
|
+
LogFiles: "logFiles",
|
|
3527
|
+
CorrelationRows: "correlationRows",
|
|
3528
|
+
FailedCorrelationRows: "failedCorrelationRows"
|
|
3540
3529
|
});
|
|
3541
3530
|
defineAliasProperty(projected, "StartedUtc", () => parseAliasDate(projected.startedUtc));
|
|
3542
3531
|
defineAliasProperty(projected, "CompletedUtc", () => parseAliasDate(projected.completedUtc));
|
|
3532
|
+
defineAliasProperty(projected, "Duration", () => projected.durationMs);
|
|
3543
3533
|
return projected;
|
|
3544
3534
|
}
|
|
3545
|
-
function requireDetailedReportFinalizerResult(result) {
|
|
3546
|
-
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
3547
|
-
throw new TypeError("Detailed report finalizer must return LoadStrikeRunResult.");
|
|
3548
|
-
}
|
|
3549
|
-
return result;
|
|
3550
|
-
}
|
|
3551
3535
|
function resolveResponseCustomLatency(customLatencyMs, usesOverloadDefaults) {
|
|
3552
3536
|
if (!Number.isFinite(customLatencyMs)) {
|
|
3553
3537
|
return usesOverloadDefaults ? -1 : 0;
|
|
@@ -4145,9 +4129,10 @@ function detailedToNodeStats(result, metricStats) {
|
|
|
4145
4129
|
scenarioStats,
|
|
4146
4130
|
stepStats,
|
|
4147
4131
|
pluginsData,
|
|
4148
|
-
disabledSinks: [...result.disabledSinks],
|
|
4149
|
-
sinkErrors: result.sinkErrors.map((sinkError) => ({ ...sinkError })),
|
|
4150
|
-
reportFiles: [...result.reportFiles],
|
|
4132
|
+
disabledSinks: [...(result.disabledSinks ?? [])],
|
|
4133
|
+
sinkErrors: (result.sinkErrors ?? []).map((sinkError) => ({ ...sinkError })),
|
|
4134
|
+
reportFiles: [...(result.reportFiles ?? [])],
|
|
4135
|
+
logFiles: [...(result.logFiles ?? [])],
|
|
4151
4136
|
findScenarioStats: (scenarioName) => scenarioStats.find((scenario) => scenario.scenarioName === scenarioName),
|
|
4152
4137
|
getScenarioStats: (scenarioName) => {
|
|
4153
4138
|
const value = scenarioStats.find((scenario) => scenario.scenarioName === scenarioName);
|
|
@@ -4214,6 +4199,7 @@ function buildEmptyNodeStats(args) {
|
|
|
4214
4199
|
disabledSinks: [],
|
|
4215
4200
|
sinkErrors: [],
|
|
4216
4201
|
reportFiles: [],
|
|
4202
|
+
logFiles: [],
|
|
4217
4203
|
findScenarioStats: (scenarioName) => undefined,
|
|
4218
4204
|
getScenarioStats: (scenarioName) => {
|
|
4219
4205
|
throw new Error(`Scenario stats not found: ${scenarioName}`);
|
|
@@ -4261,27 +4247,37 @@ function nodeStatsToClusterPayload(result) {
|
|
|
4261
4247
|
thresholds: result.thresholds,
|
|
4262
4248
|
pluginsData: result.pluginsData,
|
|
4263
4249
|
nodeInfo: result.nodeInfo,
|
|
4264
|
-
testInfo: result.testInfo
|
|
4250
|
+
testInfo: result.testInfo,
|
|
4251
|
+
logFiles: [...(result.logFiles ?? [])]
|
|
4265
4252
|
};
|
|
4266
4253
|
}
|
|
4267
|
-
function toDetailedRunResultFromNodeStats(result, startedUtc, sinkErrors) {
|
|
4254
|
+
function toDetailedRunResultFromNodeStats(result, startedUtc, sinkErrors, policyErrors = []) {
|
|
4268
4255
|
return attachRunResultAliases({
|
|
4269
4256
|
startedUtc,
|
|
4270
4257
|
completedUtc: result.completedUtc,
|
|
4258
|
+
allBytes: result.allBytes,
|
|
4271
4259
|
allRequestCount: result.allRequestCount,
|
|
4272
4260
|
allOkCount: result.allOkCount,
|
|
4273
4261
|
allFailCount: result.allFailCount,
|
|
4274
4262
|
failedThresholds: result.failedThresholds,
|
|
4263
|
+
durationMs: result.durationMs,
|
|
4275
4264
|
nodeInfo: result.nodeInfo,
|
|
4276
4265
|
testInfo: result.testInfo,
|
|
4266
|
+
thresholds: result.thresholds.map((value) => ({ ...value })),
|
|
4277
4267
|
thresholdResults: result.thresholds.map((value) => ({ ...value })),
|
|
4268
|
+
metricStats: result.metrics,
|
|
4278
4269
|
metrics: flattenMetricValues(result.metrics),
|
|
4279
4270
|
scenarioStats: result.scenarioStats,
|
|
4280
4271
|
stepStats: result.stepStats,
|
|
4272
|
+
scenarioDurationsMs: Object.fromEntries(result.scenarioStats.map((value) => [value.scenarioName, value.durationMs])),
|
|
4281
4273
|
pluginsData: result.pluginsData.map((value) => normalizePluginData(value.pluginName, value)),
|
|
4282
4274
|
disabledSinks: [...result.disabledSinks],
|
|
4283
4275
|
sinkErrors: [...sinkErrors, ...result.sinkErrors],
|
|
4284
|
-
|
|
4276
|
+
policyErrors: policyErrors.map((value) => attachRuntimePolicyErrorAliases({ ...value })),
|
|
4277
|
+
reportFiles: [...result.reportFiles],
|
|
4278
|
+
logFiles: [...(result.logFiles ?? [])],
|
|
4279
|
+
correlationRows: buildDetailedCorrelationRows(),
|
|
4280
|
+
failedCorrelationRows: buildDetailedFailedCorrelationRows()
|
|
4285
4281
|
});
|
|
4286
4282
|
}
|
|
4287
4283
|
function flattenMetricValues(metricStats) {
|
|
@@ -4374,6 +4370,7 @@ function clusterNodeResultToNodeStats(result, testInfo, fallbackNodeInfo) {
|
|
|
4374
4370
|
disabledSinks: [],
|
|
4375
4371
|
sinkErrors: [],
|
|
4376
4372
|
reportFiles: [],
|
|
4373
|
+
logFiles: normalizeAliasStringArray(result.stats.logFiles),
|
|
4377
4374
|
findScenarioStats: (scenarioName) => scenarioStats.find((value) => value.scenarioName === scenarioName),
|
|
4378
4375
|
getScenarioStats: (scenarioName) => {
|
|
4379
4376
|
const value = scenarioStats.find((scenario) => scenario.scenarioName === scenarioName);
|
|
@@ -4420,6 +4417,7 @@ function aggregateNodeStats(testInfo, coordinatorNodeInfo, nodes) {
|
|
|
4420
4417
|
disabledSinks: [],
|
|
4421
4418
|
sinkErrors: [],
|
|
4422
4419
|
reportFiles: [],
|
|
4420
|
+
logFiles: mergeStringArrays(...nodes.map((value) => value.logFiles ?? [])),
|
|
4423
4421
|
findScenarioStats: (scenarioName) => scenarioStats.find((value) => value.scenarioName === scenarioName),
|
|
4424
4422
|
getScenarioStats: (scenarioName) => {
|
|
4425
4423
|
const value = scenarioStats.find((scenario) => scenario.scenarioName === scenarioName);
|
|
@@ -4812,6 +4810,8 @@ class ManagedScenarioTrackingRuntime {
|
|
|
4812
4810
|
this.timeoutSweepIntervalMs = Math.trunc(pickTrackingNumber(tracking, ["TimeoutSweepIntervalMs", "timeoutSweepIntervalMs"], pickTrackingNumber(tracking, ["TimeoutSweepIntervalSeconds", "timeoutSweepIntervalSeconds"], 1) * 1000));
|
|
4813
4811
|
this.timeoutBatchSize = Math.trunc(pickTrackingNumber(tracking, ["TimeoutBatchSize", "timeoutBatchSize"], 200));
|
|
4814
4812
|
this.timeoutCountsAsFailure = pickTrackingBoolean(tracking, "TimeoutCountsAsFailure", "timeoutCountsAsFailure", true);
|
|
4813
|
+
const trackingFieldValueCaseSensitive = pickTrackingBoolean(tracking, "TrackingFieldValueCaseSensitive", "trackingFieldValueCaseSensitive", true);
|
|
4814
|
+
const gatherByFieldValueCaseSensitive = pickTrackingBoolean(tracking, "GatherByFieldValueCaseSensitive", "gatherByFieldValueCaseSensitive", true);
|
|
4815
4815
|
this.sourceAdapter = transports_js_1.EndpointAdapterFactory.create(this.sourceEndpoint);
|
|
4816
4816
|
this.destinationAdapter = this.destinationEndpoint ? transports_js_1.EndpointAdapterFactory.create(this.destinationEndpoint) : null;
|
|
4817
4817
|
this.correlationStore = mapRuntimeCorrelationStore(tracking, runNamespace);
|
|
@@ -4819,18 +4819,20 @@ class ManagedScenarioTrackingRuntime {
|
|
|
4819
4819
|
sourceTrackingField: this.sourceEndpoint.trackingField,
|
|
4820
4820
|
destinationTrackingField: this.destinationEndpoint?.trackingField,
|
|
4821
4821
|
destinationGatherByField: this.destinationEndpoint?.gatherByField,
|
|
4822
|
+
trackingFieldValueCaseSensitive,
|
|
4823
|
+
gatherByFieldValueCaseSensitive,
|
|
4822
4824
|
correlationTimeoutMs: this.correlationTimeoutMs,
|
|
4823
4825
|
timeoutCountsAsFailure: this.timeoutCountsAsFailure,
|
|
4824
4826
|
store: this.correlationStore ?? undefined,
|
|
4825
4827
|
plugins: [
|
|
4826
4828
|
{
|
|
4827
|
-
onMatched: async (trackingId, _source, destination, latencyMs) => {
|
|
4829
|
+
onMatched: async (trackingId, _source, destination, latencyMs, gatherByValue) => {
|
|
4828
4830
|
const eventId = this.shiftPendingValue(this.pendingEventIds, trackingId);
|
|
4829
4831
|
const sourceTimestampUtc = this.shiftPendingValue(this.pendingSourceTimestamps, trackingId);
|
|
4830
4832
|
const destinationTimestampUtc = new Date().toISOString();
|
|
4831
|
-
const
|
|
4833
|
+
const observedGatherByValue = gatherByValue ?? (this.destinationEndpoint?.gatherByField
|
|
4832
4834
|
? readRuntimeTrackingId(destination, this.destinationEndpoint.gatherByField)
|
|
4833
|
-
: undefined;
|
|
4835
|
+
: undefined);
|
|
4834
4836
|
addCorrelationRow({
|
|
4835
4837
|
occurredUtc: new Date().toISOString(),
|
|
4836
4838
|
scenarioName: this.scenarioName,
|
|
@@ -4846,7 +4848,7 @@ class ManagedScenarioTrackingRuntime {
|
|
|
4846
4848
|
isSuccess: true,
|
|
4847
4849
|
isFailure: false,
|
|
4848
4850
|
gatherByField: this.gatherByFieldExpression,
|
|
4849
|
-
gatherByValue:
|
|
4851
|
+
gatherByValue: observedGatherByValue ?? undefined
|
|
4850
4852
|
});
|
|
4851
4853
|
this.resolveTrackingWaiter(trackingId, {
|
|
4852
4854
|
status: "matched",
|
|
@@ -5617,10 +5619,10 @@ function normalizeRuntimeObservedTrackingPayload(payload, endpoint) {
|
|
|
5617
5619
|
function readRuntimeTrackingId(payload, selector) {
|
|
5618
5620
|
const normalized = selector.trim().toLowerCase();
|
|
5619
5621
|
if (normalized.startsWith("header:")) {
|
|
5620
|
-
const headerName = selector.slice("header:".length).trim()
|
|
5622
|
+
const headerName = selector.slice("header:".length).trim();
|
|
5621
5623
|
for (const [key, value] of Object.entries(payload.headers ?? {})) {
|
|
5622
5624
|
const resolved = String(value ?? "").trim();
|
|
5623
|
-
if (key
|
|
5625
|
+
if (key === headerName && resolved) {
|
|
5624
5626
|
return resolved;
|
|
5625
5627
|
}
|
|
5626
5628
|
}
|
|
@@ -5896,6 +5898,49 @@ function createLogger(loggerConfig, minimumLogLevel) {
|
|
|
5896
5898
|
};
|
|
5897
5899
|
}
|
|
5898
5900
|
}
|
|
5901
|
+
return wrapLoggerWithMinimumLevel(baseLogger, minimumLogLevel);
|
|
5902
|
+
}
|
|
5903
|
+
function createLoggerSetup(loggerConfig, minimumLogLevel, options, testInfo, nodeInfo) {
|
|
5904
|
+
if (typeof loggerConfig === "function") {
|
|
5905
|
+
return {
|
|
5906
|
+
logger: createLogger(loggerConfig, minimumLogLevel),
|
|
5907
|
+
logFiles: []
|
|
5908
|
+
};
|
|
5909
|
+
}
|
|
5910
|
+
const logFilePath = resolveDefaultLogFilePath(options, testInfo, nodeInfo);
|
|
5911
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.resolve)(options.reportFolderPath ?? "./reports"), { recursive: true });
|
|
5912
|
+
(0, node_fs_1.writeFileSync)(logFilePath, "", "utf8");
|
|
5913
|
+
return {
|
|
5914
|
+
logger: wrapLoggerWithMinimumLevel(createDefaultLogger(logFilePath), minimumLogLevel),
|
|
5915
|
+
logFiles: [logFilePath]
|
|
5916
|
+
};
|
|
5917
|
+
}
|
|
5918
|
+
function createDefaultLogger(logFilePath) {
|
|
5919
|
+
const write = (level, message) => {
|
|
5920
|
+
const line = formatDefaultLoggerLine(level, message);
|
|
5921
|
+
const output = `${line}\n`;
|
|
5922
|
+
if (level === "debug") {
|
|
5923
|
+
console.debug(line);
|
|
5924
|
+
}
|
|
5925
|
+
else if (level === "info") {
|
|
5926
|
+
console.info(line);
|
|
5927
|
+
}
|
|
5928
|
+
else if (level === "warn") {
|
|
5929
|
+
console.warn(line);
|
|
5930
|
+
}
|
|
5931
|
+
else {
|
|
5932
|
+
console.error(line);
|
|
5933
|
+
}
|
|
5934
|
+
(0, node_fs_1.appendFileSync)(logFilePath, output, "utf8");
|
|
5935
|
+
};
|
|
5936
|
+
return {
|
|
5937
|
+
debug: (message) => write("debug", message),
|
|
5938
|
+
info: (message) => write("info", message),
|
|
5939
|
+
warn: (message) => write("warn", message),
|
|
5940
|
+
error: (message) => write("error", message)
|
|
5941
|
+
};
|
|
5942
|
+
}
|
|
5943
|
+
function wrapLoggerWithMinimumLevel(baseLogger, minimumLogLevel) {
|
|
5899
5944
|
const threshold = logLevelOrder(minimumLogLevel);
|
|
5900
5945
|
return {
|
|
5901
5946
|
debug: (message) => {
|
|
@@ -5920,6 +5965,16 @@ function createLogger(loggerConfig, minimumLogLevel) {
|
|
|
5920
5965
|
}
|
|
5921
5966
|
};
|
|
5922
5967
|
}
|
|
5968
|
+
function formatDefaultLoggerLine(level, message) {
|
|
5969
|
+
const code = level === "debug"
|
|
5970
|
+
? "DBG"
|
|
5971
|
+
: level === "info"
|
|
5972
|
+
? "INF"
|
|
5973
|
+
: level === "warn"
|
|
5974
|
+
? "WRN"
|
|
5975
|
+
: "ERR";
|
|
5976
|
+
return `${new Date().toISOString()} [${code}] ${message}`;
|
|
5977
|
+
}
|
|
5923
5978
|
function logLevelOrder(level) {
|
|
5924
5979
|
const normalized = String(level ?? "").trim().toLowerCase();
|
|
5925
5980
|
if (!normalized) {
|
|
@@ -5951,11 +6006,41 @@ function formatUtcReportTimestamp(value) {
|
|
|
5951
6006
|
const second = String(value.getUTCSeconds()).padStart(2, "0");
|
|
5952
6007
|
return `${year}${month}${day}_${hour}${minute}${second}`;
|
|
5953
6008
|
}
|
|
6009
|
+
function resolveDefaultReportTimestamp(result) {
|
|
6010
|
+
const createdUtc = result.testInfo?.createdUtc ?? result.startedUtc;
|
|
6011
|
+
const parsed = createdUtc ? new Date(createdUtc) : undefined;
|
|
6012
|
+
return parsed && !Number.isNaN(parsed.getTime())
|
|
6013
|
+
? formatUtcReportTimestamp(parsed)
|
|
6014
|
+
: formatUtcReportTimestamp(new Date());
|
|
6015
|
+
}
|
|
6016
|
+
function resolveDefaultLogFilePath(options, testInfo, nodeInfo) {
|
|
6017
|
+
const reportFolder = (0, node_path_1.resolve)(options.reportFolderPath ?? "./reports");
|
|
6018
|
+
const parsedCreatedUtc = testInfo.createdUtc ? new Date(testInfo.createdUtc) : undefined;
|
|
6019
|
+
const timestamp = parsedCreatedUtc && !Number.isNaN(parsedCreatedUtc.getTime())
|
|
6020
|
+
? formatUtcReportTimestamp(parsedCreatedUtc)
|
|
6021
|
+
: formatUtcReportTimestamp(new Date());
|
|
6022
|
+
const suffixParts = [];
|
|
6023
|
+
if (nodeInfo.nodeType !== "SingleNode") {
|
|
6024
|
+
suffixParts.push(nodeInfo.nodeType === "Coordinator" ? "coordinator" : "agent");
|
|
6025
|
+
}
|
|
6026
|
+
const localMachineName = node_os_1.default.hostname().trim().toLowerCase();
|
|
6027
|
+
if (nodeInfo.machineName.trim() &&
|
|
6028
|
+
(nodeInfo.nodeType !== "SingleNode" || nodeInfo.machineName.trim().toLowerCase() !== localMachineName)) {
|
|
6029
|
+
suffixParts.push(nodeInfo.machineName);
|
|
6030
|
+
}
|
|
6031
|
+
const suffix = suffixParts.length
|
|
6032
|
+
? `-${suffixParts.map((value) => sanitizeReportFileName(value)).join("-")}`
|
|
6033
|
+
: "";
|
|
6034
|
+
return (0, node_path_1.resolve)(reportFolder, sanitizeReportFileName(`loadstrike-log-${timestamp}${suffix}.txt`));
|
|
6035
|
+
}
|
|
5954
6036
|
function sanitizeReportFileName(value) {
|
|
5955
6037
|
const normalized = String(value ?? "");
|
|
5956
6038
|
return normalized
|
|
5957
6039
|
.replace(/[<>:"/\\|?*\u0000-\u001F]/g, "_") || "loadstrike-run";
|
|
5958
6040
|
}
|
|
6041
|
+
function mergeStringArrays(...values) {
|
|
6042
|
+
return Array.from(new Set(values.flatMap((value) => (value ?? []).filter((entry) => entry.trim().length > 0))));
|
|
6043
|
+
}
|
|
5959
6044
|
function loadJsonObject(path) {
|
|
5960
6045
|
try {
|
|
5961
6046
|
const raw = (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(path), "utf8");
|
|
@@ -6192,6 +6277,16 @@ function normalizeRunnerOptionCollectionShapes(options) {
|
|
|
6192
6277
|
validateNamedWorkerPlugins(normalized.workerPlugins ?? []);
|
|
6193
6278
|
return normalized;
|
|
6194
6279
|
}
|
|
6280
|
+
function normalizedRuntimePolicyErrorMode(value) {
|
|
6281
|
+
const normalized = String(value ?? "fail").trim().toLowerCase();
|
|
6282
|
+
if (normalized === "fail") {
|
|
6283
|
+
return "fail";
|
|
6284
|
+
}
|
|
6285
|
+
if (normalized === "continue") {
|
|
6286
|
+
return "continue";
|
|
6287
|
+
}
|
|
6288
|
+
throw new Error("Runtime policy error mode must be either Fail or Continue.");
|
|
6289
|
+
}
|
|
6195
6290
|
function extractContextOverridesFromConfig(config) {
|
|
6196
6291
|
const rootConfig = asRecord(config);
|
|
6197
6292
|
const loadStrikeSection = asRecord(tryReadConfigValue(rootConfig, "LoadStrike"));
|
|
@@ -6245,6 +6340,7 @@ function extractContextOverridesFromConfig(config) {
|
|
|
6245
6340
|
setString("AgentGroup", "AgentGroup", "LoadStrike:AgentGroup");
|
|
6246
6341
|
setString("NatsServerUrl", "NatsServerUrl", "LoadStrike:NatsServerUrl");
|
|
6247
6342
|
setString("RunnerKey", "RunnerKey", "LoadStrike:RunnerKey");
|
|
6343
|
+
setString("RuntimePolicyErrorMode", "RuntimePolicyErrorMode", "LoadStrike:RuntimePolicyErrorMode");
|
|
6248
6344
|
const nodeType = pick("NodeType", "LoadStrike:NodeType");
|
|
6249
6345
|
if (nodeType != null) {
|
|
6250
6346
|
const parsed = tryParseNodeTypeToken(nodeType);
|
|
@@ -6259,7 +6355,6 @@ function extractContextOverridesFromConfig(config) {
|
|
|
6259
6355
|
patch.MinimumLogLevel = parsed;
|
|
6260
6356
|
}
|
|
6261
6357
|
}
|
|
6262
|
-
setString("LicenseValidationServerUrl", "LicenseValidationServerUrl", "LoadStrike:LicenseValidation:ServerUrl", "LicenseValidation:ServerUrl");
|
|
6263
6358
|
const agentsCount = toInt(pick("AgentsCount", "LoadStrike:AgentsCount"));
|
|
6264
6359
|
if (agentsCount > 0) {
|
|
6265
6360
|
patch.AgentsCount = agentsCount;
|
|
@@ -6394,7 +6489,6 @@ function toRunContext(options) {
|
|
|
6394
6489
|
CoordinatorTargetScenarios: normalized.coordinatorTargetScenarios,
|
|
6395
6490
|
NatsServerUrl: normalized.natsServerUrl,
|
|
6396
6491
|
RunnerKey: normalized.runnerKey,
|
|
6397
|
-
LicenseValidationServerUrl: normalized.licenseValidationServerUrl,
|
|
6398
6492
|
LicenseValidationTimeoutSeconds: normalized.licenseValidationTimeoutSeconds,
|
|
6399
6493
|
ConfigPath: normalized.configPath,
|
|
6400
6494
|
InfraConfigPath: normalized.infraConfigPath,
|
|
@@ -6407,8 +6501,6 @@ function toRunContext(options) {
|
|
|
6407
6501
|
ReportFileName: normalized.reportFileName,
|
|
6408
6502
|
ReportFolderPath: normalized.reportFolderPath,
|
|
6409
6503
|
ReportFormats: normalized.reportFormats,
|
|
6410
|
-
ReportFinalizer: normalized.reportFinalizer,
|
|
6411
|
-
DetailedReportFinalizer: normalized.detailedReportFinalizer,
|
|
6412
6504
|
ReportingIntervalSeconds: normalized.reportingIntervalSeconds,
|
|
6413
6505
|
MinimumLogLevel: normalized.minimumLogLevel,
|
|
6414
6506
|
LoggerConfig: normalized.loggerConfig,
|
|
@@ -6416,6 +6508,7 @@ function toRunContext(options) {
|
|
|
6416
6508
|
SinkRetryCount: normalized.sinkRetryCount,
|
|
6417
6509
|
SinkRetryBackoffMs: normalized.sinkRetryBackoffMs,
|
|
6418
6510
|
RuntimePolicies: normalized.runtimePolicies,
|
|
6511
|
+
RuntimePolicyErrorMode: normalized.runtimePolicyErrorMode,
|
|
6419
6512
|
ScenarioCompletionTimeoutSeconds: normalized.scenarioCompletionTimeoutSeconds,
|
|
6420
6513
|
ClusterCommandTimeoutSeconds: normalized.clusterCommandTimeoutSeconds,
|
|
6421
6514
|
RestartIterationMaxAttempts: normalized.restartIterationMaxAttempts,
|
|
@@ -6424,6 +6517,26 @@ function toRunContext(options) {
|
|
|
6424
6517
|
GlobalCustomSettings: normalized.globalCustomSettings
|
|
6425
6518
|
};
|
|
6426
6519
|
}
|
|
6520
|
+
class RuntimePolicyCallbackError extends Error {
|
|
6521
|
+
constructor(message) {
|
|
6522
|
+
super(message);
|
|
6523
|
+
this.name = "RuntimePolicyCallbackError";
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
function resolveRuntimePolicyName(policy) {
|
|
6527
|
+
const namedPolicy = policy.policyName ?? policy.PolicyName;
|
|
6528
|
+
if (typeof namedPolicy === "string" && namedPolicy.trim()) {
|
|
6529
|
+
return namedPolicy;
|
|
6530
|
+
}
|
|
6531
|
+
const constructorValue = Reflect.get(policy, "constructor");
|
|
6532
|
+
const constructorName = constructorValue && typeof constructorValue === "object"
|
|
6533
|
+
? Reflect.get(constructorValue, "name")
|
|
6534
|
+
: typeof constructorValue === "function"
|
|
6535
|
+
? Reflect.get(constructorValue, "name")
|
|
6536
|
+
: "";
|
|
6537
|
+
const fallback = typeof constructorName === "string" ? constructorName.trim() : "";
|
|
6538
|
+
return fallback || "runtime-policy";
|
|
6539
|
+
}
|
|
6427
6540
|
function buildLicenseValidationPayload(options, scenarios) {
|
|
6428
6541
|
const context = {
|
|
6429
6542
|
...toRunContext(options)
|
|
@@ -6437,8 +6550,7 @@ function buildLicenseValidationPayload(options, scenarios) {
|
|
|
6437
6550
|
Weight: scenario.getWeight(),
|
|
6438
6551
|
LoadSimulations: [...scenario.getSimulations()],
|
|
6439
6552
|
Thresholds: [...scenario.getThresholds()],
|
|
6440
|
-
Tracking: scenario.getTrackingConfiguration() ?? {}
|
|
6441
|
-
LicenseFeatures: scenario.getLicenseFeatures()
|
|
6553
|
+
Tracking: scenario.getTrackingConfiguration() ?? {}
|
|
6442
6554
|
}));
|
|
6443
6555
|
return {
|
|
6444
6556
|
Context: context,
|
|
@@ -6487,7 +6599,6 @@ function looksLikeRunContext(value) {
|
|
|
6487
6599
|
"ClusterId",
|
|
6488
6600
|
"CoordinatorTargetScenarios",
|
|
6489
6601
|
"RunnerKey",
|
|
6490
|
-
"LicenseValidationServerUrl",
|
|
6491
6602
|
"LicenseValidationTimeoutSeconds",
|
|
6492
6603
|
"MinimumLogLevel",
|
|
6493
6604
|
"LoggerConfig",
|
|
@@ -6497,8 +6608,6 @@ function looksLikeRunContext(value) {
|
|
|
6497
6608
|
"ReportFileName",
|
|
6498
6609
|
"ReportFolderPath",
|
|
6499
6610
|
"ReportFormats",
|
|
6500
|
-
"ReportFinalizer",
|
|
6501
|
-
"DetailedReportFinalizer",
|
|
6502
6611
|
"ReportingIntervalSeconds",
|
|
6503
6612
|
"ReportingSinks",
|
|
6504
6613
|
"SinkRetryCount",
|
|
@@ -6635,12 +6744,6 @@ function resolveSinkSaveRealtimeMetrics(sink) {
|
|
|
6635
6744
|
? method.bind(sink)
|
|
6636
6745
|
: undefined;
|
|
6637
6746
|
}
|
|
6638
|
-
function resolveSinkSaveFinalStats(sink) {
|
|
6639
|
-
const method = sink.saveFinalStats ?? sink.SaveFinalStats;
|
|
6640
|
-
return typeof method === "function"
|
|
6641
|
-
? method.bind(sink)
|
|
6642
|
-
: undefined;
|
|
6643
|
-
}
|
|
6644
6747
|
function resolveSinkSaveRunResult(sink) {
|
|
6645
6748
|
const method = sink.saveRunResult ?? sink.SaveRunResult;
|
|
6646
6749
|
return typeof method === "function"
|
|
@@ -6679,6 +6782,46 @@ function addCorrelationRow(row) {
|
|
|
6679
6782
|
correlationRows.splice(0, correlationRows.length - MAX_CORRELATION_ROWS);
|
|
6680
6783
|
}
|
|
6681
6784
|
}
|
|
6785
|
+
function buildDetailedFailedCorrelationRows() {
|
|
6786
|
+
return [...failedResponseRows]
|
|
6787
|
+
.reverse()
|
|
6788
|
+
.map((row) => ({
|
|
6789
|
+
OccurredUtc: row.occurredUtc,
|
|
6790
|
+
Scenario: row.scenarioName,
|
|
6791
|
+
Source: row.sourceEndpoint,
|
|
6792
|
+
Destination: row.destinationEndpoint,
|
|
6793
|
+
RunMode: row.runMode,
|
|
6794
|
+
StatusCode: row.statusCode,
|
|
6795
|
+
TrackingId: row.trackingId ?? "",
|
|
6796
|
+
EventId: row.eventId ?? "",
|
|
6797
|
+
SourceTimestampUtc: row.sourceTimestampUtc ?? "",
|
|
6798
|
+
DestinationTimestampUtc: row.destinationTimestampUtc ?? "",
|
|
6799
|
+
LatencyMs: formatOptionalLatency(row.latencyMs),
|
|
6800
|
+
Message: row.message ?? ""
|
|
6801
|
+
}));
|
|
6802
|
+
}
|
|
6803
|
+
function buildDetailedCorrelationRows() {
|
|
6804
|
+
return [...correlationRows]
|
|
6805
|
+
.reverse()
|
|
6806
|
+
.map((row) => ({
|
|
6807
|
+
OccurredUtc: row.occurredUtc,
|
|
6808
|
+
Scenario: row.scenarioName,
|
|
6809
|
+
Source: row.sourceEndpoint,
|
|
6810
|
+
Destination: row.destinationEndpoint,
|
|
6811
|
+
RunMode: row.runMode,
|
|
6812
|
+
StatusCode: row.statusCode,
|
|
6813
|
+
IsSuccess: row.isSuccess,
|
|
6814
|
+
IsFailure: row.isFailure,
|
|
6815
|
+
GatherByField: row.gatherByField ?? "",
|
|
6816
|
+
GatherByValue: row.gatherByValue ?? "",
|
|
6817
|
+
TrackingId: row.trackingId ?? "",
|
|
6818
|
+
EventId: row.eventId ?? "",
|
|
6819
|
+
SourceTimestampUtc: row.sourceTimestampUtc ?? "",
|
|
6820
|
+
DestinationTimestampUtc: row.destinationTimestampUtc ?? "",
|
|
6821
|
+
LatencyMs: formatOptionalLatency(row.latencyMs),
|
|
6822
|
+
Message: row.message ?? ""
|
|
6823
|
+
}));
|
|
6824
|
+
}
|
|
6682
6825
|
function resolveWorkerPlugins(customPlugins) {
|
|
6683
6826
|
const registry = new Map();
|
|
6684
6827
|
for (const plugin of createBuiltInWorkerPlugins()) {
|
|
@@ -7011,3 +7154,122 @@ function readConfiguredSinkName(sink) {
|
|
|
7011
7154
|
async function sleep(ms) {
|
|
7012
7155
|
await new Promise((resolve) => setTimeout(resolve, Math.max(ms, 0)));
|
|
7013
7156
|
}
|
|
7157
|
+
exports.__loadstrikeTestExports = {
|
|
7158
|
+
LoadStrikeContext,
|
|
7159
|
+
LoadStrikeCounter,
|
|
7160
|
+
LoadStrikeGauge,
|
|
7161
|
+
LoadStrikeMetric,
|
|
7162
|
+
LoadStrikeResponse,
|
|
7163
|
+
LoadStrikeRunner,
|
|
7164
|
+
LoadStrikeScenario,
|
|
7165
|
+
LoadStrikeSimulation,
|
|
7166
|
+
LoadStrikeStep,
|
|
7167
|
+
ManagedScenarioTrackingRuntime,
|
|
7168
|
+
ScenarioStatsAccumulator,
|
|
7169
|
+
StepStatsAccumulator,
|
|
7170
|
+
TrackingFieldSelector: correlation_js_1.TrackingFieldSelector,
|
|
7171
|
+
addCorrelationRow,
|
|
7172
|
+
addFailedResponseRow,
|
|
7173
|
+
aggregateNodeStats,
|
|
7174
|
+
asRecord,
|
|
7175
|
+
assertNoDisableLicenseEnforcementOption,
|
|
7176
|
+
buildEmptyNodeStats,
|
|
7177
|
+
buildGroupedCorrelationRows,
|
|
7178
|
+
buildMeasurementPlaceholder,
|
|
7179
|
+
buildRichHtmlReport,
|
|
7180
|
+
buildThresholdCheckExpression,
|
|
7181
|
+
buildTrackingLeaseKey,
|
|
7182
|
+
buildTrackingRunNamespace,
|
|
7183
|
+
clusterNodeResultToNodeStats,
|
|
7184
|
+
combineAbortSignals,
|
|
7185
|
+
computeScenarioRequestCount,
|
|
7186
|
+
computeWarmUpIterations,
|
|
7187
|
+
createBuiltInWorkerPlugins,
|
|
7188
|
+
createLogger,
|
|
7189
|
+
createRuntimeRandom,
|
|
7190
|
+
delayWithAbort,
|
|
7191
|
+
detailedToNodeStats,
|
|
7192
|
+
evaluateThresholdsForScenarios,
|
|
7193
|
+
executeTrackedScenarioInvocation,
|
|
7194
|
+
extractContextOverridesFromArgs,
|
|
7195
|
+
extractContextOverridesFromConfig,
|
|
7196
|
+
findCaseInsensitiveKey,
|
|
7197
|
+
formatOptionalLatency,
|
|
7198
|
+
formatUtcReportTimestamp,
|
|
7199
|
+
hasPluginRows,
|
|
7200
|
+
inferRuntimeLegacyHttpResponseSource,
|
|
7201
|
+
isComparisonFailed,
|
|
7202
|
+
loadJsonObject,
|
|
7203
|
+
logLevelOrder,
|
|
7204
|
+
looksLikeRunContext,
|
|
7205
|
+
mapRuntimeCorrelationStore,
|
|
7206
|
+
mapRuntimeTrackingEndpointSpec,
|
|
7207
|
+
mergeDefinedRecord,
|
|
7208
|
+
normalizeCliOverrideKey,
|
|
7209
|
+
normalizeMetricValue,
|
|
7210
|
+
normalizeOptionalReportFormats,
|
|
7211
|
+
normalizeOptionalStringArray,
|
|
7212
|
+
normalizeReplyArguments,
|
|
7213
|
+
normalizePluginData,
|
|
7214
|
+
normalizeReportFormats,
|
|
7215
|
+
normalizeRunArgsInput,
|
|
7216
|
+
normalizeRunContextCollectionShapes,
|
|
7217
|
+
normalizeRunnerOptionCollectionShapes,
|
|
7218
|
+
normalizeRuntimeHttpTrackingPayloadSource,
|
|
7219
|
+
normalizeRuntimeTrackingPayload,
|
|
7220
|
+
normalizeStringArray,
|
|
7221
|
+
normalizeFailureArguments,
|
|
7222
|
+
normalizeTrackingRunMode,
|
|
7223
|
+
normalizeThresholdScope,
|
|
7224
|
+
parseAliasDate,
|
|
7225
|
+
parseMinimumLogLevelToken,
|
|
7226
|
+
parseNodeTypeToken,
|
|
7227
|
+
parseStrictBooleanToken,
|
|
7228
|
+
percentile,
|
|
7229
|
+
pickOptionalTrackingSelectorString,
|
|
7230
|
+
pickTrackingNumber,
|
|
7231
|
+
produceOrConsumeTrackingPayload,
|
|
7232
|
+
readConfiguredSinkName,
|
|
7233
|
+
readRuntimeRedisCorrelationStoreOptions,
|
|
7234
|
+
readRuntimeTrackingId,
|
|
7235
|
+
recordPluginLifecycleError,
|
|
7236
|
+
requireNonEmpty,
|
|
7237
|
+
attachScenarioStatsAliases,
|
|
7238
|
+
attachNodeStatsAliases,
|
|
7239
|
+
attachRunResultAliases,
|
|
7240
|
+
resolveClusterExecutionMode,
|
|
7241
|
+
resolveThresholdActualValue,
|
|
7242
|
+
resetCrossPlatformReportRegistries,
|
|
7243
|
+
resolveSinkDispose,
|
|
7244
|
+
resolveSinkInit,
|
|
7245
|
+
resolveSinkName,
|
|
7246
|
+
resolveSinkSaveRealtimeMetrics,
|
|
7247
|
+
resolveSinkSaveRealtimeStats,
|
|
7248
|
+
resolveSinkSaveRunResult,
|
|
7249
|
+
resolveSinkStart,
|
|
7250
|
+
resolveSinkStop,
|
|
7251
|
+
resolveWorkerPlugins,
|
|
7252
|
+
runtimeParseBodyAsObject,
|
|
7253
|
+
sanitizeReportFileName,
|
|
7254
|
+
sanitizeTrackingNamespacePart,
|
|
7255
|
+
setRuntimeJsonPathValue,
|
|
7256
|
+
stripCaseInsensitivePrefix,
|
|
7257
|
+
toBoolean,
|
|
7258
|
+
toDetailedRunResultFromNodeStats,
|
|
7259
|
+
toInt,
|
|
7260
|
+
toNullableInt,
|
|
7261
|
+
toNumber,
|
|
7262
|
+
toRunContext,
|
|
7263
|
+
toTrackingStringMap,
|
|
7264
|
+
tryParseDotnetDurationMs,
|
|
7265
|
+
tryParseMinimumLogLevelToken,
|
|
7266
|
+
tryParseNodeTypeToken,
|
|
7267
|
+
tryReadConfigValue,
|
|
7268
|
+
validateNamedReportingSinks,
|
|
7269
|
+
validateRegisteredScenarios,
|
|
7270
|
+
validateRuntimeRedisCorrelationStoreConfiguration,
|
|
7271
|
+
validateRuntimeTrackingConfiguration,
|
|
7272
|
+
validateScenarioNames,
|
|
7273
|
+
waitForTrackingOutcome,
|
|
7274
|
+
withLifecycleErrors
|
|
7275
|
+
};
|