@@ -84,6 +84,38 @@ describe('rateLimit', () => {
8484 } ) ;
8585} ) ;
8686
87+ it ( 'keys expire exactly at the window limit with sliding time advances' , async ( ) => {
88+ vi . useFakeTimers ( ) ;
89+ const ip = '9.9.9.9' ;
90+ const windowMs = 1000 ;
91+ const limit = 5 ;
92+
93+ // First request: creates the tracker with 1s TTL
94+ let res = await rateLimit ( ip , limit , windowMs ) ;
95+ expect ( res . success ) . toBe ( true ) ;
96+ expect ( res . remaining ) . toBe ( limit - 1 ) ;
97+
98+ // Advance half the window and make another request
99+ vi . advanceTimersByTime ( 500 ) ;
100+ res = await rateLimit ( ip , limit , windowMs ) ;
101+ expect ( res . success ) . toBe ( true ) ;
102+
103+ // Advance to exactly the original window boundary (total = 1000ms)
104+ vi . advanceTimersByTime ( 500 ) ;
105+
106+ // At the exact boundary the entry should still be considered valid
107+ res = await rateLimit ( ip , limit , windowMs ) ;
108+ expect ( res . success ) . toBe ( true ) ;
109+
110+ // Move just past the window expiry
111+ vi . advanceTimersByTime ( 1 ) ;
112+
113+ // Now the key must have expired and a fresh window starts
114+ res = await rateLimit ( ip , limit , windowMs ) ;
115+ expect ( res . success ) . toBe ( true ) ;
116+ expect ( res . remaining ) . toBe ( limit - 1 ) ;
117+ } ) ;
118+
87119it ( 'allows requests after many expired IP entries' , async ( ) => {
88120 const windowMs = 1000 ;
89121
0 commit comments