Skip to content

Add rate-limiter edge-case tests for window boundary, clock skew, and IP-vs-user precedence #380

@greatest0fallt1me

Description

@greatest0fallt1me

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

  • Tests cover the exact-resetAt boundary (refill+allow) and the just-before-resetAt case (still limited).
  • A backwards-moving now is tested and never yields a negative retryAfterMs.
  • Retry-After second-rounding (Math.ceil(retryAfterMs/1000), min 1) is asserted.
  • User-vs-IP bucket independence and JWT-vs-header user bucket sharing are both tested.
  • At least one fast-check property test fuzzes now sequences.
  • All new tests pass under npm run test:unit.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions