@@ -23,6 +23,9 @@ import org.mockito.MockedStatic
23
23
import org.mockito.Mockito.mockStatic
24
24
import org.mockito.Mockito.doThrow
25
25
import org.mockito.Mockito.mock
26
+ import org.mockito.Mockito.clearInvocations
27
+ import org.mockito.ArgumentMatchers.matches
28
+ import org.mockito.Mockito.never
26
29
27
30
class IterableKeychainTest {
28
31
@@ -202,8 +205,9 @@ class IterableKeychainTest {
202
205
keychain.saveUserId(null )
203
206
keychain.saveAuthToken(null )
204
207
205
- // Verify exactly one putString call for each save operation
206
- verify(mockEditor, times(3 )).putString(any(), isNull())
208
+ // Verify remove calls for both key and plaintext flag
209
+ verify(mockEditor, times(6 )).remove(any()) // 2 removes per save (key + plaintext flag)
210
+ verify(mockEditor, times(3 )).remove(matches(" .*_plaintext" ))
207
211
// Verify exactly one apply call for each save operation
208
212
verify(mockEditor, times(3 )).apply ()
209
213
}
@@ -285,4 +289,41 @@ class IterableKeychainTest {
285
289
// Verify attemptMigration was called exactly once
286
290
verify(mockMigrator, times(1 )).attemptMigration()
287
291
}
292
+
293
+ @Test
294
+ fun testEncryptionAndDecryptionFailure () {
295
+ // Setup encryption and decryption to fail
296
+ `when `(mockEncryptor.encrypt(any())).thenThrow(RuntimeException (" Simulated encryption failure" ))
297
+ `when `(mockEncryptor.decrypt(any())).thenThrow(RuntimeException (" Simulated decryption failure" ))
298
+
299
+ val testData = " test data for encryption failure"
300
+
301
+ // Save data - should fall back to plaintext
302
+ keychain.saveUserId(testData)
303
+
304
+ // Verify plaintext save operations
305
+ verify(mockEditor).putString(eq(" iterable-user-id" ), eq(testData))
306
+ verify(mockEditor).putBoolean(eq(" iterable-user-id_plaintext" ), eq(true ))
307
+
308
+ // Setup SharedPreferences to return the saved data
309
+ `when `(mockSharedPrefs.getString(eq(" iterable-user-id" ), isNull())).thenReturn(testData)
310
+ `when `(mockSharedPrefs.getBoolean(eq(" iterable-user-id_plaintext" ), eq(false ))).thenReturn(true )
311
+
312
+ // Verify data can be retrieved
313
+ assertEquals(testData, keychain.getUserId())
314
+
315
+ // Verify encryption was attempted and failed
316
+ verify(mockEncryptor).encrypt(eq(testData))
317
+ verify(mockEncryptor, never()).decrypt(any()) // Should not attempt decryption for plaintext
318
+
319
+ // Test null handling
320
+ clearInvocations(mockEditor)
321
+ keychain.saveUserId(null )
322
+ verify(mockEditor).remove(eq(" iterable-user-id" ))
323
+ verify(mockEditor).remove(eq(" iterable-user-id_plaintext" ))
324
+
325
+ // Verify no more encryption attempts for null
326
+ verify(mockEncryptor, never()).encrypt(isNull())
327
+ verify(mockEncryptor, never()).decrypt(isNull())
328
+ }
288
329
}
0 commit comments