loadtest 6.0.0 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -84,7 +84,7 @@ but the resulting figure is much more robust.
84
84
  `loadtest` is also quite extensible.
85
85
  Using the provided API it is very easy to integrate loadtest with your package, and run programmatic load tests.
86
86
  loadtest makes it very easy to run load tests as part of systems tests, before deploying a new version of your software.
87
- The results include mean response times and percentiles,
87
+ The result includes mean response times and percentiles,
88
88
  so that you can abort deployment e.g. if 99% of the requests don't finish in 10 ms or less.
89
89
 
90
90
  ### Usage Don'ts
@@ -111,6 +111,7 @@ The following parameters are compatible with Apache ab.
111
111
  #### `-n requests`
112
112
 
113
113
  Number of requests to send out.
114
+ Default is no limit; will keep on sending if not specified.
114
115
 
115
116
  Note: the total number of requests sent can be bigger than the parameter if there is a concurrency parameter;
116
117
  loadtest will report just the first `n`.
@@ -119,6 +120,7 @@ loadtest will report just the first `n`.
119
120
 
120
121
  loadtest will create a certain number of clients; this parameter controls how many.
121
122
  Requests from them will arrive concurrently to the server.
123
+ Default value is 1.
122
124
 
123
125
  Note: requests are not sent in parallel (from different processes),
124
126
  but concurrently (a second request may be sent before the first has been answered).
@@ -126,6 +128,7 @@ but concurrently (a second request may be sent before the first has been answere
126
128
  #### `-t timelimit`
127
129
 
128
130
  Max number of seconds to wait until requests no longer go out.
131
+ Default is no limit; will keep on sending if not specified.
129
132
 
130
133
  Note: this is different than Apache `ab`, which stops _receiving_ requests after the given seconds.
131
134
 
@@ -175,15 +178,17 @@ Send the string as the PATCH body. E.g.: `-A '{"key": "a9acf03f"}'`
175
178
 
176
179
  #### `-m method`
177
180
 
178
- Send method to link. Accept: [GET, POST, PUT, DELETE, PATCH, get, post, put, delete, patch], Default is GET
179
- E.g.: -m POST
181
+ Set method that will be sent to the test URL.
182
+ Accepts: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`,
183
+ and lowercase versions. Default is `GET`.
184
+ Example: `-m POST`.
180
185
 
181
- #### `--data POST some variables`
186
+ #### `--data body`
182
187
 
183
- Send some data. It does not support method GET.
184
- E.g: `--data '{"username": "test", "password": "test"}' -T 'application/x-www-form-urlencoded' -m POST`
188
+ Add some data to send in the body. It does not support method GET.
189
+ Requires setting the method with `-m` and the type with `-T`.
190
+ Example: `--data '{"username": "test", "password": "test"}' -T 'application/x-www-form-urlencoded' -m POST`
185
191
 
186
- It required `-m` and `-T 'application/x-www-form-urlencoded'`
187
192
 
188
193
  #### `-p POST-file`
189
194
 
@@ -256,6 +261,7 @@ The following parameters are _not_ compatible with Apache ab.
256
261
 
257
262
  Controls the number of requests per second that are sent.
258
263
  Can be fractional, e.g. `--rps 0.5` sends one request every two seconds.
264
+ Not used by default: each request is sent as soon as the previous one is responded.
259
265
 
260
266
  Note: Concurrency doesn't affect the final number of requests per second,
261
267
  since rps will be shared by all the clients. E.g.:
@@ -309,17 +315,15 @@ Open connections using keep-alive.
309
315
 
310
316
  Note: instead of using the default agent, this option is now an alias for `-k`.
311
317
 
312
- #### `--quiet` (deprecated)
318
+ #### `--quiet`
313
319
 
314
320
  Do not show any messages.
315
321
 
316
- Note: deprecated in version 6+, shows a warning.
317
-
318
322
  #### `--debug` (deprecated)
319
323
 
320
324
  Show debug messages.
321
325
 
322
- Note: deprecated in version 6+, shows a warning.
326
+ Note: deprecated in version 6+.
323
327
 
324
328
  #### `--insecure`
325
329
 
@@ -383,7 +387,7 @@ with concurrency 10 (only relevant results are shown):
383
387
  99% 14 ms
384
388
  100% 35997 ms (longest request)
385
389
 
386
- Results were quite erratic, with some requests taking up to 36 seconds;
390
+ The result was quite erratic, with some requests taking up to 36 seconds;
387
391
  this suggests that Node.js is queueing some requests for a long time, and answering them irregularly.
388
392
  Now we will try a fixed rate of 1000 rps:
389
393
 
@@ -438,10 +442,10 @@ We now know that our server can accept 500 rps without problems.
438
442
  Not bad for a single-process naïve Node.js server...
439
443
  We may refine our results further to find at which point from 500 to 1000 rps our server breaks down.
440
444
 
441
- But instead let us research how to improve the results.
445
+ But instead let us research how to improve the result.
442
446
  One obvious candidate is to add keep-alive to the requests so we don't have to create
443
447
  a new connection for every request.
444
- The results (with the same test server) are impressive:
448
+ The result (with the same test server) is impressive:
445
449
 
446
450
  $ loadtest http://localhost:7357/ -t 20 -c 10 -k
447
451
  ...
@@ -659,11 +663,11 @@ loadTest(options, function(error) {
659
663
 
660
664
  #### `statusCallback`
661
665
 
662
- If present, this function executes after every request operation completes. Provides immediate access to test results while the
666
+ If present, this function executes after every request operation completes. Provides immediate access to the test result while the
663
667
  test batch is still running. This can be used for more detailed custom logging or developing your own spreadsheet or
664
- statistical analysis of results.
668
+ statistical analysis of the result.
665
669
 
666
- The results and error passed to the callback are in the same format as the results passed to the final callback.
670
+ The result and error passed to the callback are in the same format as the result passed to the final callback.
667
671
 
668
672
  In addition, the following three properties are added to the `result` object:
669
673
 
@@ -754,9 +758,9 @@ function contentInspector(result) {
754
758
  },
755
759
  ```
756
760
 
757
- ### Results
761
+ ### Result
758
762
 
759
- The latency results passed to your callback at the end of the load test contains a full set of data, including:
763
+ The latency result passed to your callback at the end of the load test contains a full set of data, including:
760
764
  mean latency, number of errors and percentiles.
761
765
  An example follows:
762
766
 
@@ -796,14 +800,20 @@ The second parameter contains info about the current request:
796
800
 
797
801
  ### Start Test Server
798
802
 
799
- To start the test server use the exported function `startServer()` with a set of options and an optional callback:
803
+ To start the test server use the exported function `startServer()` with a set of options:
800
804
 
801
805
  ```javascript
802
806
  import {startServer} from 'loadtest'
803
- const server = startServer({port: 8000})
807
+ const server = await startServer({port: 8000})
804
808
  ```
805
809
 
806
810
  This function returns an HTTP server which can be `close()`d when it is no longer useful.
811
+ As a legacy from before promises existed,
812
+ if the optional callback is passed then it will not behave as `async`:
813
+
814
+ ```
815
+ const server = startServer({port: 8000}, error => console.error(error))
816
+ ```
807
817
 
808
818
  The following options are available.
809
819
 
package/bin/loadtest.js CHANGED
@@ -3,6 +3,7 @@
3
3
  import {readFile} from 'fs/promises'
4
4
  import * as stdio from 'stdio'
5
5
  import {loadTest} from '../lib/loadtest.js'
6
+ import {showResult} from '../lib/show.js'
6
7
 
7
8
 
8
9
  const options = stdio.getopt({
@@ -31,8 +32,8 @@ const options = stdio.getopt({
31
32
  insecure: {description: 'Allow self-signed certificates over https'},
32
33
  key: {args: 1, description: 'The client key to use'},
33
34
  cert: {args: 1, description: 'The client certificate to use'},
35
+ quiet: {description: 'Do not log any messages'},
34
36
  agent: {description: 'Use a keep-alive http agent (deprecated)'},
35
- quiet: {description: 'Do not log any messages (deprecated)'},
36
37
  debug: {description: 'Show debug messages (deprecated)'}
37
38
  });
38
39
 
@@ -52,7 +53,8 @@ async function processAndRun(options) {
52
53
  }
53
54
  options.url = options.args[0];
54
55
  try {
55
- loadTest(options)
56
+ const result = await loadTest(options)
57
+ showResult(options, result)
56
58
  } catch(error) {
57
59
  console.error(error.message)
58
60
  help()
package/lib/latency.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import * as crypto from 'crypto'
2
+ import {showResult} from './show.js'
2
3
 
3
4
 
4
5
  /**
5
6
  * Latency measurements. Options can be:
6
7
  * - maxRequests: max number of requests to measure before stopping.
7
8
  * - maxSeconds: max seconds, alternative to max requests.
8
- * An optional callback(error, results) will be called with an error,
9
- * or the results after max is reached.
9
+ * An optional callback(error, result) will be called with an error,
10
+ * or the result after max is reached.
10
11
  */
11
12
  export class Latency {
12
13
  constructor(options, callback) {
@@ -106,7 +107,7 @@ export class Latency {
106
107
  showPartial() {
107
108
  const elapsedSeconds = this.getElapsed(this.lastShown) / 1000;
108
109
  const meanTime = this.partialTime / this.partialRequests || 0.0;
109
- const results = {
110
+ const result = {
110
111
  meanLatencyMs: Math.round(meanTime * 10) / 10,
111
112
  rps: Math.round(this.partialRequests / elapsedSeconds)
112
113
  };
@@ -114,10 +115,12 @@ export class Latency {
114
115
  if (this.options.maxRequests) {
115
116
  percent = ' (' + Math.round(100 * this.totalRequests / this.options.maxRequests) + '%)';
116
117
  }
117
- console.info('Requests: %s%s, requests per second: %s, mean latency: %s ms', this.totalRequests, percent, results.rps, results.meanLatencyMs);
118
- if (this.totalErrors) {
119
- percent = Math.round(100 * 10 * this.totalErrors / this.totalRequests) / 10;
120
- console.info('Errors: %s, accumulated errors: %s, %s% of total requests', this.partialErrors, this.totalErrors, percent);
118
+ if (!this.options.quiet) {
119
+ console.info('Requests: %s%s, requests per second: %s, mean latency: %s ms', this.totalRequests, percent, result.rps, result.meanLatencyMs);
120
+ if (this.totalErrors) {
121
+ percent = Math.round(100 * 10 * this.totalErrors / this.totalRequests) / 10;
122
+ console.info('Errors: %s, accumulated errors: %s, %s% of total requests', this.partialErrors, this.totalErrors, percent);
123
+ }
121
124
  }
122
125
  this.partialTime = 0;
123
126
  this.partialRequests = 0;
@@ -163,14 +166,14 @@ export class Latency {
163
166
  finish() {
164
167
  this.running = false;
165
168
  if (this.callback) {
166
- return this.callback(null, this.getResults());
169
+ return this.callback(null, this.getResult());
167
170
  }
168
171
  }
169
172
 
170
173
  /**
171
- * Get final results.
174
+ * Get final result.
172
175
  */
173
- getResults() {
176
+ getResult() {
174
177
  const elapsedSeconds = this.getElapsed(this.initialTime) / 1000;
175
178
  const meanTime = this.totalTime / this.totalRequests;
176
179
  return {
@@ -215,53 +218,15 @@ export class Latency {
215
218
  }
216
219
 
217
220
  /**
218
- * Show final results.
221
+ * Show final result.
219
222
  */
220
223
  show() {
221
224
  if (this.totalsShown) {
222
225
  return;
223
226
  }
224
227
  this.totalsShown = true;
225
- const results = this.getResults();
226
- console.info('');
227
- console.info('Target URL: %s', this.options.url);
228
- if (this.options.maxRequests) {
229
- console.info('Max requests: %s', this.options.maxRequests);
230
- } else if (this.options.maxSeconds) {
231
- console.info('Max time (s): %s', this.options.maxSeconds);
232
- }
233
- console.info('Concurrency level: %s', this.options.concurrency);
234
- let agent = 'none';
235
- if (this.options.agentKeepAlive) {
236
- agent = 'keepalive';
237
- }
238
- console.info('Agent: %s', agent);
239
- if (this.options.requestsPerSecond) {
240
- console.info('Requests per second: %s', this.options.requestsPerSecond * this.options.concurrency);
241
- }
242
- console.info('');
243
- console.info('Completed requests: %s', results.totalRequests);
244
- console.info('Total errors: %s', results.totalErrors);
245
- console.info('Total time: %s s', results.totalTimeSeconds);
246
- console.info('Requests per second: %s', results.rps);
247
- console.info('Mean latency: %s ms', results.meanLatencyMs);
248
- console.info('');
249
- console.info('Percentage of the requests served within a certain time');
250
-
251
- Object.keys(results.percentiles).forEach(percentile => {
252
- console.info(' %s% %s ms', percentile, results.percentiles[percentile]);
253
- });
254
-
255
- console.info(' 100% %s ms (longest request)', this.maxLatencyMs);
256
- if (results.totalErrors) {
257
- console.info('');
258
- console.info(' 100% %s ms (longest request)', this.maxLatencyMs);
259
- console.info('');
260
- Object.keys(results.errorCodes).forEach(errorCode => {
261
- const padding = ' '.repeat(errorCode.length < 4 ? 4 - errorCode.length : 1);
262
- console.info(' %s%s: %s errors', padding, errorCode, results.errorCodes[errorCode]);
263
- });
264
- }
228
+ const result = this.getResult();
229
+ showResult(this.options, result)
265
230
  }
266
231
  }
267
232
 
package/lib/loadtest.js CHANGED
@@ -14,33 +14,47 @@ https.globalAgent.maxSockets = 1000;
14
14
 
15
15
  /**
16
16
  * Run a load test.
17
- * Options is an object which may have:
18
- * - url: mandatory URL to access.
19
- * - concurrency: how many concurrent clients to use.
20
- * - maxRequests: how many requests to send
21
- * - maxSeconds: how long to run the tests.
22
- * - cookies: a string or an array of strings, each with name:value.
23
- * - headers: a map with headers: {key1: value1, key2: value2}.
24
- * - method: the method to use: POST, PUT. Default: GET, what else.
25
- * - body: the contents to send along a POST or PUT request.
26
- * - contentType: the MIME type to use for the body, default text/plain.
27
- * - requestsPerSecond: how many requests per second to send.
28
- * - agentKeepAlive: if true, then use connection keep-alive.
29
- * - indexParam: string to replace with a unique index.
30
- * - insecure: allow https using self-signed certs.
31
- * - debug: show debug messages (deprecated).
32
- * - quiet: do not log any messages (deprecated).
33
- * An optional callback will be called if/when the test finishes.
17
+ * Parameters:
18
+ * - `options`: an object which may have:
19
+ * - url: mandatory URL to access.
20
+ * - concurrency: how many concurrent clients to use.
21
+ * - maxRequests: how many requests to send
22
+ * - maxSeconds: how long to run the tests.
23
+ * - cookies: a string or an array of strings, each with name:value.
24
+ * - headers: a map with headers: {key1: value1, key2: value2}.
25
+ * - method: the method to use: POST, PUT. Default: GET, what else.
26
+ * - body: the contents to send along a POST or PUT request.
27
+ * - contentType: the MIME type to use for the body, default text/plain.
28
+ * - requestsPerSecond: how many requests per second to send.
29
+ * - agentKeepAlive: if true, then use connection keep-alive.
30
+ * - indexParam: string to replace with a unique index.
31
+ * - insecure: allow https using self-signed certs.
32
+ * - quiet: do not log any messages.
33
+ * - debug: show debug messages (deprecated).
34
+ * - `callback`: optional `function(result, error)` called if/when the test finishes;
35
+ * if not present a promise is returned.
34
36
  */
35
37
  export function loadTest(options, callback) {
36
- processOptions(options, error => {
37
- if (error) {
38
- if (callback) return callback(error)
39
- throw new error
40
- }
41
- const operation = new Operation(options, callback);
38
+ if (!callback) {
39
+ return loadTestAsync(options)
40
+ }
41
+ loadTestAsync(options).then(result => callback(null, result)).catch(error => callback(null, error))
42
+ }
43
+
44
+ async function loadTestAsync(options) {
45
+ await processOptions(options)
46
+ return await runOperation(options)
47
+ }
48
+
49
+ function runOperation(options) {
50
+ return new Promise((resolve, reject) => {
51
+ const operation = new Operation(options, (error, result) => {
52
+ if (error) {
53
+ return reject(error)
54
+ }
55
+ return resolve(result)
56
+ });
42
57
  operation.start();
43
- return operation;
44
58
  })
45
59
  }
46
60
 
@@ -93,7 +107,7 @@ class Operation {
93
107
  next();
94
108
  }
95
109
  if (this.options.statusCallback) {
96
- this.options.statusCallback(error, result, this.latency.getResults());
110
+ this.options.statusCallback(error, result, this.latency.getResult());
97
111
  }
98
112
  }
99
113
 
@@ -156,7 +170,7 @@ class Operation {
156
170
  this.clients[index].stop();
157
171
  });
158
172
  if (this.finalCallback) {
159
- const result = this.latency.getResults();
173
+ const result = this.latency.getResult();
160
174
  result.instanceIndex = this.instanceIndex;
161
175
  this.finalCallback(null, result);
162
176
  } else {
package/lib/options.js CHANGED
@@ -7,11 +7,7 @@ import {addHeaders} from '../lib/headers.js'
7
7
  import {loadConfig} from '../lib/config.js'
8
8
 
9
9
 
10
- export function processOptions(options, callback) {
11
- processOptionsAsync(options).then(result => callback(null, result)).catch(error => callback(error))
12
- }
13
-
14
- async function processOptionsAsync(options) {
10
+ export async function processOptions(options) {
15
11
  const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)))
16
12
  if (!options.url) {
17
13
  throw new Error('Missing URL in options')
@@ -45,7 +41,7 @@ async function processOptionsAsync(options) {
45
41
  options.body = await readBody(options.postFile, '-p');
46
42
  }
47
43
  if (options.data) {
48
- options.body = JSON.parse(options.data);
44
+ options.body = options.data
49
45
  }
50
46
  if (options.method) {
51
47
  const acceptedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'get', 'post', 'put', 'delete', 'patch'];
package/lib/show.js ADDED
@@ -0,0 +1,47 @@
1
+
2
+
3
+ /**
4
+ * Show result of a load test.
5
+ */
6
+ export function showResult(options, result) {
7
+ console.info('');
8
+ console.info('Target URL: %s', options.url);
9
+ if (options.maxRequests) {
10
+ console.info('Max requests: %s', options.maxRequests);
11
+ } else if (options.maxSeconds) {
12
+ console.info('Max time (s): %s', options.maxSeconds);
13
+ }
14
+ console.info('Concurrency level: %s', options.concurrency);
15
+ let agent = 'none';
16
+ if (options.agentKeepAlive) {
17
+ agent = 'keepalive';
18
+ }
19
+ console.info('Agent: %s', agent);
20
+ if (options.requestsPerSecond) {
21
+ console.info('Requests per second: %s', options.requestsPerSecond * options.concurrency);
22
+ }
23
+ console.info('');
24
+ console.info('Completed requests: %s', result.totalRequests);
25
+ console.info('Total errors: %s', result.totalErrors);
26
+ console.info('Total time: %s s', result.totalTimeSeconds);
27
+ console.info('Requests per second: %s', result.rps);
28
+ console.info('Mean latency: %s ms', result.meanLatencyMs);
29
+ console.info('');
30
+ console.info('Percentage of the requests served within a certain time');
31
+
32
+ Object.keys(result.percentiles).forEach(percentile => {
33
+ console.info(' %s% %s ms', percentile, result.percentiles[percentile]);
34
+ });
35
+
36
+ console.info(' 100% %s ms (longest request)', result.maxLatencyMs);
37
+ if (result.totalErrors) {
38
+ console.info('');
39
+ Object.keys(result.errorCodes).forEach(errorCode => {
40
+ const padding = ' '.repeat(errorCode.length < 4 ? 4 - errorCode.length : 1);
41
+ console.info(' %s%s: %s errors', padding, errorCode, result.errorCodes[errorCode]);
42
+ });
43
+ }
44
+ }
45
+
46
+
47
+
package/lib/testserver.js CHANGED
@@ -23,7 +23,7 @@ class TestServer {
23
23
 
24
24
  /**
25
25
  * Start the server.
26
- * An optional callback will be called after the server has started.
26
+ * The callback parameter will be called after the server has started.
27
27
  */
28
28
  start(callback) {
29
29
  if (this.options.socket) {
@@ -45,10 +45,8 @@ class TestServer {
45
45
  return this.createError('Could not start server on port ' + this.port + ': ' + error, callback);
46
46
  });
47
47
  this.server.listen(this.port, () => {
48
- console.info(`Listening on http://localhost:${this.port}/`);
49
- if (callback) {
50
- callback();
51
- }
48
+ if (!this.options.quiet) console.info(`Listening on http://localhost:${this.port}/`)
49
+ callback(null, this.server)
52
50
  });
53
51
  this.wsServer.on('request', request => {
54
52
  // explicity omitting origin check here.
@@ -61,19 +59,16 @@ class TestServer {
61
59
  }
62
60
  });
63
61
  connection.on('close', () => {
64
- console.info('Peer %s disconnected', connection.remoteAddress);
62
+ if (!this.options.quiet) console.info('Peer %s disconnected', connection.remoteAddress);
65
63
  });
66
64
  });
67
- return this.server;
65
+ return this.server
68
66
  }
69
67
 
70
68
  /**
71
69
  * Log an error, or send to the callback if present.
72
70
  */
73
71
  createError(message, callback) {
74
- if (!callback) {
75
- return console.error(message);
76
- }
77
72
  callback(message);
78
73
  }
79
74
 
@@ -106,7 +101,7 @@ class TestServer {
106
101
  */
107
102
  socketListen(socket) {
108
103
  socket.on('error', error => {
109
- console.error('socket error: %s', error);
104
+ if (!this.options.quiet) console.error('socket error: %s', error);
110
105
  socket.end();
111
106
  });
112
107
  socket.on('data', data => this.readData(data));
@@ -116,16 +111,16 @@ class TestServer {
116
111
  * Read some data off the socket.
117
112
  */
118
113
  readData(data) {
119
- console.info('data: %s', data);
114
+ if (!this.options.quiet) console.info('data: %s', data);
120
115
  }
121
116
 
122
117
  /**
123
118
  * Debug headers and other interesting information: POST body.
124
119
  */
125
120
  debug(request) {
126
- console.info('Headers for %s to %s: %s', request.method, request.url, util.inspect(request.headers));
121
+ if (!this.options.quiet) console.info('Headers for %s to %s: %s', request.method, request.url, util.inspect(request.headers));
127
122
  if (request.body) {
128
- console.info('Body: %s', request.body);
123
+ if (!this.options.quiet) console.info('Body: %s', request.body);
129
124
  }
130
125
  }
131
126
 
@@ -152,7 +147,7 @@ class TestServer {
152
147
  }
153
148
  const percent = parseInt(this.options.percent, 10);
154
149
  if (!percent) {
155
- console.error('Invalid error percent %s', this.options.percent);
150
+ if (!this.options.quiet) console.error('Invalid error percent %s', this.options.percent);
156
151
  return false;
157
152
  }
158
153
  return (Math.random() < percent / 100);
@@ -160,16 +155,26 @@ class TestServer {
160
155
  }
161
156
 
162
157
  /**
163
- * Start a test server. Options can contain:
164
- * - port: the port to use, default 7357.
165
- * - delay: wait the given milliseconds before answering.
166
- * - quiet: do not log any messages (deprecated).
167
- * - percent: give an error (default 500) on some % of requests.
168
- * - error: set an HTTP error code, default is 500.
169
- * An optional callback is called after the server has started.
158
+ * Start a test server. Parameters:
159
+ * - `options`, can contain:
160
+ * - port: the port to use, default 7357.
161
+ * - delay: wait the given milliseconds before answering.
162
+ * - quiet: do not log any messages.
163
+ * - percent: give an error (default 500) on some % of requests.
164
+ * - error: set an HTTP error code, default is 500.
165
+ * - `callback`: optional callback, called after the server has started.
166
+ * If not present will return a promise.
170
167
  */
171
168
  export function startServer(options, callback) {
172
169
  const server = new TestServer(options);
173
- return server.start(callback);
170
+ if (callback) {
171
+ return server.start(callback)
172
+ }
173
+ return new Promise((resolve, reject) => {
174
+ server.start((error, result) => {
175
+ if (error) return reject(error)
176
+ return resolve(result)
177
+ })
178
+ })
174
179
  }
175
180
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loadtest",
3
- "version": "6.0.0",
3
+ "version": "6.1.0",
4
4
  "type": "module",
5
5
  "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.",
6
6
  "homepage": "https://github.com/alexfernandez/loadtest",
@@ -25,11 +25,11 @@ const options = {
25
25
  }
26
26
  };
27
27
 
28
- loadTest(options, (error, results) => {
28
+ loadTest(options, (error, result) => {
29
29
  if (error) {
30
30
  return console.error('Got an error: %s', error);
31
31
  }
32
- console.log(results);
32
+ console.log(result);
33
33
  console.log('Tests run successfully');
34
34
  });
35
35
 
@@ -25,9 +25,9 @@ const options: loadtest.LoadTestOptions = {
25
25
  },
26
26
  }
27
27
 
28
- loadtest.loadTest(options, (error, results) => {
28
+ loadtest.loadTest(options, (error, result) => {
29
29
  if (error) {
30
30
  return console.error(`Got an error: ${error}`)
31
31
  }
32
- console.log("Tests run successfully", {results})
32
+ console.log("Tests run successfully", {result})
33
33
  })
@@ -6,7 +6,7 @@ const PORT = 10453;
6
6
 
7
7
 
8
8
  function testBodyGenerator(callback) {
9
- const server = startServer({port: PORT}, error => {
9
+ const server = startServer({port: PORT, quiet: true}, error => {
10
10
  if (error) {
11
11
  return callback('Could not start test server');
12
12
  }
@@ -16,6 +16,7 @@ function testBodyGenerator(callback) {
16
16
  maxRequests: 100,
17
17
  concurrency: 10,
18
18
  postFile: 'sample/post-file.js',
19
+ quiet: true,
19
20
  }
20
21
  loadTest(options, (error, result) => {
21
22
  if (error) {
@@ -4,13 +4,17 @@ import {join} from 'path'
4
4
  import {loadTest, startServer} from '../index.js'
5
5
 
6
6
  const PORT = 10408;
7
+ const serverOptions = {
8
+ port: PORT,
9
+ quiet: true,
10
+ }
7
11
 
8
12
 
9
13
  /**
10
14
  * Run an integration test.
11
15
  */
12
16
  function testIntegration(callback) {
13
- const server = startServer({ port: PORT }, error => {
17
+ const server = startServer(serverOptions, error => {
14
18
  if (error) {
15
19
  return callback(error);
16
20
  }
@@ -22,6 +26,7 @@ function testIntegration(callback) {
22
26
  body: {
23
27
  hi: 'there',
24
28
  },
29
+ quiet: true,
25
30
  };
26
31
  loadTest(options, (error, result) => {
27
32
  if (error) {
@@ -31,7 +36,7 @@ function testIntegration(callback) {
31
36
  if (error) {
32
37
  return callback(error);
33
38
  }
34
- return callback(null, 'Test results: ' + JSON.stringify(result));
39
+ return callback(null, 'Test result: ' + JSON.stringify(result));
35
40
  });
36
41
  });
37
42
  });
@@ -42,12 +47,13 @@ function testIntegration(callback) {
42
47
  * Run an integration test using configuration file.
43
48
  */
44
49
  function testIntegrationFile(callback) {
45
- const server = startServer({ port: PORT }, error => {
50
+ const server = startServer(serverOptions, error => {
46
51
  if (error) {
47
52
  return callback(error);
48
53
  }
49
54
  execFile('node',
50
- [join('./', 'bin', 'loadtest.js'), `http://localhost:${PORT}/`, '-n', '100'],
55
+ [join('./', 'bin', 'loadtest.js'), `http://localhost:${PORT}/`,
56
+ '-n', '100', '--quiet'],
51
57
  (error, stdout) => {
52
58
  if (error) {
53
59
  return callback(error);
@@ -56,7 +62,7 @@ function testIntegrationFile(callback) {
56
62
  if (error) {
57
63
  return callback(error);
58
64
  }
59
- return callback(null, 'Test results: ' + stdout);
65
+ return callback(null, 'Test result: ' + stdout);
60
66
  });
61
67
  });
62
68
  });
@@ -68,7 +74,7 @@ function testIntegrationFile(callback) {
68
74
  * Run an integration test.
69
75
  */
70
76
  function testWSIntegration(callback) {
71
- const server = startServer({ port: PORT }, error => {
77
+ const server = startServer(serverOptions, error => {
72
78
  if (error) {
73
79
  return callback(error);
74
80
  }
@@ -85,6 +91,7 @@ function testWSIntegration(callback) {
85
91
  type: 'ping',
86
92
  hi: 'there',
87
93
  },
94
+ quiet: true,
88
95
  };
89
96
  loadTest(options, (error, result) => {
90
97
  if (error) {
@@ -94,7 +101,7 @@ function testWSIntegration(callback) {
94
101
  if (error) {
95
102
  return callback(error);
96
103
  }
97
- return callback(null, 'Test results: ' + JSON.stringify(result));
104
+ return callback(null, 'Test result: ' + JSON.stringify(result));
98
105
  });
99
106
  });
100
107
  });
@@ -108,6 +115,7 @@ function testDelay(callback) {
108
115
  let options = {
109
116
  port: PORT + 1,
110
117
  delay: delay,
118
+ quiet: true,
111
119
  };
112
120
  const server = startServer(options, error => {
113
121
  if (error) {
@@ -116,6 +124,7 @@ function testDelay(callback) {
116
124
  options = {
117
125
  url: 'http://localhost:' + (PORT + 1),
118
126
  maxRequests: 10,
127
+ quiet: true,
119
128
  };
120
129
  loadTest(options, (error, result) => {
121
130
  if (error) {
@@ -133,10 +142,29 @@ function testDelay(callback) {
133
142
  });
134
143
  }
135
144
 
145
+ async function testPromise() {
146
+ const server = await startServer(serverOptions)
147
+ const options = {
148
+ url: 'http://localhost:' + PORT,
149
+ maxRequests: 100,
150
+ concurrency: 10,
151
+ method: 'POST',
152
+ body: {
153
+ hi: 'there',
154
+ },
155
+ quiet: true,
156
+ };
157
+ const result = await loadTest(options)
158
+ await server.close()
159
+ return 'Test result: ' + JSON.stringify(result)
160
+ }
161
+
136
162
  /**
137
163
  * Run all tests.
138
164
  */
139
165
  export function test(callback) {
140
- testing.run([testIntegration, testIntegrationFile, testDelay, testWSIntegration], 4000, callback);
166
+ testing.run([
167
+ testIntegration, testIntegrationFile, testDelay, testWSIntegration, testPromise,
168
+ ], 4000, callback);
141
169
  }
142
170
 
package/test/latency.js CHANGED
@@ -49,7 +49,7 @@ function testLatencyPercentiles(callback) {
49
49
  };
50
50
  const latency = new Latency(options, error => {
51
51
  testing.check(error, 'Error while testing latency percentiles', callback);
52
- const percentiles = latency.getResults().percentiles;
52
+ const percentiles = latency.getResult().percentiles;
53
53
 
54
54
  Object.keys(percentiles).forEach(percentile => {
55
55
  testing.assert(percentiles[percentile] !== false, 'Empty percentile for %s', percentile, callback);
package/test/loadtest.js CHANGED
@@ -10,6 +10,7 @@ function testMaxSeconds(callback) {
10
10
  url: 'http://localhost:7357/',
11
11
  maxSeconds: 0.1,
12
12
  concurrency: 1,
13
+ quiet: true,
13
14
  };
14
15
  loadTest(options, callback);
15
16
  }
@@ -23,6 +24,7 @@ function testWSEcho(callback) {
23
24
  url: 'ws://localhost:7357/',
24
25
  maxSeconds: 0.1,
25
26
  concurrency: 1,
27
+ quiet: true,
26
28
  };
27
29
  loadTest(options, callback);
28
30
  }
@@ -32,7 +34,8 @@ function testIndexParam(callback) {
32
34
  url: 'http://localhost:7357/replace',
33
35
  concurrency:1,
34
36
  maxSeconds: 0.1,
35
- indexParam: "replace"
37
+ indexParam: "replace",
38
+ quiet: true,
36
39
  };
37
40
  loadTest(options, callback);
38
41
  }
@@ -43,7 +46,8 @@ function testIndexParamWithBody(callback) {
43
46
  concurrency:1,
44
47
  maxSeconds: 0.1,
45
48
  indexParam: "replace",
46
- body: '{"id": "replace"}'
49
+ body: '{"id": "replace"}',
50
+ quiet: true,
47
51
  };
48
52
  loadTest(options, callback);
49
53
  }
@@ -57,7 +61,8 @@ function testIndexParamWithCallback(callback) {
57
61
  indexParamCallback: function() {
58
62
  //https://gist.github.com/6174/6062387
59
63
  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
60
- }
64
+ },
65
+ quiet: true,
61
66
  };
62
67
  loadTest(options, callback);
63
68
  }
@@ -72,7 +77,8 @@ function testIndexParamWithCallbackAndBody(callback) {
72
77
  indexParamCallback: function() {
73
78
  //https://gist.github.com/6174/6062387
74
79
  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
75
- }
80
+ },
81
+ quiet: true,
76
82
  };
77
83
  loadTest(options, callback);
78
84
  }
@@ -82,6 +88,9 @@ function testIndexParamWithCallbackAndBody(callback) {
82
88
  * Run all tests.
83
89
  */
84
90
  export function test(callback) {
85
- testing.run([testMaxSeconds, testWSEcho, testIndexParam, testIndexParamWithBody, testIndexParamWithCallback, testIndexParamWithCallbackAndBody], callback);
91
+ testing.run([
92
+ testMaxSeconds, testWSEcho, testIndexParam, testIndexParamWithBody,
93
+ testIndexParamWithCallback, testIndexParamWithCallbackAndBody,
94
+ ], callback);
86
95
  }
87
96
 
@@ -6,7 +6,7 @@ const PORT = 10453;
6
6
 
7
7
 
8
8
  function testRequestGenerator(callback) {
9
- const server = startServer({port: PORT}, error => {
9
+ const server = startServer({port: PORT, quiet: true}, error => {
10
10
  if (error) {
11
11
  return callback('Could not start test server');
12
12
  }
@@ -24,6 +24,7 @@ function testRequestGenerator(callback) {
24
24
  request.write(message);
25
25
  return request;
26
26
  },
27
+ quiet: true,
27
28
  };
28
29
  loadTest(options, (error, result) => {
29
30
  if (error) {
@@ -5,6 +5,7 @@ import {startServer} from '../lib/testserver.js'
5
5
  function testStartServer(callback) {
6
6
  const options = {
7
7
  port: 10530,
8
+ quiet: true,
8
9
  };
9
10
  const server = startServer(options, error => {
10
11
  testing.check(error, 'Could not start server', callback);