@@ -120,22 +120,50 @@ describe("ClaudeCodeRouterConfig", () => {
120120 } ) ;
121121 } ) ;
122122
123+ describe ( "error handling" , ( ) => {
124+ beforeEach ( ( ) => {
125+ config = new ClaudeCodeRouterConfig ( ) ;
126+ } ) ;
127+
128+ it ( "should handle transformer file write errors" , async ( ) => {
129+ const error = new Error ( "Write failed" ) ;
130+ fs . writeFile . mockRejectedValueOnce ( error ) ;
131+ fs . writeJson . mockResolvedValue ( ) ; // Make sure other fs operations succeed
132+
133+ await expect ( config . createTransformerFile ( ) ) . rejects . toThrow (
134+ "Write failed"
135+ ) ;
136+ } ) ;
137+
138+ it ( "should handle config file write errors" , async ( ) => {
139+ const error = new Error ( "JSON write failed" ) ;
140+ fs . writeJson . mockRejectedValueOnce ( error ) ;
141+ fs . writeFile . mockResolvedValue ( ) ; // Make sure other fs operations succeed
142+
143+ await expect ( config . createConfigFile ( "test-key" , "cn" ) ) . rejects . toThrow (
144+ "JSON write failed"
145+ ) ;
146+ } ) ;
147+ } ) ;
148+
123149 describe ( "createConfigFile" , ( ) => {
124150 beforeEach ( ( ) => {
125151 config = new ClaudeCodeRouterConfig ( ) ;
126152 } ) ;
127153
128154 it ( "should create config file with API Key from environment variable" , async ( ) => {
129155 const testApiKey = "test-api-key-from-env" ;
156+ const testRegion = "cn" ;
130157
131- await config . createConfigFile ( testApiKey ) ;
158+ await config . createConfigFile ( testApiKey , testRegion ) ;
132159
133160 expect ( fs . writeJson ) . toHaveBeenCalledWith (
134161 config . configFile ,
135162 expect . objectContaining ( {
136163 Providers : expect . arrayContaining ( [
137164 expect . objectContaining ( {
138165 api_key : testApiKey ,
166+ api_base_url : "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions" ,
139167 } ) ,
140168 ] ) ,
141169 } ) ,
@@ -145,15 +173,17 @@ describe("ClaudeCodeRouterConfig", () => {
145173
146174 it ( "should create config file with provided API Key" , async ( ) => {
147175 const testApiKey = "test-api-key-provided" ;
176+ const testRegion = "intl" ;
148177
149- await config . createConfigFile ( testApiKey ) ;
178+ await config . createConfigFile ( testApiKey , testRegion ) ;
150179
151180 expect ( fs . writeJson ) . toHaveBeenCalledWith (
152181 config . configFile ,
153182 expect . objectContaining ( {
154183 Providers : expect . arrayContaining ( [
155184 expect . objectContaining ( {
156185 api_key : testApiKey ,
186+ api_base_url : "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions" ,
157187 } ) ,
158188 ] ) ,
159189 } ) ,
@@ -162,7 +192,7 @@ describe("ClaudeCodeRouterConfig", () => {
162192 } ) ;
163193
164194 it ( "should use undefined API Key when no API Key is provided" , async ( ) => {
165- await config . createConfigFile ( ) ;
195+ await config . createConfigFile ( undefined , "cn" ) ;
166196
167197 expect ( fs . writeJson ) . toHaveBeenCalledWith (
168198 config . configFile ,
@@ -178,7 +208,7 @@ describe("ClaudeCodeRouterConfig", () => {
178208 } ) ;
179209
180210 it ( "should contain correct configuration structure" , async ( ) => {
181- await config . createConfigFile ( ) ;
211+ await config . createConfigFile ( "test-key" , "cn" ) ;
182212
183213 const writeJsonCall = fs . writeJson . mock . calls [ 0 ] ;
184214 const configContent = writeJsonCall [ 1 ] ;
@@ -232,6 +262,7 @@ describe("ClaudeCodeRouterConfig", () => {
232262 jest . spyOn ( config , "createConfigFile" ) . mockResolvedValue ( ) ;
233263 jest . spyOn ( config , "createTransformerFile" ) . mockResolvedValue ( ) ;
234264 jest . spyOn ( config , "promptForApiKey" ) . mockResolvedValue ( "user-input-key" ) ;
265+ jest . spyOn ( config , "promptForRegion" ) . mockResolvedValue ( "cn" ) ;
235266 } ) ;
236267
237268 afterEach ( ( ) => {
@@ -244,8 +275,9 @@ describe("ClaudeCodeRouterConfig", () => {
244275
245276 await config . setup ( ) ;
246277
278+ expect ( config . promptForRegion ) . toHaveBeenCalled ( ) ;
247279 expect ( config . createDirectories ) . toHaveBeenCalled ( ) ;
248- expect ( config . createConfigFile ) . toHaveBeenCalledWith ( "env-test-key" ) ;
280+ expect ( config . createConfigFile ) . toHaveBeenCalledWith ( "env-test-key" , "cn" ) ;
249281 expect ( config . createTransformerFile ) . toHaveBeenCalled ( ) ;
250282 expect ( config . promptForApiKey ) . not . toHaveBeenCalled ( ) ;
251283 } ) ;
@@ -260,7 +292,7 @@ describe("ClaudeCodeRouterConfig", () => {
260292 "DASHSCOPE_API_KEY environment variable detected"
261293 )
262294 ) ;
263- expect ( config . createConfigFile ) . toHaveBeenCalledWith ( "test-key-from-env" ) ;
295+ expect ( config . createConfigFile ) . toHaveBeenCalledWith ( "test-key-from-env" , "cn" ) ;
264296 } ) ;
265297
266298 it ( "should prompt for API Key when environment variable is not present" , async ( ) => {
@@ -274,7 +306,7 @@ describe("ClaudeCodeRouterConfig", () => {
274306 )
275307 ) ;
276308 expect ( config . promptForApiKey ) . toHaveBeenCalled ( ) ;
277- expect ( config . createConfigFile ) . toHaveBeenCalledWith ( "user-input-key" ) ;
309+ expect ( config . createConfigFile ) . toHaveBeenCalledWith ( "user-input-key" , "cn" ) ;
278310 } ) ;
279311
280312 it ( "should handle errors during setup process" , async ( ) => {
@@ -287,6 +319,111 @@ describe("ClaudeCodeRouterConfig", () => {
287319 } ) ;
288320 } ) ;
289321
322+ describe ( "promptForRegion" , ( ) => {
323+ beforeEach ( ( ) => {
324+ config = new ClaudeCodeRouterConfig ( ) ;
325+
326+ // Mock console.log
327+ jest . spyOn ( console , "log" ) . mockImplementation ( ) ;
328+ } ) ;
329+
330+ afterEach ( ( ) => {
331+ console . log . mockRestore ( ) ;
332+ } ) ;
333+
334+ it ( "should prompt for region and return 'cn' when user selects 1" , async ( ) => {
335+ const mockReadline = {
336+ question : jest . fn ( ) ,
337+ close : jest . fn ( )
338+ } ;
339+
340+ const readline = require ( "readline" ) ;
341+ jest . spyOn ( readline , "createInterface" ) . mockReturnValue ( mockReadline ) ;
342+
343+ mockReadline . question . mockImplementation ( ( prompt , callback ) => {
344+ callback ( "1" ) ;
345+ } ) ;
346+
347+ const result = await config . promptForRegion ( ) ;
348+
349+ expect ( readline . createInterface ) . toHaveBeenCalledWith ( {
350+ input : process . stdin ,
351+ output : process . stdout
352+ } ) ;
353+ expect ( mockReadline . question ) . toHaveBeenCalled ( ) ;
354+ expect ( mockReadline . close ) . toHaveBeenCalled ( ) ;
355+ expect ( result ) . toBe ( "cn" ) ;
356+ } ) ;
357+
358+ it ( "should prompt for region and return 'intl' when user selects 2" , async ( ) => {
359+ const mockReadline = {
360+ question : jest . fn ( ) ,
361+ close : jest . fn ( )
362+ } ;
363+
364+ const readline = require ( "readline" ) ;
365+ jest . spyOn ( readline , "createInterface" ) . mockReturnValue ( mockReadline ) ;
366+
367+ mockReadline . question . mockImplementation ( ( prompt , callback ) => {
368+ callback ( "2" ) ;
369+ } ) ;
370+
371+ const result = await config . promptForRegion ( ) ;
372+
373+ expect ( result ) . toBe ( "intl" ) ;
374+ expect ( mockReadline . close ) . toHaveBeenCalled ( ) ;
375+ } ) ;
376+
377+ it ( "should re-prompt when invalid input is provided" , async ( ) => {
378+ const mockReadline = {
379+ question : jest . fn ( ) ,
380+ close : jest . fn ( )
381+ } ;
382+
383+ const readline = require ( "readline" ) ;
384+ jest . spyOn ( readline , "createInterface" ) . mockReturnValue ( mockReadline ) ;
385+
386+ let callCount = 0 ;
387+ mockReadline . question . mockImplementation ( ( prompt , callback ) => {
388+ callCount ++ ;
389+ if ( callCount === 1 ) {
390+ callback ( "invalid" ) ;
391+ } else {
392+ callback ( "1" ) ;
393+ }
394+ } ) ;
395+
396+ const result = await config . promptForRegion ( ) ;
397+
398+ expect ( mockReadline . question ) . toHaveBeenCalledTimes ( 2 ) ;
399+ expect ( result ) . toBe ( "cn" ) ;
400+ expect ( console . log ) . toHaveBeenCalledWith (
401+ expect . stringContaining ( "Please enter 1 or 2" )
402+ ) ;
403+ } ) ;
404+ } ) ;
405+
406+ describe ( "getApiBaseUrl" , ( ) => {
407+ beforeEach ( ( ) => {
408+ config = new ClaudeCodeRouterConfig ( ) ;
409+ } ) ;
410+
411+ it ( "should return China URL for 'cn' region" , ( ) => {
412+ const result = config . getApiBaseUrl ( "cn" ) ;
413+ expect ( result ) . toBe ( "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions" ) ;
414+ } ) ;
415+
416+ it ( "should return International URL for 'intl' region" , ( ) => {
417+ const result = config . getApiBaseUrl ( "intl" ) ;
418+ expect ( result ) . toBe ( "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions" ) ;
419+ } ) ;
420+
421+ it ( "should default to China URL for unknown region" , ( ) => {
422+ const result = config . getApiBaseUrl ( "unknown" ) ;
423+ expect ( result ) . toBe ( "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions" ) ;
424+ } ) ;
425+ } ) ;
426+
290427 describe ( "promptForApiKey" , ( ) => {
291428 beforeEach ( ( ) => {
292429 config = new ClaudeCodeRouterConfig ( ) ;
0 commit comments