node-redis-rate-limiter/test/middleware.spec.js

154 lines
3.9 KiB
JavaScript

var _ = require('lodash');
var async = require('async');
var should = require('should');
var redis = require('redis');
var express = require('express');
var supertest = require('supertest');
var middleware = require('../lib/middleware');
describe('Middleware', function() {
this.slow(5000);
this.timeout(5000);
var client = null;
var limiter = null;
before(function(done) {
client = redis.createClient(6379, 'localhost', {enable_offline_queue: false});
client.on('ready', done);
});
describe('IP throttling', function() {
before(function() {
limiter = middleware({
redis: client,
key: 'ip',
rate: '10/second'
});
});
beforeEach(function(done) {
client.del('ratelimit:127.0.0.1', done);
});
it('passes through under the limit', function(done) {
var server = express();
server.use(limiter);
server.use(okResponse);
var reqs = requests(server, 9, '/test');
async.parallel(reqs, function(err, data) {
withStatus(data, 200).should.have.length(9);
done();
});
});
it('returns HTTP 429 over the limit', function(done) {
var server = express();
server.use(limiter);
server.use(okResponse);
var reqs = requests(server, 12, '/test');
async.parallel(reqs, function(err, data) {
withStatus(data, 200).should.have.length(10);
withStatus(data, 429).should.have.length(2);
done();
});
});
it('works across several rate-limit windows', function(done) {
var server = express();
server.use(limiter);
server.use(okResponse);
async.series([
parallelRequests(server, 9, '/test'),
wait(1100),
parallelRequests(server, 12, '/test'),
wait(1100),
parallelRequests(server, 9, '/test')
], function(err, data) {
withStatus(data[0], 200).should.have.length(9);
withStatus(data[2], 200).should.have.length(10);
withStatus(data[2], 429).should.have.length(2);
withStatus(data[4], 200).should.have.length(9);
done();
});
});
});
describe('Custom key throttling', function() {
before(function() {
limiter = middleware({
redis: client,
key: function(req) { return req.query.user; },
rate: '10/second'
});
});
beforeEach(function(done) {
async.series([
client.del.bind(client, 'ratelimit:a'),
client.del.bind(client, 'ratelimit:b'),
client.del.bind(client, 'ratelimit:c')
], done);
});
it('uses a different bucket for each custom key (user)', function(done) {
var server = express();
server.use(limiter);
server.use(okResponse);
var reqs = _.flatten([
requests(server, 5, '/test?user=a'),
requests(server, 12, '/test?user=b'),
requests(server, 10, '/test?user=c')
]);
async.parallel(reqs, function(err, data) {
withStatus(data, 200).should.have.length(25);
withStatus(data, 429).should.have.length(2);
withStatus(data, 429)[0].url.should.eql('/test?user=b');
withStatus(data, 429)[1].url.should.eql('/test?user=b');
done();
});
});
});
});
function requests(server, count, url) {
return _.times(count, function() {
return function(next) {
supertest(server).get(url).end(next);
};
});
}
function parallelRequests(server, count, url) {
return function(next) {
async.parallel(requests(server, count, url), next);
};
}
function wait(millis) {
return function(next) {
setTimeout(next, 1100);
};
}
function okResponse(req, res, next) {
res.writeHead(200);
res.end('ok');
}
function withStatus(data, code) {
var pretty = data.map(function(d) {
return {
url: d.req.path,
statusCode: d.res.statusCode,
body: d.res.body
}
});
return _.filter(pretty, {statusCode: code});
}