diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt new file mode 100644 index 0000000000..4672a82bf4 --- /dev/null +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.lockscreen.impl + +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticatorManager +import io.element.android.features.lockscreen.impl.biometric.FakeBiometricAuthenticatorManager +import io.element.android.features.lockscreen.impl.fixtures.aLockScreenConfig +import io.element.android.features.lockscreen.impl.pin.PinCodeManager +import io.element.android.features.lockscreen.impl.pin.createDefaultPinCodeManager +import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore +import io.element.android.features.lockscreen.impl.storage.LockScreenStore +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver +import io.element.android.services.appnavstate.api.AppForegroundStateService +import io.element.android.services.appnavstate.test.FakeAppForegroundStateService +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultLockScreenServiceTest { + @Test + fun `when the pin is not mandatory and no pin is configured isSetupRequired emits false`() = runTest { + val sut = createDefaultLockScreenService( + lockScreenConfig = aLockScreenConfig(isPinMandatory = false) + ) + sut.isSetupRequired().test { + assertThat(awaitItem()).isFalse() + } + } + + @Test + fun `when the pin is mandatory, isSetupRequired emits true`() = runTest { + val lockScreenStore = InMemoryLockScreenStore() + val sut = createDefaultLockScreenService( + lockScreenConfig = aLockScreenConfig(isPinMandatory = true), + lockScreenStore = lockScreenStore, + ) + sut.isSetupRequired().test { + assertThat(awaitItem()).isTrue() + // When the user configures the pin code, the setup is not required anymore + lockScreenStore.saveEncryptedPinCode("encryptedCode") + assertThat(awaitItem()).isFalse() + // Users deletes the pin code + lockScreenStore.deleteEncryptedPinCode() + assertThat(awaitItem()).isTrue() + } + } + + @Test + fun `when the last session is deleted, the pin code is removed`() = runTest { + val sessionObserver = FakeSessionObserver() + val lockScreenStore = InMemoryLockScreenStore() + val sut = createDefaultLockScreenService( + lockScreenConfig = aLockScreenConfig(isPinMandatory = true), + lockScreenStore = lockScreenStore, + sessionObserver = sessionObserver, + ) + sut.isPinSetup().test { + assertThat(awaitItem()).isFalse() + // When the user configure the pin code, the setup is not required anymore + lockScreenStore.saveEncryptedPinCode("encryptedCode") + assertThat(awaitItem()).isTrue() + sessionObserver.onSessionDeleted("userId", wasLastSession = false) + expectNoEvents() + sessionObserver.onSessionDeleted("userId", wasLastSession = true) + assertThat(awaitItem()).isFalse() + } + } +} + +private fun TestScope.createDefaultLockScreenService( + lockScreenConfig: LockScreenConfig = aLockScreenConfig(), + lockScreenStore: LockScreenStore = InMemoryLockScreenStore(), + pinCodeManager: PinCodeManager = createDefaultPinCodeManager( + lockScreenStore = lockScreenStore, + ), + sessionObserver: SessionObserver = FakeSessionObserver(), + appForegroundStateService: AppForegroundStateService = FakeAppForegroundStateService(), + biometricAuthenticatorManager: BiometricAuthenticatorManager = FakeBiometricAuthenticatorManager(), +) = DefaultLockScreenService( + lockScreenConfig = lockScreenConfig, + lockScreenStore = lockScreenStore, + pinCodeManager = pinCodeManager, + coroutineScope = backgroundScope, + sessionObserver = sessionObserver, + appForegroundStateService = appForegroundStateService, + biometricAuthenticatorManager = biometricAuthenticatorManager, +) diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt index ed942c45b9..5c29c7ecea 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt @@ -10,19 +10,18 @@ package io.element.android.features.lockscreen.impl.pin import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore +import io.element.android.features.lockscreen.impl.storage.LockScreenStore +import io.element.android.libraries.cryptography.api.EncryptionDecryptionService +import io.element.android.libraries.cryptography.api.SecretKeyRepository import io.element.android.libraries.cryptography.impl.AESEncryptionDecryptionService import io.element.android.libraries.cryptography.test.SimpleSecretKeyRepository import kotlinx.coroutines.test.runTest import org.junit.Test class DefaultPinCodeManagerTest { - private val lockScreenStore = InMemoryLockScreenStore() - private val secretKeyRepository = SimpleSecretKeyRepository() - private val encryptionDecryptionService = AESEncryptionDecryptionService() - private val pinCodeManager = DefaultPinCodeManager(secretKeyRepository, encryptionDecryptionService, lockScreenStore) - @Test fun `given a pin code when create and delete assert no pin code left`() = runTest { + val pinCodeManager = createDefaultPinCodeManager() pinCodeManager.hasPinCode().test { assertThat(awaitItem()).isFalse() pinCodeManager.createPinCode("1234") @@ -34,6 +33,7 @@ class DefaultPinCodeManagerTest { @Test fun `given a pin code when create and verify with the same pin succeed`() = runTest { + val pinCodeManager = createDefaultPinCodeManager() val pinCode = "1234" pinCodeManager.createPinCode(pinCode) assertThat(pinCodeManager.verifyPinCode(pinCode)).isTrue() @@ -41,7 +41,18 @@ class DefaultPinCodeManagerTest { @Test fun `given a pin code when create and verify with a different pin fails`() = runTest { + val pinCodeManager = createDefaultPinCodeManager() pinCodeManager.createPinCode("1234") assertThat(pinCodeManager.verifyPinCode("1235")).isFalse() } } + +fun createDefaultPinCodeManager( + lockScreenStore: LockScreenStore = InMemoryLockScreenStore(), + secretKeyRepository: SecretKeyRepository = SimpleSecretKeyRepository(), + encryptionDecryptionService: EncryptionDecryptionService = AESEncryptionDecryptionService(), +) = DefaultPinCodeManager( + lockScreenStore = lockScreenStore, + secretKeyRepository = secretKeyRepository, + encryptionDecryptionService = encryptionDecryptionService, +) diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt index ef11f5d2d3..eb34a713bb 100644 --- a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt +++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt @@ -28,7 +28,7 @@ class FakeSessionObserver : SessionObserver { listeners.forEach { it.onSessionCreated(userId) } } - suspend fun onSessionDeleted(userId: String) { - listeners.forEach { it.onSessionDeleted(userId, false) } + suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean = true) { + listeners.forEach { it.onSessionDeleted(userId, wasLastSession = wasLastSession) } } }