@@ -13,6 +13,7 @@ const {
1313 mockSetZooCodeUserInfo,
1414 mockVisibleProvider,
1515} = vi . hoisted ( ( ) => {
16+ // Visible provider double provides the OpenRouter, Requesty, and Zoo Code callbacks.
1617 const mockVisibleProvider = {
1718 handleOpenRouterCallback : vi . fn ( ) ,
1819 handleRequestyCallback : vi . fn ( ) ,
@@ -45,13 +46,70 @@ import type { ClineProvider } from "../../core/webview/ClineProvider"
4546
4647const makeUri = ( uri : Pick < vscode . Uri , "path" | "query" > ) : vscode . Uri => uri as vscode . Uri
4748
49+ const makeCallbackProvider = ( callback = vi . fn ( ) ) : ClineProvider =>
50+ // Provider double provides handleZooCodeCallback only.
51+ ( { handleZooCodeCallback : callback } ) as unknown as ClineProvider
52+
4853describe ( "handleUri" , ( ) => {
4954 beforeEach ( ( ) => {
5055 vi . clearAllMocks ( )
5156 mockGetVisibleInstance . mockReturnValue ( mockVisibleProvider )
5257 mockGetAllInstances . mockReturnValue ( [ mockVisibleProvider ] )
5358 } )
5459
60+ it . each ( [ "/openrouter" , "/requesty" ] ) ( "dispatches %s codes without losing literal plus signs" , async ( path ) => {
61+ await handleUri ( makeUri ( { path, query : "code=a+b%2Bc&baseUrl=https%3A%2F%2Fexample.com" } ) )
62+
63+ if ( path === "/openrouter" ) {
64+ expect ( mockVisibleProvider . handleOpenRouterCallback ) . toHaveBeenCalledWith ( "a+b+c" )
65+ expect ( mockVisibleProvider . handleRequestyCallback ) . not . toHaveBeenCalled ( )
66+ } else {
67+ expect ( mockVisibleProvider . handleRequestyCallback ) . toHaveBeenCalledWith ( "a+b+c" , "https://example.com" )
68+ expect ( mockVisibleProvider . handleOpenRouterCallback ) . not . toHaveBeenCalled ( )
69+ }
70+ } )
71+
72+ it ( "passes null when Requesty has no base URL" , async ( ) => {
73+ await handleUri ( makeUri ( { path : "/requesty" , query : "code=test-code" } ) )
74+ expect ( mockVisibleProvider . handleRequestyCallback ) . toHaveBeenCalledWith ( "test-code" , null )
75+ } )
76+
77+ it . each ( [ "/openrouter" , "/requesty" ] ) ( "ignores %s when no provider is visible" , async ( path ) => {
78+ mockGetVisibleInstance . mockReturnValue ( null )
79+ await handleUri ( makeUri ( { path, query : "code=test-code" } ) )
80+ expect ( mockVisibleProvider . handleOpenRouterCallback ) . not . toHaveBeenCalled ( )
81+ expect ( mockVisibleProvider . handleRequestyCallback ) . not . toHaveBeenCalled ( )
82+ } )
83+
84+ it . each ( [ "/openrouter" , "/requesty" , "/auth-callback" , "/unknown" ] ) (
85+ "ignores %s without callback credentials" ,
86+ async ( path ) => {
87+ await handleUri ( makeUri ( { path, query : "" } ) )
88+ expect ( mockVisibleProvider . handleOpenRouterCallback ) . not . toHaveBeenCalled ( )
89+ expect ( mockVisibleProvider . handleRequestyCallback ) . not . toHaveBeenCalled ( )
90+ expect ( mockHandleZooCodeAuthCallback ) . not . toHaveBeenCalled ( )
91+ expect ( mockSetZooCodeUserInfo ) . not . toHaveBeenCalled ( )
92+ expect ( mockVisibleProvider . handleZooCodeCallback ) . not . toHaveBeenCalled ( )
93+ } ,
94+ )
95+
96+ it ( "logs non-Error callback failures and continues to the next provider" , async ( ) => {
97+ mockHandleZooCodeAuthCallback . mockResolvedValue ( true )
98+ const log = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } )
99+ const failingProvider = makeCallbackProvider ( vi . fn ( ) . mockRejectedValue ( "storage unavailable" ) )
100+ mockGetAllInstances . mockReturnValue ( [ failingProvider , mockVisibleProvider ] )
101+ try {
102+ await handleUri ( makeUri ( { path : "/auth-callback" , query : "token=test-token" } ) )
103+ expect ( log ) . toHaveBeenCalledWith (
104+ "Failed to persist Zoo Gateway token for a provider instance:" ,
105+ "storage unavailable" ,
106+ )
107+ expect ( mockVisibleProvider . handleZooCodeCallback ) . toHaveBeenCalledWith ( "test-token" )
108+ } finally {
109+ log . mockRestore ( )
110+ }
111+ } )
112+
55113 it ( "ignores legacy cloud auth callback" , async ( ) => {
56114 await handleUri (
57115 makeUri ( {
@@ -146,8 +204,8 @@ describe("handleUri", () => {
146204 // stayed unauthenticated until reload.
147205 mockHandleZooCodeAuthCallback . mockResolvedValue ( true )
148206
149- const hiddenProvider = { handleZooCodeCallback : vi . fn ( ) } as unknown as ClineProvider
150- const secondHidden = { handleZooCodeCallback : vi . fn ( ) } as unknown as ClineProvider
207+ const hiddenProvider = makeCallbackProvider ( )
208+ const secondHidden = makeCallbackProvider ( )
151209 mockGetAllInstances . mockReturnValue ( [ mockVisibleProvider , hiddenProvider , secondHidden ] )
152210
153211 await handleUri (
@@ -172,14 +230,14 @@ describe("handleUri", () => {
172230
173231 const order : string [ ] = [ ]
174232 const makeProvider = ( name : string ) =>
175- ( {
176- handleZooCodeCallback : vi . fn ( async ( ) => {
233+ makeCallbackProvider (
234+ vi . fn ( async ( ) => {
177235 order . push ( `${ name } :start` )
178236 // Yield to the event loop so a concurrent call would interleave.
179237 await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) )
180238 order . push ( `${ name } :end` )
181239 } ) ,
182- } ) as unknown as ClineProvider
240+ )
183241
184242 const a = makeProvider ( "a" )
185243 const b = makeProvider ( "b" )
@@ -198,12 +256,12 @@ describe("handleUri", () => {
198256 it ( "continues fan-out when one instance fails to persist the callback token" , async ( ) => {
199257 mockHandleZooCodeAuthCallback . mockResolvedValue ( true )
200258
201- const failingProvider = {
202- handleZooCodeCallback : vi . fn ( async ( ) => {
259+ const failingProvider = makeCallbackProvider (
260+ vi . fn ( async ( ) => {
203261 throw new Error ( "profile store unavailable" )
204262 } ) ,
205- } as unknown as ClineProvider
206- const healthyProvider = { handleZooCodeCallback : vi . fn ( ) } as unknown as ClineProvider
263+ )
264+ const healthyProvider = makeCallbackProvider ( )
207265 mockGetAllInstances . mockReturnValue ( [ failingProvider , healthyProvider ] )
208266
209267 await handleUri (
0 commit comments