Skip to content

TEST-002: Unit tests for errorHandler and rateLimit middleware#8

Draft
Copilot wants to merge 4 commits intomainfrom
copilot/test-error-handler-rate-limit
Draft

TEST-002: Unit tests for errorHandler and rateLimit middleware#8
Copilot wants to merge 4 commits intomainfrom
copilot/test-error-handler-rate-limit

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 10, 2026

Adds comprehensive unit tests for the two untested middleware modules: errorHandler.ts and rateLimit.ts.

errorHandler.test.ts (49 tests)

  • Error classesAppError, ValidationError, NotFoundError, UnauthorizedError: constructor args, statusCode, code, details, instanceof chain
  • createErrorResponse() — response shape, requestId/path/timestamp/details inclusion, stack suppression in production, generic ErrorINTERNAL_ERROR wrapping
  • errorHandlerMiddleware() — status code per error type, requestId/correlationId fallback, req.logger delegation
  • asyncHandler() — forwards rejected promises to next(), no spurious next() call on success
  • notFoundMiddleware()NotFoundError with 404, method+path in message and details

rateLimit.test.ts (21 tests)

  • Fixed-window counter — passes up to maxRequests, blocks the exceeding request, resets after window expiry
  • Per-key isolation — separate counters per IP, socket.remoteAddress fallback, custom keyGenerator grouping
  • 429 responseRetry-After header, RATE_LIMITED code, custom statusCode/message/handler
  • Rate-limit headersX-RateLimit-Limit, X-RateLimit-Remaining (decrements, clamps at 0), X-RateLimit-Reset
  • skip predicate and createRateLimiter factory

jest.config.js

Added forceExit: true to prevent Jest from hanging on the module-level setInterval inside RateLimitStore (cleanup timer is never exported, so tests can't call destroy()).

Original prompt

TEST-002: Write unit tests for API middleware (errorHandler, rateLimit)

Work Item Details

Note: Please focus on the descriptions and information that provide context about the task requirements, functionality, and implementation details. Dates, priorities, and administrative metadata are less relevant for coding tasks.

Description

Unit Tests for Middleware

errorHandler.ts (294 lines)

File: services/api/src/middleware/errorHandler.ts

  • Test AppError, ValidationError, NotFoundError, UnauthorizedError classes: constructor, statusCode, errorCode properties
  • Test createErrorResponse(): correct JSON structure with status, message, errorCode, requestId
  • Test errorHandlerMiddleware(): handles AppError vs generic Error vs unknown, sets correct status codes
  • Test asyncHandler(): catches async errors and forwards to next()
  • Test notFoundMiddleware(): returns 404 for unknown routes

rateLimit.ts (220 lines)

File: services/api/src/middleware/rateLimit.ts

  • Test token bucket algorithm: refill rate, burst capacity
  • Test per-IP tracking: different IPs get separate buckets
  • Test rate limit exceeded: returns 429 with Retry-After header
  • Test rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining

Work item: AB#1733
Created via Azure DevOps


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 2 commits March 10, 2026 11:17
…it to jest config

Co-authored-by: edinc <4791281+edinc@users.noreply.github.com>
Co-authored-by: edinc <4791281+edinc@users.noreply.github.com>
Copilot AI changed the title [WIP] Add unit tests for API middleware errorHandler and rateLimit TEST-002: Unit tests for errorHandler and rateLimit middleware Mar 10, 2026
@edinc edinc requested a review from Copilot March 10, 2026 12:39
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Jest unit test coverage for two Express middleware modules (errorHandler and rateLimit) and adjusts the API Jest config to avoid hangs caused by a module-level timer.

Changes:

  • Added comprehensive unit tests for errorHandler (errors, response shaping, middleware behavior, async wrapper, 404 handler).
  • Added unit tests for rateLimit (per-key buckets, headers, 429 behavior, skip/keyGenerator/custom handler, factory).
  • Updated services/api/jest.config.js to set forceExit: true due to an uncleaned module-level setInterval in the rate limiter.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
services/api/src/tests/errorHandler.test.ts Adds unit tests covering error classes and error-handler middleware utilities.
services/api/src/tests/rateLimit.test.ts Adds unit tests covering rate limiting behavior, headers, and configuration options.
services/api/jest.config.js Forces Jest to exit to work around an open-handle timer from the rate limiter store.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +4 to +7
* Covers:
* - Token bucket algorithm: allows up to maxRequests, blocks when exceeded, resets after window
* - Per-IP tracking: separate buckets per key
* - Rate limit exceeded: 429 status + Retry-After header
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file header and section labels describe the implementation as a “token bucket algorithm”, but the current middleware logic is a fixed-window counter (it increments a count until resetTime, then resets). This mismatch can mislead future maintainers about what behavior is actually being tested—please adjust the test descriptions (or the middleware docs) to match the real algorithm.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2ff7b45. Updated the file header, the describe block name ('token bucket algorithm''fixed-window counter algorithm'), and the inline comments inside those tests to accurately reflect the fixed-window counter behaviour of the middleware.

Comment on lines +4 to +6
// Required because RateLimitStore creates a module-level setInterval for cleanup
// that cannot be cleared from tests (defaultStore is not exported).
forceExit: true,
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting forceExit: true makes the Jest run always exit even if there are still open handles, which can hide real resource leaks and cause async work to be cut off. Since the hang is caused by the module-level setInterval in RateLimitStore, it would be better to address that directly (e.g., unref() the interval or make the store/timer lifecycle controllable for tests) and remove forceExit.

Suggested change
// Required because RateLimitStore creates a module-level setInterval for cleanup
// that cannot be cleared from tests (defaultStore is not exported).
forceExit: true,

Copilot uses AI. Check for mistakes.
… counter, not token bucket)

Co-authored-by: edinc <4791281+edinc@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants