@artilleryio/int-core 2.21.0 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +11 -13
- package/lib/engine_http.js +78 -111
- package/lib/engine_socketio.js +60 -76
- package/lib/engine_ws.js +34 -45
- package/lib/phases.js +18 -20
- package/lib/readers.js +5 -9
- package/lib/runner.js +43 -45
- package/lib/ssms.js +22 -16
- package/lib/weighted-pick.js +10 -18
- package/package.json +4 -4
- package/.eslintrc +0 -46
package/lib/engine_socketio.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
4
4
|
|
|
5
|
-
'use strict';
|
|
6
|
-
|
|
7
5
|
const async = require('async');
|
|
8
6
|
const _ = require('lodash');
|
|
9
7
|
|
|
@@ -26,22 +24,21 @@ function SocketIoEngine(script) {
|
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
SocketIoEngine.prototype.createScenario = function (scenarioSpec, ee) {
|
|
29
|
-
const self = this;
|
|
30
27
|
// Adds scenario overridden configuration into the static config
|
|
31
28
|
this.socketioOpts = { ...this.socketioOpts, ...scenarioSpec.socketio };
|
|
32
29
|
|
|
33
|
-
const tasks = _.map(scenarioSpec.flow,
|
|
30
|
+
const tasks = _.map(scenarioSpec.flow, (rs) => {
|
|
34
31
|
if (typeof rs.think !== 'undefined') {
|
|
35
32
|
return engineUtil.createThink(
|
|
36
33
|
rs,
|
|
37
|
-
_.get(
|
|
34
|
+
_.get(this.config, 'defaults.think', {})
|
|
38
35
|
);
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
return
|
|
38
|
+
return this.step(rs, ee);
|
|
42
39
|
});
|
|
43
40
|
|
|
44
|
-
return
|
|
41
|
+
return this.compile(tasks, scenarioSpec.flow, ee);
|
|
45
42
|
};
|
|
46
43
|
|
|
47
44
|
function markEndTime(ee, _, startedAt) {
|
|
@@ -117,56 +114,51 @@ function processResponse(ee, data, response, context, callback) {
|
|
|
117
114
|
const fauxResponse = { body: JSON.stringify(data) };
|
|
118
115
|
|
|
119
116
|
// Handle the capture or match clauses...
|
|
120
|
-
engineUtil.captureOrMatch(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// Were we unable to invoke captureOrMatch?
|
|
126
|
-
if (err) {
|
|
127
|
-
debug(data);
|
|
128
|
-
ee.emit('error', err);
|
|
117
|
+
engineUtil.captureOrMatch(response, fauxResponse, context, (err, result) => {
|
|
118
|
+
// Were we unable to invoke captureOrMatch?
|
|
119
|
+
if (err) {
|
|
120
|
+
debug(data);
|
|
121
|
+
ee.emit('error', err);
|
|
129
122
|
|
|
130
|
-
|
|
131
|
-
|
|
123
|
+
return callback(err, context);
|
|
124
|
+
}
|
|
132
125
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
126
|
+
if (result !== null) {
|
|
127
|
+
// Do we have any failed matches?
|
|
128
|
+
const failedMatches = _.filter(result.matches, (v) => {
|
|
129
|
+
return !v.success;
|
|
130
|
+
});
|
|
138
131
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
132
|
+
// How to handle failed matches?
|
|
133
|
+
if (failedMatches.length > 0) {
|
|
134
|
+
debug(failedMatches);
|
|
135
|
+
// TODO: Should log the details of the match somewhere
|
|
136
|
+
ee.emit('error', 'Failed match');
|
|
137
|
+
return callback(new Error('Failed match'), context);
|
|
138
|
+
} else {
|
|
139
|
+
// Populate the context with captured values
|
|
140
|
+
_.each(result.captures, (v, k) => {
|
|
141
|
+
context.vars[k] = v.value;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
151
144
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
145
|
+
// Replace the base object context
|
|
146
|
+
// Question: Should this be JSON object or String?
|
|
147
|
+
context.vars.$ = fauxResponse.body;
|
|
155
148
|
|
|
156
|
-
|
|
157
|
-
|
|
149
|
+
// Increment the success count...
|
|
150
|
+
context._successCount++;
|
|
158
151
|
|
|
159
|
-
|
|
160
|
-
}
|
|
152
|
+
return callback(null, context);
|
|
161
153
|
}
|
|
162
|
-
);
|
|
154
|
+
});
|
|
163
155
|
}
|
|
164
156
|
|
|
165
157
|
SocketIoEngine.prototype.step = function (requestSpec, ee) {
|
|
166
158
|
const self = this;
|
|
167
159
|
|
|
168
160
|
if (requestSpec.loop) {
|
|
169
|
-
const steps = _.map(requestSpec.loop,
|
|
161
|
+
const steps = _.map(requestSpec.loop, (rs) => {
|
|
170
162
|
if (!rs.emit && !rs.loop) {
|
|
171
163
|
return self.httpDelegate.step(rs, ee);
|
|
172
164
|
}
|
|
@@ -183,7 +175,7 @@ SocketIoEngine.prototype.step = function (requestSpec, ee) {
|
|
|
183
175
|
});
|
|
184
176
|
}
|
|
185
177
|
|
|
186
|
-
const f =
|
|
178
|
+
const f = (context, callback) => {
|
|
187
179
|
// Only process emit requests; delegate the rest to the HTTP engine (or think utility)
|
|
188
180
|
if (requestSpec.think) {
|
|
189
181
|
return engineUtil.createThink(
|
|
@@ -216,13 +208,13 @@ SocketIoEngine.prototype.step = function (requestSpec, ee) {
|
|
|
216
208
|
]
|
|
217
209
|
: Array.from(requestSpec.emit).map((arg) => template(arg, context));
|
|
218
210
|
|
|
219
|
-
const endCallback =
|
|
211
|
+
const endCallback = (err, context, needEmit) => {
|
|
220
212
|
if (err) {
|
|
221
213
|
debug(err);
|
|
222
214
|
}
|
|
223
215
|
|
|
224
216
|
if (isAcknowledgeRequired(requestSpec)) {
|
|
225
|
-
const ackCallback =
|
|
217
|
+
const ackCallback = (...args) => {
|
|
226
218
|
const response = {
|
|
227
219
|
data: template(
|
|
228
220
|
requestSpec.acknowledge.data || requestSpec.acknowledge.args,
|
|
@@ -232,14 +224,14 @@ SocketIoEngine.prototype.step = function (requestSpec, ee) {
|
|
|
232
224
|
match: template(requestSpec.acknowledge.match, context)
|
|
233
225
|
};
|
|
234
226
|
// Make sure data, capture or match has a default json spec for parsing socketio responses
|
|
235
|
-
_.each(response,
|
|
227
|
+
_.each(response, (r) => {
|
|
236
228
|
if (_.isPlainObject(r) && !('json' in r)) {
|
|
237
229
|
r.json = '$.0'; // Default to the first callback argument
|
|
238
230
|
}
|
|
239
231
|
});
|
|
240
232
|
|
|
241
233
|
// Acknowledge data can take up multiple arguments of the emit callback
|
|
242
|
-
processResponse(ee, args, response, context,
|
|
234
|
+
processResponse(ee, args, response, context, (err) => {
|
|
243
235
|
if (!err) {
|
|
244
236
|
markEndTime(ee, context, startedAt);
|
|
245
237
|
}
|
|
@@ -280,14 +272,14 @@ SocketIoEngine.prototype.step = function (requestSpec, ee) {
|
|
|
280
272
|
|
|
281
273
|
// Listen for the socket.io response on the specified channel
|
|
282
274
|
let done = false;
|
|
283
|
-
|
|
275
|
+
const responseData = [];
|
|
284
276
|
|
|
285
277
|
socketio.on(response.channel, function receive(...args) {
|
|
286
278
|
responseData.push(...args);
|
|
287
279
|
if (isValid(responseData, response)) {
|
|
288
280
|
done = true;
|
|
289
281
|
|
|
290
|
-
processResponse(ee, responseData, response, context,
|
|
282
|
+
processResponse(ee, responseData, response, context, (err) => {
|
|
291
283
|
if (!err) {
|
|
292
284
|
markEndTime(ee, context, startedAt);
|
|
293
285
|
}
|
|
@@ -307,22 +299,16 @@ SocketIoEngine.prototype.step = function (requestSpec, ee) {
|
|
|
307
299
|
setTimeout(function responseTimeout() {
|
|
308
300
|
if (!done) {
|
|
309
301
|
if (responseData.length) {
|
|
310
|
-
processResponse(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
response,
|
|
314
|
-
context,
|
|
315
|
-
function (err) {
|
|
316
|
-
if (!err) {
|
|
317
|
-
markEndTime(ee, context, startedAt);
|
|
318
|
-
}
|
|
319
|
-
// Stop listening on the response channel
|
|
320
|
-
socketio.off(response.channel);
|
|
321
|
-
|
|
322
|
-
// called
|
|
323
|
-
return endCallback(err, context, false);
|
|
302
|
+
processResponse(ee, responseData, response, context, (err) => {
|
|
303
|
+
if (!err) {
|
|
304
|
+
markEndTime(ee, context, startedAt);
|
|
324
305
|
}
|
|
325
|
-
|
|
306
|
+
// Stop listening on the response channel
|
|
307
|
+
socketio.off(response.channel);
|
|
308
|
+
|
|
309
|
+
// called
|
|
310
|
+
return endCallback(err, context, false);
|
|
311
|
+
});
|
|
326
312
|
|
|
327
313
|
return;
|
|
328
314
|
}
|
|
@@ -341,7 +327,7 @@ SocketIoEngine.prototype.step = function (requestSpec, ee) {
|
|
|
341
327
|
// Set default namespace in emit action
|
|
342
328
|
requestSpec.namespace = template(requestSpec.namespace, context) || '';
|
|
343
329
|
|
|
344
|
-
self.loadContextSocket(requestSpec.namespace, context,
|
|
330
|
+
self.loadContextSocket(requestSpec.namespace, context, (err) => {
|
|
345
331
|
if (err) {
|
|
346
332
|
debug(err);
|
|
347
333
|
ee.emit('error', err.message);
|
|
@@ -378,18 +364,18 @@ SocketIoEngine.prototype.loadContextSocket = function (namespace, context, cb) {
|
|
|
378
364
|
|
|
379
365
|
wildcardPatch(socket);
|
|
380
366
|
|
|
381
|
-
socket.on('*',
|
|
367
|
+
socket.on('*', () => {
|
|
382
368
|
context.__receivedMessageCount++;
|
|
383
369
|
});
|
|
384
370
|
|
|
385
|
-
socket.once('connect',
|
|
371
|
+
socket.once('connect', () => {
|
|
386
372
|
cb(null, socket);
|
|
387
373
|
});
|
|
388
|
-
socket.once('connect_error',
|
|
374
|
+
socket.once('connect_error', (err) => {
|
|
389
375
|
cb(err, null);
|
|
390
376
|
});
|
|
391
377
|
|
|
392
|
-
socket.once('error',
|
|
378
|
+
socket.once('error', (err) => {
|
|
393
379
|
cb(err, socket);
|
|
394
380
|
});
|
|
395
381
|
} else {
|
|
@@ -397,11 +383,11 @@ SocketIoEngine.prototype.loadContextSocket = function (namespace, context, cb) {
|
|
|
397
383
|
}
|
|
398
384
|
};
|
|
399
385
|
|
|
400
|
-
SocketIoEngine.prototype.closeContextSockets =
|
|
386
|
+
SocketIoEngine.prototype.closeContextSockets = (context) => {
|
|
401
387
|
if (context.sockets && Object.keys(context.sockets).length > 0) {
|
|
402
388
|
const namespaces = Object.keys(context.sockets);
|
|
403
389
|
|
|
404
|
-
namespaces.forEach(
|
|
390
|
+
namespaces.forEach((namespace) => {
|
|
405
391
|
context.sockets[namespace].disconnect();
|
|
406
392
|
});
|
|
407
393
|
}
|
|
@@ -429,9 +415,7 @@ SocketIoEngine.prototype.compile = function (tasks, scenarioSpec, ee) {
|
|
|
429
415
|
initialContext = self.httpDelegate.setInitialContext(initialContext);
|
|
430
416
|
|
|
431
417
|
initialContext._pendingRequests = _.size(
|
|
432
|
-
_.reject(scenarioSpec,
|
|
433
|
-
return typeof rs.think === 'number';
|
|
434
|
-
})
|
|
418
|
+
_.reject(scenarioSpec, (rs) => typeof rs.think === 'number')
|
|
435
419
|
);
|
|
436
420
|
|
|
437
421
|
const steps = _.flatten([
|
package/lib/engine_ws.js
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
4
4
|
|
|
5
|
-
'use strict';
|
|
6
|
-
|
|
7
5
|
const async = require('async');
|
|
8
6
|
const _ = require('lodash');
|
|
9
7
|
const WebSocket = require('ws');
|
|
10
8
|
const HttpsProxyAgent = require('https-proxy-agent');
|
|
11
9
|
const debug = require('debug')('ws');
|
|
12
|
-
const url = require('url');
|
|
10
|
+
const url = require('node:url');
|
|
13
11
|
const engineUtil = require('@artilleryio/int-commons').engine_util;
|
|
14
12
|
const template = engineUtil.template;
|
|
15
13
|
|
|
@@ -20,19 +18,18 @@ function WSEngine(script) {
|
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
WSEngine.prototype.createScenario = function (scenarioSpec, ee) {
|
|
23
|
-
const
|
|
24
|
-
const tasks = _.map(scenarioSpec.flow, function (rs) {
|
|
21
|
+
const tasks = _.map(scenarioSpec.flow, (rs) => {
|
|
25
22
|
if (typeof rs.think !== 'undefined') {
|
|
26
23
|
return engineUtil.createThink(
|
|
27
24
|
rs,
|
|
28
|
-
_.get(
|
|
25
|
+
_.get(this.config, 'defaults.think', {})
|
|
29
26
|
);
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
return
|
|
29
|
+
return this.step(rs, ee);
|
|
33
30
|
});
|
|
34
31
|
|
|
35
|
-
return
|
|
32
|
+
return this.compile(tasks, scenarioSpec.flow, ee);
|
|
36
33
|
};
|
|
37
34
|
|
|
38
35
|
function getMessageHandler(context, params, ee, timeout, callback) {
|
|
@@ -61,7 +58,7 @@ function getMessageHandler(context, params, ee, timeout, callback) {
|
|
|
61
58
|
let fauxResponse;
|
|
62
59
|
try {
|
|
63
60
|
fauxResponse = { body: JSON.parse(data) };
|
|
64
|
-
} catch (
|
|
61
|
+
} catch (_err) {
|
|
65
62
|
fauxResponse = { body: event.data };
|
|
66
63
|
}
|
|
67
64
|
|
|
@@ -81,20 +78,19 @@ function getMessageHandler(context, params, ee, timeout, callback) {
|
|
|
81
78
|
debug('captures: ', captures);
|
|
82
79
|
|
|
83
80
|
// match and capture are strict by default:
|
|
84
|
-
const haveFailedMatches = _.some(
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
const haveFailedMatches = _.some(
|
|
82
|
+
result.matches,
|
|
83
|
+
(v) => !v.success && v.strict !== false
|
|
84
|
+
);
|
|
87
85
|
|
|
88
|
-
const haveFailedCaptures = _.some(result.captures,
|
|
89
|
-
return v.failed;
|
|
90
|
-
});
|
|
86
|
+
const haveFailedCaptures = _.some(result.captures, (v) => v.failed);
|
|
91
87
|
|
|
92
88
|
if (haveFailedMatches || haveFailedCaptures) {
|
|
93
89
|
// TODO: Emit the details of each failed capture/match
|
|
94
90
|
return callback(new Error('Failed capture or match'), context);
|
|
95
91
|
}
|
|
96
92
|
|
|
97
|
-
_.each(result.matches,
|
|
93
|
+
_.each(result.matches, (v) => {
|
|
98
94
|
ee.emit('match', v.success, {
|
|
99
95
|
expected: v.expected,
|
|
100
96
|
got: v.got,
|
|
@@ -103,7 +99,7 @@ function getMessageHandler(context, params, ee, timeout, callback) {
|
|
|
103
99
|
});
|
|
104
100
|
});
|
|
105
101
|
|
|
106
|
-
_.each(result.captures,
|
|
102
|
+
_.each(result.captures, (v, k) => {
|
|
107
103
|
_.set(context.vars, k, v.value);
|
|
108
104
|
});
|
|
109
105
|
|
|
@@ -114,18 +110,14 @@ function getMessageHandler(context, params, ee, timeout, callback) {
|
|
|
114
110
|
}
|
|
115
111
|
|
|
116
112
|
WSEngine.prototype.step = function (requestSpec, ee) {
|
|
117
|
-
const self = this;
|
|
118
|
-
|
|
119
113
|
if (requestSpec.loop) {
|
|
120
|
-
const steps = _.map(requestSpec.loop,
|
|
121
|
-
return self.step(rs, ee);
|
|
122
|
-
});
|
|
114
|
+
const steps = _.map(requestSpec.loop, (rs) => this.step(rs, ee));
|
|
123
115
|
|
|
124
116
|
return engineUtil.createLoopWithCount(requestSpec.count || -1, steps, {
|
|
125
117
|
loopValue: requestSpec.loopValue || '$loopCount',
|
|
126
118
|
overValues: requestSpec.over,
|
|
127
|
-
whileTrue:
|
|
128
|
-
?
|
|
119
|
+
whileTrue: this.config.processor
|
|
120
|
+
? this.config.processor[requestSpec.whileTrue]
|
|
129
121
|
: undefined
|
|
130
122
|
});
|
|
131
123
|
}
|
|
@@ -133,18 +125,16 @@ WSEngine.prototype.step = function (requestSpec, ee) {
|
|
|
133
125
|
if (requestSpec.think) {
|
|
134
126
|
return engineUtil.createThink(
|
|
135
127
|
requestSpec,
|
|
136
|
-
_.get(
|
|
128
|
+
_.get(this.config, 'defaults.think', {})
|
|
137
129
|
);
|
|
138
130
|
}
|
|
139
131
|
|
|
140
132
|
if (requestSpec.function) {
|
|
141
|
-
return
|
|
142
|
-
const processFunc =
|
|
133
|
+
return (context, callback) => {
|
|
134
|
+
const processFunc = this.config.processor[requestSpec.function];
|
|
143
135
|
if (processFunc) {
|
|
144
136
|
if (processFunc.constructor.name === 'Function') {
|
|
145
|
-
processFunc(context, ee,
|
|
146
|
-
return callback(null, context);
|
|
147
|
-
});
|
|
137
|
+
processFunc(context, ee, () => callback(null, context));
|
|
148
138
|
} else {
|
|
149
139
|
return processFunc(context, ee)
|
|
150
140
|
.then(() => {
|
|
@@ -159,32 +149,31 @@ WSEngine.prototype.step = function (requestSpec, ee) {
|
|
|
159
149
|
}
|
|
160
150
|
|
|
161
151
|
if (requestSpec.log) {
|
|
162
|
-
return
|
|
152
|
+
return (context, callback) => {
|
|
163
153
|
console.log(template(requestSpec.log, context));
|
|
164
|
-
return process.nextTick(
|
|
154
|
+
return process.nextTick(() => {
|
|
165
155
|
callback(null, context);
|
|
166
156
|
});
|
|
167
157
|
};
|
|
168
158
|
}
|
|
169
159
|
|
|
170
160
|
if (requestSpec.connect) {
|
|
171
|
-
return
|
|
172
|
-
|
|
161
|
+
return (context, callback) =>
|
|
162
|
+
process.nextTick(() => {
|
|
173
163
|
callback(null, context);
|
|
174
164
|
});
|
|
175
|
-
};
|
|
176
165
|
}
|
|
177
166
|
|
|
178
|
-
const f =
|
|
167
|
+
const f = (context, callback) => {
|
|
179
168
|
const params = requestSpec.wait || requestSpec.send;
|
|
180
169
|
|
|
181
170
|
// match exists on a string, so check match is not a prototype
|
|
182
|
-
|
|
171
|
+
const captureOrMatch = _.has(params, 'capture') || _.has(params, 'match');
|
|
183
172
|
|
|
184
173
|
if (captureOrMatch) {
|
|
185
174
|
// only process response if we're capturing
|
|
186
|
-
|
|
187
|
-
|
|
175
|
+
const timeout =
|
|
176
|
+
this.config.timeout || _.get(this.config, 'ws.timeout') || 10;
|
|
188
177
|
context.ws.onmessage = getMessageHandler(
|
|
189
178
|
context,
|
|
190
179
|
params,
|
|
@@ -212,7 +201,7 @@ WSEngine.prototype.step = function (requestSpec, ee) {
|
|
|
212
201
|
ee.emit('rate', 'websocket.send_rate');
|
|
213
202
|
debug('WS send: %s', payload);
|
|
214
203
|
|
|
215
|
-
context.ws.send(payload,
|
|
204
|
+
context.ws.send(payload, (err) => {
|
|
216
205
|
if (err) {
|
|
217
206
|
debug(err);
|
|
218
207
|
ee.emit('error', err);
|
|
@@ -228,7 +217,7 @@ WSEngine.prototype.step = function (requestSpec, ee) {
|
|
|
228
217
|
debug('WS wait: %j', params);
|
|
229
218
|
} else {
|
|
230
219
|
// in the end, we could not send anything, so report it and stop
|
|
231
|
-
|
|
220
|
+
const err = 'invalid_step';
|
|
232
221
|
debug(err, requestSpec);
|
|
233
222
|
ee.emit('error', err);
|
|
234
223
|
return callback(err, context);
|
|
@@ -243,7 +232,7 @@ function getWsOptions(config) {
|
|
|
243
232
|
const subprotocols = _.get(config, 'ws.subprotocols', []);
|
|
244
233
|
const headers = _.get(config, 'ws.headers', {});
|
|
245
234
|
|
|
246
|
-
const subprotocolHeader = _.find(headers, (
|
|
235
|
+
const subprotocolHeader = _.find(headers, (_value, headerName) => {
|
|
247
236
|
return headerName.toLowerCase() === 'sec-websocket-protocol';
|
|
248
237
|
});
|
|
249
238
|
|
|
@@ -323,13 +312,13 @@ WSEngine.prototype.compile = function (tasks, scenarioSpec, ee) {
|
|
|
323
312
|
wsArgs.options
|
|
324
313
|
);
|
|
325
314
|
|
|
326
|
-
ws.on('open',
|
|
315
|
+
ws.on('open', () => {
|
|
327
316
|
contextWithoutWsArgs.ws = ws;
|
|
328
317
|
|
|
329
318
|
return cb(null, contextWithoutWsArgs);
|
|
330
319
|
});
|
|
331
320
|
|
|
332
|
-
ws.once('error',
|
|
321
|
+
ws.once('error', (err) => {
|
|
333
322
|
debug(err);
|
|
334
323
|
ee.emit('error', err.message || err.code);
|
|
335
324
|
|
|
@@ -347,7 +336,7 @@ WSEngine.prototype.compile = function (tasks, scenarioSpec, ee) {
|
|
|
347
336
|
debug(err);
|
|
348
337
|
}
|
|
349
338
|
|
|
350
|
-
if (context
|
|
339
|
+
if (context?.ws) {
|
|
351
340
|
context.ws.close();
|
|
352
341
|
}
|
|
353
342
|
|
package/lib/phases.js
CHANGED
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
4
4
|
|
|
5
|
-
'use strict';
|
|
6
|
-
|
|
7
5
|
const EventEmitter = require('eventemitter3');
|
|
8
6
|
const async = require('async');
|
|
9
7
|
const _ = require('lodash');
|
|
10
8
|
const isUndefined = _.isUndefined;
|
|
11
9
|
const arrivals = require('arrivals');
|
|
12
10
|
const debug = require('debug')('phases');
|
|
13
|
-
const { randomUUID } = require('crypto');
|
|
11
|
+
const { randomUUID } = require('node:crypto');
|
|
14
12
|
const driftless = require('driftless');
|
|
15
13
|
const ms = require('ms');
|
|
16
14
|
|
|
@@ -23,9 +21,9 @@ async function sleep(ms) {
|
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
function phaser(phaseSpecs) {
|
|
26
|
-
|
|
24
|
+
const ee = new EventEmitter();
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
const tasks = _.map(phaseSpecs, (spec, i) => {
|
|
29
27
|
[
|
|
30
28
|
'arrivalRate',
|
|
31
29
|
'arrivalCount',
|
|
@@ -33,12 +31,12 @@ function phaser(phaseSpecs) {
|
|
|
33
31
|
'rampTo',
|
|
34
32
|
'duration',
|
|
35
33
|
'maxVusers'
|
|
36
|
-
].forEach(
|
|
37
|
-
if (isUndefined(spec[k]) || spec[k]
|
|
34
|
+
].forEach((k) => {
|
|
35
|
+
if (isUndefined(spec[k]) || spec[k] === 'number') {
|
|
38
36
|
return;
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
if (k
|
|
39
|
+
if (k === 'duration' || k === 'pause') {
|
|
42
40
|
//if it's already a number in string format, don't apply ms, as it's the default behaviour, so we don't want to do ms calculations
|
|
43
41
|
//otherwise, ms returns the value in milliseconds, so we need to convert to seconds
|
|
44
42
|
const convertedDuration = Number.isInteger(_.toNumber(spec[k]))
|
|
@@ -91,8 +89,8 @@ function phaser(phaseSpecs) {
|
|
|
91
89
|
console.log('Unknown phase spec\n%j\nThis should not happen', spec);
|
|
92
90
|
});
|
|
93
91
|
|
|
94
|
-
ee.run =
|
|
95
|
-
async.series(tasks,
|
|
92
|
+
ee.run = () => {
|
|
93
|
+
async.series(tasks, (err) => {
|
|
96
94
|
if (err) {
|
|
97
95
|
debug(err);
|
|
98
96
|
}
|
|
@@ -106,11 +104,11 @@ function phaser(phaseSpecs) {
|
|
|
106
104
|
|
|
107
105
|
function createPause(spec, ee) {
|
|
108
106
|
const duration = spec.pause * 1000;
|
|
109
|
-
const task =
|
|
107
|
+
const task = (callback) => {
|
|
110
108
|
spec.startTime = Date.now();
|
|
111
109
|
spec.id = randomUUID();
|
|
112
110
|
ee.emit('phaseStarted', spec);
|
|
113
|
-
setTimeout(
|
|
111
|
+
setTimeout(() => {
|
|
114
112
|
spec.endTime = Date.now();
|
|
115
113
|
ee.emit('phaseCompleted', spec);
|
|
116
114
|
return callback(null);
|
|
@@ -164,7 +162,7 @@ function createRamp(spec, ee) {
|
|
|
164
162
|
debug(`periodArrivals ${periodArrivals}`);
|
|
165
163
|
debug(`periodTick ${periodTick}`);
|
|
166
164
|
|
|
167
|
-
return async function rampTask(
|
|
165
|
+
return async function rampTask(_callback) {
|
|
168
166
|
spec.startTime = Date.now();
|
|
169
167
|
spec.id = randomUUID();
|
|
170
168
|
ee.emit('phaseStarted', spec);
|
|
@@ -193,7 +191,7 @@ function createRamp(spec, ee) {
|
|
|
193
191
|
// ensure we don't go past 1s
|
|
194
192
|
const delay = Math.min(periodTick[currentPeriod], 1000);
|
|
195
193
|
let currentArrivals = 0;
|
|
196
|
-
|
|
194
|
+
const arrivalTimer = driftless.setDriftlessInterval(function arrivals() {
|
|
197
195
|
if (currentArrivals < periodArrivals[currentPeriod]) {
|
|
198
196
|
ee.emit('arrival', spec);
|
|
199
197
|
currentArrivals++;
|
|
@@ -207,7 +205,7 @@ function createRamp(spec, ee) {
|
|
|
207
205
|
}
|
|
208
206
|
|
|
209
207
|
function createArrivalCount(spec, ee) {
|
|
210
|
-
const task =
|
|
208
|
+
const task = (callback) => {
|
|
211
209
|
spec.startTime = Date.now();
|
|
212
210
|
spec.id = randomUUID();
|
|
213
211
|
ee.emit('phaseStarted', spec);
|
|
@@ -216,10 +214,10 @@ function createArrivalCount(spec, ee) {
|
|
|
216
214
|
if (spec.arrivalCount > 0) {
|
|
217
215
|
const interval = duration / spec.arrivalCount;
|
|
218
216
|
const p = arrivals.uniform.process(interval, duration);
|
|
219
|
-
p.on('arrival',
|
|
217
|
+
p.on('arrival', () => {
|
|
220
218
|
ee.emit('arrival', spec);
|
|
221
219
|
});
|
|
222
|
-
p.on('finished',
|
|
220
|
+
p.on('finished', () => {
|
|
223
221
|
spec.endTime = Date.now();
|
|
224
222
|
ee.emit('phaseCompleted', spec);
|
|
225
223
|
return callback(null);
|
|
@@ -234,7 +232,7 @@ function createArrivalCount(spec, ee) {
|
|
|
234
232
|
}
|
|
235
233
|
|
|
236
234
|
function createArrivalRate(spec, ee) {
|
|
237
|
-
const task =
|
|
235
|
+
const task = (callback) => {
|
|
238
236
|
spec.startTime = Date.now();
|
|
239
237
|
spec.id = randomUUID();
|
|
240
238
|
ee.emit('phaseStarted', spec);
|
|
@@ -242,10 +240,10 @@ function createArrivalRate(spec, ee) {
|
|
|
242
240
|
const duration = spec.duration * 1000;
|
|
243
241
|
debug('creating a %s process for arrivalRate', spec.mode);
|
|
244
242
|
const p = arrivals[spec.mode].process(ar, duration);
|
|
245
|
-
p.on('arrival',
|
|
243
|
+
p.on('arrival', () => {
|
|
246
244
|
ee.emit('arrival', spec);
|
|
247
245
|
});
|
|
248
|
-
p.on('finished',
|
|
246
|
+
p.on('finished', () => {
|
|
249
247
|
spec.endTime = Date.now();
|
|
250
248
|
ee.emit('phaseCompleted', spec);
|
|
251
249
|
return callback(null);
|
package/lib/readers.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
4
4
|
|
|
5
|
-
'use strict';
|
|
6
|
-
|
|
7
5
|
const _ = require('lodash');
|
|
8
6
|
|
|
9
7
|
module.exports = createReader;
|
|
@@ -25,8 +23,8 @@ function createReader(order, spec) {
|
|
|
25
23
|
|
|
26
24
|
function createSequencedReader() {
|
|
27
25
|
let i = 0;
|
|
28
|
-
return
|
|
29
|
-
|
|
26
|
+
return (data) => {
|
|
27
|
+
const result = data[i];
|
|
30
28
|
if (i < data.length - 1) {
|
|
31
29
|
i++;
|
|
32
30
|
} else {
|
|
@@ -39,14 +37,14 @@ function createSequencedReader() {
|
|
|
39
37
|
function createEverythingReader(spec) {
|
|
40
38
|
let parsedData;
|
|
41
39
|
|
|
42
|
-
return
|
|
40
|
+
return (data) => {
|
|
43
41
|
if (!parsedData) {
|
|
44
42
|
parsedData = [];
|
|
45
43
|
|
|
46
44
|
// Parse the row into an object based on the fields spec
|
|
47
45
|
if (spec.fields?.length > 0) {
|
|
48
46
|
for (const row of data) {
|
|
49
|
-
|
|
47
|
+
const o = {};
|
|
50
48
|
for (let i = 0; i < spec.fields.length; i++) {
|
|
51
49
|
const fieldName = spec.fields[i];
|
|
52
50
|
o[fieldName] = row[i];
|
|
@@ -64,7 +62,5 @@ function createEverythingReader(spec) {
|
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
function createRandomReader() {
|
|
67
|
-
return
|
|
68
|
-
return data[Math.max(0, _.random(0, data.length - 1))];
|
|
69
|
-
};
|
|
65
|
+
return (data) => data[Math.max(0, _.random(0, data.length - 1))];
|
|
70
66
|
}
|