UNPKG

5.32 kBJavaScriptView Raw
1var chai = require('chai')
2 , request = require('supertest')
3 , sinon = require('sinon')
4 , redis = require('redis').createClient()
5 , v = require('valentine')
6 , subject = require('../')
7
8chai.use(require('sinon-chai'))
9
10describe('rate-limiter', function () {
11 var express, app, limiter
12
13 beforeEach(function () {
14 express = require('express')
15 app = express()
16 limiter = subject(app, redis)
17 })
18
19 afterEach(function (done) {
20 redis.flushdb(done)
21 })
22
23 it('should work', function (done) {
24 var map = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
25 var clock = sinon.useFakeTimers()
26
27 limiter({
28 path: '/route',
29 method: 'get',
30 lookup: ['connection.remoteAddress'],
31 total: 10,
32 expire: 1000 * 60 * 60
33 })
34
35 app.get('/route', function (req, res) {
36 res.send(200, 'hello')
37 })
38
39 var out = (map).map(function (item) {
40 return function (f) {
41 process.nextTick(function () {
42 request(app)
43 .get('/route')
44 .expect('X-RateLimit-Limit', 10)
45 .expect('X-RateLimit-Remaining', item - 1)
46 .expect('X-RateLimit-Reset', 3600)
47 .expect(200, function (e) {f(e)})
48 })
49 }
50 })
51 out.push(function (f) {
52 request(app)
53 .get('/route')
54 .expect('X-RateLimit-Limit', 10)
55 .expect('X-RateLimit-Remaining', 0)
56 .expect('X-RateLimit-Reset', 3600)
57 .expect('Retry-After', /\d+/)
58 .expect(429, function (e) {f(e)})
59 })
60 out.push(function (f) {
61 // expire the time
62 clock.tick(1000 * 60 * 60 + 1)
63 request(app)
64 .get('/route')
65 .expect('X-RateLimit-Limit', 10)
66 .expect('X-RateLimit-Remaining', 9)
67 .expect('X-RateLimit-Reset', 7201)
68 .expect(200, function (e) {
69 clock.restore()
70 f(e)
71 })
72 })
73 v.waterfall(out, done)
74 })
75
76 context('options', function() {
77 it('should process options.skipHeaders', function (done) {
78 limiter({
79 path: '/route',
80 method: 'get',
81 lookup: ['connection.remoteAddress'],
82 total: 0,
83 expire: 1000 * 60 * 60,
84 skipHeaders: true
85 })
86
87 app.get('/route', function (req, res) {
88 res.send(200, 'hello')
89 })
90
91 request(app)
92 .get('/route')
93 .expect(function(res) {
94 if ('X-RateLimit-Limit' in res.headers) return 'X-RateLimit-Limit Header not to be set'
95 })
96 .expect(function(res) {
97 if ('X-RateLimit-Remaining' in res.headers) return 'X-RateLimit-Remaining Header not to be set'
98 })
99 .expect(function(res) {
100 if ('Retry-After' in res.headers) return 'Retry-After not to be set'
101 })
102 .expect(429, done)
103 })
104
105 it('should process ignoreErrors', function (done) {
106 limiter({
107 path: '/route',
108 method: 'get',
109 lookup: ['connection.remoteAddress'],
110 total: 10,
111 expire: 1000 * 60 * 60,
112 ignoreErrors: true
113 })
114
115 app.get('/route', function (req, res) {
116 res.send(200, 'hello')
117 })
118
119 var stub = sinon.stub(redis, 'get', function(key, callback) {
120 callback({err: true})
121 })
122
123 request(app)
124 .get('/route')
125 .expect(200, function (e) {
126 done(e)
127 stub.restore()
128 })
129 })
130
131 it('should process lookup as a function', function (done) {
132 limiter({
133 path: '*',
134 method: 'all',
135 lookup: function (req, res, opts, next) {
136 opts.lookup = 'query.api_key';
137 opts.total = 20
138 return next()
139 },
140 total: 3,
141 expire: 1000 * 60 * 60
142 })
143
144 app.get('/route', function (req, res) {
145 res.send(200, 'hello')
146 })
147
148 request(app)
149 .get('/route?api_key=foobar')
150 .expect('X-RateLimit-Limit', 20)
151 .expect('X-RateLimit-Remaining', 19)
152 .expect(200, function (e) {
153 done(e)
154 })
155 })
156 })
157
158 context('direct middleware', function () {
159
160 it('is able to mount without `path` and `method`', function (done) {
161 var clock = sinon.useFakeTimers()
162 var middleware = limiter({
163 lookup: 'connection.remoteAddress',
164 total: 3,
165 expire: 1000 * 60 * 60
166 })
167 app.get('/direct', middleware, function (req, res, next) {
168 res.send(200, 'is direct')
169 })
170 v.waterfall(
171 function (f) {
172 process.nextTick(function () {
173 request(app)
174 .get('/direct')
175 .expect('X-RateLimit-Limit', 3)
176 .expect('X-RateLimit-Remaining', 2)
177 .expect(200, function (e) {f(e)})
178 })
179 },
180 function (f) {
181 process.nextTick(function () {
182 request(app)
183 .get('/direct')
184 .expect('X-RateLimit-Limit', 3)
185 .expect('X-RateLimit-Remaining', 1)
186 .expect(200, function (e) {f(e)})
187 })
188 },
189 function (f) {
190 process.nextTick(function () {
191 request(app)
192 .get('/direct')
193 .expect('X-RateLimit-Limit', 3)
194 .expect('X-RateLimit-Remaining', 0)
195 .expect('Retry-After', /\d+/)
196 .expect(429, function (e) { f(null) })
197 })
198 },
199 function (e) {
200 done(e)
201 }
202 )
203 })
204 })
205})