@@ -513,45 +513,48 @@ describe("AIMDBucket", () => {
513513 bucket = new AIMDBucket ( { initialRate : 10 } ) ;
514514 } ) ;
515515
516- it ( "should process pending requests when tokens are completed with sufficient time gaps" , async ( ) => {
517- // This test verifies the fix for the lockup bug where pending requests get stuck
516+ it ( "should process pending requests when no events trigger refill (lockup bug fix)" , async ( ) => {
518517 bucket = new AIMDBucket ( {
519- initialRate : 4 , // Start with low rate like user's case
520- tokenReturnTimeoutMs : 10000 , // Long timeout so tokens don't auto-expire
518+ initialRate : 3 , // Low rate
519+ tokenReturnTimeoutMs : 30000 , // Long timeout so no auto-timeouts interfere
521520 } ) ;
522521
523- // Acquire tokens that will be resolved immediately (up to initial capacity)
524- const immediateTokens = await Promise . all ( [ bucket . acquire ( ) , bucket . acquire ( ) , bucket . acquire ( ) , bucket . acquire ( ) ] ) ;
525- expect ( immediateTokens ) . toHaveLength ( 4 ) ;
522+ // Get initial 3 tokens immediately (up to initial capacity)
523+ const token1 = await bucket . acquire ( ) ;
524+ const token2 = await bucket . acquire ( ) ;
525+ const token3 = await bucket . acquire ( ) ;
526526
527- // These should become pending since we've exhausted the bucket
528- const pendingTokenPromises = [ bucket . acquire ( ) , bucket . acquire ( ) , bucket . acquire ( ) , bucket . acquire ( ) , bucket . acquire ( ) ] ;
527+ // Advance time to get 1 more token through refill
528+ await vi . advanceTimersByTimeAsync ( 334 ) ; // ~1/3 second = 1 token at rate 3
529+ const token4 = await bucket . acquire ( ) ;
529530
530- // Advance time slightly to see initial state
531- await vi . advanceTimersByTimeAsync ( 100 ) ;
532531 let stats = bucket . getStatistics ( ) ;
533532 expect ( stats . tokensIssued ) . toBe ( 4 ) ;
534- expect ( stats . pendingCount ) . toBe ( 5 ) ;
533+ expect ( stats . currentRate ) . toBe ( 3 ) ;
535534
536- // Complete the immediate tokens successfully to trigger rate increase
537- // Add some time between completions to allow bucket to refill
538- for ( let i = 0 ; i < immediateTokens . length ; i ++ ) {
539- immediateTokens [ i ] . success ( ) ;
540- await vi . advanceTimersByTimeAsync ( 500 ) ; // Allow some refill time
541- }
535+ token1 . success ( ) ;
536+ token2 . success ( ) ;
537+ token3 . success ( ) ;
538+ token4 . success ( ) ;
542539
543- // At this point, some pending requests should have been processed during token completions
540+ const pendingPromises = [ bucket . acquire ( ) , bucket . acquire ( ) , bucket . acquire ( ) , bucket . acquire ( ) , bucket . acquire ( ) ] ;
541+
542+ await vi . advanceTimersByTimeAsync ( 50 ) ;
544543 stats = bucket . getStatistics ( ) ;
544+ expect ( stats . currentRate ) . toBe ( 3 ) ;
545+ expect ( stats . tokensIssued ) . toBe ( 4 ) ;
545546 expect ( stats . successCount ) . toBe ( 4 ) ;
547+ expect ( stats . pendingCount ) . toBe ( 5 ) ;
546548
547- // All pending requests should have been processed due to the fix
549+ // Now advance time significantly, but make NO new acquire() calls and don't complete any more tokens. This a lockup scenario. Time passes, bucket should refill, but pending requests never get processed because no events trigger _refill()
550+ await vi . advanceTimersByTimeAsync ( 5000 ) ; // 5 seconds = 15 tokens should be available
551+
552+ stats = bucket . getStatistics ( ) ;
548553 expect ( stats . pendingCount ) . toBe ( 0 ) ;
549- expect ( stats . tokensIssued ) . toBe ( 9 ) ; // 4 immediate + 5 pending
554+ expect ( stats . tokensIssued ) . toBe ( 9 ) ;
550555
551- // Verify that all pending promises were resolved
552- const resolvedTokens = await Promise . all ( pendingTokenPromises ) ;
556+ const resolvedTokens = await Promise . all ( pendingPromises ) ;
553557 expect ( resolvedTokens ) . toHaveLength ( 5 ) ;
554- resolvedTokens . forEach ( ( token ) => expect ( token ) . toBeInstanceOf ( AIMDBucketToken ) ) ;
555558 } ) ;
556559
557560 it ( "should process pending requests when tokens timeout automatically" , async ( ) => {
0 commit comments