@@ -173,6 +173,7 @@ describe("ConfigManager", () => {
173173 test : {
174174 ...newConfig ,
175175 id : testConfigId ,
176+ rateLimitSeconds : 0 , // Default rate limit is 0 seconds
176177 } ,
177178 } ,
178179 modeApiConfigs : {
@@ -216,6 +217,7 @@ describe("ConfigManager", () => {
216217 apiProvider : "anthropic" ,
217218 apiKey : "new-key" ,
218219 id : "test-id" ,
220+ rateLimitSeconds : 0 , // Default rate limit is 0 seconds
219221 } ,
220222 } ,
221223 }
@@ -489,4 +491,133 @@ describe("ConfigManager", () => {
489491 )
490492 } )
491493 } )
494+
495+ describe ( "migrateRateLimitToProfiles" , ( ) => {
496+ it ( "should migrate global rate limit to all profiles" , async ( ) => {
497+ // Setup mock context with global rate limit
498+ const mockGlobalState = {
499+ get : jest . fn ( ) . mockResolvedValue ( 15 ) , // Mock global rate limit of 15 seconds
500+ update : jest . fn ( ) ,
501+ }
502+ ; ( mockContext as any ) . globalState = mockGlobalState
503+
504+ // Setup existing configs without rate limits
505+ const existingConfig : ApiConfigData = {
506+ currentApiConfigName : "default" ,
507+ apiConfigs : {
508+ default : {
509+ id : "default" ,
510+ apiProvider : "anthropic" ,
511+ } ,
512+ test : {
513+ id : "test-id" ,
514+ apiProvider : "openrouter" ,
515+ } ,
516+ } ,
517+ }
518+
519+ mockSecrets . get . mockResolvedValue ( JSON . stringify ( existingConfig ) )
520+
521+ // Call the migration method
522+ await configManager . migrateRateLimitToProfiles ( )
523+
524+ // Verify the configs were updated with the rate limit
525+ const storedConfig = JSON . parse ( mockSecrets . store . mock . calls [ 0 ] [ 1 ] )
526+ expect ( storedConfig . apiConfigs . default . rateLimitSeconds ) . toBe ( 15 )
527+ expect ( storedConfig . apiConfigs . test . rateLimitSeconds ) . toBe ( 15 )
528+
529+ // Verify the global rate limit was removed
530+ expect ( mockGlobalState . update ) . toHaveBeenCalledWith ( "rateLimitSeconds" , undefined )
531+ } )
532+
533+ it ( "should handle empty config case" , async ( ) => {
534+ // Create a new instance with fresh mocks for this test
535+ jest . resetAllMocks ( )
536+
537+ const testMockContext = { ...mockContext }
538+ const testMockSecrets = {
539+ get : jest . fn ( ) ,
540+ store : jest . fn ( ) ,
541+ delete : jest . fn ( ) ,
542+ onDidChange : jest . fn ( ) ,
543+ }
544+ testMockContext . secrets = testMockSecrets
545+
546+ // Setup mock context with global rate limit
547+ const testMockGlobalState = {
548+ get : jest . fn ( ) . mockResolvedValue ( 10 ) , // Mock global rate limit of 10 seconds
549+ update : jest . fn ( ) . mockResolvedValue ( undefined ) ,
550+ keys : jest . fn ( ) . mockReturnValue ( [ ] ) ,
551+ setKeysForSync : jest . fn ( ) ,
552+ }
553+ testMockContext . globalState = testMockGlobalState
554+
555+ // Setup empty config
556+ const emptyConfig : ApiConfigData = {
557+ currentApiConfigName : "default" ,
558+ apiConfigs : { } ,
559+ }
560+
561+ // Mock the readConfig and writeConfig methods
562+ testMockSecrets . get . mockResolvedValue ( JSON . stringify ( emptyConfig ) )
563+ testMockSecrets . store . mockResolvedValue ( undefined )
564+
565+ // Create a test instance that won't be affected by other tests
566+ const testConfigManager = new ConfigManager ( testMockContext as any )
567+
568+ // Override the lock method for this test
569+ testConfigManager [ "_lock" ] = Promise . resolve ( )
570+ const originalLock = testConfigManager [ "lock" ]
571+ testConfigManager [ "lock" ] = function ( cb : ( ) => Promise < any > ) {
572+ return cb ( )
573+ }
574+
575+ // Call the migration method
576+ await testConfigManager . migrateRateLimitToProfiles ( )
577+
578+ // Verify the global rate limit was removed even with empty config
579+ expect ( testMockGlobalState . update ) . toHaveBeenCalledWith ( "rateLimitSeconds" , undefined )
580+ } )
581+
582+ it ( "should handle errors gracefully" , async ( ) => {
583+ // Create a new instance with fresh mocks for this test
584+ jest . resetAllMocks ( )
585+
586+ const testMockContext = { ...mockContext }
587+ const testMockSecrets = {
588+ get : jest . fn ( ) ,
589+ store : jest . fn ( ) ,
590+ delete : jest . fn ( ) ,
591+ onDidChange : jest . fn ( ) ,
592+ }
593+ testMockContext . secrets = testMockSecrets
594+
595+ // Setup mock context with global rate limit
596+ const testMockGlobalState = {
597+ get : jest . fn ( ) . mockResolvedValue ( 5 ) , // Mock global rate limit of 5 seconds
598+ update : jest . fn ( ) . mockResolvedValue ( undefined ) ,
599+ keys : jest . fn ( ) . mockReturnValue ( [ ] ) ,
600+ setKeysForSync : jest . fn ( ) ,
601+ }
602+ testMockContext . globalState = testMockGlobalState
603+
604+ // Force an error during read
605+ testMockSecrets . get . mockRejectedValue ( new Error ( "Storage failed" ) )
606+
607+ // Create a test instance that won't be affected by other tests
608+ const testConfigManager = new ConfigManager ( testMockContext as any )
609+
610+ // Override the lock method for this test
611+ testConfigManager [ "_lock" ] = Promise . resolve ( )
612+ const originalLock = testConfigManager [ "lock" ]
613+ testConfigManager [ "lock" ] = function ( cb : ( ) => Promise < any > ) {
614+ return Promise . reject ( new Error ( "Failed to read config from secrets: Error: Storage failed" ) )
615+ }
616+
617+ // Expect the migration to throw an error
618+ await expect ( testConfigManager . migrateRateLimitToProfiles ( ) ) . rejects . toThrow (
619+ "Failed to migrate rate limit settings: Error: Failed to read config from secrets: Error: Storage failed" ,
620+ )
621+ } )
622+ } )
492623} )
0 commit comments