From f89b7af71a0ce624fffc1487db1388983c0ee7ab Mon Sep 17 00:00:00 2001 From: Doug Luce Date: Tue, 28 Oct 2014 13:10:03 -0700 Subject: [PATCH 1/6] Fix an express deprecation This message was given when rate limiting is hit: ``` express deprecated res.json(status, obj): Use res.status(status).json(obj) instead node_modules/express-rate/lib/rate.js:61:17 ``` The deprecation is mentioned in https://github.com/strongloop/express/releases/tag/4.7.0 Also update the docs and example. --- Readme.md | 4 ++-- examples/simple-redis.js | 4 ++-- lib/rate.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Readme.md b/Readme.md index 7c8e44f..43d5df7 100644 --- a/Readme.md +++ b/Readme.md @@ -96,7 +96,7 @@ function (req, res, rate, limit, resetTime) { function (req, res, rate, limit, resetTime, next) { // HTTP code 420 from http://mehack.com/inventing-a-http-response-code-aka-seriously - res.json({error: 'Rate limit exceeded. Check headers for limit information.'}, {status: 420}); + res.status(420).json({error: 'Rate limit exceeded. Check headers for limit information.'}); } ``` @@ -218,7 +218,7 @@ var headersMiddleware = rate.middleware( onLimitReached: function (req, res, rate, limit, resetTime, next) { // HTTP code 420 from http://mehack.com/inventing-a-http-response-code-aka-seriously - res.json({error: 'Rate limit exceeded. Check headers for limit information.'}, {status: 420}); + res.status(420).json({error: 'Rate limit exceeded. Check headers for limit information.'}); } }); diff --git a/examples/simple-redis.js b/examples/simple-redis.js index 89d056e..a913e3a 100644 --- a/examples/simple-redis.js +++ b/examples/simple-redis.js @@ -102,7 +102,7 @@ var headersMiddleware = rate.middleware( onLimitReached: function (req, res, rate, limit, resetTime, next) { // HTTP code 420 from http://mehack.com/inventing-a-http-response-code-aka-seriously - res.json({error: 'Rate limit exceeded. Check headers for limit information.'}, {status: 420}); + res.status(420).json({error: 'Rate limit exceeded. Check headers for limit information.'}); } }); @@ -113,4 +113,4 @@ app.get('/headers', headersMiddleware, function(req, res){ res.send(text); }); -app.listen(3000); \ No newline at end of file +app.listen(3000); diff --git a/lib/rate.js b/lib/rate.js index 5a81ed1..b489506 100644 --- a/lib/rate.js +++ b/lib/rate.js @@ -58,7 +58,7 @@ exports.defaults = function () { onLimitReached: function (req, res, rate, limit, resetTime, next) { // HTTP code 420 from http://mehack.com/inventing-a-http-response-code-aka-seriously - res.json({error: 'Rate limit exceeded. Check headers for limit information.'}, {status: 420}); + res.status(420).json({error: 'Rate limit exceeded. Check headers for limit information.'}); } }; From 987cf2fea4aaaa781fdfd74eb2dea9bb460a94b1 Mon Sep 17 00:00:00 2001 From: Doug Luce Date: Wed, 29 Oct 2014 14:28:26 -0700 Subject: [PATCH 2/6] Mochatize half the tests. First set of tests cast under Mocha. Simplified them a bit further with async. Did a little refactoring. Hopefully it's better. --- test/common.rate.js | 303 +++++++++++++++++++------------------------- test/redis.test.js | 57 ++++----- 2 files changed, 150 insertions(+), 210 deletions(-) diff --git a/test/common.rate.js b/test/common.rate.js index 4999565..5a60475 100644 --- a/test/common.rate.js +++ b/test/common.rate.js @@ -1,210 +1,163 @@ - - -var express = require('express'), - assert = require('assert'), - should = require('should'), +var request = require('supertest'), + express = require('express'), + assert = require('chai').assert, + async = require('async'), rate = require('../lib/rate'), memory = require('../lib/memory'); -module.exports = { +var getRouteKey = function(req) {return module.exports.routeKey}; - routeKey: 'get:/^\\/\\/?$/i', - - 'baseline: server can be tested' : function () { - - var server = express.createServer(); +var rand = function(n) { + return Math.ceil(Math.random() * n); +} - var responseBody = 'works'; +var performNRequests = function (middleware, n, callback) { + + var app = express(); + var responseBody = 'works'; + + app.get('/', middleware, + function (req, res) { + res.send(responseBody); + } + ); + + async.times(n, function(n, next) { + request(app) + .get('/') + .expect(responseBody) + .end(next); + }, callback); +} - server.get('/', function (req, res) { + +module.exports = { + + routeKey: 'whoola', // get:/^\\/\\/?$/i', + + 'baseline: server can be tested' : function (done) { + + var app = express(); + + var responseBody = 'works'; + + app.get('/', function (req, res) { res.send(responseBody); }); - - assert.response(server, - { url: '/' }, - { body: responseBody }); - }, - - 'server records rate' : function (handler, done) { - - var self = this; - + + request(app) + .get('/') + .expect(responseBody, done) + }, + + 'server records rate' : function (handler, done) { + handler.reset(module.exports.routeKey, null, null, function () { - - var middleware = rate.middleware({handler: handler, interval: 2}); - self.performRandomRequests(middleware, Math.random() * 100, function (responseNumber, requestsToPerform) { - - - if (responseNumber === requestsToPerform) { - - handler.getRate(module.exports.routeKey, null, null, function (rate) { - - assert.eql(rate, requestsToPerform, 'Rate of received from middleware does not match sent.'); - - done(); - }); - } - - }); - + var middleware = rate.middleware({handler: handler, interval: 2, getRouteKey: getRouteKey}); + var requestsToPerform = rand(10) + 5; + performNRequests(middleware, requestsToPerform, function () { + handler.getRate(module.exports.routeKey, null, null, function (rate) { + assert.equal(rate, requestsToPerform, 'Rate of received from middleware does not match sent. (' + + rate + ' != ' + requestsToPerform + ')'); + done(); + }); + }); }); - }, - + }, - 'Reset after interval': function (handler, done) { - - var self = this; + 'Reset after interval': function (handler, done) { handler.reset(module.exports.routeKey, null, null, function () { - var secondsIncrement = 1; - var resetTime = (new Date()).getTime() + (secondsIncrement * 1000); - var middleware = rate.middleware({handler: handler, interval: secondsIncrement}); - var completed = false; - var inflections = 0; - var lastRateCount = null; + var middleware = rate.middleware({handler: handler, interval: 1, getRouteKey: getRouteKey}); - self.performRandomRequests(middleware, Math.random() * 15000, function (responseNumber, requestsToPerform) { + var firstHits = rand(10) + 1; + var secondHits = rand(10) + 1; - handler.getRate(module.exports.routeKey, null, null, function (rate) { - - if (lastRateCount && rate < lastRateCount) - inflections += 1; - - lastRateCount = rate; - - if (responseNumber === requestsToPerform) { - assert.eql((inflections > 0), true, 'There was no inflections, meaning the rate was never reversed, but was monotonically increasing.'); - - done(); - } + var iterate = function (hits, expectedRate, next) { + performNRequests(middleware, hits, function () { + handler.getRate(module.exports.routeKey, null, null, function (rate) { + assert(rate == expectedRate, 'Did not see the expected number of hits'); + next() + }); }); + }; - }); + iterate(firstHits, firstHits, function() { // A few hits. + iterate(secondHits, firstHits + secondHits, function() { // A few more. + setTimeout(function() { // Counter should have reset by now. + iterate(firstHits, firstHits, done); + }, 1000); + }); + }); }); - }, - - 'Routes can be rate limited, reallowed, and have proper headers': function (handler, done) { - - var self = this; - + }, + + 'Routes can be rate limited, reallowed, and have proper headers': function (handler, done) { handler.reset(module.exports.routeKey, null, null, function () { - - var server = express.createServer(); - + var app = express(); var responseBody = 'works'; var errorJson = 'limited'; - + var limit = 10; - var middleware = rate.middleware({handler: handler, interval: 5, limit: limit, - onLimitReached: function (req, res, rate, limit, resetTime, next) { + var middleware = rate.middleware({handler: handler, interval: 5, limit: limit, getRouteKey: getRouteKey, + onLimitReached: function (req, res, rate, limit, resetTime, next) { + // HTTP code 420 from http://mehack.com/inventing-a-http-response-code-aka-seriously + res.status(420).send(errorJson); + } + }); + + app.get('/', middleware, + function (req, res) { + res.send(responseBody); + } + ); - // HTTP code 420 from http://mehack.com/inventing-a-http-response-code-aka-seriously - res.send(errorJson, {status: 420}); - } - }); - - server.get('/', middleware, - function (req, res) { - res.send(responseBody); - } - ); - - var responsesReceived = 0; - var stop = false; var limitedRequests = 0; var lastCheck = false; - - + var makeRequest = function (response) { - - assert.response(server, - { url: '/' }, - response, - function (res) { - responsesReceived += 1; - - assert.isDefined(res.headers['X-RateLimit-Limit'.toLowerCase()]); - assert.isDefined(res.headers['X-RateLimit-Remaining'.toLowerCase()]); - assert.isDefined(res.headers['X-RateLimit-Reset'.toLowerCase()]); - - // check the headers - assert.eql(res.headers['X-RateLimit-Limit'.toLowerCase()], limit, 'Limit does not match the header limit returned from server.'); - - if(res.body === errorJson) { - limitedRequests += 1; + request(app) + .get('/') + .expect(response.body) + .expect('X-RateLimit-Limit', limit, 'Limit does not match the header limit returned from server.') + .expect(function(res) { + assert.isDefined(res.headers['X-RateLimit-Limit'.toLowerCase()]); + assert.isDefined(res.headers['X-RateLimit-Remaining'.toLowerCase()]); + assert.isDefined(res.headers['X-RateLimit-Reset'.toLowerCase()]); + + // check the headers + if (parseInt(res.headers['X-RateLimit-Remaining'.toLowerCase()]) > 0) { + + if (!lastCheck) { + makeRequest({body: responseBody}); + } else { + done(); + return false; } - - if (parseInt(res.headers['X-RateLimit-Remaining'.toLowerCase()]) > 0) { - - - - if (!lastCheck) { - makeRequest({body: responseBody}); - } else { - - done(); - } - - } else { - - if (limitedRequests > 5) { - - var currentTime = (new Date()).getTime(); - var sleepForMs = parseInt(res.headers['X-RateLimit-Reset'.toLowerCase()]) - currentTime; - // TODO: bug, have to add an extra second for differences between redis time and local time - // need to find correct way to do this - sleepForMs += 1000; - - setTimeout(function () { - - lastCheck = true; - makeRequest({body: responseBody}); - - }, sleepForMs); - - } else { - - makeRequest({body: errorJson}); - } + } else { + limitedRequests += 1; + if (limitedRequests > 5) { + + var currentTime = (new Date()).getTime(); + var sleepForMs = parseInt(res.headers['X-RateLimit-Reset'.toLowerCase()]) - currentTime; + // TODO: bug, have to add an extra second for differences between redis time and local time + // need to find correct way to do this + sleepForMs += 1000; + setTimeout(function () { + lastCheck = true; + makeRequest({body: responseBody}); + }, sleepForMs); + } else { + makeRequest({body: errorJson}); } } - ); - + }).end(function(){}); } - makeRequest({body: responseBody}); }); - }, - - performRandomRequests: function (middleware, n, callback) { - - var server = express.createServer(); - - var responseBody = 'works'; - - - server.get('/', middleware, - function (req, res) { - res.send(responseBody); - } - ); - - var requestsToPerform = Math.ceil(n), - responsesReceived = 0; - - for(var i = 0; i < requestsToPerform; i += 1) { - - assert.response(server, - { url: '/' }, - { body: responseBody }, - function (res) { - responsesReceived += 1; - callback(responsesReceived, requestsToPerform); - } - ); - } - } + } +} -} \ No newline at end of file + diff --git a/test/redis.test.js b/test/redis.test.js index 751d98e..31cf0f3 100644 --- a/test/redis.test.js +++ b/test/redis.test.js @@ -4,38 +4,25 @@ var common = require('./common.rate'); redis = require('redis'), client = redis.createClient(); -var onFinished = function () { - client.end(); -}; - -module.exports = { - - - // - // TODO: I have not figured out how tu run these tests in parallel or in serial (--serial flag) because of the - // shared REDIS resource, if run one at a time, each of them pass for me, but not together - // - - /* - 'baseline: server can be tested' : function () { - common['baseline: server can be tested'](); - }, - - - 'server records rate': function (done) { - var handler = new rate.Redis.RedisRateHandler({client: client}); - common['server records rate'](handler, onFinished); - }, - - 'Reset after interval': function (done) { - var handler = new rate.Redis.RedisRateHandler({client: client}); - common['Reset after interval'](handler, onFinished); - }, - */ - - 'Routes can be rate limited, reallowed, and have proper headers': function (done) { - var handler = new rate.Redis.RedisRateHandler({client: client}); - common['Routes can be rate limited, reallowed, and have proper headers'](handler, onFinished); - } - -}; \ No newline at end of file +/* +it('baseline: server can be tested', function (done) { + common['baseline: server can be tested'](done); +}); + +it('server records rate', function (done) { + var handler = new rate.Redis.RedisRateHandler({client: client}); + common['server records rate'](handler, done); +}); + + +it('Reset after interval', function (done) { + var handler = new rate.Redis.RedisRateHandler({client: client}); + common['Reset after interval'](handler, done); +}); +*/ + +it('Routes can be rate limited, reallowed, and have proper headers', function (done) { + this.timeout(10000); + var handler = new rate.Redis.RedisRateHandler({client: client}); + common['Routes can be rate limited, reallowed, and have proper headers'](handler, done); +}); From 9d2bdffb38f306a7e0785b55a8a485a261bdae4a Mon Sep 17 00:00:00 2001 From: Doug Luce Date: Wed, 29 Oct 2014 14:02:23 -0700 Subject: [PATCH 3/6] Refactor Redis tests. Check Redis directly to see whether the key has expired. This should avoid adding fudge factors to the test duration, although fudge is still necessary to compensate for Redis' 1-second expiration resolution. Also expire the proper key out of the store. When the middleware is being used, the route key has the remotekey added to it. Since that's not strictly known until a request is made, I've put in the localhost IP address as that's what express is giving me for a remote address on my box. No clue if that holds up universally. Also tried to make the logic a little cleaner, although it's still far from perfect. --- test/common.rate.js | 58 +++++++++++++++++++++++++++------------------ test/redis.test.js | 40 +++++++++++++++---------------- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/test/common.rate.js b/test/common.rate.js index 5a60475..7206b55 100644 --- a/test/common.rate.js +++ b/test/common.rate.js @@ -6,14 +6,15 @@ var request = require('supertest'), rate = require('../lib/rate'), memory = require('../lib/memory'); -var getRouteKey = function(req) {return module.exports.routeKey}; +var getRouteKey = function(req) { + return module.exports.routeKey +}; var rand = function(n) { return Math.ceil(Math.random() * n); -} +}; var performNRequests = function (middleware, n, callback) { - var app = express(); var responseBody = 'works'; @@ -29,12 +30,20 @@ var performNRequests = function (middleware, n, callback) { .expect(responseBody) .end(next); }, callback); -} +}; + +var checkerr = function(callback) { + return function(err, res) { + if (err) throw err; + if (typeof callback == 'function') + callback(); + }; +}; module.exports = { - routeKey: 'whoola', // get:/^\\/\\/?$/i', + routeKey: 'get:/^\\/\\/?$/i', 'baseline: server can be tested' : function (done) { @@ -48,7 +57,8 @@ module.exports = { request(app) .get('/') - .expect(responseBody, done) + .expect(responseBody) + .end(checkerr(done)) }, 'server records rate' : function (handler, done) { @@ -95,13 +105,13 @@ module.exports = { }, 'Routes can be rate limited, reallowed, and have proper headers': function (handler, done) { - handler.reset(module.exports.routeKey, null, null, function () { + handler.reset(module.exports.routeKey, '127.0.0.1', null, function () { var app = express(); var responseBody = 'works'; var errorJson = 'limited'; var limit = 10; - var middleware = rate.middleware({handler: handler, interval: 5, limit: limit, getRouteKey: getRouteKey, + var middleware = rate.middleware({handler: handler, interval: 1, limit: limit, getRouteKey: getRouteKey, onLimitReached: function (req, res, rate, limit, resetTime, next) { // HTTP code 420 from http://mehack.com/inventing-a-http-response-code-aka-seriously res.status(420).send(errorJson); @@ -121,7 +131,6 @@ module.exports = { request(app) .get('/') .expect(response.body) - .expect('X-RateLimit-Limit', limit, 'Limit does not match the header limit returned from server.') .expect(function(res) { assert.isDefined(res.headers['X-RateLimit-Limit'.toLowerCase()]); assert.isDefined(res.headers['X-RateLimit-Remaining'.toLowerCase()]); @@ -129,31 +138,34 @@ module.exports = { // check the headers if (parseInt(res.headers['X-RateLimit-Remaining'.toLowerCase()]) > 0) { - if (!lastCheck) { makeRequest({body: responseBody}); } else { done(); - return false; } - } else { + } else { // We're being limited. limitedRequests += 1; if (limitedRequests > 5) { - - var currentTime = (new Date()).getTime(); - var sleepForMs = parseInt(res.headers['X-RateLimit-Reset'.toLowerCase()]) - currentTime; - // TODO: bug, have to add an extra second for differences between redis time and local time - // need to find correct way to do this - sleepForMs += 1000; - setTimeout(function () { - lastCheck = true; - makeRequest({body: responseBody}); - }, sleepForMs); + var checkTTL = function() { // Wait until the store TTL expires. + handler.checkttl(getRouteKey(), function(err,resp) { + if (resp >= 0) { // Still not expired. + setTimeout(checkTTL, 50); + } else { + var currentTime = (new Date()).getTime(); + var expectedExpiryTime = parseInt(res.headers['X-RateLimit-Reset'.toLowerCase()]); + var redisInaccuracy = 1000; // Redis clock resolution, 1 second. + assert.operator(currentTime, '>', expectedExpiryTime - redisInaccuracy); + lastCheck = true; + makeRequest({body: responseBody}); + } + }); + }; + checkTTL(); } else { makeRequest({body: errorJson}); } } - }).end(function(){}); + }).end(checkerr()); } makeRequest({body: responseBody}); }); diff --git a/test/redis.test.js b/test/redis.test.js index 31cf0f3..8861027 100644 --- a/test/redis.test.js +++ b/test/redis.test.js @@ -4,25 +4,25 @@ var common = require('./common.rate'); redis = require('redis'), client = redis.createClient(); -/* -it('baseline: server can be tested', function (done) { - common['baseline: server can be tested'](done); -}); - -it('server records rate', function (done) { - var handler = new rate.Redis.RedisRateHandler({client: client}); - common['server records rate'](handler, done); -}); - +describe('Redis-based rate handling', function () { + var handler = new rate.Redis.RedisRateHandler({client: client}); -it('Reset after interval', function (done) { - var handler = new rate.Redis.RedisRateHandler({client: client}); - common['Reset after interval'](handler, done); -}); -*/ + it('baseline: server can be tested', function (done) { + common['baseline: server can be tested'](done); + }); + it('server records rate', function (done) { + common['server records rate'](handler, done); + }); + it('Reset after interval', function (done) { + common['Reset after interval'](handler, done); + }); -it('Routes can be rate limited, reallowed, and have proper headers', function (done) { - this.timeout(10000); - var handler = new rate.Redis.RedisRateHandler({client: client}); - common['Routes can be rate limited, reallowed, and have proper headers'](handler, done); -}); + handler.checkttl = function(key, cb) { + return client.ttl(key, cb); + }; + + it('Routes can be rate limited, reallowed, and have proper headers', function (done) { + this.timeout(3000); + common['Routes can be rate limited, reallowed, and have proper headers'](handler, done); + }); +}); From af4ec0b7b886c09f0d83bbb1227cebbbb5a5f3d2 Mon Sep 17 00:00:00 2001 From: Doug Luce Date: Wed, 29 Oct 2014 14:15:27 -0700 Subject: [PATCH 4/6] Mochatize the memory tests. I'm using a single handler for all these tests. This introduces potential for one test to affect other tests (i.e. it's not very "unit-y"). I think the gain in showing resiliancy during multiple uses is worth the downside. Removed the timeout from the Redis test as the interval is now a second and it really should be able to end in under 2. --- test/memory.test.js | 39 +++++++++++++++++++-------------------- test/redis.test.js | 1 - 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/test/memory.test.js b/test/memory.test.js index 1cdadb1..2c6abd6 100644 --- a/test/memory.test.js +++ b/test/memory.test.js @@ -2,28 +2,27 @@ var common = require('./common.rate'); rate = require('../lib/rate'); -var onFinished = function () { - -}; +describe('Memory-based rate handling', function () { + var handler = new rate.Memory.MemoryRateHandler(); -module.exports = { + it('baseline: server can be tested', function (done) { + common['baseline: server can be tested'](done); + }); - 'baseline: server can be tested' : function () { - common['baseline: server can be tested'](); - }, + it('server records rate', function (done) { + common['server records rate'](handler, done); + }); - 'server records rate': function (done) { - var handler = new rate.Memory.MemoryRateHandler(); - common['server records rate'](handler, onFinished); - }, + it('Reset after interval', function (done) { + common['Reset after interval'](handler, done); + }); - 'Reset after interval': function (done) { - var handler = new rate.Memory.MemoryRateHandler(); - common['Reset after interval'](handler, onFinished); - }, + handler.checkttl = function(key, cb) { + var currentTime = (new Date()).getTime(); + return cb(null,handler.rates[key].ttl - currentTime); + }; - 'Routes can be rate limited, reallowed, and have proper headers': function (done) { - var handler = new rate.Memory.MemoryRateHandler(); - common['Routes can be rate limited, reallowed, and have proper headers'](handler, onFinished); - } -}; \ No newline at end of file + it('Routes can be rate limited, reallowed, and have proper headers', function (done) { + common['Routes can be rate limited, reallowed, and have proper headers'](handler, done); + }); +}); diff --git a/test/redis.test.js b/test/redis.test.js index 8861027..6925d38 100644 --- a/test/redis.test.js +++ b/test/redis.test.js @@ -22,7 +22,6 @@ describe('Redis-based rate handling', function () { }; it('Routes can be rate limited, reallowed, and have proper headers', function (done) { - this.timeout(3000); common['Routes can be rate limited, reallowed, and have proper headers'](handler, done); }); }); From ec30d5f92cec9826fe6398ff8753612739ccdc3a Mon Sep 17 00:00:00 2001 From: Doug Luce Date: Wed, 29 Oct 2014 14:29:50 -0700 Subject: [PATCH 5/6] Update package with dev dependencies So that tests will run. NPM also reformatted this thing, looks a little more standard now. --- package.json | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 41f2487..521014c 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,27 @@ -{ "name": "express-rate" - , "description": "Rate monitoring and limiting for express.js apps." - , "version": "0.0.1" - , "author": "Ilya Volodarsky " - , "keywords": ["rate", "monitor", "limit", "monitoring", "performance"] - , "repository": "git@github.com:segmentio/express-rate.git" - , "main": "./index.js" - , "dependencies": { "redis": ">= 0.0.1" } - , "devDependencies": { "connect": "1.4.x" } - , "engines": { "node": ">= 0.1.98" } -} \ No newline at end of file +{ + "name": "express-rate", + "description": "Rate monitoring and limiting for express.js apps.", + "version": "0.0.1", + "author": "Ilya Volodarsky ", + "keywords": [ + "rate", + "monitor", + "limit", + "monitoring", + "performance" + ], + "repository": "git@github.com:segmentio/express-rate.git", + "main": "./index.js", + "dependencies": { + "redis": ">= 0.0.1" + }, + "devDependencies": { + "express": ">= 4.7.0", + "async": "~0.9.0", + "chai": "~1.9.2", + "supertest": "~0.14.0" + }, + "engines": { + "node": ">= 0.1.98" + } +} From 0dc60c8e0dacd04fe9a7d8b4004ea47f3bdcbc23 Mon Sep 17 00:00:00 2001 From: Doug Luce Date: Thu, 30 Oct 2014 10:06:39 -0700 Subject: [PATCH 6/6] Move handler-specific variable into handler object. So the common stuff doesn't look like it has exceptions just for Redis. --- test/common.rate.js | 3 +-- test/memory.test.js | 2 ++ test/redis.test.js | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/common.rate.js b/test/common.rate.js index 7206b55..64dfdd9 100644 --- a/test/common.rate.js +++ b/test/common.rate.js @@ -153,8 +153,7 @@ module.exports = { } else { var currentTime = (new Date()).getTime(); var expectedExpiryTime = parseInt(res.headers['X-RateLimit-Reset'.toLowerCase()]); - var redisInaccuracy = 1000; // Redis clock resolution, 1 second. - assert.operator(currentTime, '>', expectedExpiryTime - redisInaccuracy); + assert.operator(currentTime, '>', expectedExpiryTime - handler.clockResolution); lastCheck = true; makeRequest({body: responseBody}); } diff --git a/test/memory.test.js b/test/memory.test.js index 2c6abd6..e94b4f1 100644 --- a/test/memory.test.js +++ b/test/memory.test.js @@ -22,6 +22,8 @@ describe('Memory-based rate handling', function () { return cb(null,handler.rates[key].ttl - currentTime); }; + handler.clockResolution = 0; // Memory TTLs are accurate. + it('Routes can be rate limited, reallowed, and have proper headers', function (done) { common['Routes can be rate limited, reallowed, and have proper headers'](handler, done); }); diff --git a/test/redis.test.js b/test/redis.test.js index 6925d38..92131f4 100644 --- a/test/redis.test.js +++ b/test/redis.test.js @@ -20,6 +20,8 @@ describe('Redis-based rate handling', function () { handler.checkttl = function(key, cb) { return client.ttl(key, cb); }; + + handler.clockResolution = 1000; // Redis clock accuracy in milliseconds. it('Routes can be rate limited, reallowed, and have proper headers', function (done) { common['Routes can be rate limited, reallowed, and have proper headers'](handler, done);