Description
InMemoryRestRateLimiter.check(key, now = Date.now()) (src/middleware/restRateLimit.ts:30-50) refills its bucket when now >= bucket.resetAt and returns retryAfterMs = Math.max(bucket.resetAt - now, 0) when exhausted, while getRestRateLimitKey (src/middleware/restRateLimit.ts:57-64) chooses user:<id> when a user id is resolved and otherwise ip:<clientIp>. The current suite src/middleware/restRateLimit.test.ts has a single describe block and does not exercise the time-boundary math, backwards-moving clocks, or the user-over-IP precedence transition. This issue hardens coverage of those edge cases.
Requirements and context
- Use the injectable
now parameter of check(...) (src/middleware/restRateLimit.ts:30) to test: a request exactly at resetAt (must refill and allow), one millisecond before resetAt (must still count against the window), and the computed retryAfterMs/Retry-After rounding (src/middleware/restRateLimit.ts:75-79).
- Test clock skew: a
now that moves backwards must not crash and must not produce a negative retryAfterMs (the code clamps with Math.max(..., 0) — assert this invariant).
- Test precedence: the same caller transitioning from unauthenticated (
ip: bucket) to authenticated (user: bucket) must use independent buckets; and the same user id presented via JWT vs x-user-id must share a bucket (consistent with resolveRequestUserId in src/middleware/requireAuth.ts).
- Consider property-based tests with
fast-check (already a devDependency and used in src/repositories/apiKeyRepository.test.ts and src/validators/amountValidator.test.ts) to fuzz now sequences and assert monotonic, non-negative retryAfterMs.
- Non-functional: tests must be deterministic (inject
now; do not rely on real timers).
Acceptance criteria
Suggested execution
1. Fork the repo and create a branch
git checkout -b testing/rest-rate-limit-edges
2. Implement changes — none required in source; if a real bug is found, fix it minimally in src/middleware/restRateLimit.ts.
3. Write/extend tests — extend src/middleware/restRateLimit.test.ts (Jest + fast-check, *.test.ts).
4. Test and commit
npm run lint
npm run typecheck
npm run test:unit -- restRateLimit
npm run test:coverage -- restRateLimit
Example commit message
test(rate-limit): cover window boundary, clock skew, and IP/user precedence
Guidelines
Push coverage of src/middleware/restRateLimit.ts toward the repo's 90%+ target (README.md). Document any newly discovered behavior in test names/comments. Timeframe: 96 hours.
Description
InMemoryRestRateLimiter.check(key, now = Date.now())(src/middleware/restRateLimit.ts:30-50) refills its bucket whennow >= bucket.resetAtand returnsretryAfterMs = Math.max(bucket.resetAt - now, 0)when exhausted, whilegetRestRateLimitKey(src/middleware/restRateLimit.ts:57-64) choosesuser:<id>when a user id is resolved and otherwiseip:<clientIp>. The current suitesrc/middleware/restRateLimit.test.tshas a singledescribeblock and does not exercise the time-boundary math, backwards-moving clocks, or the user-over-IP precedence transition. This issue hardens coverage of those edge cases.Requirements and context
nowparameter ofcheck(...)(src/middleware/restRateLimit.ts:30) to test: a request exactly atresetAt(must refill and allow), one millisecond beforeresetAt(must still count against the window), and the computedretryAfterMs/Retry-Afterrounding (src/middleware/restRateLimit.ts:75-79).nowthat moves backwards must not crash and must not produce a negativeretryAfterMs(the code clamps withMath.max(..., 0)— assert this invariant).ip:bucket) to authenticated (user:bucket) must use independent buckets; and the same user id presented via JWT vsx-user-idmust share a bucket (consistent withresolveRequestUserIdinsrc/middleware/requireAuth.ts).fast-check(already a devDependency and used insrc/repositories/apiKeyRepository.test.tsandsrc/validators/amountValidator.test.ts) to fuzznowsequences and assert monotonic, non-negativeretryAfterMs.now; do not rely on real timers).Acceptance criteria
resetAtboundary (refill+allow) and the just-before-resetAtcase (still limited).nowis tested and never yields a negativeretryAfterMs.Retry-Aftersecond-rounding (Math.ceil(retryAfterMs/1000), min 1) is asserted.fast-checkproperty test fuzzesnowsequences.npm run test:unit.Suggested execution
1. Fork the repo and create a branch
2. Implement changes — none required in source; if a real bug is found, fix it minimally in
src/middleware/restRateLimit.ts.3. Write/extend tests — extend
src/middleware/restRateLimit.test.ts(Jest +fast-check,*.test.ts).4. Test and commit
Example commit message
Guidelines
Push coverage of
src/middleware/restRateLimit.tstoward the repo's 90%+ target (README.md). Document any newly discovered behavior in test names/comments. Timeframe: 96 hours.