@@ -240,7 +240,8 @@ describe("McpHub", () => {
240240 return mockClient
241241 } )
242242
243- // Mock the config file read
243+ // Mock the config file read BEFORE creating McpHub to avoid racing
244+ // the constructor's initializeGlobalMcpServers()
244245 vi . mocked ( fs . readFile ) . mockResolvedValue (
245246 JSON . stringify ( {
246247 mcpServers : {
@@ -252,9 +253,9 @@ describe("McpHub", () => {
252253 } ) ,
253254 )
254255
255- // Create McpHub and let it initialize
256+ // Create McpHub and wait for constructor init to complete
256257 const mcpHub = new McpHub ( mockProvider as ClineProvider )
257- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
258+ await mcpHub . waitUntilReady ( )
258259
259260 // Find the connection
260261 const connection = mcpHub . connections . find ( ( conn ) => conn . server . name === "union-test-server" )
@@ -271,7 +272,8 @@ describe("McpHub", () => {
271272 } )
272273
273274 it ( "should create disconnected connections for disabled servers" , async ( ) => {
274- // Mock the config file read with a disabled server
275+ // Mock the config file read BEFORE creating McpHub to avoid racing
276+ // the constructor's initializeGlobalMcpServers()
275277 vi . mocked ( fs . readFile ) . mockResolvedValue (
276278 JSON . stringify ( {
277279 mcpServers : {
@@ -284,9 +286,9 @@ describe("McpHub", () => {
284286 } ) ,
285287 )
286288
287- // Create McpHub and let it initialize
289+ // Create McpHub and wait for constructor init to complete
288290 const mcpHub = new McpHub ( mockProvider as ClineProvider )
289- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
291+ await mcpHub . waitUntilReady ( )
290292
291293 // Find the connection
292294 const connection = mcpHub . connections . find ( ( conn ) => conn . server . name === "disabled-union-server" )
@@ -311,11 +313,9 @@ describe("McpHub", () => {
311313 } ) ,
312314 )
313315
314- // Create a mock McpHub instance
316+ // Create McpHub and wait for constructor init to complete
315317 const mcpHub = new McpHub ( mockProvider as ClineProvider )
316-
317- // Wait for initialization
318- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
318+ await mcpHub . waitUntilReady ( )
319319
320320 // Clear any connections that might have been created
321321 mcpHub . connections = [ ]
@@ -2692,6 +2692,35 @@ describe("McpHub", () => {
26922692 expect ( mockAuthProvider . close ) . toHaveBeenCalled ( )
26932693 } )
26942694
2695+ it ( "should dispose the cancellation listener when the OAuth flow times out" , async ( ) => {
2696+ vi . useFakeTimers ( )
2697+ const mockDispose = vi . fn ( )
2698+ vsc . window . withProgress . mockImplementationOnce ( ( _options : any , task : any ) => {
2699+ const progress = { report : vi . fn ( ) }
2700+ const cancellationToken = {
2701+ isCancellationRequested : false ,
2702+ onCancellationRequested : vi . fn ( ( ) => ( { dispose : mockDispose } ) ) ,
2703+ }
2704+ return task ( progress , cancellationToken )
2705+ } )
2706+ vsc . window . showInformationMessage . mockImplementation ( ( ) => new Promise ( ( ) => { } ) )
2707+
2708+ const flowPromise = ( mcpHub as any ) . _initiateOAuthFlow (
2709+ serverName ,
2710+ source ,
2711+ config ,
2712+ mockAuthProvider ,
2713+ mockTransport ,
2714+ mockConnection ,
2715+ )
2716+
2717+ await vi . advanceTimersByTimeAsync ( OAUTH_FLOW_TIMEOUT_MS )
2718+ await flowPromise
2719+
2720+ // cleanup(cancellationDisposable) inside the timeout handler must dispose the listener
2721+ expect ( mockDispose ) . toHaveBeenCalled ( )
2722+ } )
2723+
26952724 it ( "should resolve without calling _completeOAuthFlow when tokens exist at click time" , async ( ) => {
26962725 // Tokens are present when Authenticate is clicked (click-time guard in the loop).
26972726 // First call (pre-withProgress early-return check) returns null so withProgress runs.
0 commit comments