Fix tests timing out during interactive OAuth flow (#2510) - #2544
Fix tests timing out during interactive OAuth flow (#2510)#2544GhagSagar23 wants to merge 6 commits into
Conversation
|
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
Fixes modelcontextprotocol#2510 by serializing auth requests in StreamableHTTPClientTransport and properly handling 401s without crashing the test runner.
615c4b9 to
a740d28
Compare
gnanirahulnutakki
left a comment
There was a problem hiding this comment.
Reviewed exact head a740d28ca71f6bc4221f63ad01fd9faac0ea15d3 against #2510's redirect-completion requirement. The client package passes typecheck, lint, and all 771 tests locally, but the current tests still encode the pre-fix rejection path. I found one lifecycle issue that leaves the reported mid-session request dead-ended.
| stepUpRetries = 0 | ||
| ): Promise<void> { | ||
| if (this._pendingAuthPromise) { | ||
| await this._pendingAuthPromise; |
There was a problem hiding this comment.
[P1] Keep the triggering request pending across REDIRECT
This await only observes a promise that finishAuth() creates later. In #2510's mid-session path, _send() is already inside onUnauthorized; handleOAuthUnauthorized() receives REDIRECT and throws UnauthorizedError, so the original send() rejects before the browser callback can call finishAuth() or create this promise. On this exact head, the isolated existing test uses custom fetch during auth flow on 401 - no global fetch fallback still passes specifically by expecting that rejection, and this PR changes no tests. Thus the code may exchange a callback code later, but it still cannot resume and retry the triggering request. Please establish the deferred when REDIRECT is produced, leave the triggering request pending, resolve/reject it from finishAuth()/close(), retry the request, and cover that full sequence.
… completes - Updates `StreamableHTTPClientTransport` to intercept `UnauthorizedError` during requests. - Retains resolve/reject handlers in a `_pendingAuthPromise` to prevent premature request failure and manual resends. - Retries the pending request upon successful `finishAuth()` completion. - Updates tests in `streamableHttp.test.ts` to handle the new pending promise behavior instead of immediate rejection. - Fixes Vitest mock leaking between test suites in `streamableHttp.test.ts` by using `mockReset` instead of `mockRestore`. - Updates `probeAuthSeam.test.ts` to align with the new `finishAuth` contract where probe requests remain pending until resolved.
|
I've updated the implementation to address this feedback! Triggering requests now correctly remain pending in the background until Changes made:
All 798 tests across the client packages are green. Let me know if there's anything else you'd like me to address! |
* StreamableHTTPClientTransport._send and _startOrAuthSse now wait for finishAuth() rather than rejecting immediately with UnauthorizedError when a REDIRECT occurs. * Refactored OAuth test suite to support asynchronous redirect flows. Tests now hang gracefully during REDIRECT, waiting for finishAuth() rather than asserting immediate rejections. * Updated simpleOAuthClient example to accurately reflect the new asynchronous flow. * Fixed sporadic vitest timeouts and FakeTimers leakage by properly managing unhandled transport promises during tests.
Summary
This PR fixes the test timeouts and potential hanging requests that occur when an interactive OAuth flow (returning
REDIRECT) is triggered by theStreamableHTTPClientTransport. It addresses issue #2510.Changes
StreamableHTTPClientTransport._sendandStreamableHTTPClientTransport._startOrAuthSseto explicitly await the_pendingAuthPromisewhen theauth()function returnsREDIRECT._handleAuthResultmethod to serialize requests waiting on the OAuth redirect flow and to throwUnauthorizedErrorwhen the result is notAUTHORIZED.finishAuthproperly resolves or rejects the pending auth promise.streamableHttp.test.tsto mockredirectToAuthorizationby throwing anUnauthorizedError, which properly simulates the rejection path for the tests without timing out.