@@ -35,7 +35,7 @@ async function setTokenCountInClient(
3535 await redisClient . set ( uuid , JSON . stringify ( value ) ) ;
3636}
3737
38- xdescribe ( 'Test SlidingWindowCounter Rate Limiter' , ( ) => {
38+ describe ( 'Test TokenBucket Rate Limiter' , ( ) => {
3939 beforeEach ( async ( ) => {
4040 // init a mock redis cache
4141 client = new RedisMock ( ) ;
@@ -50,10 +50,7 @@ xdescribe('Test SlidingWindowCounter Rate Limiter', () => {
5050 afterEach ( ( ) => {
5151 client . flushall ( ) ;
5252 } ) ;
53-
54- test ( 'fixed window is initially empty' , async ( ) => {
55- setTokenCountInClient ( client , user1 , 0 , 0 , timestamp ) ;
56-
53+ test ( 'fixed window and cache are initially empty' , async ( ) => {
5754 // window is intially empty
5855 const withdraw5 = 5 ;
5956 expect ( ( await limiter . processRequest ( user1 , timestamp , withdraw5 ) ) . tokens ) . toBe (
@@ -64,7 +61,7 @@ xdescribe('Test SlidingWindowCounter Rate Limiter', () => {
6461 expect ( tokenCountFull . previousTokens ) . toBe ( 0 ) ;
6562 } ) ;
6663
67- test ( 'fixed window and cache are initially empty' , async ( ) => {
64+ test ( 'fixed window is initially empty' , async ( ) => {
6865 // window is intially empty
6966 const withdraw5 = 5 ;
7067 expect ( ( await limiter . processRequest ( user1 , timestamp , withdraw5 ) ) . tokens ) . toBe (
@@ -87,28 +84,33 @@ xdescribe('Test SlidingWindowCounter Rate Limiter', () => {
8784 ) . toBe ( CAPACITY - ( initial + partialWithdraw ) ) ;
8885
8986 const tokenCountPartial = await getWindowFromClient ( client , user2 ) ;
90- expect ( tokenCountPartial . currentTokens ) . toBe (
91- CAPACITY - ( initial + partialWithdraw )
92- ) ;
87+ expect ( tokenCountPartial . currentTokens ) . toBe ( initial + partialWithdraw ) ;
9388 } ) ;
9489
9590 // window partially full and no leftover tokens after request
9691 test ( 'fixed window is partially full and request has no leftover tokens' , async ( ) => {
9792 const initial = 6 ;
9893 await setTokenCountInClient ( client , user2 , initial , 0 , timestamp ) ;
99- expect ( ( await limiter . processRequest ( user2 , timestamp , initial ) ) . tokens ) . toBe ( 0 ) ;
94+ expect (
95+ ( await limiter . processRequest ( user2 , timestamp , CAPACITY - initial ) ) . tokens
96+ ) . toBe ( 0 ) ;
10097 const tokenCountPartialToEmpty = await getWindowFromClient ( client , user2 ) ;
101- expect ( tokenCountPartialToEmpty . currentTokens ) . toBe ( 0 ) ;
98+ expect ( tokenCountPartialToEmpty . currentTokens ) . toBe ( 10 ) ;
10299 } ) ;
103100
104101 // Window initially full but enough time elapsed to paritally fill window since last request
105- test ( 'current window is initially full but after new fixed window is initialized request is allowed' , async ( ) => {
102+ test ( 'fixed window is initially full but after new fixed window is initialized request is allowed' , async ( ) => {
106103 await setTokenCountInClient ( client , user4 , 10 , 0 , timestamp ) ;
107104 // tokens returned in processRequest is equal to the capacity
108105 // still available in the fixed window
109- expect (
110- ( await limiter . processRequest ( user4 , timestamp + WINDOW_SIZE + 1 , 1 ) ) . tokens
111- ) . toBe ( 0 ) ; // here, we expect the rolling window to only allow 1 token, b/c
106+
107+ const result = await limiter . processRequest ( user4 , timestamp + WINDOW_SIZE , 1 ) ;
108+
109+ // should be allowed because formula is floored
110+ expect ( result . success ) . toBe ( true ) ;
111+ expect ( result . tokens ) . toBe ( 0 ) ;
112+
113+ // here, we expect the rolling window to only allow 1 token, b/c
112114 // only 1ms has passed since the previous fixed window
113115
114116 // `currentTokens` cached is the amount of tokens
@@ -214,8 +216,8 @@ xdescribe('Test SlidingWindowCounter Rate Limiter', () => {
214216 ( await limiter . processRequest ( user2 , timestamp + WINDOW_SIZE , 5 ) ) . tokens
215217 ) . toBe ( CAPACITY - initRequest ) ;
216218
217- // expect current tokens in the window to still be 0
218- expect ( ( await getWindowFromClient ( client , user2 ) ) . currentTokens ) . toBe ( 0 ) ;
219+ // expect current tokens in the window to still be 6
220+ expect ( ( await getWindowFromClient ( client , user2 ) ) . currentTokens ) . toBe ( 6 ) ;
219221 } ) ;
220222
221223 // 3 rolling window tests with different proportions (.25, .5, .75)
@@ -291,7 +293,7 @@ xdescribe('Test SlidingWindowCounter Rate Limiter', () => {
291293 // currentTokens (in current fixed window): 0
292294 // previousTokens (in previous fixed window): 8
293295 const count = await getWindowFromClient ( client , user4 ) ;
294- expect ( count . currentTokens ) . toBe ( 4 ) ;
296+ expect ( count . currentTokens ) . toBe ( 0 ) ;
295297 expect ( count . previousTokens ) . toBe ( initRequest ) ;
296298 } ) ;
297299 } ) ;
@@ -336,18 +338,17 @@ xdescribe('Test SlidingWindowCounter Rate Limiter', () => {
336338 } ) ;
337339
338340 test ( 'fixed window and current/previous tokens update as expected' , async ( ) => {
339- await setTokenCountInClient ( client , user1 , 0 , 0 , timestamp ) ;
340- // fills first window with 4 tokens
341+ // fills first window with 5 tokens
341342 await limiter . processRequest ( user1 , timestamp , 5 ) ;
342- // fills second window with 5 tokens
343+ // fills second window with 4 tokens
343344 expect (
344345 await (
345- await limiter . processRequest ( user1 , timestamp + WINDOW_SIZE + 1 , 4 )
346+ await limiter . processRequest ( user1 , timestamp + WINDOW_SIZE , 4 )
346347 ) . tokens
347- ) . toBe ( 6 ) ;
348+ ) . toBe ( 2 ) ;
348349 // currentTokens (in current fixed window): 0
349350 // previousTokens (in previous fixed window): 8
350- const count = await getWindowFromClient ( client , user4 ) ;
351+ const count = await getWindowFromClient ( client , user1 ) ;
351352 // ensures that fixed window is updated when a request goes over
352353 expect ( count . fixedWindowStart ) . toBe ( timestamp + WINDOW_SIZE ) ;
353354 // ensures that previous tokens property updates on fixed window change
@@ -363,10 +364,11 @@ xdescribe('Test SlidingWindowCounter Rate Limiter', () => {
363364
364365 await newLimiter . processRequest ( user1 , timestamp , 8 ) ;
365366
366- // expect that a new window is entered, leaving 9 tokens available after a 1 token request
367+ // expect that a new window is entered, leaving 2 tokens available after both requests
368+ // 8 * .99 -> 7 (floored) + 1 = 8
367369 expect (
368370 ( await newLimiter . processRequest ( user1 , timestamp + newWindowSize + 1 , 1 ) ) . tokens
369- ) . toBe ( 9 ) ;
371+ ) . toBe ( 2 ) ;
370372 } ) ;
371373
372374 test ( 'sliding window allows custom capacities' , async ( ) => {
@@ -461,12 +463,14 @@ xdescribe('Test SlidingWindowCounter Rate Limiter', () => {
461463
462464 expect ( redisData . currentTokens ) . toBe ( 2 ) ;
463465
464- await limiter . processRequest ( user1 , timestamp + WINDOW_SIZE + 1 , 3 ) ;
466+ // new window
467+ await limiter . processRequest ( user1 , timestamp + WINDOW_SIZE , 3 ) ;
465468
466469 redisData = await getWindowFromClient ( client , user1 ) ;
467470
468471 expect ( redisData . currentTokens ) . toBe ( 3 ) ;
469472 expect ( redisData . previousTokens ) . toBe ( 2 ) ;
473+ expect ( redisData . fixedWindowStart ) . toBe ( timestamp + WINDOW_SIZE ) ;
470474 } ) ;
471475
472476 test ( 'all windows should be able to be reset' , async ( ) => {
0 commit comments