@@ -108,6 +108,38 @@ describe('rateLimit', () => {
108108 } ) ;
109109} ) ;
110110
111+ it ( 'keys expire exactly at the window limit with sliding time advances' , async ( ) => {
112+ vi . useFakeTimers ( ) ;
113+ const ip = '9.9.9.9' ;
114+ const windowMs = 1000 ;
115+ const limit = 5 ;
116+
117+ // First request: creates the tracker with 1s TTL
118+ let res = await rateLimit ( ip , limit , windowMs ) ;
119+ expect ( res . success ) . toBe ( true ) ;
120+ expect ( res . remaining ) . toBe ( limit - 1 ) ;
121+
122+ // Advance half the window and make another request
123+ vi . advanceTimersByTime ( 500 ) ;
124+ res = await rateLimit ( ip , limit , windowMs ) ;
125+ expect ( res . success ) . toBe ( true ) ;
126+
127+ // Advance to exactly the original window boundary (total = 1000ms)
128+ vi . advanceTimersByTime ( 500 ) ;
129+
130+ // At the exact boundary the entry should still be considered valid
131+ res = await rateLimit ( ip , limit , windowMs ) ;
132+ expect ( res . success ) . toBe ( true ) ;
133+
134+ // Move just past the window expiry
135+ vi . advanceTimersByTime ( 1 ) ;
136+
137+ // Now the key must have expired and a fresh window starts
138+ res = await rateLimit ( ip , limit , windowMs ) ;
139+ expect ( res . success ) . toBe ( true ) ;
140+ expect ( res . remaining ) . toBe ( limit - 1 ) ;
141+ } ) ;
142+
111143it ( 'allows requests after many expired IP entries' , async ( ) => {
112144 const windowMs = 1000 ;
113145
0 commit comments