loadtest
Advanced tools
Comparing version 5.2.0 to 6.0.0
#!/usr/bin/env node | ||
'use strict'; | ||
/** | ||
* Binary to run loadtest. | ||
* (C) 2013 Manuel Ernst, Alex Fernández. | ||
*/ | ||
import {readFile} from 'fs/promises' | ||
import * as stdio from 'stdio' | ||
import {loadTest} from '../lib/loadtest.js' | ||
// requires | ||
const stdio = require('stdio'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const urlLib = require('url'); | ||
const loadTest = require('../lib/loadtest.js'); | ||
const headers = require('../lib/headers.js'); | ||
const packageJson = require(__dirname + '/../package.json'); | ||
const config = require('../lib/config'); | ||
// init | ||
const options = stdio.getopt({ | ||
@@ -42,152 +30,36 @@ maxRequests: {key: 'n', args: 1, description: 'Number of requests to perform'}, | ||
rps: {args: 1, description: 'Specify the requests per second for each client'}, | ||
agent: {description: 'Use a keep-alive http agent (deprecated)'}, | ||
index: {args: 1, description: 'Replace the value of given arg with an index in the URL'}, | ||
quiet: {description: 'Do not log any messages'}, | ||
debug: {description: 'Show debug messages'}, | ||
insecure: {description: 'Allow self-signed certificates over https'}, | ||
key: {args: 1, description: 'The client key to use'}, | ||
cert: {args: 1, description: 'The client certificate to use'} | ||
cert: {args: 1, description: 'The client certificate to use'}, | ||
agent: {description: 'Use a keep-alive http agent (deprecated)'}, | ||
quiet: {description: 'Do not log any messages (deprecated)'}, | ||
debug: {description: 'Show debug messages (deprecated)'} | ||
}); | ||
if (options.version) { | ||
console.log('Loadtest version: %s', packageJson.version); | ||
process.exit(0); | ||
} | ||
// is there an url? if not, break and display help | ||
if (!options.args || options.args.length < 1) { | ||
console.error('Missing URL to load-test'); | ||
help(); | ||
} else if (options.args.length > 1) { | ||
console.error('Too many arguments: %s', options.args); | ||
help(); | ||
} | ||
const configuration = config.loadConfig(options); | ||
options.url = options.args[0]; | ||
options.agentKeepAlive = options.keepalive || options.agent || configuration.agentKeepAlive; | ||
options.indexParam = options.index || configuration.indexParam; | ||
//TODO: add index Param | ||
// Allow a post body string in options | ||
// Ex -P '{"foo": "bar"}' | ||
if (options.postBody) { | ||
options.method = 'POST'; | ||
options.body = options.postBody; | ||
} | ||
if (options.postFile) { | ||
options.method = 'POST'; | ||
options.body = readBody(options.postFile, '-p'); | ||
} | ||
if (options.data) { | ||
options.body = JSON.parse(options.data); | ||
} | ||
if (options.method) { | ||
const acceptedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'get', 'post', 'put', 'delete', 'patch']; | ||
if (acceptedMethods.indexOf(options.method) === -1) { | ||
options.method = 'GET'; | ||
async function processAndRun(options) { | ||
if (options.version) { | ||
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url))) | ||
console.log('Loadtest version: %s', packageJson.version); | ||
process.exit(0); | ||
} | ||
} | ||
if(options.putFile) { | ||
options.method = 'PUT'; | ||
options.body = readBody(options.putFile, '-u'); | ||
} | ||
if (options.patchBody) { | ||
options.method = 'PATCH'; | ||
options.body = options.patchBody; | ||
} | ||
if(options.patchFile) { | ||
options.method = 'PATCH'; | ||
options.body = readBody(options.patchFile, '-a'); | ||
} | ||
if(!options.method) { | ||
options.method = configuration.method; | ||
} | ||
if(!options.body) { | ||
if(configuration.body) { | ||
options.body = configuration.body; | ||
} else if(configuration.file) { | ||
options.body = readBody(configuration.file, 'configuration.request.file'); | ||
} | ||
} | ||
options.requestsPerSecond = options.rps ? parseFloat(options.rps) : configuration.requestsPerSecond; | ||
if(!options.key) { | ||
options.key = configuration.key; | ||
} | ||
if(options.key) { | ||
options.key = fs.readFileSync(options.key); | ||
} | ||
if(!options.cert) { | ||
options.cert = configuration.cert; | ||
} | ||
if(options.cert) { | ||
options.cert = fs.readFileSync(options.cert); | ||
} | ||
const defaultHeaders = options.headers || !configuration.headers ? {} : configuration.headers; | ||
defaultHeaders['host'] = urlLib.parse(options.url).host; | ||
defaultHeaders['user-agent'] = 'loadtest/' + packageJson.version; | ||
defaultHeaders['accept'] = '*/*'; | ||
if (options.headers) { | ||
headers.addHeaders(options.headers, defaultHeaders); | ||
console.log('headers: %s, %j', typeof defaultHeaders, defaultHeaders); | ||
} | ||
options.headers = defaultHeaders; | ||
if (!options.requestGenerator) { | ||
options.requestGenerator = configuration.requestGenerator; | ||
} | ||
if (options.requestGenerator) { | ||
options.requestGenerator = require(path.resolve(options.requestGenerator)); | ||
} | ||
// Use configuration file for other values | ||
if(!options.maxRequests) { | ||
options.maxRequests = configuration.maxRequests; | ||
} | ||
if(!options.concurrency) { | ||
options.concurrency = configuration.concurrency; | ||
} | ||
if(!options.maxSeconds) { | ||
options.maxSeconds = configuration.maxSeconds; | ||
} | ||
if(!options.timeout && configuration.timeout) { | ||
options.timeout = configuration.timeout; | ||
} | ||
if(!options.contentType) { | ||
options.contentType = configuration.contentType; | ||
} | ||
if(!options.cookies) { | ||
options.cookies = configuration.cookies; | ||
} | ||
if(!options.secureProtocol) { | ||
options.secureProtocol = configuration.secureProtocol; | ||
} | ||
if(!options.insecure) { | ||
options.insecure = configuration.insecure; | ||
} | ||
if(!options.recover) { | ||
options.recover = configuration.recover; | ||
} | ||
if(!options.proxy) { | ||
options.proxy = configuration.proxy; | ||
} | ||
loadTest.loadTest(options); | ||
function readBody(filename, option) { | ||
if (typeof filename !== 'string') { | ||
console.error('Invalid file to open with %s: %s', option, filename); | ||
// is there an url? if not, break and display help | ||
if (!options.args || options.args.length < 1) { | ||
console.error('Missing URL to load-test'); | ||
help(); | ||
} else if (options.args.length > 1) { | ||
console.error('Too many arguments: %s', options.args); | ||
help(); | ||
} | ||
if(path.extname(filename) === '.js') { | ||
return require(path.resolve(filename)); | ||
options.url = options.args[0]; | ||
try { | ||
loadTest(options) | ||
} catch(error) { | ||
console.error(error.message) | ||
help() | ||
} | ||
} | ||
const ret = fs.readFileSync(filename, {encoding: 'utf8'}).replace("\n", ""); | ||
await processAndRun(options) | ||
return ret; | ||
} | ||
/** | ||
@@ -194,0 +66,0 @@ * Show online help. |
#!/usr/bin/env node | ||
'use strict'; | ||
/** | ||
* Binary to run test server. | ||
* (C) 2013 Manuel Ernst, Alex Fernández. | ||
*/ | ||
import * as stdio from 'stdio' | ||
import {startServer} from '../lib/testserver.js' | ||
import {loadConfig} from '../lib/config.js' | ||
// requires | ||
const stdio = require('stdio'); | ||
const testServer = require('../lib/testserver'); | ||
const config = require('../lib/config') | ||
// init | ||
const options = stdio.getopt({ | ||
@@ -20,3 +13,3 @@ delay: {key: 'd', args: 1, description: 'Delay the response for the given milliseconds'}, | ||
}); | ||
const configuration = config.loadConfig(options) | ||
const configuration = loadConfig() | ||
if (options.args && options.args.length == 1) { | ||
@@ -49,3 +42,3 @@ options.port = parseInt(options.args[0], 10); | ||
testServer.startServer(options); | ||
startServer(options); | ||
18
index.js
@@ -1,16 +0,10 @@ | ||
'use strict'; | ||
import {loadTest} from './lib/loadtest.js' | ||
import {startServer} from './lib/testserver.js' | ||
/** | ||
* Package contains a load test script and a test server. | ||
* (C) 2013 Alex Fernández. | ||
*/ | ||
const loadtest = {loadTest, startServer} | ||
export default loadtest | ||
// requires | ||
const loadtest = require('./lib/loadtest.js'); | ||
const testserver = require('./lib/testserver.js'); | ||
export * from './lib/loadtest.js' | ||
export * from './lib/testserver.js' | ||
// exports | ||
exports.loadTest = loadtest.loadTest; | ||
exports.startServer = testserver.startServer; | ||
@@ -1,12 +0,6 @@ | ||
"use strict"; | ||
import * as urlLib from 'url' | ||
import {addUserAgent} from './headers.js' | ||
const urlLib = require('url'); | ||
const Log = require('log'); | ||
const headers = require('./headers.js'); | ||
// globals | ||
const log = new Log('info'); | ||
class BaseClient { | ||
export class BaseClient { | ||
constructor(operation, params) { | ||
@@ -25,3 +19,2 @@ this.operation = operation; | ||
if (error) { | ||
log.debug('Connection %s failed: %s', id, error); | ||
if (result) { | ||
@@ -32,4 +25,2 @@ errorCode = result; | ||
} | ||
} else { | ||
log.debug('Connection %s ended', id); | ||
} | ||
@@ -61,26 +52,18 @@ this.operation.latency.end(id, errorCode); | ||
if (typeof this.params.body == 'string') { | ||
log.debug('Received string body'); | ||
this.generateMessage = () => this.params.body; | ||
} else if (typeof this.params.body == 'object') { | ||
log.debug('Received JSON body'); | ||
this.generateMessage = () => this.params.body; | ||
} else if (typeof this.params.body == 'function') { | ||
log.debug('Received function body'); | ||
this.generateMessage = this.params.body; | ||
} else { | ||
log.error('Unrecognized body: %s', typeof this.params.body); | ||
console.error('Unrecognized body: %s', typeof this.params.body); | ||
} | ||
this.options.headers['Content-Type'] = this.params.contentType || 'text/plain'; | ||
} | ||
headers.addUserAgent(this.options.headers); | ||
addUserAgent(this.options.headers); | ||
if (this.params.secureProtocol) { | ||
this.options.secureProtocol = this.params.secureProtocol; | ||
} | ||
log.debug('Options: %j',this.options); | ||
} | ||
} | ||
module.exports = { | ||
BaseClient, | ||
} | ||
@@ -1,10 +0,2 @@ | ||
"use strict"; | ||
/** | ||
* Support for configuration file. | ||
*/ | ||
// requires | ||
const Log = require("log"); | ||
const { | ||
import { | ||
Confinode, | ||
@@ -21,6 +13,4 @@ Level, | ||
stringItem | ||
} = require("confinode"); | ||
} from 'confinode' | ||
// globals | ||
const log = new Log("info"); | ||
@@ -30,9 +20,3 @@ /** | ||
*/ | ||
exports.loadConfig = function(options) { | ||
if (options.debug) { | ||
log.level = Log.DEBUG; | ||
} | ||
if (options.quiet) { | ||
log.level = Log.NOTICE; | ||
} | ||
export function loadConfig() { | ||
const description = literal({ | ||
@@ -66,3 +50,3 @@ delay: numberItem(0), | ||
return result ? result.configuration : {}; | ||
}; | ||
} | ||
@@ -75,12 +59,12 @@ /** | ||
case Level.Error: | ||
log.error(msg.toString()); | ||
console.error(msg.toString()); | ||
break; | ||
case Level.Warning: | ||
log.warning(msg.toString()); | ||
console.warn(msg.toString()); | ||
break; | ||
case Level.Information: | ||
log.info(msg.toString()); | ||
console.info(msg.toString()); | ||
break; | ||
default: | ||
log.debug(msg.toString()); | ||
// nothing | ||
} | ||
@@ -106,1 +90,2 @@ } | ||
} | ||
@@ -1,16 +0,7 @@ | ||
'use strict'; | ||
/** | ||
* Support for custom headers. | ||
* (C) 2013 Alex Fernández. | ||
*/ | ||
// requires | ||
const testing = require('testing'); | ||
/** | ||
* Add all raw headers given to the given array. | ||
*/ | ||
exports.addHeaders = function(rawHeaders, headers) { | ||
export function addHeaders(rawHeaders, headers) { | ||
if (Array.isArray(rawHeaders)) { | ||
@@ -25,3 +16,3 @@ rawHeaders.forEach(function(header) { | ||
} | ||
}; | ||
} | ||
@@ -41,62 +32,10 @@ /** | ||
function testAddHeaders(callback) { | ||
const tests = [ { | ||
raw: 'k:v', | ||
headers: { 'k': 'v' } | ||
}, { | ||
raw: ['k:v', 'k:v2'], | ||
headers: { 'k': 'v2' } | ||
}, { | ||
raw: ['k:v', 'k2:v2'], | ||
headers: { 'k': 'v', 'k2': 'v2' } | ||
}, { | ||
raw: 'K:v', | ||
headers: { 'k': 'v' } | ||
}, { | ||
raw: 'k:v:w', | ||
headers: { 'k': 'v:w' } | ||
} | ||
]; | ||
tests.forEach(function(test) { | ||
const headers = {}; | ||
exports.addHeaders(test.raw, headers); | ||
testing.assertEquals(headers, test.headers, 'Wrong headers', callback); | ||
}); | ||
testing.success(callback); | ||
} | ||
/** | ||
* Add a user-agent header if not present. | ||
*/ | ||
exports.addUserAgent = function(headers) { | ||
export function addUserAgent(headers) { | ||
if(!headers['user-agent']) { | ||
headers['user-agent'] = 'node.js loadtest bot'; | ||
} | ||
}; | ||
function testAddUserAgent(callback) { | ||
const headers = {'k': 'v', 'q': 'r' }; | ||
exports.addUserAgent(headers); | ||
testing.assertEquals(Object.keys(headers).length, 3, 'Did not add user agent', callback); | ||
const userAgent = headers['user-agent']; | ||
testing.assert(userAgent.includes('bot'), 'Invalid user agent', callback); | ||
exports.addUserAgent(headers); | ||
testing.assertEquals(Object.keys(headers).length, 3, 'Should not add user agent', callback); | ||
testing.success(callback); | ||
} | ||
/** | ||
* Run all tests. | ||
*/ | ||
exports.test = function(callback) { | ||
testing.run([ | ||
testAddHeaders, | ||
testAddUserAgent, | ||
], callback); | ||
}; | ||
// run tests if invoked directly | ||
if (__filename == process.argv[1]) { | ||
exports.test(testing.show); | ||
} | ||
@@ -1,17 +0,3 @@ | ||
'use strict'; | ||
/** | ||
* Measure latency for a load test. | ||
* (C) 2013 Alex Fernández. | ||
*/ | ||
// requires | ||
const testing = require('testing'); | ||
const Log = require('log'); | ||
// globals | ||
const log = new Log('info'); | ||
/** | ||
@@ -22,3 +8,3 @@ * A high resolution timer. Params: | ||
*/ | ||
class HighResolutionTimer { | ||
export class HighResolutionTimer { | ||
@@ -62,3 +48,3 @@ /** | ||
const drift = diff / this.delayMs - this.counter; | ||
log.debug('Seconds: ' + Math.round(diff / 1000) + ', counter: ' + this.counter + ', drift: ' + drift); | ||
console.debug('Seconds: ' + Math.round(diff / 1000) + ', counter: ' + this.counter + ', drift: ' + drift); | ||
} | ||
@@ -82,40 +68,1 @@ | ||
/** | ||
* Test a high resolution timer. | ||
*/ | ||
function testTimerStop(callback) { | ||
const timer = new HighResolutionTimer(10, callback); | ||
setImmediate(() => timer.stop()) | ||
} | ||
function testTimerRun(callback) { | ||
let run = 0 | ||
const timer = new HighResolutionTimer(100, () => run++) | ||
setTimeout(() => { | ||
testing.equals(run, 3, callback) | ||
timer.stop() | ||
testing.success(callback) | ||
}, 250) | ||
} | ||
/** | ||
* Run package tests. | ||
*/ | ||
function test(callback) { | ||
const tests = [ | ||
testTimerStop, | ||
testTimerRun, | ||
]; | ||
testing.run(tests, callback); | ||
} | ||
module.exports = { | ||
HighResolutionTimer, | ||
test, | ||
} | ||
// run tests if invoked directly | ||
if (__filename == process.argv[1]) { | ||
test(testing.show); | ||
} | ||
@@ -1,24 +0,12 @@ | ||
'use strict'; | ||
import * as urlLib from 'url' | ||
import * as http from 'http' | ||
import * as https from 'https' | ||
import * as querystring from 'querystring' | ||
import * as websocket from 'websocket' | ||
import {HighResolutionTimer} from './hrtimer.js' | ||
import {addUserAgent} from './headers.js' | ||
import * as agentkeepalive from 'agentkeepalive' | ||
import * as HttpsProxyAgent from 'https-proxy-agent' | ||
/** | ||
* Load Test a URL, website or websocket. | ||
* (C) 2013 Alex Fernández. | ||
*/ | ||
// requires | ||
const testing = require('testing'); | ||
const urlLib = require('url'); | ||
const http = require('http'); | ||
const https = require('https'); | ||
const qs = require('querystring'); | ||
const websocket = require('websocket'); | ||
const Log = require('log'); | ||
const {HighResolutionTimer} = require('./hrtimer.js'); | ||
const headers = require('./headers.js'); | ||
// globals | ||
const log = new Log('info'); | ||
/** | ||
@@ -28,5 +16,5 @@ * Create a new HTTP client. | ||
*/ | ||
exports.create = function(operation, params) { | ||
export function create(operation, params) { | ||
return new HttpClient(operation, params); | ||
}; | ||
} | ||
@@ -62,3 +50,3 @@ /** | ||
if (this.params.agentKeepAlive) { | ||
const KeepAlive = (this.options.protocol == 'https:') ? require('agentkeepalive').HttpsAgent : require('agentkeepalive'); | ||
const KeepAlive = (this.options.protocol == 'https:') ? agentkeepalive.HttpsAgent : agentkeepalive; | ||
let maxSockets = 10; | ||
@@ -79,15 +67,12 @@ if (this.params.requestsPerSecond) { | ||
if (typeof this.params.body == 'string') { | ||
log.debug('Received string body'); | ||
this.generateMessage = () => this.params.body; | ||
} else if (typeof this.params.body == 'object') { | ||
log.debug('Received JSON body'); | ||
if (this.params.contentType === 'application/x-www-form-urlencoded') { | ||
this.params.body = qs.stringify(this.params.body); | ||
this.params.body = querystring.stringify(this.params.body); | ||
} | ||
this.generateMessage = () => this.params.body; | ||
} else if (typeof this.params.body == 'function') { | ||
log.debug('Received function body'); | ||
this.generateMessage = this.params.body; | ||
} else { | ||
log.error('Unrecognized body: %s', typeof this.params.body); | ||
console.error('Unrecognized body: %s', typeof this.params.body); | ||
} | ||
@@ -105,7 +90,6 @@ this.options.headers['Content-Type'] = this.params.contentType || 'text/plain'; | ||
} | ||
headers.addUserAgent(this.options.headers); | ||
addUserAgent(this.options.headers); | ||
if (this.params.secureProtocol) { | ||
this.options.secureProtocol = this.params.secureProtocol; | ||
} | ||
log.debug('Options: %j', this.options); | ||
} | ||
@@ -152,3 +136,2 @@ | ||
} | ||
const HttpsProxyAgent = require('https-proxy-agent'); | ||
@@ -186,3 +169,3 @@ // adding proxy configuration | ||
if (!timeout) { | ||
log.error('Invalid timeout %s', this.params.timeout); | ||
console.error('Invalid timeout %s', this.params.timeout); | ||
} | ||
@@ -209,3 +192,2 @@ request.setTimeout(timeout, () => { | ||
if (error) { | ||
log.debug('Connection %s failed: %s', id, error); | ||
if (result) { | ||
@@ -219,4 +201,2 @@ errorCode = result.statusCode; | ||
} | ||
} else { | ||
log.debug('Connection %s ended', id); | ||
} | ||
@@ -249,6 +229,4 @@ | ||
return connection => { | ||
log.debug('HTTP client connected to %s with id %s', this.params.url, id); | ||
connection.setEncoding('utf8'); | ||
connection.on('data', chunk => { | ||
log.debug('Body: %s', chunk); | ||
body += chunk; | ||
@@ -287,27 +265,1 @@ }); | ||
function testHttpClient(callback) { | ||
const options = { | ||
url: 'http://localhost:7357/', | ||
maxSeconds: 0.1, | ||
concurrency: 1, | ||
quiet: true, | ||
}; | ||
exports.create({}, options); | ||
testing.success(callback); | ||
} | ||
/** | ||
* Run all tests. | ||
*/ | ||
exports.test = function (callback) { | ||
testing.run([ | ||
testHttpClient, | ||
], callback); | ||
}; | ||
// run tests if invoked directly | ||
if (__filename == process.argv[1]) { | ||
exports.test(testing.show); | ||
} | ||
@@ -1,19 +0,4 @@ | ||
'use strict'; | ||
import * as crypto from 'crypto' | ||
/** | ||
* Measure latency for a load test. | ||
* (C) 2013 Alex Fernández. | ||
*/ | ||
// requires | ||
const testing = require('testing'); | ||
const util = require('util'); | ||
const crypto = require('crypto'); | ||
const Log = require('log'); | ||
// globals | ||
const log = new Log('info'); | ||
/** | ||
@@ -23,7 +8,6 @@ * Latency measurements. Options can be: | ||
* - maxSeconds: max seconds, alternative to max requests. | ||
* - quiet: do not log messages. | ||
* An optional callback(error, results) will be called with an error, | ||
* or the results after max is reached. | ||
*/ | ||
class Latency { | ||
export class Latency { | ||
constructor(options, callback) { | ||
@@ -49,8 +33,2 @@ this.options = options | ||
this.requestIdToIndex = {}; | ||
if (options.quiet) { | ||
log.level = Log.NOTICE; | ||
} | ||
if (options.debug) { | ||
log.level = Log.DEBUG; | ||
} | ||
} | ||
@@ -82,3 +60,2 @@ | ||
if (!(requestId in this.requests)) { | ||
log.debug('Message id ' + requestId + ' not found'); | ||
return -2; | ||
@@ -100,3 +77,2 @@ } | ||
add(time, errorCode) { | ||
log.debug('New value: %s', time); | ||
this.partialTime += time; | ||
@@ -115,3 +91,2 @@ this.partialRequests++; | ||
} | ||
log.debug('Partial requests: %s', this.partialRequests); | ||
const rounded = Math.floor(time); | ||
@@ -125,3 +100,2 @@ if (rounded > this.maxLatencyMs) { | ||
if (!this.histogramMs[rounded]) { | ||
log.debug('Initializing histogram for %s', rounded); | ||
this.histogramMs[rounded] = 0; | ||
@@ -149,6 +123,6 @@ } | ||
} | ||
log.info('Requests: %s%s, requests per second: %s, mean latency: %s ms', this.totalRequests, percent, results.rps, results.meanLatencyMs); | ||
console.info('Requests: %s%s, requests per second: %s, mean latency: %s ms', this.totalRequests, percent, results.rps, results.meanLatencyMs); | ||
if (this.totalErrors) { | ||
percent = Math.round(100 * 10 * this.totalErrors / this.totalRequests) / 10; | ||
log.info('Errors: %s, accumulated errors: %s, %s% of total requests', this.partialErrors, this.totalErrors, percent); | ||
console.info('Errors: %s, accumulated errors: %s, %s% of total requests', this.partialErrors, this.totalErrors, percent); | ||
} | ||
@@ -183,5 +157,3 @@ this.partialTime = 0; | ||
isFinished() { | ||
log.debug('Total requests %s, max requests: %s', this.totalRequests, this.options.maxRequests); | ||
if (this.options.maxRequests && this.totalRequests >= this.options.maxRequests) { | ||
log.debug('Max requests reached: %s', this.totalRequests); | ||
return true; | ||
@@ -191,3 +163,2 @@ } | ||
if (this.options.maxSeconds && elapsedSeconds >= this.options.maxSeconds) { | ||
log.debug('Max seconds reached: %s', this.totalRequests); | ||
return true; | ||
@@ -206,3 +177,2 @@ } | ||
} | ||
this.show(); | ||
} | ||
@@ -233,3 +203,2 @@ | ||
computePercentiles() { | ||
log.debug('Histogram: %s', util.inspect(this.histogramMs)); | ||
const percentiles = { | ||
@@ -247,3 +216,2 @@ 50: false, | ||
} | ||
log.debug('Histogram for %s: %s', ms, this.histogramMs[ms]); | ||
counted += this.histogramMs[ms]; | ||
@@ -253,3 +221,2 @@ const percent = counted / this.totalRequests * 100; | ||
Object.keys(percentiles).forEach(percentile => { | ||
log.debug('Checking percentile %s for %s', percentile, percent); | ||
if (!percentiles[percentile] && percent > percentile) { | ||
@@ -272,10 +239,10 @@ percentiles[percentile] = ms; | ||
const results = this.getResults(); | ||
log.info(''); | ||
log.info('Target URL: %s', this.options.url); | ||
console.info(''); | ||
console.info('Target URL: %s', this.options.url); | ||
if (this.options.maxRequests) { | ||
log.info('Max requests: %s', this.options.maxRequests); | ||
console.info('Max requests: %s', this.options.maxRequests); | ||
} else if (this.options.maxSeconds) { | ||
log.info('Max time (s): %s', this.options.maxSeconds); | ||
console.info('Max time (s): %s', this.options.maxSeconds); | ||
} | ||
log.info('Concurrency level: %s', this.options.concurrency); | ||
console.info('Concurrency level: %s', this.options.concurrency); | ||
let agent = 'none'; | ||
@@ -285,27 +252,27 @@ if (this.options.agentKeepAlive) { | ||
} | ||
log.info('Agent: %s', agent); | ||
console.info('Agent: %s', agent); | ||
if (this.options.requestsPerSecond) { | ||
log.info('Requests per second: %s', this.options.requestsPerSecond * this.options.concurrency); | ||
console.info('Requests per second: %s', this.options.requestsPerSecond * this.options.concurrency); | ||
} | ||
log.info(''); | ||
log.info('Completed requests: %s', results.totalRequests); | ||
log.info('Total errors: %s', results.totalErrors); | ||
log.info('Total time: %s s', results.totalTimeSeconds); | ||
log.info('Requests per second: %s', results.rps); | ||
log.info('Mean latency: %s ms', results.meanLatencyMs); | ||
log.info(''); | ||
log.info('Percentage of the requests served within a certain time'); | ||
console.info(''); | ||
console.info('Completed requests: %s', results.totalRequests); | ||
console.info('Total errors: %s', results.totalErrors); | ||
console.info('Total time: %s s', results.totalTimeSeconds); | ||
console.info('Requests per second: %s', results.rps); | ||
console.info('Mean latency: %s ms', results.meanLatencyMs); | ||
console.info(''); | ||
console.info('Percentage of the requests served within a certain time'); | ||
Object.keys(results.percentiles).forEach(percentile => { | ||
log.info(' %s% %s ms', percentile, results.percentiles[percentile]); | ||
console.info(' %s% %s ms', percentile, results.percentiles[percentile]); | ||
}); | ||
log.info(' 100% %s ms (longest request)', this.maxLatencyMs); | ||
console.info(' 100% %s ms (longest request)', this.maxLatencyMs); | ||
if (results.totalErrors) { | ||
log.info(''); | ||
log.info(' 100% %s ms (longest request)', this.maxLatencyMs); | ||
log.info(''); | ||
console.info(''); | ||
console.info(' 100% %s ms (longest request)', this.maxLatencyMs); | ||
console.info(''); | ||
Object.keys(results.errorCodes).forEach(errorCode => { | ||
const padding = ' '.repeat(errorCode.length < 4 ? 4 - errorCode.length : 1); | ||
log.info(' %s%s: %s errors', padding, errorCode, results.errorCodes[errorCode]); | ||
console.info(' %s%s: %s errors', padding, errorCode, results.errorCodes[errorCode]); | ||
}); | ||
@@ -326,90 +293,1 @@ } | ||
/** | ||
* Test latency ids. | ||
*/ | ||
function testLatencyIds(callback) { | ||
const latency = new Latency({}); | ||
const firstId = latency.start(); | ||
testing.assert(firstId, 'Invalid first latency id %s', firstId, callback); | ||
const secondId = latency.start(); | ||
testing.assert(secondId, 'Invalid second latency id', callback); | ||
testing.assert(firstId != secondId, 'Repeated latency ids', callback); | ||
testing.success(callback); | ||
} | ||
/** | ||
* Test latency measurements. | ||
*/ | ||
function testLatencyRequests(callback) { | ||
const options = { | ||
maxRequests: 10, | ||
}; | ||
const errorCode = '500'; | ||
const latency = new Latency(options, (error, result) => { | ||
testing.check(error, 'Could not compute latency', callback); | ||
testing.assertEquals(result.totalRequests, 10, 'Invalid total requests', callback); | ||
testing.assertEquals(result.totalErrors, 1, 'Invalid total errors', callback); | ||
testing.assert(errorCode in result.errorCodes, 'Error code not found', callback); | ||
testing.assertEquals(result.errorCodes[errorCode], 1, 'Should have one ' + errorCode, callback); | ||
testing.success(callback); | ||
}); | ||
let id; | ||
for (let i = 0; i < 9; i++) { | ||
id = latency.start(); | ||
latency.end(id); | ||
} | ||
id = latency.start(); | ||
latency.end(id, errorCode); | ||
} | ||
/** | ||
* Check that percentiles are correct. | ||
*/ | ||
function testLatencyPercentiles(callback) { | ||
const options = { | ||
maxRequests: 10 | ||
}; | ||
const latency = new Latency(options, error => { | ||
testing.check(error, 'Error while testing latency percentiles', callback); | ||
const percentiles = latency.getResults().percentiles; | ||
Object.keys(percentiles).forEach(percentile => { | ||
testing.assert(percentiles[percentile] !== false, 'Empty percentile for %s', percentile, callback); | ||
}); | ||
testing.success(percentiles, callback); | ||
}); | ||
for (let ms = 1; ms <= 10; ms++) { | ||
log.debug('Starting %s', ms); | ||
(function() { | ||
const id = latency.start(); | ||
setTimeout(() => { | ||
log.debug('Ending %s', id); | ||
latency.end(id); | ||
}, ms); | ||
})(); | ||
} | ||
} | ||
/** | ||
* Run package tests. | ||
*/ | ||
function test(callback) { | ||
const tests = [ | ||
testLatencyIds, | ||
testLatencyRequests, | ||
testLatencyPercentiles, | ||
]; | ||
testing.run(tests, callback); | ||
} | ||
module.exports = { | ||
Latency, | ||
test, | ||
} | ||
// run tests if invoked directly | ||
if (__filename == process.argv[1]) { | ||
test(testing.show); | ||
} | ||
@@ -1,26 +0,11 @@ | ||
'use strict'; | ||
import * as http from 'http' | ||
import * as https from 'https' | ||
import * as httpClient from './httpClient.js' | ||
import * as websocket from './websocket.js' | ||
import {Latency} from './latency.js' | ||
import {HighResolutionTimer} from './hrtimer.js' | ||
import {processOptions} from './options.js' | ||
/** | ||
* Load Test a URL, website or websocket. | ||
* (C) 2013 Alex Fernández. | ||
*/ | ||
// requires | ||
const Log = require('log'); | ||
const http = require('http'); | ||
const https = require('https'); | ||
const testing = require('testing'); | ||
const httpClient = require('./httpClient.js'); | ||
const websocket = require('./websocket.js'); | ||
const {Latency} = require('./latency.js'); | ||
const {HighResolutionTimer} = require('./hrtimer.js'); | ||
// globals | ||
const log = new Log('info'); | ||
// constants | ||
const SHOW_INTERVAL_MS = 5000; | ||
// init | ||
http.globalAgent.maxSockets = 1000; | ||
@@ -44,39 +29,20 @@ https.globalAgent.maxSockets = 1000; | ||
* - agentKeepAlive: if true, then use connection keep-alive. | ||
* - debug: show debug messages. | ||
* - quiet: do not log any messages. | ||
* - indexParam: string to replace with a unique index. | ||
* - insecure: allow https using self-signed certs. | ||
* - debug: show debug messages (deprecated). | ||
* - quiet: do not log any messages (deprecated). | ||
* An optional callback will be called if/when the test finishes. | ||
*/ | ||
exports.loadTest = function(options, callback) { | ||
if (!options.url) { | ||
log.error('Missing URL in options'); | ||
return; | ||
} | ||
options.concurrency = options.concurrency || 1; | ||
if (options.requestsPerSecond) { | ||
options.requestsPerSecond = options.requestsPerSecond / options.concurrency; | ||
} | ||
if (options.debug) { | ||
log.level = Log.DEBUG; | ||
} | ||
if (!options.url.startsWith('http://') && !options.url.startsWith('https://') && !options.url.startsWith('ws://')) { | ||
log.error('Invalid URL %s, must be http://, https:// or ws://', options.url); | ||
return; | ||
} | ||
if (callback && !('quiet' in options)) { | ||
options.quiet = true; | ||
} | ||
if (options.url.startsWith('ws:')) { | ||
if (options.requestsPerSecond) { | ||
log.error('"requestsPerSecond" not supported for WebSockets'); | ||
export function loadTest(options, callback) { | ||
processOptions(options, error => { | ||
if (error) { | ||
if (callback) return callback(error) | ||
throw new error | ||
} | ||
} | ||
const operation = new Operation(options, callback); | ||
operation.start(); | ||
return operation; | ||
}) | ||
} | ||
const operation = new Operation(options, callback); | ||
operation.start(); | ||
return operation; | ||
}; | ||
/** | ||
@@ -126,5 +92,2 @@ * Used to keep track of individual load test Operation runs. | ||
} | ||
if (this.requests > this.options.maxRequests) { | ||
log.debug('Should have no more running clients'); | ||
} | ||
} | ||
@@ -207,95 +170,1 @@ if (this.running && next) { | ||
/** | ||
* A load test with max seconds. | ||
*/ | ||
function testMaxSeconds(callback) { | ||
const options = { | ||
url: 'http://localhost:7357/', | ||
maxSeconds: 0.1, | ||
concurrency: 1, | ||
quiet: true, | ||
}; | ||
exports.loadTest(options, callback); | ||
} | ||
/** | ||
* A load test with max seconds. | ||
*/ | ||
function testWSEcho(callback) { | ||
const options = { | ||
url: 'ws://localhost:7357/', | ||
maxSeconds: 0.1, | ||
concurrency: 1, | ||
quiet: true, | ||
}; | ||
exports.loadTest(options, callback); | ||
} | ||
function testIndexParam(callback) { | ||
const options = { | ||
url: 'http://localhost:7357/replace', | ||
concurrency:1, | ||
quiet: true, | ||
maxSeconds: 0.1, | ||
indexParam: "replace" | ||
}; | ||
exports.loadTest(options, callback); | ||
} | ||
function testIndexParamWithBody(callback) { | ||
const options = { | ||
url: 'http://localhost:7357/replace', | ||
concurrency:1, | ||
quiet: true, | ||
maxSeconds: 0.1, | ||
indexParam: "replace", | ||
body: '{"id": "replace"}' | ||
}; | ||
exports.loadTest(options, callback); | ||
} | ||
function testIndexParamWithCallback(callback) { | ||
const options = { | ||
url: 'http://localhost:7357/replace', | ||
concurrency:1, | ||
quiet: true, | ||
maxSeconds: 0.1, | ||
indexParam: "replace", | ||
indexParamCallback: function() { | ||
//https://gist.github.com/6174/6062387 | ||
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); | ||
} | ||
}; | ||
exports.loadTest(options, callback); | ||
} | ||
function testIndexParamWithCallbackAndBody(callback) { | ||
const options = { | ||
url: 'http://localhost:7357/replace', | ||
concurrency:1, | ||
quiet: true, | ||
maxSeconds: 0.1, | ||
body: '{"id": "replace"}', | ||
indexParam: "replace", | ||
indexParamCallback: function() { | ||
//https://gist.github.com/6174/6062387 | ||
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); | ||
} | ||
}; | ||
exports.loadTest(options, callback); | ||
} | ||
/** | ||
* Run all tests. | ||
*/ | ||
exports.test = function(callback) { | ||
testing.run([testMaxSeconds, testWSEcho, testIndexParam, testIndexParamWithBody, testIndexParamWithCallback, testIndexParamWithCallbackAndBody], callback); | ||
}; | ||
// run tests if invoked directly | ||
if (__filename == process.argv[1]) { | ||
exports.test(testing.show); | ||
} | ||
@@ -1,22 +0,7 @@ | ||
'use strict'; | ||
import * as http from 'http' | ||
import {server as WebSocketServer} from 'websocket' | ||
import * as util from 'util' | ||
import * as net from 'net' | ||
import {Latency} from './latency.js' | ||
/** | ||
* Test server to load test. | ||
* (C) 2013 Alex Fernández. | ||
*/ | ||
// requires | ||
const testing = require('testing'); | ||
const http = require('http'); | ||
const WebSocketServer = require('websocket').server; | ||
const util = require('util'); | ||
const net = require('net'); | ||
const Log = require('log'); | ||
const {Latency} = require('./latency.js'); | ||
// globals | ||
const log = new Log('info'); | ||
// constants | ||
const PORT = 7357; | ||
@@ -37,5 +22,2 @@ const LOG_HEADERS_INTERVAL_SECONDS = 1; | ||
this.debuggedTime = Date.now(); | ||
if (options.quiet) { | ||
log.level = 'notice'; | ||
} | ||
} | ||
@@ -67,3 +49,3 @@ | ||
this.server.listen(this.port, () => { | ||
log.info('Listening on port %s', this.port); | ||
console.info(`Listening on http://localhost:${this.port}/`); | ||
if (callback) { | ||
@@ -76,9 +58,6 @@ callback(); | ||
const connection = request.accept(null, request.origin); | ||
log.debug(' Connection accepted.'); | ||
connection.on('message', message => { | ||
if (message.type === 'utf8') { | ||
log.debug('Received Message: ' + message.utf8Data); | ||
connection.sendUTF(message.utf8Data); | ||
} else if (message.type === 'binary') { | ||
log.debug('Received Binary Message of ' + message.binaryData.length + ' bytes'); | ||
connection.sendBytes(message.binaryData); | ||
@@ -88,3 +67,3 @@ } | ||
connection.on('close', () => { | ||
log.info('Peer %s disconnected', connection.remoteAddress); | ||
console.info('Peer %s disconnected', connection.remoteAddress); | ||
}); | ||
@@ -100,3 +79,3 @@ }); | ||
if (!callback) { | ||
return log.error(message); | ||
return console.error(message); | ||
} | ||
@@ -135,3 +114,3 @@ callback(message); | ||
socket.on('error', error => { | ||
log.error('socket error: %s', error); | ||
console.error('socket error: %s', error); | ||
socket.end(); | ||
@@ -146,3 +125,3 @@ }); | ||
readData(data) { | ||
log.info('data: %s', data); | ||
console.info('data: %s', data); | ||
} | ||
@@ -154,5 +133,5 @@ | ||
debug(request) { | ||
log.info('Headers for %s to %s: %s', request.method, request.url, util.inspect(request.headers)); | ||
console.info('Headers for %s to %s: %s', request.method, request.url, util.inspect(request.headers)); | ||
if (request.body) { | ||
log.info('Body: %s', request.body); | ||
console.info('Body: %s', request.body); | ||
} | ||
@@ -184,3 +163,3 @@ } | ||
if (!percent) { | ||
log.error('Invalid error percent %s', this.options.percent); | ||
console.error('Invalid error percent %s', this.options.percent); | ||
return false; | ||
@@ -196,40 +175,11 @@ } | ||
* - delay: wait the given milliseconds before answering. | ||
* - quiet: do not log any messages. | ||
* - quiet: do not log any messages (deprecated). | ||
* - percent: give an error (default 500) on some % of requests. | ||
* - error: set an HTTP error code, default is 500. | ||
* An optional callback is called after the server has started. | ||
* In this case the quiet option is enabled. | ||
*/ | ||
exports.startServer = function(options, callback) { | ||
if (callback) { | ||
options.quiet = true; | ||
} | ||
export function startServer(options, callback) { | ||
const server = new TestServer(options); | ||
return server.start(callback); | ||
}; | ||
function testStartServer(callback) { | ||
const options = { | ||
port: 10530, | ||
}; | ||
const server = exports.startServer(options, error => { | ||
testing.check(error, 'Could not start server', callback); | ||
server.close(error => { | ||
testing.check(error, 'Could not stop server', callback); | ||
testing.success(callback); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Run the tests. | ||
*/ | ||
exports.test = function(callback) { | ||
testing.run([testStartServer], 5000, callback); | ||
}; | ||
// start server if invoked directly | ||
if (__filename == process.argv[1]) { | ||
exports.test(testing.show); | ||
} | ||
@@ -1,17 +0,4 @@ | ||
'use strict'; | ||
import websocket from 'websocket' | ||
import {BaseClient} from './baseClient.js' | ||
/** | ||
* Load test a websocket. | ||
* (C) 2013 Alex Fernández. | ||
*/ | ||
// requires | ||
const WebSocketClient = require('websocket').client; | ||
const testing = require('testing'); | ||
const Log = require('log'); | ||
const BaseClient = require('./baseClient.js').BaseClient; | ||
// globals | ||
const log = new Log('info'); | ||
let latency; | ||
@@ -23,5 +10,5 @@ | ||
*/ | ||
exports.create = function(operation, params) { | ||
export function create(operation, params) { | ||
return new WebsocketClient(operation, params); | ||
}; | ||
} | ||
@@ -44,9 +31,6 @@ /** | ||
start() { | ||
this.client = new WebSocketClient(); | ||
this.client.on('connectFailed', error => { | ||
log.debug('WebSocket client connection error ' + error); | ||
}); | ||
this.client = new websocket.client(); | ||
this.client.on('connectFailed', () => {}); | ||
this.client.on('connect', connection => this.connect(connection)); | ||
this.client.connect(this.params.url, []); | ||
log.debug('WebSocket client connected to ' + this.params.url); | ||
} | ||
@@ -60,3 +44,2 @@ | ||
this.connection.close(); | ||
log.debug('WebSocket client disconnected from ' + this.params.url); | ||
} | ||
@@ -106,3 +89,3 @@ } | ||
if (message.type != 'utf8') { | ||
log.error('Invalid message type ' + message.type); | ||
console.error('Invalid message type ' + message.type); | ||
return; | ||
@@ -115,8 +98,6 @@ } | ||
catch(e) { | ||
log.error('Invalid JSON: ' + message.utf8Data); | ||
console.error('Invalid JSON: ' + message.utf8Data); | ||
return; | ||
} | ||
log.debug("Received response %j", json); | ||
// eat the client_connected message we get at the beginning | ||
@@ -131,3 +112,2 @@ if ((json && json[0] && json[0][0] == 'client_connected')) { | ||
latency.add(newCall - this.lastCall); | ||
log.debug('latency: ' + (newCall - this.lastCall)); | ||
this.lastCall = null; | ||
@@ -161,27 +141,1 @@ } | ||
function testWebsocketClient(callback) { | ||
const options = { | ||
url: 'ws://localhost:7357/', | ||
maxSeconds: 0.1, | ||
concurrency: 1, | ||
quiet: true, | ||
}; | ||
exports.create({}, options); | ||
testing.success(callback); | ||
} | ||
/** | ||
* Run tests, currently nothing. | ||
*/ | ||
exports.test = function(callback) { | ||
testing.run([ | ||
testWebsocketClient, | ||
], callback); | ||
}; | ||
// start tests if invoked directly | ||
if (__filename == process.argv[1]) { | ||
exports.test(testing.show); | ||
} | ||
{ | ||
"name": "loadtest", | ||
"version": "5.2.0", | ||
"version": "6.0.0", | ||
"type": "module", | ||
"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.", | ||
@@ -20,9 +21,8 @@ "homepage": "https://github.com/alexfernandez/loadtest", | ||
"https-proxy-agent": "^2.2.1", | ||
"log": "1.4.*", | ||
"stdio": "^0.2.3", | ||
"testing": "^1.1.1", | ||
"websocket": "^1.0.28" | ||
"stdio": "0.2.7", | ||
"testing": "^3.0.3", | ||
"websocket": "^1.0.34" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^4.19.1" | ||
"eslint": "^8.47.0" | ||
}, | ||
@@ -39,3 +39,3 @@ "keywords": [ | ||
"engines": { | ||
"node": ">=10" | ||
"node": ">=16" | ||
}, | ||
@@ -48,3 +48,3 @@ "bin": { | ||
"scripts": { | ||
"test": "node test.js", | ||
"test": "node test/all.js", | ||
"posttest": "eslint ." | ||
@@ -51,0 +51,0 @@ }, |
@@ -36,4 +36,5 @@ [![Build Status](https://secure.travis-ci.org/alexfernandez/loadtest.svg)](http://travis-ci.org/alexfernandez/loadtest) | ||
Versions 5 and later should be used at least with Node.js v10 or later: | ||
Versions 6 and later should be used at least with Node.js v16 or later: | ||
* Node.js v16 or later: ^6.0.0 | ||
* Node.js v10 or later: ^5.0.0 | ||
@@ -193,4 +194,4 @@ * Node.js v8 or later: 4.x.y. | ||
If `POST-file` has `.js` extension it will be `require`d. It should be a valid node module and it | ||
should `export` a single function, which is invoked with an automatically generated request identifier | ||
If `POST-file` has `.js` extension it will be `import`ed. It should be a valid node module and it | ||
should `export` a default function, which is invoked with an automatically generated request identifier | ||
to provide the body of each request. | ||
@@ -202,3 +203,3 @@ This is useful if you want to generate request bodies dynamically and vary them for each request. | ||
```javascript | ||
module.exports = function(requestId) { | ||
export default function request(requestId) { | ||
// this object will be serialized to JSON and sent in the body of the request | ||
@@ -208,6 +209,8 @@ return { | ||
requestId: requestId | ||
}; | ||
}; | ||
} | ||
} | ||
``` | ||
See sample file in `sample/post-file.js`, and test in `test/body-generator.js`. | ||
#### `-u PUT-file` | ||
@@ -218,7 +221,7 @@ | ||
If `PUT-file` has `.js` extension it will be `require`d. It should be a valid node module and it | ||
should `export` a single function, which is invoked with an automatically generated request identifier | ||
If `PUT-file` has `.js` extension it will be `import`ed. It should be a valid node module and it | ||
should `export` a default function, which is invoked with an automatically generated request identifier | ||
to provide the body of each request. | ||
This is useful if you want to generate request bodies dynamically and vary them for each request. | ||
For an example function see above for `-p`. | ||
For examples see above for `-p`. | ||
@@ -230,7 +233,7 @@ #### `-a PATCH-file` | ||
If `PATCH-file` has `.js` extension it will be `require`d. It should be a valid node module and it | ||
should `export` a single function, which is invoked with an automatically generated request identifier | ||
If `PATCH-file` has `.js` extension it will be `import`ed. It should be a valid node module and it | ||
should `export` a default function, which is invoked with an automatically generated request identifier | ||
to provide the body of each request. | ||
This is useful if you want to generate request bodies dynamically and vary them for each request. | ||
For an example function see above for `-p`. | ||
For examples see above for `-p`. | ||
@@ -315,10 +318,14 @@ ##### `-r` | ||
#### `--quiet` | ||
#### `--quiet` (deprecated) | ||
Do not show any messages. | ||
#### `--debug` | ||
Note: deprecated in version 6+, shows a warning. | ||
#### `--debug` (deprecated) | ||
Show debug messages. | ||
Note: deprecated in version 6+, shows a warning. | ||
#### `--insecure` | ||
@@ -483,15 +490,14 @@ | ||
```javascript | ||
const loadtest = require('loadtest'); | ||
import {loadTest} from 'loadtest' | ||
const options = { | ||
url: 'http://localhost:8000', | ||
maxRequests: 1000, | ||
}; | ||
loadtest.loadTest(options, function(error, result) | ||
{ | ||
if (error) | ||
{ | ||
return console.error('Got an error: %s', error); | ||
} | ||
loadTest(options, function(error, result) { | ||
if (error) { | ||
return console.error('Got an error: %s', error) | ||
} | ||
console.log('Tests run successfully'); | ||
}); | ||
console.log('Tests run successfully') | ||
}) | ||
``` | ||
@@ -599,6 +605,8 @@ | ||
#### `quiet` | ||
#### `quiet` (deprecated) | ||
Do not show any messages. | ||
Note: deprecated in version 6+, shows a warning. | ||
#### `indexParam` | ||
@@ -645,3 +653,3 @@ | ||
```javascript | ||
const loadtest = require('loadtest'); | ||
import {loadTest} from 'loadtest' | ||
@@ -652,10 +660,10 @@ const options = { | ||
secureProtocol: 'TLSv1_method' | ||
}; | ||
} | ||
loadtest.loadTest(options, function(error) { | ||
loadTest(options, function(error) { | ||
if (error) { | ||
return console.error('Got an error: %s', error); | ||
return console.error('Got an error: %s', error) | ||
} | ||
console.log('Tests run successfully'); | ||
}); | ||
console.log('Tests run successfully') | ||
}) | ||
``` | ||
@@ -682,10 +690,10 @@ | ||
```javascript | ||
const loadtest = require('loadtest'); | ||
import {loadTest} from 'loadtest' | ||
function statusCallback(error, result, latency) { | ||
console.log('Current latency %j, result %j, error %j', latency, result, error); | ||
console.log('----'); | ||
console.log('Request elapsed milliseconds: ', result.requestElapsed); | ||
console.log('Request index: ', result.requestIndex); | ||
console.log('Request loadtest() instance index: ', result.instanceIndex); | ||
console.log('Current latency %j, result %j, error %j', latency, result, error) | ||
console.log('----') | ||
console.log('Request elapsed milliseconds: ', result.requestElapsed) | ||
console.log('Request index: ', result.requestIndex) | ||
console.log('Request loadtest() instance index: ', result.instanceIndex) | ||
} | ||
@@ -697,10 +705,10 @@ | ||
statusCallback: statusCallback | ||
}; | ||
} | ||
loadtest.loadTest(options, function(error) { | ||
loadTest(options, function(error) { | ||
if (error) { | ||
return console.error('Got an error: %s', error); | ||
return console.error('Got an error: %s', error) | ||
} | ||
console.log('Tests run successfully'); | ||
}); | ||
console.log('Tests run successfully') | ||
}) | ||
``` | ||
@@ -807,4 +815,4 @@ | ||
```javascript | ||
const testserver = require('testserver'); | ||
const server = testserver.startServer({ port: 8000 }); | ||
import {startServer} from 'loadtest' | ||
const server = startServer({port: 8000}) | ||
``` | ||
@@ -873,2 +881,4 @@ | ||
See sample file in `sample/.loadtestrc`. | ||
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_. | ||
@@ -878,3 +888,3 @@ | ||
The file `lib/integration.js` shows a complete example, which is also a full integration test: | ||
The file `test/integration.js` shows a complete example, which is also a full integration test: | ||
it starts the server, send 1000 requests, waits for the callback and closes down the server. | ||
@@ -881,0 +891,0 @@ |
@@ -1,3 +0,1 @@ | ||
'use strict'; | ||
/** | ||
@@ -9,3 +7,3 @@ * Sample request generator usage. | ||
const loadtest = require('../lib/loadtest.js'); | ||
import {loadTest} from '../index.js' | ||
@@ -31,3 +29,3 @@ const options = { | ||
loadtest.loadTest(options, (error, results) => { | ||
loadTest(options, (error, results) => { | ||
if (error) { | ||
@@ -34,0 +32,0 @@ return console.error('Got an error: %s', error); |
Sorry, the diff of this file is not supported yet
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
6
37
893
3
Yes
91669
2037
+ Addedtesting@3.1.0(transitive)
- Removedlog@1.4.*
- Removedlog@1.4.0(transitive)
- Removedtesting@1.1.2(transitive)
Updatedstdio@0.2.7
Updatedtesting@^3.0.3
Updatedwebsocket@^1.0.34