Skip to content

Conversation

@mcollina
Copy link
Member

Fixes #4693

Problem

When a MockAgent had a delayed response (using .delay()) and the request was aborted via an AbortSignal, the delayed response callback would still fire and attempt to call handler methods (onHeaders, onData, onComplete) even after the handler's onError had been called. This violated state invariants expected by handlers like DecoratorHandler (used by the decompress interceptor), which has assertions that onResponseStart should not be called after onError.

This issue was particularly noticeable when using MockAgent with interceptors like decompress, which would throw an assertion error when receiving a delayed response after abort.

Solution

Modified lib/mock/mock-utils.js in the mockDispatch function to:

  1. Track abort state: Added aborted flag and timer reference to track if the request has been aborted and the delayed response timer.

  2. Proper onConnect handling: Moved handler.onConnect?.() call before scheduling the delayed response so the abort callback can be registered.

  3. Abort callback cleanup: When the abort callback is invoked:

    • Sets the aborted flag
    • Clears the pending setTimeout timer if it exists
    • Calls handler.onError(err) to notify the handler
  4. Prevent late responses: The handleReply function now checks the aborted flag at two points:

    • Before processing (immediately)
    • After async body resolution (for callback-based data)

    If aborted, it silently returns without invoking any handler callbacks.

Tests

Added test/mock-delayed-abort.js with three test cases:

  1. Basic delayed mock with abort - verifies no uncaught errors
  2. Delayed mock with decompress interceptor - verifies the actual use case from the bug report
  3. Delayed mock with custom DecoratorHandler - verifies state invariants

All existing tests continue to pass.

Checklist

  • Added tests for the change
  • Documentation updated (not needed for bug fix)
  • TypeScript types are correct (verified)
  • Linting passes
  • All tests pass

When a MockAgent had a delayed response and the request was aborted via
AbortSignal, the delayed response callback would still fire and attempt to
call handler methods after onError had been called. This violated state
invariants expected by handlers like DecoratorHandler (used by decompress
interceptor).

The fix:
- Tracks abort state in mockDispatch
- Clears pending setTimeout timer when abort is called
- Checks abort flag in handleReply before invoking handler callbacks
- Properly calls handler.onConnect to register abort callback

Fixes #4693
@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 82.85714% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.24%. Comparing base (cfd42c6) to head (8f3490d).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
lib/mock/mock-utils.js 82.85% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4772      +/-   ##
==========================================
- Coverage   93.27%   93.24%   -0.03%     
==========================================
  Files         109      109              
  Lines       34001    34034      +33     
==========================================
+ Hits        31713    31734      +21     
- Misses       2288     2300      +12     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

MockAgent delayed response with AbortSignal

4 participants