diff --git a/.gitignore b/.gitignore index 4d609a57..d3c2732c 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,7 @@ node_modules # Dependency lock package-lock.json - +yarn.lock # Optional npm cache directory .npm diff --git a/README.md b/README.md index d436d8f7..74da5613 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,12 @@ Available options: -E/--expectBody EXPECTED Ensure the body matches this value. If enabled, mismatches count towards bailout. Enabling this option will slow down the load testing. + -N/--expectDuration EXPECTED + Ensure the response duration less than expectDuration. If bigger, reqOverTimes count towards bailout. + Enabling this option will slow down the load testing. + -P/--expectBodySize EXPECTED + Ensure the response body bytes less than expectBodySize. If bigger, reqOverSizes count towards bailout. + Enabling this option will slow down the load testing. --renderStatusCodes Print status codes and their respective statistics. --debug diff --git a/autocannon.js b/autocannon.js index 3fb2cf68..2066fe31 100755 --- a/autocannon.js +++ b/autocannon.js @@ -62,6 +62,8 @@ const alias = { socketPath: 'S', excludeErrorStats: 'x', expectBody: 'E', + expectDuration: 'N', + expectBodySize: 'P', workers: 'w', warmup: 'W', help: 'h' diff --git a/lib/aggregateResult.js b/lib/aggregateResult.js index b6976f05..c409b388 100644 --- a/lib/aggregateResult.js +++ b/lib/aggregateResult.js @@ -20,6 +20,8 @@ function aggregateResult (results, opts, histograms) { acc.errors += r.errors acc.timeouts += r.timeouts acc.mismatches += r.mismatches + acc.reqOverTimes += r.reqOverTimes + acc.reqOverSizes += r.reqOverSizes acc.non2xx += r.non2xx acc.resets += r.resets acc['1xx'] += r['1xx'] @@ -53,6 +55,8 @@ function aggregateResult (results, opts, histograms) { errors: aggregated.errors, timeouts: aggregated.timeouts, mismatches: aggregated.mismatches, + reqOverTimes: aggregated.reqOverTimes, + reqOverSizes: aggregated.reqOverSizes, non2xx: aggregated.non2xx, resets: aggregated.resets, '1xx': aggregated['1xx'], diff --git a/lib/httpClient.js b/lib/httpClient.js index 027d95fd..337047ba 100644 --- a/lib/httpClient.js +++ b/lib/httpClient.js @@ -22,6 +22,8 @@ function Client (opts) { this.opts.pipelining = this.opts.pipelining || 1 this.opts.port = this.opts.port || 80 this.opts.expectBody = this.opts.expectBody || null + this.opts.expectDuration = this.opts.expectDuration || Infinity + this.opts.expectBodySize = this.opts.expectBodySize || Infinity this.opts.tlsOptions = this.opts.tlsOptions || {} this.timeout = (this.opts.timeout || 10) * 1000 this.ipc = !!this.opts.socketPath @@ -90,11 +92,19 @@ function Client (opts) { if (!this.destroyed && this.reconnectRate && this.reqsMade % this.reconnectRate === 0) { return this._resetConnection() } + if (!this.destroyed) { - if (this.opts.expectBody && this.opts.expectBody !== resp.body) { + if (this.opts.expectBody && !resp.body.includes(this.opts.expectBody)) { return this.emit('mismatch', resp.body) } + if (this.opts.expectDuration && resp.duration > this.opts.expectDuration) { + return this.emit('reqOverTime', resp.duration) + } + if (this.opts.expectBodySize && resp.bytes > this.opts.expectBodySize) { + return this.emit('reqOverSize', resp.bytes) + } + this.requestIterator.recordBody(resp.req, resp.headers.statusCode, resp.body, resp.headers.headers) this.emit('response', resp.headers.statusCode, resp.bytes, resp.duration, this.rate) diff --git a/lib/run.js b/lib/run.js index 780fd2e2..7442f46a 100644 --- a/lib/run.js +++ b/lib/run.js @@ -37,6 +37,8 @@ function run (opts, tracker, cb) { let errors = 0 let timeouts = 0 let mismatches = 0 + let reqOverTimes = 0 + let reqOverSizes = 0 let totalBytes = 0 let totalRequests = 0 let totalCompletedRequests = 0 @@ -75,6 +77,8 @@ function run (opts, tracker, cb) { url.socketPath = opts.socketPath url.servername = opts.servername url.expectBody = opts.expectBody + url.expectDuration = opts.expectDuration + url.expectBodySize = opts.expectBodySize return url }) @@ -117,6 +121,8 @@ function run (opts, tracker, cb) { errors: errors, timeouts: timeouts, mismatches: mismatches, + reqOverSizes, + reqOverTimes, non2xx: statusCodes[0] + statusCodes[2] + statusCodes[3] + statusCodes[4], statusCodeStats, resets: resets, @@ -148,6 +154,8 @@ function run (opts, tracker, cb) { errors = 0 timeouts = 0 mismatches = 0 + reqOverTimes = 0 + reqOverSizes = 0 totalBytes = 0 totalRequests = 0 totalCompletedRequests = 0 @@ -201,6 +209,8 @@ function run (opts, tracker, cb) { client.on('response', onResponse) client.on('connError', onError) client.on('mismatch', onExpectMismatch) + client.on('reqOverTime', () => { reqOverTimes++ }) + client.on('reqOverSize', () => { reqOverSizes++ }) client.on('reset', () => { resets++ }) client.on('timeout', onTimeout) client.on('request', () => { totalRequests++ }) diff --git a/test/run.test.js b/test/run.test.js index fa1c57e0..69397947 100644 --- a/test/run.test.js +++ b/test/run.test.js @@ -61,6 +61,8 @@ test('init', (t) => { t.equal(result.errors, 0, 'no errors') t.equal(result.mismatches, 0, 'no mismatches') + t.equal(result.reqOverTimes, 0, 'no reqOverTimes') + t.equal(result.reqOverSizes, 0, 'no reqOverSizes') t.equal(result.resets, 0, 'no resets') t.equal(result['1xx'], 0, '1xx codes') @@ -126,6 +128,8 @@ test('tracker.stop()', (t) => { t.equal(result.errors, 0, 'no errors') t.equal(result.mismatches, 0, 'no mismatches') + t.equal(result.reqOverTimes, 0, 'no reqOverTimes') + t.equal(result.reqOverSizes, 0, 'no reqOverSizes') t.equal(result['1xx'], 0, '1xx codes') t.equal(result['2xx'], result.requests.total, '2xx codes') @@ -327,6 +331,69 @@ test('run should produce 0 mismatches with expectBody set and matches', (t) => { }) }) +test('run should produce count of reqOverTimes with expectDuration set', (t) => { + t.plan(2) + + const server = helper.startServer({ delayResponse: 10 }) + + initJob({ + url: 'http://localhost:' + server.address().port, + expectDuration: 1, + maxOverallRequests: 10, + timeout: 100 + }, function (err, result) { + t.error(err) + t.equal(result.reqOverTimes, 10) + t.end() + }) +}) + +test('run should produce 0 reqOverTimes with expectDuration set and matches', (t) => { + t.plan(2) + + initJob({ + url: 'http://localhost:' + server.address().port, + expectDuration: 1000, + maxOverallRequests: 10 + }, function (err, result) { + t.error(err) + t.equal(result.reqOverTimes, 0) + t.end() + }) +}) + +test('run should produce count of reqOverSizes with expectBodySize set', (t) => { + t.plan(2) + + const responseBody = 'hello dave hello dave hello dave hello dave hello dave hello dave hello dave hello dave hello dave' + const server = helper.startServer({ body: responseBody }) + + initJob({ + url: 'http://localhost:' + server.address().port, + expectBodySize: 10, + maxOverallRequests: 10, + timeout: 100 + }, function (err, result) { + t.error(err) + t.equal(result.reqOverSizes, 10) + t.end() + }) +}) + +test('run should produce 0 reqOverSizes with expectBodySize set and matches', (t) => { + t.plan(2) + + initJob({ + url: 'http://localhost:' + server.address().port, + expectBodySize: 1000, + maxOverallRequests: 10 + }, function (err, result) { + t.error(err) + t.equal(result.reqOverSizes, 0) + t.end() + }) +}) + test('run should accept a unix socket/windows pipe', (t) => { t.plan(11) diff --git a/test/workers.test.js b/test/workers.test.js index a18fd77f..0c45bf76 100644 --- a/test/workers.test.js +++ b/test/workers.test.js @@ -80,6 +80,8 @@ test('init with workers', { skip: !hasWorkerSupport }, (t) => { t.equal(result.errors, 0, 'no errors') t.equal(result.mismatches, 0, 'no mismatches') + t.equal(result.reqOverTimes, 0, 'no reqOverTimes') + t.equal(result.reqOverSizes, 0, 'no reqOverSizes') t.equal(result.resets, 0, 'no resets') t.equal(result['1xx'], 0, '1xx codes')