Skip to content

Commit 955d0c5

Browse files
committed
updated tests
1 parent 9df25e9 commit 955d0c5

File tree

1 file changed

+188
-35
lines changed

1 file changed

+188
-35
lines changed

test/rateLimiters/slidingWindowCounter.test.ts

Lines changed: 188 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ describe('Test TokenBucket Rate Limiter', () => {
5050
afterEach(() => {
5151
client.flushall();
5252
});
53+
test('fixed window and cache are initially empty', async () => {
54+
// window is intially empty
55+
const withdraw5 = 5;
56+
expect((await limiter.processRequest(user1, timestamp, withdraw5)).tokens).toBe(
57+
CAPACITY - withdraw5
58+
);
59+
const tokenCountFull = await getWindowFromClient(client, user1);
60+
expect(tokenCountFull.currentTokens).toBe(CAPACITY - withdraw5);
61+
expect(tokenCountFull.previousTokens).toBe(0);
62+
});
63+
5364
test('fixed window is initially empty', async () => {
5465
// window is intially empty
5566
const withdraw5 = 5;
@@ -58,6 +69,7 @@ describe('Test TokenBucket Rate Limiter', () => {
5869
);
5970
const tokenCountFull = await getWindowFromClient(client, user1);
6071
expect(tokenCountFull.currentTokens).toBe(CAPACITY - withdraw5);
72+
expect(tokenCountFull.previousTokens).toBe(0);
6173
});
6274

6375
test('fixed window is partially full and request has leftover tokens', async () => {
@@ -92,9 +104,16 @@ describe('Test TokenBucket Rate Limiter', () => {
92104
// tokens returned in processRequest is equal to the capacity
93105
// still available in the fixed window
94106

95-
expect(
96-
(await limiter.processRequest(user4, timestamp + WINDOW_SIZE + 1, 1)).tokens
97-
).toBe(0); // here, we expect the rolling window to only allow 1 token, b/c
107+
// adds additional ms so that:
108+
// rolling window proportion: .99999...
109+
// 1 + 10 * .9 = 10 (floored)
110+
const result = await limiter.processRequest(user4, timestamp + WINDOW_SIZE + 1, 1);
111+
112+
// should be allowed because formula is floored
113+
expect(result.success).toBe(true);
114+
expect(result.tokens).toBe(0);
115+
116+
// here, we expect the rolling window to only allow 1 token, b/c
98117
// only 1ms has passed since the previous fixed window
99118

100119
// `currentTokens` cached is the amount of tokens
@@ -105,7 +124,31 @@ describe('Test TokenBucket Rate Limiter', () => {
105124
expect(count.currentTokens).toBe(1);
106125
});
107126

108-
// three different tests within, with different rolling window proportions (.25, .5, .75)
127+
// five different tests within, with different rolling window proportions (0.01, .25, .5, .75, 1)
128+
test('rolling window at 100% allows requests under capacity', async () => {
129+
// 100% of rolling window present in previous fixed window
130+
// 1*60000 = 60000 (time after initial fixedWindowStart
131+
// to set rolling window at 100% of previous fixed window)
132+
133+
// to set initial fixedWindowStart
134+
await setTokenCountInClient(client, user4, 0, 0, timestamp);
135+
136+
// large request at very end of first fixed window
137+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, 8);
138+
139+
// 2 + 8 * 1 = 10, right at capacity (request should be allowed)
140+
// tokens until capacity: 0 (tokens property returned by processRequest method)
141+
const result = await limiter.processRequest(user4, timestamp + WINDOW_SIZE, 2);
142+
expect(result.tokens).toBe(0);
143+
expect(result.success).toBe(true);
144+
145+
// currentTokens (in current fixed window): 4
146+
// previousTokens (in previous fixed window): 8
147+
const count1 = await getWindowFromClient(client, user4);
148+
expect(count1.currentTokens).toBe(2);
149+
expect(count1.previousTokens).toBe(8);
150+
});
151+
109152
test('rolling window at 75% allows requests under capacity', async () => {
110153
// 75% of rolling window present in previous fixed window
111154
// 1.25*60000 = 75000 (time after initial fixedWindowStart
@@ -115,13 +158,17 @@ describe('Test TokenBucket Rate Limiter', () => {
115158
await setTokenCountInClient(client, user4, 0, 0, timestamp);
116159

117160
// large request at very end of first fixed window
118-
await limiter.processRequest(user4, timestamp + WINDOW_SIZE, 8);
161+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, 8);
119162

120163
// 4 + 8 * .75 = 10, right at capacity (request should be allowed)
121164
// tokens until capacity: 0 (tokens property returned by processRequest method)
122-
expect(
123-
(await limiter.processRequest(user4, timestamp + WINDOW_SIZE * 1.25, 4)).tokens
124-
).toBe(0);
165+
const result = await limiter.processRequest(
166+
user4,
167+
timestamp + WINDOW_SIZE * 1.25,
168+
4
169+
);
170+
expect(result.tokens).toBe(0);
171+
expect(result.success).toBe(true);
125172

126173
// currentTokens (in current fixed window): 4
127174
// previousTokens (in previous fixed window): 8
@@ -139,13 +186,17 @@ describe('Test TokenBucket Rate Limiter', () => {
139186
await setTokenCountInClient(client, user4, 0, 0, timestamp);
140187

141188
// large request at very end of first fixed window
142-
await limiter.processRequest(user4, timestamp + WINDOW_SIZE, 8);
189+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, 8);
143190

144191
// 4 + 8 * .5 = 8, under capacity (request should be allowed)
145192
// tokens until capacity: 2 (tokens property returned by processRequest method)
146-
expect(
147-
(await limiter.processRequest(user4, timestamp + WINDOW_SIZE * 1.5, 4)).tokens
148-
).toBe(2);
193+
const result = await limiter.processRequest(
194+
user4,
195+
timestamp + WINDOW_SIZE * 1.5,
196+
4
197+
);
198+
expect(result.tokens).toBe(2);
199+
expect(result.success).toBe(true);
149200

150201
// currentTokens (in current fixed window): 4
151202
// previousTokens (in previous fixed window): 8
@@ -163,20 +214,52 @@ describe('Test TokenBucket Rate Limiter', () => {
163214
await setTokenCountInClient(client, user4, 0, 0, timestamp);
164215

165216
// large request at very end of first fixed window
166-
await limiter.processRequest(user4, timestamp + WINDOW_SIZE, 8);
217+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, 8);
167218

168219
// 4 + 8 * .25 = 6, under capacity (request should be allowed)
169220
// tokens until capacity: 4 (tokens property returned by processRequest method)
170-
expect(
171-
(await limiter.processRequest(user4, timestamp + WINDOW_SIZE * 1.75, 4)).tokens
172-
).toBe(4);
221+
const result = await limiter.processRequest(
222+
user4,
223+
timestamp + WINDOW_SIZE * 1.75,
224+
4
225+
);
226+
expect(result.tokens).toBe(4);
227+
expect(result.success).toBe(true);
173228

174229
// currentTokens (in current fixed window): 4
175230
// previousTokens (in previous fixed window): 8
176231
const count = await getWindowFromClient(client, user4);
177232
expect(count.currentTokens).toBe(4);
178233
expect(count.previousTokens).toBe(8);
179234
});
235+
236+
test('rolling window at 1% allows requests under capacity', async () => {
237+
// 1% of rolling window present in previous fixed window
238+
// 0.01*60000 = 600 (time after initial fixedWindowStart
239+
// to set rolling window at 1% of previous fixed window)
240+
241+
// to set initial fixedWindowStart
242+
await setTokenCountInClient(client, user4, 0, 0, timestamp);
243+
244+
// large request at very end of first fixed window
245+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, 8);
246+
247+
// 10 + 8 * .01 = 10, right at capacity (request should be allowed)
248+
// tokens until capacity: 0 (tokens property returned by processRequest method)
249+
const result = await limiter.processRequest(
250+
user4,
251+
timestamp + WINDOW_SIZE * 1.99,
252+
4
253+
);
254+
expect(result.tokens).toBe(0);
255+
expect(result.success).toBe(true);
256+
257+
// currentTokens (in current fixed window): 4
258+
// previousTokens (in previous fixed window): 8
259+
const count1 = await getWindowFromClient(client, user4);
260+
expect(count1.currentTokens).toBe(4);
261+
expect(count1.previousTokens).toBe(8);
262+
});
180263
});
181264

182265
describe('after a BLOCKED request...', () => {
@@ -196,15 +279,40 @@ describe('Test TokenBucket Rate Limiter', () => {
196279

197280
await setTokenCountInClient(client, user2, initRequest, 0, timestamp);
198281
// expect remaining tokens to be 4, b/c the 5 token request should be blocked
199-
expect(
200-
(await limiter.processRequest(user2, timestamp + WINDOW_SIZE, 5)).tokens
201-
).toBe(CAPACITY - initRequest);
282+
const result = await limiter.processRequest(user2, timestamp + WINDOW_SIZE - 1, 5);
283+
284+
expect(result.success).toBe(false);
285+
expect(result.tokens).toBe(CAPACITY - initRequest);
202286

203287
// expect current tokens in the window to still be 6
204288
expect((await getWindowFromClient(client, user2)).currentTokens).toBe(6);
205289
});
206290

207-
// 3 rolling window tests with different proportions (.25, .5, .75)
291+
// 5 rolling window tests with different proportions (.01, .25, .5, .75, 1)
292+
test('rolling window at 100% blocks requests over allowed limit set by formula', async () => {
293+
// 100% of rolling window present in previous fixed window
294+
// 1*60000 = 60000 (time after initial fixedWindowStart
295+
// to set rolling window at 100% of previous fixed window)
296+
297+
// to set initial fixedWindowStart
298+
await setTokenCountInClient(client, user4, 0, 0, timestamp);
299+
300+
const initRequest = 8;
301+
302+
// large request at very end of first fixed window
303+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, initRequest);
304+
305+
// 3 + 8 * 1 = 11, above capacity (request should be blocked)
306+
const result = await limiter.processRequest(user4, timestamp + WINDOW_SIZE, 3);
307+
expect(result.tokens).toBe(10);
308+
expect(result.success).toBe(false);
309+
310+
// currentTokens (in current fixed window): 0
311+
// previousTokens (in previous fixed window): 8
312+
const count1 = await getWindowFromClient(client, user4);
313+
expect(count1.currentTokens).toBe(0);
314+
expect(count1.previousTokens).toBe(initRequest);
315+
});
208316
test('rolling window at 75% blocks requests over allowed limit set by formula', async () => {
209317
// 75% of rolling window present in previous fixed window
210318
// 1.25*60000 = 75000 (time after initial fixedWindowStart
@@ -216,12 +324,16 @@ describe('Test TokenBucket Rate Limiter', () => {
216324
const initRequest = 8;
217325

218326
// large request at very end of first fixed window
219-
await limiter.processRequest(user4, timestamp + WINDOW_SIZE, initRequest);
327+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, initRequest);
220328

221329
// 5 + 8 * .75 = 11, above capacity (request should be blocked)
222-
expect(
223-
(await limiter.processRequest(user4, timestamp + WINDOW_SIZE * 1.25, 5)).tokens
224-
).toBe(10);
330+
const result = await limiter.processRequest(
331+
user4,
332+
timestamp + WINDOW_SIZE * 1.25,
333+
5
334+
);
335+
expect(result.tokens).toBe(10);
336+
expect(result.success).toBe(false);
225337

226338
// currentTokens (in current fixed window): 0
227339
// previousTokens (in previous fixed window): 8
@@ -242,12 +354,12 @@ describe('Test TokenBucket Rate Limiter', () => {
242354
const initRequest = 8;
243355

244356
// large request at very end of first fixed window
245-
await limiter.processRequest(user4, timestamp + WINDOW_SIZE, initRequest);
357+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, initRequest);
246358

247359
// 7 + 8 * .5 = 11, over capacity (request should be blocked)
248-
expect(
249-
(await limiter.processRequest(user4, timestamp + WINDOW_SIZE * 1.5, 7)).tokens
250-
).toBe(10);
360+
const result = await limiter.processRequest(user4, timestamp + WINDOW_SIZE * 1.5, 7);
361+
expect(result.tokens).toBe(10);
362+
expect(result.success).toBe(false);
251363

252364
// currentTokens (in current fixed window): 0
253365
// previousTokens (in previous fixed window): 8
@@ -267,19 +379,43 @@ describe('Test TokenBucket Rate Limiter', () => {
267379
const initRequest = 8;
268380

269381
// large request at very end of first fixed window
270-
await limiter.processRequest(user4, timestamp + WINDOW_SIZE, initRequest);
382+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, initRequest);
271383

272384
// 9 + 8 * .25 = 11, over capacity (request should be blocked)
273-
expect(
274-
(await limiter.processRequest(user4, timestamp + WINDOW_SIZE * 1.75, 9)).tokens
275-
).toBe(10);
385+
const result = await limiter.processRequest(user4, timestamp + WINDOW_SIZE * 1.75, 9);
386+
expect(result.tokens).toBe(10);
387+
expect(result.success).toBe(false);
276388

277389
// currentTokens (in current fixed window): 0
278390
// previousTokens (in previous fixed window): 8
279391
const count = await getWindowFromClient(client, user4);
280392
expect(count.currentTokens).toBe(0);
281393
expect(count.previousTokens).toBe(initRequest);
282394
});
395+
test('rolling window at 100% blocks requests over allowed limit set by formula', async () => {
396+
// 1% of rolling window present in previous fixed window
397+
// .01*60000 = 600 (time after initial fixedWindowStart
398+
// to set rolling window at 100% of previous fixed window)
399+
400+
// to set initial fixedWindowStart
401+
await setTokenCountInClient(client, user4, 0, 0, timestamp);
402+
403+
const initRequest = 8;
404+
405+
// large request at very end of first fixed window
406+
await limiter.processRequest(user4, timestamp + WINDOW_SIZE - 1, initRequest);
407+
408+
// 11 + 8 * .01 = 11, above capacity (request should be blocked)
409+
const result = await limiter.processRequest(user4, timestamp + WINDOW_SIZE, 11);
410+
expect(result.tokens).toBe(10);
411+
expect(result.success).toBe(false);
412+
413+
// currentTokens (in current fixed window): 0
414+
// previousTokens (in previous fixed window): 8
415+
const count1 = await getWindowFromClient(client, user4);
416+
expect(count1.currentTokens).toBe(0);
417+
expect(count1.previousTokens).toBe(initRequest);
418+
});
283419
});
284420

285421
describe('SlidingWindowCounter functions as expected', () => {
@@ -327,14 +463,14 @@ describe('Test TokenBucket Rate Limiter', () => {
327463
// fills second window with 4 tokens
328464
expect(
329465
await (
330-
await limiter.processRequest(user1, timestamp + WINDOW_SIZE + 1, 4)
466+
await limiter.processRequest(user1, timestamp + WINDOW_SIZE, 4)
331467
).tokens
332468
).toBe(2);
333469
// currentTokens (in current fixed window): 0
334470
// previousTokens (in previous fixed window): 8
335471
const count = await getWindowFromClient(client, user1);
336472
// ensures that fixed window is updated when a request goes over
337-
expect(count.fixedWindowStart).toBe(timestamp + WINDOW_SIZE + 1);
473+
expect(count.fixedWindowStart).toBe(timestamp + WINDOW_SIZE);
338474
// ensures that previous tokens property updates on fixed window change
339475
expect(count.previousTokens).toBe(5);
340476
// ensures that current tokens only represents tokens from current window requests
@@ -416,6 +552,21 @@ describe('Test TokenBucket Rate Limiter', () => {
416552
false
417553
);
418554
});
555+
556+
test('updates correctly when > WINDOW_SIZE * 2 has surpassed', async () => {
557+
await setTokenCountInClient(client, user1, 1, 0, timestamp);
558+
559+
// to make sure that previous tokens is not 1
560+
const result = await limiter.processRequest(user1, timestamp + WINDOW_SIZE * 2, 1);
561+
562+
expect(result.tokens).toBe(9);
563+
564+
const redisData: RedisWindow = await getWindowFromClient(client, user1);
565+
566+
expect(redisData.currentTokens).toBe(1);
567+
expect(redisData.previousTokens).toBe(0);
568+
expect(redisData.fixedWindowStart).toBe(timestamp + WINDOW_SIZE * 2);
569+
});
419570
});
420571

421572
describe('SlidingWindowCounter correctly updates Redis cache', () => {
@@ -447,12 +598,14 @@ describe('Test TokenBucket Rate Limiter', () => {
447598

448599
expect(redisData.currentTokens).toBe(2);
449600

450-
await limiter.processRequest(user1, timestamp + WINDOW_SIZE + 1, 3);
601+
// new window
602+
await limiter.processRequest(user1, timestamp + WINDOW_SIZE, 3);
451603

452604
redisData = await getWindowFromClient(client, user1);
453605

454606
expect(redisData.currentTokens).toBe(3);
455607
expect(redisData.previousTokens).toBe(2);
608+
expect(redisData.fixedWindowStart).toBe(timestamp + WINDOW_SIZE);
456609
});
457610

458611
test('all windows should be able to be reset', async () => {

0 commit comments

Comments
 (0)