loadtest 5.2.0 → 6.0.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/.eslintrc CHANGED
@@ -6,7 +6,7 @@ env:
6
6
  extends: eslint:recommended
7
7
  parserOptions:
8
8
  sourceType: module
9
- ecmaVersion: 8
9
+ ecmaVersion: 14
10
10
  rules:
11
11
  no-inner-declarations: 0
12
12
  no-unused-vars:
package/README.md CHANGED
@@ -33,8 +33,9 @@ For access to the API just add package `loadtest` to your `package.json` devDepe
33
33
 
34
34
  ### Compatibility
35
35
 
36
- Versions 5 and later should be used at least with Node.js v10 or later:
36
+ Versions 6 and later should be used at least with Node.js v16 or later:
37
37
 
38
+ * Node.js v16 or later: ^6.0.0
38
39
  * Node.js v10 or later: ^5.0.0
39
40
  * Node.js v8 or later: 4.x.y.
40
41
  * Node.js v6 or earlier: ^3.1.0.
@@ -189,44 +190,46 @@ It required `-m` and `-T 'application/x-www-form-urlencoded'`
189
190
  Send the data contained in the given file in the POST body.
190
191
  Remember to set `-T` to the correct content-type.
191
192
 
192
- If `POST-file` has `.js` extension it will be `require`d. It should be a valid node module and it
193
- should `export` a single function, which is invoked with an automatically generated request identifier
193
+ If `POST-file` has `.js` extension it will be `import`ed. It should be a valid node module and it
194
+ should `export` a default function, which is invoked with an automatically generated request identifier
194
195
  to provide the body of each request.
195
196
  This is useful if you want to generate request bodies dynamically and vary them for each request.
196
197
 
197
198
  Example:
198
199
 
199
200
  ```javascript
200
- module.exports = function(requestId) {
201
+ export default function request(requestId) {
201
202
  // this object will be serialized to JSON and sent in the body of the request
202
203
  return {
203
204
  key: 'value',
204
205
  requestId: requestId
205
- };
206
- };
206
+ }
207
+ }
207
208
  ```
208
209
 
210
+ See sample file in `sample/post-file.js`, and test in `test/body-generator.js`.
211
+
209
212
  #### `-u PUT-file`
210
213
 
211
214
  Send the data contained in the given file as a PUT request.
212
215
  Remember to set `-T` to the correct content-type.
213
216
 
214
- If `PUT-file` has `.js` extension it will be `require`d. It should be a valid node module and it
215
- should `export` a single function, which is invoked with an automatically generated request identifier
217
+ If `PUT-file` has `.js` extension it will be `import`ed. It should be a valid node module and it
218
+ should `export` a default function, which is invoked with an automatically generated request identifier
216
219
  to provide the body of each request.
217
220
  This is useful if you want to generate request bodies dynamically and vary them for each request.
218
- For an example function see above for `-p`.
221
+ For examples see above for `-p`.
219
222
 
220
223
  #### `-a PATCH-file`
221
224
 
222
225
  Send the data contained in the given file as a PATCH request.
223
226
  Remember to set `-T` to the correct content-type.
224
227
 
225
- If `PATCH-file` has `.js` extension it will be `require`d. It should be a valid node module and it
226
- should `export` a single function, which is invoked with an automatically generated request identifier
228
+ If `PATCH-file` has `.js` extension it will be `import`ed. It should be a valid node module and it
229
+ should `export` a default function, which is invoked with an automatically generated request identifier
227
230
  to provide the body of each request.
228
231
  This is useful if you want to generate request bodies dynamically and vary them for each request.
229
- For an example function see above for `-p`.
232
+ For examples see above for `-p`.
230
233
 
231
234
  ##### `-r`
232
235
 
@@ -306,14 +309,18 @@ Open connections using keep-alive.
306
309
 
307
310
  Note: instead of using the default agent, this option is now an alias for `-k`.
308
311
 
309
- #### `--quiet`
312
+ #### `--quiet` (deprecated)
310
313
 
311
314
  Do not show any messages.
312
315
 
313
- #### `--debug`
316
+ Note: deprecated in version 6+, shows a warning.
317
+
318
+ #### `--debug` (deprecated)
314
319
 
315
320
  Show debug messages.
316
321
 
322
+ Note: deprecated in version 6+, shows a warning.
323
+
317
324
  #### `--insecure`
318
325
 
319
326
  Allow invalid and self-signed certificates over https.
@@ -473,19 +480,18 @@ thus allowing you to load test your application in your own tests.
473
480
  To run a load test, just call the exported function `loadTest()` with a set of options and an optional callback:
474
481
 
475
482
  ```javascript
476
- const loadtest = require('loadtest');
483
+ import {loadTest} from 'loadtest'
484
+
477
485
  const options = {
478
486
  url: 'http://localhost:8000',
479
487
  maxRequests: 1000,
480
- };
481
- loadtest.loadTest(options, function(error, result)
482
- {
483
- if (error)
484
- {
485
- return console.error('Got an error: %s', error);
488
+ }
489
+ loadTest(options, function(error, result) {
490
+ if (error) {
491
+ return console.error('Got an error: %s', error)
486
492
  }
487
- console.log('Tests run successfully');
488
- });
493
+ console.log('Tests run successfully')
494
+ })
489
495
  ```
490
496
 
491
497
  The callback `function(error, result)` will be invoked when the max number of requests is reached,
@@ -588,10 +594,12 @@ Use an agent with 'Connection: Keep-alive'.
588
594
  Note: Uses [agentkeepalive](https://npmjs.org/package/agentkeepalive),
589
595
  which performs better than the default node.js agent.
590
596
 
591
- #### `quiet`
597
+ #### `quiet` (deprecated)
592
598
 
593
599
  Do not show any messages.
594
600
 
601
+ Note: deprecated in version 6+, shows a warning.
602
+
595
603
  #### `indexParam`
596
604
 
597
605
  The given string will be replaced in the final URL with a unique index.
@@ -633,20 +641,20 @@ The TLS/SSL method to use. (e.g. TLSv1_method)
633
641
  Example:
634
642
 
635
643
  ```javascript
636
- const loadtest = require('loadtest');
644
+ import {loadTest} from 'loadtest'
637
645
 
638
646
  const options = {
639
647
  url: 'https://www.example.com',
640
648
  maxRequests: 100,
641
649
  secureProtocol: 'TLSv1_method'
642
- };
650
+ }
643
651
 
644
- loadtest.loadTest(options, function(error) {
652
+ loadTest(options, function(error) {
645
653
  if (error) {
646
- return console.error('Got an error: %s', error);
654
+ return console.error('Got an error: %s', error)
647
655
  }
648
- console.log('Tests run successfully');
649
- });
656
+ console.log('Tests run successfully')
657
+ })
650
658
  ```
651
659
 
652
660
  #### `statusCallback`
@@ -668,28 +676,28 @@ You will need to check if `error` is populated in order to determine which objec
668
676
  Example:
669
677
 
670
678
  ```javascript
671
- const loadtest = require('loadtest');
679
+ import {loadTest} from 'loadtest'
672
680
 
673
681
  function statusCallback(error, result, latency) {
674
- console.log('Current latency %j, result %j, error %j', latency, result, error);
675
- console.log('----');
676
- console.log('Request elapsed milliseconds: ', result.requestElapsed);
677
- console.log('Request index: ', result.requestIndex);
678
- console.log('Request loadtest() instance index: ', result.instanceIndex);
682
+ console.log('Current latency %j, result %j, error %j', latency, result, error)
683
+ console.log('----')
684
+ console.log('Request elapsed milliseconds: ', result.requestElapsed)
685
+ console.log('Request index: ', result.requestIndex)
686
+ console.log('Request loadtest() instance index: ', result.instanceIndex)
679
687
  }
680
688
 
681
689
  const options = {
682
690
  url: 'http://localhost:8000',
683
691
  maxRequests: 1000,
684
692
  statusCallback: statusCallback
685
- };
693
+ }
686
694
 
687
- loadtest.loadTest(options, function(error) {
695
+ loadTest(options, function(error) {
688
696
  if (error) {
689
- return console.error('Got an error: %s', error);
697
+ return console.error('Got an error: %s', error)
690
698
  }
691
- console.log('Tests run successfully');
692
- });
699
+ console.log('Tests run successfully')
700
+ })
693
701
  ```
694
702
 
695
703
 
@@ -791,8 +799,8 @@ The second parameter contains info about the current request:
791
799
  To start the test server use the exported function `startServer()` with a set of options and an optional callback:
792
800
 
793
801
  ```javascript
794
- const testserver = require('testserver');
795
- const server = testserver.startServer({ port: 8000 });
802
+ import {startServer} from 'loadtest'
803
+ const server = startServer({port: 8000})
796
804
  ```
797
805
 
798
806
  This function returns an HTTP server which can be `close()`d when it is no longer useful.
@@ -856,11 +864,13 @@ The expected structure of the file is the following:
856
864
  }
857
865
  ```
858
866
 
867
+ See sample file in `sample/.loadtestrc`.
868
+
859
869
  For more information about the actual configuration file name, read the [confinode user manual](https://github.com/slune-org/confinode/blob/master/doc/en/usermanual.md#configuration-search). In the list of the [supported file types](https://github.com/slune-org/confinode/blob/master/doc/extensions.md), please note that only synchronous loaders can be used with _loadtest_.
860
870
 
861
871
  ### Complete Example
862
872
 
863
- The file `lib/integration.js` shows a complete example, which is also a full integration test:
873
+ The file `test/integration.js` shows a complete example, which is also a full integration test:
864
874
  it starts the server, send 1000 requests, waits for the callback and closes down the server.
865
875
 
866
876
  ## Versioning
package/bin/loadtest.js CHANGED
@@ -1,22 +1,10 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
2
 
4
- /**
5
- * Binary to run loadtest.
6
- * (C) 2013 Manuel Ernst, Alex Fernández.
7
- */
3
+ import {readFile} from 'fs/promises'
4
+ import * as stdio from 'stdio'
5
+ import {loadTest} from '../lib/loadtest.js'
8
6
 
9
- // requires
10
- const stdio = require('stdio');
11
- const fs = require('fs');
12
- const path = require('path');
13
- const urlLib = require('url');
14
- const loadTest = require('../lib/loadtest.js');
15
- const headers = require('../lib/headers.js');
16
- const packageJson = require(__dirname + '/../package.json');
17
- const config = require('../lib/config');
18
7
 
19
- // init
20
8
  const options = stdio.getopt({
21
9
  maxRequests: {key: 'n', args: 1, description: 'Number of requests to perform'},
22
10
  concurrency: {key: 'c', args: 1, description: 'Number of requests to make'},
@@ -39,156 +27,40 @@ const options = stdio.getopt({
39
27
  version: {key: 'V', description: 'Show version number and exit'},
40
28
  proxy: {args: 1, description: 'Use a proxy for requests e.g. http://localhost:8080 '},
41
29
  rps: {args: 1, description: 'Specify the requests per second for each client'},
42
- agent: {description: 'Use a keep-alive http agent (deprecated)'},
43
30
  index: {args: 1, description: 'Replace the value of given arg with an index in the URL'},
44
- quiet: {description: 'Do not log any messages'},
45
- debug: {description: 'Show debug messages'},
46
31
  insecure: {description: 'Allow self-signed certificates over https'},
47
32
  key: {args: 1, description: 'The client key to use'},
48
- cert: {args: 1, description: 'The client certificate to use'}
33
+ cert: {args: 1, description: 'The client certificate to use'},
34
+ agent: {description: 'Use a keep-alive http agent (deprecated)'},
35
+ quiet: {description: 'Do not log any messages (deprecated)'},
36
+ debug: {description: 'Show debug messages (deprecated)'}
49
37
  });
50
- if (options.version) {
51
- console.log('Loadtest version: %s', packageJson.version);
52
- process.exit(0);
53
- }
54
- // is there an url? if not, break and display help
55
- if (!options.args || options.args.length < 1) {
56
- console.error('Missing URL to load-test');
57
- help();
58
- } else if (options.args.length > 1) {
59
- console.error('Too many arguments: %s', options.args);
60
- help();
61
- }
62
38
 
63
- const configuration = config.loadConfig(options);
64
-
65
- options.url = options.args[0];
66
- options.agentKeepAlive = options.keepalive || options.agent || configuration.agentKeepAlive;
67
- options.indexParam = options.index || configuration.indexParam;
68
-
69
- //TODO: add index Param
70
- // Allow a post body string in options
71
- // Ex -P '{"foo": "bar"}'
72
- if (options.postBody) {
73
- options.method = 'POST';
74
- options.body = options.postBody;
75
- }
76
- if (options.postFile) {
77
- options.method = 'POST';
78
- options.body = readBody(options.postFile, '-p');
79
- }
80
- if (options.data) {
81
- options.body = JSON.parse(options.data);
82
- }
83
- if (options.method) {
84
- const acceptedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'get', 'post', 'put', 'delete', 'patch'];
85
- if (acceptedMethods.indexOf(options.method) === -1) {
86
- options.method = 'GET';
39
+ async function processAndRun(options) {
40
+ if (options.version) {
41
+ const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)))
42
+ console.log('Loadtest version: %s', packageJson.version);
43
+ process.exit(0);
87
44
  }
88
- }
89
- if(options.putFile) {
90
- options.method = 'PUT';
91
- options.body = readBody(options.putFile, '-u');
92
- }
93
- if (options.patchBody) {
94
- options.method = 'PATCH';
95
- options.body = options.patchBody;
96
- }
97
- if(options.patchFile) {
98
- options.method = 'PATCH';
99
- options.body = readBody(options.patchFile, '-a');
100
- }
101
- if(!options.method) {
102
- options.method = configuration.method;
103
- }
104
- if(!options.body) {
105
- if(configuration.body) {
106
- options.body = configuration.body;
107
- } else if(configuration.file) {
108
- options.body = readBody(configuration.file, 'configuration.request.file');
109
- }
110
- }
111
- options.requestsPerSecond = options.rps ? parseFloat(options.rps) : configuration.requestsPerSecond;
112
- if(!options.key) {
113
- options.key = configuration.key;
114
- }
115
- if(options.key) {
116
- options.key = fs.readFileSync(options.key);
117
- }
118
- if(!options.cert) {
119
- options.cert = configuration.cert;
120
- }
121
- if(options.cert) {
122
- options.cert = fs.readFileSync(options.cert);
123
- }
124
-
125
- const defaultHeaders = options.headers || !configuration.headers ? {} : configuration.headers;
126
- defaultHeaders['host'] = urlLib.parse(options.url).host;
127
- defaultHeaders['user-agent'] = 'loadtest/' + packageJson.version;
128
- defaultHeaders['accept'] = '*/*';
129
-
130
- if (options.headers) {
131
- headers.addHeaders(options.headers, defaultHeaders);
132
- console.log('headers: %s, %j', typeof defaultHeaders, defaultHeaders);
133
- }
134
- options.headers = defaultHeaders;
135
-
136
- if (!options.requestGenerator) {
137
- options.requestGenerator = configuration.requestGenerator;
138
- }
139
- if (options.requestGenerator) {
140
- options.requestGenerator = require(path.resolve(options.requestGenerator));
141
- }
142
-
143
- // Use configuration file for other values
144
- if(!options.maxRequests) {
145
- options.maxRequests = configuration.maxRequests;
146
- }
147
- if(!options.concurrency) {
148
- options.concurrency = configuration.concurrency;
149
- }
150
- if(!options.maxSeconds) {
151
- options.maxSeconds = configuration.maxSeconds;
152
- }
153
- if(!options.timeout && configuration.timeout) {
154
- options.timeout = configuration.timeout;
155
- }
156
- if(!options.contentType) {
157
- options.contentType = configuration.contentType;
158
- }
159
- if(!options.cookies) {
160
- options.cookies = configuration.cookies;
161
- }
162
- if(!options.secureProtocol) {
163
- options.secureProtocol = configuration.secureProtocol;
164
- }
165
- if(!options.insecure) {
166
- options.insecure = configuration.insecure;
167
- }
168
- if(!options.recover) {
169
- options.recover = configuration.recover;
170
- }
171
- if(!options.proxy) {
172
- options.proxy = configuration.proxy;
173
- }
174
-
175
- loadTest.loadTest(options);
176
-
177
- function readBody(filename, option) {
178
- if (typeof filename !== 'string') {
179
- console.error('Invalid file to open with %s: %s', option, filename);
45
+ // is there an url? if not, break and display help
46
+ if (!options.args || options.args.length < 1) {
47
+ console.error('Missing URL to load-test');
48
+ help();
49
+ } else if (options.args.length > 1) {
50
+ console.error('Too many arguments: %s', options.args);
180
51
  help();
181
52
  }
182
-
183
- if(path.extname(filename) === '.js') {
184
- return require(path.resolve(filename));
53
+ options.url = options.args[0];
54
+ try {
55
+ loadTest(options)
56
+ } catch(error) {
57
+ console.error(error.message)
58
+ help()
185
59
  }
186
-
187
- const ret = fs.readFileSync(filename, {encoding: 'utf8'}).replace("\n", "");
188
-
189
- return ret;
190
60
  }
191
61
 
62
+ await processAndRun(options)
63
+
192
64
  /**
193
65
  * Show online help.
194
66
  */
package/bin/testserver.js CHANGED
@@ -1,23 +1,16 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
2
 
4
- /**
5
- * Binary to run test server.
6
- * (C) 2013 Manuel Ernst, Alex Fernández.
7
- */
3
+ import * as stdio from 'stdio'
4
+ import {startServer} from '../lib/testserver.js'
5
+ import {loadConfig} from '../lib/config.js'
8
6
 
9
- // requires
10
- const stdio = require('stdio');
11
- const testServer = require('../lib/testserver');
12
- const config = require('../lib/config')
13
7
 
14
- // init
15
8
  const options = stdio.getopt({
16
9
  delay: {key: 'd', args: 1, description: 'Delay the response for the given milliseconds'},
17
10
  error: {key: 'e', args: 1, description: 'Return an HTTP error code'},
18
11
  percent: {key: 'p', args: 1, description: 'Return an error (default 500) only for some % of requests'},
19
12
  });
20
- const configuration = config.loadConfig(options)
13
+ const configuration = loadConfig()
21
14
  if (options.args && options.args.length == 1) {
22
15
  options.port = parseInt(options.args[0], 10);
23
16
  if (!options.port) {
@@ -45,5 +38,5 @@ if(!options.percent) {
45
38
  options.percent = configuration.percent
46
39
  }
47
40
 
48
- testServer.startServer(options);
41
+ startServer(options);
49
42
 
package/index.js CHANGED
@@ -1,16 +1,10 @@
1
- 'use strict';
1
+ import {loadTest} from './lib/loadtest.js'
2
+ import {startServer} from './lib/testserver.js'
2
3
 
3
- /**
4
- * Package contains a load test script and a test server.
5
- * (C) 2013 Alex Fernández.
6
- */
4
+ const loadtest = {loadTest, startServer}
7
5
 
6
+ export default loadtest
8
7
 
9
- // requires
10
- const loadtest = require('./lib/loadtest.js');
11
- const testserver = require('./lib/testserver.js');
12
-
13
- // exports
14
- exports.loadTest = loadtest.loadTest;
15
- exports.startServer = testserver.startServer;
8
+ export * from './lib/loadtest.js'
9
+ export * from './lib/testserver.js'
16
10
 
package/lib/baseClient.js CHANGED
@@ -1,14 +1,8 @@
1
- "use strict";
1
+ import * as urlLib from 'url'
2
+ import {addUserAgent} from './headers.js'
2
3
 
3
- const urlLib = require('url');
4
- const Log = require('log');
5
- const headers = require('./headers.js');
6
4
 
7
- // globals
8
- const log = new Log('info');
9
-
10
-
11
- class BaseClient {
5
+ export class BaseClient {
12
6
  constructor(operation, params) {
13
7
  this.operation = operation;
14
8
  this.params = params;
@@ -22,14 +16,11 @@ class BaseClient {
22
16
  return (error, result) => {
23
17
  let errorCode = null;
24
18
  if (error) {
25
- log.debug('Connection %s failed: %s', id, error);
26
19
  if (result) {
27
20
  errorCode = result;
28
21
  } else {
29
22
  errorCode = '-1';
30
23
  }
31
- } else {
32
- log.debug('Connection %s ended', id);
33
24
  }
34
25
  this.operation.latency.end(id, errorCode);
35
26
  let callback;
@@ -56,28 +47,20 @@ class BaseClient {
56
47
  this.options.agent = false;
57
48
  if (this.params.body) {
58
49
  if (typeof this.params.body == 'string') {
59
- log.debug('Received string body');
60
50
  this.generateMessage = () => this.params.body;
61
51
  } else if (typeof this.params.body == 'object') {
62
- log.debug('Received JSON body');
63
52
  this.generateMessage = () => this.params.body;
64
53
  } else if (typeof this.params.body == 'function') {
65
- log.debug('Received function body');
66
54
  this.generateMessage = this.params.body;
67
55
  } else {
68
- log.error('Unrecognized body: %s', typeof this.params.body);
56
+ console.error('Unrecognized body: %s', typeof this.params.body);
69
57
  }
70
58
  this.options.headers['Content-Type'] = this.params.contentType || 'text/plain';
71
59
  }
72
- headers.addUserAgent(this.options.headers);
60
+ addUserAgent(this.options.headers);
73
61
  if (this.params.secureProtocol) {
74
62
  this.options.secureProtocol = this.params.secureProtocol;
75
63
  }
76
- log.debug('Options: %j',this.options);
77
64
  }
78
65
  }
79
66
 
80
- module.exports = {
81
- BaseClient,
82
- }
83
-
package/lib/config.js CHANGED
@@ -1,12 +1,4 @@
1
- "use strict";
2
-
3
- /**
4
- * Support for configuration file.
5
- */
6
-
7
- // requires
8
- const Log = require("log");
9
- const {
1
+ import {
10
2
  Confinode,
11
3
  Level,
12
4
  anyItem,
@@ -18,21 +10,13 @@ const {
18
10
  numberItem,
19
11
  optional,
20
12
  stringItem
21
- } = require("confinode");
13
+ } from 'confinode'
22
14
 
23
- // globals
24
- const log = new Log("info");
25
15
 
26
16
  /**
27
17
  * Load configuration from file.
28
18
  */
29
- exports.loadConfig = function(options) {
30
- if (options.debug) {
31
- log.level = Log.DEBUG;
32
- }
33
- if (options.quiet) {
34
- log.level = Log.NOTICE;
35
- }
19
+ export function loadConfig() {
36
20
  const description = literal({
37
21
  delay: numberItem(0),
38
22
  error: numberItem(0),
@@ -61,7 +45,7 @@ exports.loadConfig = function(options) {
61
45
  const confinode = new Confinode("loadtest", description, { logger, mode: "sync" });
62
46
  const result = confinode.search();
63
47
  return result ? result.configuration : {};
64
- };
48
+ }
65
49
 
66
50
  /**
67
51
  * Logging function of confinode.
@@ -69,16 +53,16 @@ exports.loadConfig = function(options) {
69
53
  function logger(msg) {
70
54
  switch (msg.Level) {
71
55
  case Level.Error:
72
- log.error(msg.toString());
56
+ console.error(msg.toString());
73
57
  break;
74
58
  case Level.Warning:
75
- log.warning(msg.toString());
59
+ console.warn(msg.toString());
76
60
  break;
77
61
  case Level.Information:
78
- log.info(msg.toString());
62
+ console.info(msg.toString());
79
63
  break;
80
64
  default:
81
- log.debug(msg.toString());
65
+ // nothing
82
66
  }
83
67
  }
84
68
 
@@ -99,3 +83,4 @@ class CookieDescription {
99
83
  }
100
84
  }
101
85
  }
86
+