@@ -23,6 +23,9 @@ import org.mockito.MockedStatic
2323import org.mockito.Mockito.mockStatic
2424import org.mockito.Mockito.doThrow
2525import org.mockito.Mockito.mock
26+ import org.mockito.Mockito.clearInvocations
27+ import org.mockito.ArgumentMatchers.matches
28+ import org.mockito.Mockito.never
2629
2730class IterableKeychainTest {
2831
@@ -202,8 +205,9 @@ class IterableKeychainTest {
202205 keychain.saveUserId(null )
203206 keychain.saveAuthToken(null )
204207
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" ))
207211 // Verify exactly one apply call for each save operation
208212 verify(mockEditor, times(3 )).apply ()
209213 }
@@ -285,4 +289,41 @@ class IterableKeychainTest {
285289 // Verify attemptMigration was called exactly once
286290 verify(mockMigrator, times(1 )).attemptMigration()
287291 }
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+ }
288329}
0 commit comments