@@ -472,6 +472,63 @@ describe("ClineProvider - API Handler Rebuild Guard", () => {
472472 await expect ( provider . activateProviderProfile ( { name : "second-profile" } ) ) . resolves . toBeUndefined ( )
473473 } )
474474
475+ test ( "provider profile mutation timeout releases later queued mutations" , async ( ) => {
476+ vi . useFakeTimers ( )
477+ const logSpy = vi . spyOn ( provider , "log" )
478+ provider [ "providerSettingsManager" ] . activateProfile = vi
479+ . fn ( )
480+ . mockImplementationOnce ( ( ) => new Promise < never > ( ( ) => { } ) )
481+ . mockResolvedValueOnce ( {
482+ name : "second-profile" ,
483+ id : "second-id" ,
484+ apiProvider : "openrouter" ,
485+ openRouterModelId : "openai/gpt-4.1-mini" ,
486+ } )
487+
488+ try {
489+ const first = provider . activateProviderProfile ( { name : "first-profile" } )
490+ const firstResult = expect ( first ) . rejects . toThrow ( "Provider profile mutation timed out" )
491+ await vi . advanceTimersByTimeAsync ( 30_000 )
492+ await firstResult
493+
494+ await expect ( provider . activateProviderProfile ( { name : "second-profile" } ) ) . resolves . toBeUndefined ( )
495+ expect ( logSpy ) . toHaveBeenCalledWith ( "Provider profile mutation timed out; releasing the mutation queue" )
496+ } finally {
497+ vi . useRealTimers ( )
498+ }
499+ } )
500+
501+ test ( "mode switch resolves its default task when its queued mutation starts" , async ( ) => {
502+ let releaseProfileActivation ! : ( ) => void
503+ const profileActivation = new Promise < void > ( ( resolve ) => {
504+ releaseProfileActivation = resolve
505+ } )
506+ provider [ "providerSettingsManager" ] . activateProfile = vi . fn ( ) . mockImplementationOnce ( async ( ) => {
507+ await profileActivation
508+ return {
509+ name : "first-profile" ,
510+ id : "first-id" ,
511+ apiProvider : "openrouter" ,
512+ openRouterModelId : "openai/gpt-4" ,
513+ }
514+ } )
515+
516+ const firstTask = new Task ( defaultTaskOptions )
517+ const secondTask = new Task ( defaultTaskOptions )
518+ await provider . addClineToStack ( firstTask )
519+
520+ const profileSwitch = provider . activateProviderProfile ( { name : "first-profile" } )
521+ const modeSwitch = provider . handleModeSwitch ( "ask" as Mode )
522+ await provider . addClineToStack ( secondTask )
523+
524+ releaseProfileActivation ( )
525+ await profileSwitch
526+ await modeSwitch
527+
528+ expect ( ( firstTask as unknown as { _taskMode ?: Mode } ) . _taskMode ) . not . toBe ( "ask" )
529+ expect ( ( secondTask as unknown as { _taskMode ?: Mode } ) . _taskMode ) . toBe ( "ask" )
530+ } )
531+
475532 test ( "fan-out preparation leaves the focused task untouched" , async ( ) => {
476533 const mockTask = new Task ( {
477534 ...defaultTaskOptions ,
@@ -499,6 +556,8 @@ describe("ClineProvider - API Handler Rebuild Guard", () => {
499556 } )
500557 const emitSpy = vi . spyOn ( provider , "emit" )
501558 const postStateSpy = vi . spyOn ( provider , "postStateToWebview" ) . mockResolvedValue ( undefined )
559+ const setValueSpy = vi . spyOn ( provider . contextProxy , "setValue" )
560+ const setProviderSettingsSpy = vi . spyOn ( provider . contextProxy , "setProviderSettings" )
502561
503562 await provider . handleModeSwitch ( "ask" as Mode , null )
504563
@@ -509,6 +568,8 @@ describe("ClineProvider - API Handler Rebuild Guard", () => {
509568 expect . objectContaining ( { name : "ask-profile" } ) ,
510569 )
511570 expect ( postStateSpy ) . not . toHaveBeenCalled ( )
571+ expect ( setValueSpy ) . not . toHaveBeenCalledWith ( "currentApiConfigName" , "ask-profile" )
572+ expect ( setProviderSettingsSpy ) . not . toHaveBeenCalled ( )
512573 } )
513574
514575 test ( "calls updateApiConfiguration when provider/model unchanged but settings differ (explicit profile switch)" , async ( ) => {
0 commit comments