@@ -44,7 +44,10 @@ function securityArg(args: string[], flag: string): string {
4444 return index >= 0 ? args [ index + 1 ] ?? "" : "" ;
4545}
4646
47- function installSecurityMock ( keychain : Map < string , string > ) : void {
47+ function installSecurityMock (
48+ keychain : Map < string , string > ,
49+ options : { failProviderIndexWrites ?: boolean } = { } ,
50+ ) : void {
4851 spawnSyncMock . mockImplementation ( ( _command : string , rawArgs : string [ ] ) => {
4952 const args = rawArgs . map ( String ) ;
5053 const command = args [ 0 ] ;
@@ -64,6 +67,9 @@ function installSecurityMock(keychain: Map<string, string>): void {
6467 } ;
6568 }
6669 if ( command === "add-generic-password" ) {
70+ if ( options . failProviderIndexWrites && account === "__ade_provider_index__" ) {
71+ return { status : 1 , stdout : "" , stderr : "provider index write failed" } ;
72+ }
6773 keychain . set ( account , securityArg ( args , "-w" ) ) ;
6874 return { status : 0 , stdout : "" , stderr : "" } ;
6975 }
@@ -131,6 +137,38 @@ describe("apiKeyStore", () => {
131137 expect ( fs . existsSync ( path . join ( tempRoot , ".ade" , "secrets" , "api-keys.v1.bin" ) ) ) . toBe ( false ) ;
132138 } ) ;
133139
140+ it ( "keeps a stored Keychain key usable when the provider index write fails" , async ( ) => {
141+ installSecurityMock ( keychain , { failProviderIndexWrites : true } ) ;
142+ const store = await loadStoreModule ( ) ;
143+ store . initApiKeyStore ( tempRoot ) ;
144+
145+ store . storeApiKey ( "cursor" , "crsr_test_key" ) ;
146+
147+ expect ( store . getApiKey ( "cursor" ) ) . toBe ( "crsr_test_key" ) ;
148+ expect ( store . listStoredProviders ( ) ) . toContain ( "cursor" ) ;
149+ expect ( keychain . get ( "cursor" ) ) . toBe ( "crsr_test_key" ) ;
150+ expect ( keychain . has ( "__ade_provider_index__" ) ) . toBe ( false ) ;
151+ expect ( store . getApiKeyStoreStatus ( ) . macosKeychainError ) . toContain ( "provider index write failed" ) ;
152+ } ) ;
153+
154+ it ( "removes a deleted Keychain key from memory when the provider index write fails" , async ( ) => {
155+ keychain . set ( "__ade_provider_index__" , JSON . stringify ( [ "cursor" ] ) ) ;
156+ keychain . set ( "cursor" , "crsr_test_key" ) ;
157+ installSecurityMock ( keychain , { failProviderIndexWrites : true } ) ;
158+ const store = await loadStoreModule ( ) ;
159+ store . initApiKeyStore ( tempRoot ) ;
160+
161+ expect ( store . getApiKey ( "cursor" ) ) . toBe ( "crsr_test_key" ) ;
162+
163+ store . deleteApiKey ( "cursor" ) ;
164+
165+ expect ( store . getApiKey ( "cursor" ) ) . toBeNull ( ) ;
166+ expect ( store . listStoredProviders ( ) ) . not . toContain ( "cursor" ) ;
167+ expect ( keychain . has ( "cursor" ) ) . toBe ( false ) ;
168+ expect ( keychain . get ( "__ade_provider_index__" ) ) . toContain ( "cursor" ) ;
169+ expect ( store . getApiKeyStoreStatus ( ) . macosKeychainError ) . toContain ( "provider index write failed" ) ;
170+ } ) ;
171+
134172 it ( "migrates a decryptable legacy safeStorage blob into macOS Keychain" , async ( ) => {
135173 const secretsDir = path . join ( tempRoot , ".ade" , "secrets" ) ;
136174 fs . mkdirSync ( secretsDir , { recursive : true } ) ;
0 commit comments