Skip to content

🧪 [testing improvement] Add unit tests for SessionStore - #197

Closed
alvin000009238 wants to merge 1 commit into
mainfrom
add-sessionstore-tests-10704719854318725426
Closed

🧪 [testing improvement] Add unit tests for SessionStore#197
alvin000009238 wants to merge 1 commit into
mainfrom
add-sessionstore-tests-10704719854318725426

Conversation

@alvin000009238

Copy link
Copy Markdown
Owner

🎯 What: Added unit tests for SessionStore.kt which was missing tests.
📊 Coverage: Covered saveSession, loadSession, clearNormalSession, saveReminderSession, loadReminderSession, clear and biometric session saving and loading using Robolectric and mocked SharedPreferences.
Result: Improved test coverage by validating standard, reminder, and biometric session functionality in SessionStore.


PR created automatically by Jules for task 10704719854318725426 started by @alvin000009238

Co-authored-by: alvin000009238 <107313913+alvin000009238@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 17, 2026 14:37
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds unit tests for SessionStore using Robolectric, along with the necessary test dependencies in build.gradle.kts. The review feedback identifies a binding issue in ShadowBiometricHelper where companion object methods should be converted to instance methods to match Kotlin's singleton compilation, and suggests expanding testClearAll to ensure biometric sessions are also verified as cleared.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +138 to +171
@Implements(BiometricHelper::class)
class ShadowBiometricHelper {
companion object {
@JvmStatic
@Implementation
fun encryptWithPin(session: AuthenticatedSession, pin: String, salt: ByteArray): com.clhs.score.data.BiometricHelper.EncryptedData {
return com.clhs.score.data.BiometricHelper.EncryptedData("cipher", Base64.encodeToString("iv".toByteArray(), Base64.NO_WRAP))
}

@JvmStatic
@Implementation
fun decryptWithPin(cipherText: String, ivBase64: String, pin: String, salt: ByteArray): AuthenticatedSession {
return AuthenticatedSession("123", "token", mapOf("cookie1" to "val1"))
}

@JvmStatic
@Implementation
fun encryptPin(pin: String, cipher: Cipher): com.clhs.score.data.BiometricHelper.EncryptedData {
return com.clhs.score.data.BiometricHelper.EncryptedData("pinCipher", Base64.encodeToString("pinIv".toByteArray(), Base64.NO_WRAP))
}

@JvmStatic
@Implementation
fun decryptPin(cipherText: String, cipher: Cipher): String {
return "1234"
}

@JvmStatic
@Implementation
fun deleteSecretKey() {
// Do nothing
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In Kotlin, object declarations compile to classes with instance methods (on the INSTANCE singleton) rather than static methods, unless they are explicitly annotated with @JvmStatic. Since BiometricHelper's methods are not annotated with @JvmStatic, Robolectric expects the shadow methods to be instance methods on ShadowBiometricHelper rather than static methods in a companion object. Defining them as static companion methods will cause Robolectric to fail to bind the shadow, which will result in the real BiometricHelper being executed (potentially causing AndroidKeyStore errors or slow PBKDF2 key derivation in tests).

Remove the companion object and @JvmStatic annotations to make them instance methods.

@Implements(BiometricHelper::class)
class ShadowBiometricHelper {
    @Implementation
    fun encryptWithPin(session: AuthenticatedSession, pin: String, salt: ByteArray): com.clhs.score.data.BiometricHelper.EncryptedData {
        return com.clhs.score.data.BiometricHelper.EncryptedData("cipher", Base64.encodeToString("iv".toByteArray(), Base64.NO_WRAP))
    }

    @Implementation
    fun decryptWithPin(cipherText: String, ivBase64: String, pin: String, salt: ByteArray): AuthenticatedSession {
        return AuthenticatedSession("123", "token", mapOf("cookie1" to "val1"))
    }

    @Implementation
    fun encryptPin(pin: String, cipher: Cipher): com.clhs.score.data.BiometricHelper.EncryptedData {
        return com.clhs.score.data.BiometricHelper.EncryptedData("pinCipher", Base64.encodeToString("pinIv".toByteArray(), Base64.NO_WRAP))
    }

    @Implementation
    fun decryptPin(cipherText: String, cipher: Cipher): String {
        return "1234"
    }

    @Implementation
    fun deleteSecretKey() {
        // Do nothing
    }
}

Comment on lines +71 to +84
fun testClearAll() {
val context = ApplicationProvider.getApplicationContext<Context>()
val store = SessionStore(context)

val session = AuthenticatedSession("123", "token", mapOf("cookie1" to "val1"))
store.saveSession(session)
store.saveReminderSession(session, 1000)

store.clear()

assertNull(store.loadSession())
assertNull(store.loadReminderSession(500))
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The clear() method in SessionStore clears all preferences, which includes standard, reminder, and biometric sessions. To ensure comprehensive test coverage, the testClearAll test should also save a biometric session and assert that it is successfully cleared after calling store.clear().

    @Test
    fun testClearAll() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        val store = SessionStore(context)

        val session = AuthenticatedSession("123", "token", mapOf("cookie1" to "val1"))
        store.saveSession(session)
        store.saveReminderSession(session, 1000)

        val keyGen = KeyGenerator.getInstance("AES")
        keyGen.init(256)
        val key = keyGen.generateKey()
        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        cipher.init(Cipher.ENCRYPT_MODE, key)
        store.saveBiometricSession(session, "1234", cipher)

        store.clear()

        assertNull(store.loadSession())
        assertNull(store.loadReminderSession(500))
        assertFalse(store.hasBiometricSession())
    }

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9fde31cef8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

testImplementation("app.cash.turbine:turbine:1.2.1")
testImplementation("com.squareup.okhttp3:mockwebserver:5.4.0")
testImplementation("junit:junit:4.13.2")
testImplementation("org.robolectric:robolectric:4.11.1")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use a Robolectric version that supports targetSdk 37

This project sets targetSdk = 37, but the newly added JVM test runner depends on Robolectric 4.11.1, whose supported SDK ceiling is much older; when ./gradlew test reaches SessionStoreTest, Robolectric validates the generated manifest target SDK before running the @Config(sdk = [34]) test and rejects apps whose targetSdkVersion is above its max SDK. That makes the release workflow's existing ./gradlew --no-daemon test assembleRelease step fail as soon as this test is included, so the dependency needs to be updated to a Robolectric release that supports the app target SDK or the test should avoid Robolectric.

Useful? React with 👍 / 👎.

@alvin000009238
alvin000009238 deleted the add-sessionstore-tests-10704719854318725426 branch July 9, 2026 09:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants