ratelimit.js
Version:
A NodeJS library for efficient rate limiting using sliding windows stored in Redis.
120 lines (101 loc) • 3.57 kB
text/coffeescript
_ = require 'underscore'
async = require 'async'
redis = require 'redis'
should = require 'should'
{RateLimit} = require '../src'
describe 'RateLimit', ->
redisClient = null
ratelimit = null
beforeEach ->
redisClient = redis.createClient()
rules = [
{interval: 1, limit: 10}
{interval: 60, limit: 50}
]
ratelimit = new RateLimit redisClient, rules
incrAndCheck = (num, value, callback) ->
ratelimit.incr ['127.0.0.1'], 1, (err, isLimited) ->
isLimited.should.eql value
callback err
# Increment and response should not be limited.
incrAndFalse = _.partial incrAndCheck, _, false
incrAndTrue = _.partial incrAndCheck, _, true
# Increment N times AND all responses should not be limited.
bump = (num, incrFn, callback) ->
async.times num, incrFn, callback
describe 'incr', ->
it 'should not rate limit provided below rule rates', (done) ->
bump 8, incrAndFalse, done
it 'should not rate limit when continually below limits', (done) ->
@timeout 10000
rules = [
{interval: 1, limit: 10}
{interval: 60, limit: 100}
]
ratelimit = new RateLimit redisClient, rules
everySec = (sec, callback) ->
bump 9, incrAndFalse, ->
setTimeout callback, 1000
async.timesSeries 5, everySec, done
it 'should rate limit when over 10 req/sec', (done) ->
bump 10, incrAndFalse, (err) ->
ratelimit.incr ['127.0.0.1'], 1, (err, isLimited) ->
isLimited.should.eql true
done err
it 'should rate limit when over 20 req/min', (done) ->
async.series [
# Do 10 requests
(callback) ->
bump 10, incrAndFalse, callback
# Wait a second
(callback) ->
setTimeout callback, 1000
# Do another 10 requests
(callback) ->
bump 10, incrAndFalse, callback
# Do one more request to put us over the top for the 2nd rule
(callback) ->
incrAndTrue null, done
], done
describe 'check', ->
it 'should not be limited if the key does not exist', (done) ->
bump 1, incrAndFalse, done
it 'should return true if it has been limited', (done) ->
async.series [
(callback) ->
bump 10, incrAndFalse, callback
(callback) ->
bump 1, incrAndTrue, callback
(callback) ->
ratelimit.check '127.0.0.1', (err, isLimited) ->
isLimited.should.be.ok
callback()
], done
describe 'violatedRules', ->
it 'should return the set of rules a key has violated', (done) ->
async.series [
(callback) ->
bump 10, incrAndFalse, callback
(callback) ->
bump 1, incrAndTrue, callback
(callback) ->
ratelimit.violatedRules ['127.0.0.1'], (err, violated) ->
violated.length.should.eql 1
violated[0].should.eql {interval: 1, limit: 10}
callback err
], done
describe 'whitelist', ->
beforeEach (done) ->
ratelimit.whitelist ['127.0.0.1'], done
it 'should not be limited ever', (done) ->
bump 10, incrAndFalse, (err) ->
ratelimit.incr ['127.0.0.1'], 10, (err, isLimited) ->
isLimited.should.eql false
done err
describe 'blacklist', ->
beforeEach (done) ->
ratelimit.blacklist ['127.0.0.1'], done
it 'should be limited on the first try', (done) ->
ratelimit.incr ['127.0.0.1'], 1, (err, isLimited) ->
isLimited.should.eql true
done err