loadtest 5.0.4 → 5.2.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/README.md CHANGED
@@ -603,6 +603,25 @@ will be:
603
603
  * ...
604
604
  * body will also be replaced `body:{ userid: id_value }` will be `body:{ userid: id_1 }`
605
605
 
606
+ #### `indexParamCallback`
607
+
608
+ A function that would be executed to replace the value identified through `indexParam` through a custom value generator.
609
+
610
+ E.g.: if URL is `http://test.com/value` and `indexParam=value` and
611
+ ```javascript
612
+ indexParamCallback: function customCallBack() {
613
+ return Math.floor(Math.random() * 10); //returns a random integer from 0 to 9
614
+ }
615
+ ```
616
+ then the URL could be:
617
+
618
+ * http://test.com/1 (Randomly generated integer 1)
619
+ * http://test.com/5 (Randomly generated integer 5)
620
+ * http://test.com/6 (Randomly generated integer 6)
621
+ * http://test.com/8 (Randomly generated integer 8)
622
+ * ...
623
+ * body will also be replaced `body:{ userid: id_value }` will be `body:{ userid: id_<value from callback> }`
624
+
606
625
  #### `insecure`
607
626
 
608
627
  Allow invalid and self-signed certificates over https.
@@ -632,7 +651,7 @@ loadtest.loadTest(options, function(error) {
632
651
 
633
652
  #### `statusCallback`
634
653
 
635
- Execution this function after every request operation completes. Provides immediate access to test results while the
654
+ If present, this function executes after every request operation completes. Provides immediate access to test results while the
636
655
  test batch is still running. This can be used for more detailed custom logging or developing your own spreadsheet or
637
656
  statistical analysis of results.
638
657
 
@@ -673,6 +692,31 @@ loadtest.loadTest(options, function(error) {
673
692
  });
674
693
  ```
675
694
 
695
+
696
+ In some situations request data needs to be available in the statusCallBack.
697
+ This data can be assigned to `request.labels` in the requestGenerator:
698
+ ```javascript
699
+ const options = {
700
+ // ...
701
+ requestGenerator: (params, options, client, callback) => {
702
+ // ...
703
+ const randomInputData = Math.random().toString().substr(2, 8);
704
+ const message = JSON.stringify({ randomInputData })
705
+ const request = client(options, callback);
706
+ request.labels = randomInputData;
707
+ request.write(message);
708
+ return request;
709
+ }
710
+ };
711
+ ```
712
+
713
+ Then in statusCallBack the labels can be accessed through `result.labels`:
714
+ ```javascript
715
+ function statusCallback(error, result, latency) {
716
+ console.log(result.labels);
717
+ }
718
+ ```
719
+
676
720
  **Warning**: The format for `statusCallback` has changed in version 2.0.0 onwards.
677
721
  It used to be `statusCallback(latency, result, error)`,
678
722
  it has been changed to conform to the usual Node.js standard.
@@ -690,7 +734,7 @@ The `result` object passed to this callback function has the same fields as the
690
734
  Example:
691
735
 
692
736
  ```javascript
693
- fucntion contentInspector (result) => {
737
+ function contentInspector(result) {
694
738
  if (result.statusCode == 200) {
695
739
  const body = JSON.parse(result.body)
696
740
  // how to examine the body depends on the content that the service returns
package/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  declare namespace loadtest {
2
- export function loadTest(options: LoadTestOptions, err: Function): void;
3
-
2
+ export function loadTest(options: LoadTestOptions, err: Function): Operation;
3
+
4
+ export interface Operation {
5
+ completedRequests: number
6
+ }
7
+
4
8
  export interface LoadTestOptions {
5
9
  url: string;
6
10
  concurrency?: number;
@@ -21,6 +25,7 @@ declare namespace loadtest {
21
25
  secureProtocol?: string;
22
26
  statusCallback?(error: Error, result: any, latency: LoadTestResult): void;
23
27
  contentInspector?(result: any): void;
28
+ indexParamCallback?(): string;
24
29
  }
25
30
 
26
31
  export interface LoadTestResult {
package/lib/httpClient.js CHANGED
@@ -248,14 +248,18 @@ class HttpClient {
248
248
  callback('Connection ' + id + ' failed: ' + error, '1');
249
249
  });
250
250
  connection.on('end', () => {
251
+ const client = connection.connection || connection.client
251
252
  const result = {
252
- host: connection.connection._host,
253
+ host: client._host,
253
254
  path: connection.req.path,
254
255
  method: connection.req.method,
255
256
  statusCode: connection.statusCode,
256
257
  body: body,
257
258
  headers: connection.headers,
258
259
  };
260
+ if (connection.req.labels) {
261
+ result.labels = connection.req.labels
262
+ }
259
263
  if (contentInspector) {
260
264
  contentInspector(result)
261
265
  }
package/lib/loadtest.js CHANGED
@@ -88,8 +88,8 @@ let operationInstanceIndex = 0;
88
88
  */
89
89
  class Operation {
90
90
  constructor(options, callback) {
91
- this.options = options
92
- this.finalCallback = callback
91
+ this.options = options;
92
+ this.finalCallback = callback;
93
93
  this.running = true;
94
94
  this.latency = null;
95
95
  this.clients = {};
@@ -110,7 +110,7 @@ class Operation {
110
110
  this.stopTimeout = setTimeout(() => this.stop(), this.options.maxSeconds * 1000).unref();
111
111
  }
112
112
  this.showTimer = new HighResolutionTimer(SHOW_INTERVAL_MS, () => this.latency.showPartial());
113
- this.showTimer.unref()
113
+ this.showTimer.unref();
114
114
  }
115
115
 
116
116
  /**
@@ -139,14 +139,24 @@ class Operation {
139
139
  */
140
140
  startClients() {
141
141
  const url = this.options.url;
142
+ const strBody = JSON.stringify(this.options.body);
142
143
  for (let index = 0; index < this.options.concurrency; index++) {
143
144
  if (this.options.indexParam) {
144
- this.options.url = url.replace(new RegExp(this.options.indexParam, 'g'), index);
145
-
146
- if(this.options.body) {
147
- let strBody = JSON.stringify(this.options.body);
148
- strBody = strBody.replace(new RegExp(this.options.indexParam, 'g'), index);
149
- this.options.body = JSON.parse(strBody);
145
+ let oldToken = new RegExp(this.options.indexParam, 'g');
146
+ if(this.options.indexParamCallback instanceof Function) {
147
+ let customIndex = this.options.indexParamCallback();
148
+ this.options.url = url.replace(oldToken, customIndex);
149
+ if(this.options.body) {
150
+ let body = strBody.replace(oldToken, customIndex);
151
+ this.options.body = JSON.parse(body);
152
+ }
153
+ }
154
+ else {
155
+ this.options.url = url.replace(oldToken, index);
156
+ if(this.options.body) {
157
+ let body = strBody.replace(oldToken, index);
158
+ this.options.body = JSON.parse(body);
159
+ }
150
160
  }
151
161
  }
152
162
  let constructor = httpClient.create;
@@ -219,12 +229,66 @@ function testWSEcho(callback) {
219
229
  exports.loadTest(options, callback);
220
230
  }
221
231
 
232
+ function testIndexParam(callback) {
233
+ const options = {
234
+ url: 'http://localhost:7357/replace',
235
+ concurrency:1,
236
+ quiet: true,
237
+ maxSeconds: 0.1,
238
+ indexParam: "replace"
239
+ };
240
+ exports.loadTest(options, callback);
241
+ }
242
+
243
+ function testIndexParamWithBody(callback) {
244
+ const options = {
245
+ url: 'http://localhost:7357/replace',
246
+ concurrency:1,
247
+ quiet: true,
248
+ maxSeconds: 0.1,
249
+ indexParam: "replace",
250
+ body: '{"id": "replace"}'
251
+ };
252
+ exports.loadTest(options, callback);
253
+ }
254
+
255
+ function testIndexParamWithCallback(callback) {
256
+ const options = {
257
+ url: 'http://localhost:7357/replace',
258
+ concurrency:1,
259
+ quiet: true,
260
+ maxSeconds: 0.1,
261
+ indexParam: "replace",
262
+ indexParamCallback: function() {
263
+ //https://gist.github.com/6174/6062387
264
+ return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
265
+ }
266
+ };
267
+ exports.loadTest(options, callback);
268
+ }
269
+
270
+ function testIndexParamWithCallbackAndBody(callback) {
271
+ const options = {
272
+ url: 'http://localhost:7357/replace',
273
+ concurrency:1,
274
+ quiet: true,
275
+ maxSeconds: 0.1,
276
+ body: '{"id": "replace"}',
277
+ indexParam: "replace",
278
+ indexParamCallback: function() {
279
+ //https://gist.github.com/6174/6062387
280
+ return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
281
+ }
282
+ };
283
+ exports.loadTest(options, callback);
284
+ }
285
+
222
286
 
223
287
  /**
224
288
  * Run all tests.
225
289
  */
226
290
  exports.test = function(callback) {
227
- testing.run([testMaxSeconds, testWSEcho], callback);
291
+ testing.run([testMaxSeconds, testWSEcho, testIndexParam, testIndexParamWithBody, testIndexParamWithCallback, testIndexParamWithCallbackAndBody], callback);
228
292
  };
229
293
 
230
294
  // run tests if invoked directly
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loadtest",
3
- "version": "5.0.4",
3
+ "version": "5.2.0",
4
4
  "description": "Run load tests for your web application. Mostly ab-compatible interface, with an option to force requests per second. Includes an API for automated load testing.",
5
5
  "homepage": "https://github.com/alexfernandez/loadtest",
6
6
  "contributors": [