66 resetFreeModeRateLimits ,
77} from '../free-mode-rate-limiter'
88
9- const MINUTE_MS = 60 * 1000
9+ const SECOND_MS = 1000
10+ const MINUTE_MS = 60 * SECOND_MS
1011const HOUR_MS = 60 * MINUTE_MS
1112
1213describe ( 'free-mode-rate-limiter' , ( ) => {
@@ -29,6 +30,9 @@ describe('free-mode-rate-limiter', () => {
2930
3031 function makeRequests ( userId : string , count : number ) {
3132 for ( let i = 0 ; i < count ; i ++ ) {
33+ if ( i > 0 ) {
34+ advanceTime ( 1 * SECOND_MS + 1 )
35+ }
3236 const result = checkFreeModeRateLimit ( userId )
3337 if ( result . limited ) {
3438 throw new Error ( `Unexpectedly rate limited on request ${ i + 1 } ` )
@@ -42,15 +46,40 @@ describe('free-mode-rate-limiter', () => {
4246 expect ( result . limited ) . toBe ( false )
4347 } )
4448
49+ it ( 'limits when per-second limit is exceeded' , ( ) => {
50+ makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_SECOND )
51+
52+ const result = checkFreeModeRateLimit ( 'user-1' )
53+ expect ( result . limited ) . toBe ( true )
54+ if ( result . limited ) {
55+ expect ( result . windowName ) . toBe ( '1 second' )
56+ }
57+ } )
58+
59+ it ( 'resets per-second window after expiry' , ( ) => {
60+ makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_SECOND )
61+ expect ( checkFreeModeRateLimit ( 'user-1' ) . limited ) . toBe ( true )
62+
63+ advanceTime ( 1 * SECOND_MS + 1 )
64+
65+ const result = checkFreeModeRateLimit ( 'user-1' )
66+ expect ( result . limited ) . toBe ( false )
67+ } )
68+
4569 it ( 'allows requests up to the per-minute limit' , ( ) => {
4670 for ( let i = 0 ; i < FREE_MODE_RATE_LIMITS . PER_MINUTE ; i ++ ) {
4771 const result = checkFreeModeRateLimit ( 'user-1' )
4872 expect ( result . limited ) . toBe ( false )
73+ if ( i < FREE_MODE_RATE_LIMITS . PER_MINUTE - 1 ) {
74+ advanceTime ( 1 * SECOND_MS + 1 )
75+ }
4976 }
5077 } )
5178
5279 it ( 'limits when per-minute limit is exceeded' , ( ) => {
5380 makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_MINUTE )
81+ // Advance past the 1-second window so the per-minute window is the one that triggers
82+ advanceTime ( 1 * SECOND_MS + 1 )
5483
5584 const result = checkFreeModeRateLimit ( 'user-1' )
5685 expect ( result . limited ) . toBe ( true )
@@ -75,6 +104,9 @@ describe('free-mode-rate-limiter', () => {
75104 }
76105 }
77106
107+ // Advance past the 1-second window so the per-30-minute window is the one that triggers
108+ advanceTime ( 1 * SECOND_MS + 1 )
109+
78110 const result = checkFreeModeRateLimit ( 'user-1' )
79111 expect ( result . limited ) . toBe ( true )
80112 if ( result . limited ) {
@@ -153,6 +185,8 @@ describe('free-mode-rate-limiter', () => {
153185
154186 it ( 'does not increment counters when rate limited' , ( ) => {
155187 makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_MINUTE )
188+ // Advance past the 1-second window so the per-minute window blocks
189+ advanceTime ( 1 * SECOND_MS + 1 )
156190
157191 // These should all be rejected without changing state
158192 for ( let i = 0 ; i < 5 ; i ++ ) {
@@ -171,20 +205,27 @@ describe('free-mode-rate-limiter', () => {
171205
172206 it ( 'returns correct retryAfterMs for the violated window' , ( ) => {
173207 makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_MINUTE )
208+ // makeRequests advanced time by (PER_MINUTE - 1) * (SECOND_MS + 1)
209+ const elapsedInMakeRequests = ( FREE_MODE_RATE_LIMITS . PER_MINUTE - 1 ) * ( 1 * SECOND_MS + 1 )
210+
211+ // Advance past the 1-second window, then a bit more
212+ const additionalAdvance = 2 * SECOND_MS
213+ advanceTime ( additionalAdvance )
174214
175- // Advance 30 seconds into the 1-minute window
176- advanceTime ( 30_000 )
215+ const totalElapsed = elapsedInMakeRequests + additionalAdvance
216+ const expectedRetryAfterMs = 1 * MINUTE_MS - totalElapsed
177217
178218 const result = checkFreeModeRateLimit ( 'user-1' )
179219 expect ( result . limited ) . toBe ( true )
180220 if ( result . limited ) {
181- // Should be approximately 30 seconds remaining in the 1-minute window
182- expect ( result . retryAfterMs ) . toBe ( 1 * MINUTE_MS - 30_000 )
221+ expect ( result . windowName ) . toBe ( '1 minute' )
222+ expect ( result . retryAfterMs ) . toBe ( expectedRetryAfterMs )
183223 }
184224 } )
185225
186226 it ( 'resets per-minute window after expiry' , ( ) => {
187227 makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_MINUTE )
228+ advanceTime ( 1 * SECOND_MS + 1 )
188229
189230 const limited = checkFreeModeRateLimit ( 'user-1' )
190231 expect ( limited . limited ) . toBe ( true )
@@ -198,6 +239,7 @@ describe('free-mode-rate-limiter', () => {
198239
199240 it ( 'isolates different users' , ( ) => {
200241 makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_MINUTE )
242+ advanceTime ( 1 * SECOND_MS + 1 )
201243
202244 // user-1 is rate limited
203245 expect ( checkFreeModeRateLimit ( 'user-1' ) . limited ) . toBe ( true )
@@ -208,10 +250,7 @@ describe('free-mode-rate-limiter', () => {
208250 } )
209251
210252 it ( 'retryAfterMs is never negative' , ( ) => {
211- makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_MINUTE )
212-
213- // Advance to just before expiry
214- advanceTime ( 1 * MINUTE_MS - 1 )
253+ makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_SECOND )
215254
216255 const result = checkFreeModeRateLimit ( 'user-1' )
217256 expect ( result . limited ) . toBe ( true )
@@ -242,7 +281,7 @@ describe('free-mode-rate-limiter', () => {
242281
243282 describe ( 'resetFreeModeRateLimits' , ( ) => {
244283 it ( 'clears all rate limit state' , ( ) => {
245- makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_MINUTE )
284+ makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_SECOND )
246285 expect ( checkFreeModeRateLimit ( 'user-1' ) . limited ) . toBe ( true )
247286
248287 resetFreeModeRateLimits ( )
@@ -252,8 +291,11 @@ describe('free-mode-rate-limiter', () => {
252291 } )
253292
254293 it ( 'clears state for all users' , ( ) => {
255- makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_MINUTE )
256- makeRequests ( 'user-2' , FREE_MODE_RATE_LIMITS . PER_MINUTE )
294+ makeRequests ( 'user-1' , FREE_MODE_RATE_LIMITS . PER_SECOND )
295+ makeRequests ( 'user-2' , FREE_MODE_RATE_LIMITS . PER_SECOND )
296+
297+ expect ( checkFreeModeRateLimit ( 'user-1' ) . limited ) . toBe ( true )
298+ expect ( checkFreeModeRateLimit ( 'user-2' ) . limited ) . toBe ( true )
257299
258300 resetFreeModeRateLimits ( )
259301
0 commit comments