diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/util/CipherListViewExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/util/CipherListViewExtensions.kt new file mode 100644 index 0000000000..885eb182e8 --- /dev/null +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/util/CipherListViewExtensions.kt @@ -0,0 +1,18 @@ +package com.x8bit.bitwarden.data.autofill.util + +import com.bitwarden.vault.CardListView +import com.bitwarden.vault.CipherListView +import com.bitwarden.vault.CipherListViewType +import com.bitwarden.vault.LoginListView + +/** + * Returns the [LoginListView] if the cipher is of type [CipherListViewType.Login], otherwise null. + */ +val CipherListView.login: LoginListView? + get() = (this.type as? CipherListViewType.Login)?.v1 + +/** + * Returns the [CardListView] if the cipher is of type [CipherListViewType.Card], otherwise null. + */ +val CipherListView.card: CardListView? + get() = (this.type as? CipherListViewType.Card)?.v1 diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManager.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManager.kt index 2f3ead330b..fd6a4bb5f9 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManager.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManager.kt @@ -1,6 +1,7 @@ package com.x8bit.bitwarden.data.vault.manager import com.bitwarden.core.data.repository.model.DataState +import com.bitwarden.vault.CipherListView import com.bitwarden.vault.CipherView import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem import kotlinx.coroutines.flow.StateFlow @@ -18,6 +19,15 @@ interface TotpCodeManager { cipherList: List, ): StateFlow>> + /** + * Flow for getting a DataState with multiple verification code items for the given + * [cipherListViews]. + */ + fun getTotpCodesForCipherListViewsStateFlow( + userId: String, + cipherListViews: List, + ): StateFlow>> + /** * Flow for getting a DataState with a single verification code item. */ @@ -25,4 +35,13 @@ interface TotpCodeManager { userId: String, cipher: CipherView, ): StateFlow> + + /** + * Flow for getting a DataState with a single verification code item for the given + * [cipherListView]. + */ + fun getTotpCodeStateFlow( + userId: String, + cipherListView: CipherListView, + ): StateFlow> } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerImpl.kt index 3acb6e4bd6..469cefd1c9 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerImpl.kt @@ -3,8 +3,10 @@ package com.x8bit.bitwarden.data.vault.manager import com.bitwarden.core.DateTime import com.bitwarden.core.data.repository.model.DataState import com.bitwarden.data.manager.DispatcherManager +import com.bitwarden.vault.CipherListView import com.bitwarden.vault.CipherRepromptType import com.bitwarden.vault.CipherView +import com.x8bit.bitwarden.data.autofill.util.login import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem import kotlinx.coroutines.CoroutineScope @@ -36,6 +38,9 @@ class TotpCodeManagerImpl( private val mutableVerificationCodeStateFlowMap = mutableMapOf>>() + private val mutableCipherListViewVerificationCodeStateFlowMap = + mutableMapOf>>() + override fun getTotpCodesStateFlow( userId: String, cipherList: List, @@ -73,6 +78,43 @@ class TotpCodeManagerImpl( cipher = cipher, ) + override fun getTotpCodesForCipherListViewsStateFlow( + userId: String, + cipherListViews: List, + ): StateFlow>> { + // Generate state flows + val stateFlows = cipherListViews.map { cipherListView -> + getTotpCodeStateFlowInternal(userId, cipherListView) + } + return combine(stateFlows) { results -> + when { + results.any { it is DataState.Loading } -> { + DataState.Loading + } + + else -> { + DataState.Loaded( + data = results.mapNotNull { (it as DataState.Loaded).data }, + ) + } + } + } + .stateIn( + scope = unconfinedScope, + started = SharingStarted.WhileSubscribed(), + initialValue = DataState.Loading, + ) + } + + override fun getTotpCodeStateFlow( + userId: String, + cipherListView: CipherListView, + ): StateFlow> = + getTotpCodeStateFlowInternal( + userId = userId, + cipherListView = cipherListView, + ) + @Suppress("LongMethod") private fun getTotpCodeStateFlowInternal( userId: String, @@ -108,7 +150,6 @@ class TotpCodeManagerImpl( .onSuccess { response -> item = VerificationCodeItem( code = response.code, - totpCode = totpCode, periodSeconds = response.period.toInt(), timeLeftSeconds = response.period.toInt() - time % response.period.toInt(), @@ -129,12 +170,9 @@ class TotpCodeManagerImpl( return@flow } } else { - item?.let { - item = it.copy( - timeLeftSeconds = it.periodSeconds - - (time % it.periodSeconds), - ) - } + item = item.copy( + timeLeftSeconds = item.periodSeconds - (time % item.periodSeconds), + ) } item?.let { @@ -154,6 +192,78 @@ class TotpCodeManagerImpl( ) } } + + @Suppress("LongMethod") + private fun getTotpCodeStateFlowInternal( + userId: String, + cipherListView: CipherListView?, + ): StateFlow> { + val cipherId = cipherListView?.id ?: return MutableStateFlow(DataState.Loaded(null)) + cipherListView.login?.totp ?: return MutableStateFlow(DataState.Loaded(null)) + + return mutableCipherListViewVerificationCodeStateFlowMap.getOrPut(cipherListView) { + // Define a per-item scope so that we can clear the Flow from the scope when it is + // no longer needed. + val itemScope = CoroutineScope(dispatcherManager.unconfined) + + flow> { + + var item: VerificationCodeItem? = null + while (currentCoroutineContext().isActive) { + val dateTime = clock.instant() + val time = dateTime.epochSecond.toInt() + if (item == null || item.isExpired(clock = clock)) { + vaultSdkSource + .generateTotpForCipherListView( + cipherListView = cipherListView, + userId = userId, + time = dateTime, + ) + .onSuccess { response -> + item = VerificationCodeItem( + code = response.code, + periodSeconds = response.period.toInt(), + timeLeftSeconds = response.period.toInt() - + time % response.period.toInt(), + issueTime = clock.millis(), + uriLoginViewList = cipherListView.login?.uris, + id = cipherId, + name = cipherListView.name, + username = cipherListView.login?.username, + hasPasswordReprompt = when (cipherListView.reprompt) { + CipherRepromptType.PASSWORD -> true + CipherRepromptType.NONE -> false + }, + orgUsesTotp = cipherListView.organizationUseTotp, + ) + } + .onFailure { + emit(DataState.Loaded(null)) + return@flow + } + } else { + item = item.copy( + timeLeftSeconds = item.periodSeconds - (time % item.periodSeconds), + ) + } + + item?.let { + emit(DataState.Loaded(it)) + } + delay(ONE_SECOND_MILLISECOND) + } + } + .onCompletion { + mutableCipherListViewVerificationCodeStateFlowMap.remove(cipherListView) + itemScope.cancel() + } + .stateIn( + scope = itemScope, + started = SharingStarted.WhileSubscribed(), + initialValue = DataState.Loading, + ) + } + } } private fun VerificationCodeItem.isExpired(clock: Clock): Boolean { diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/model/VerificationCodeItem.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/model/VerificationCodeItem.kt index fadc0f357b..8dbf54fd07 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/model/VerificationCodeItem.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/model/VerificationCodeItem.kt @@ -6,7 +6,6 @@ import com.bitwarden.vault.LoginUriView * Models the items returned by the TotpCodeManager. * * @property code The verification code for the item. - * @property totpCode The totp code for the item. * @property periodSeconds The time span where the code is valid in seconds. * @property timeLeftSeconds The seconds remaining until a new code is required. * @property issueTime The time the verification code was issued. @@ -19,7 +18,6 @@ import com.bitwarden.vault.LoginUriView */ data class VerificationCodeItem( val code: String, - val totpCode: String, val periodSeconds: Int, val timeLeftSeconds: Int, val issueTime: Long, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt index 51124c5ee0..ffafb95ccd 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt @@ -120,7 +120,6 @@ class VaultItemViewModel @Inject constructor( TotpCodeItemData( periodSeconds = it.periodSeconds, timeLeftSeconds = it.timeLeftSeconds, - totpCode = it.totpCode, verificationCode = it.code, ) } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/model/TotpCodeItemData.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/model/TotpCodeItemData.kt index 5bc6cebef7..63f01dc876 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/model/TotpCodeItemData.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/model/TotpCodeItemData.kt @@ -9,12 +9,10 @@ import kotlinx.parcelize.Parcelize * @property periodSeconds The period for the verification code. * @property timeLeftSeconds The time left for the verification timer. * @property verificationCode The verification code for the item. - * @property totpCode The totp code for the item. */ @Parcelize data class TotpCodeItemData( val periodSeconds: Int, val timeLeftSeconds: Int, val verificationCode: String, - val totpCode: String, ) : Parcelable diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/CipherListViewExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/CipherListViewExtensionsTest.kt new file mode 100644 index 0000000000..630ef69e17 --- /dev/null +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/autofill/util/CipherListViewExtensionsTest.kt @@ -0,0 +1,60 @@ +package com.x8bit.bitwarden.data.autofill.util + +import com.bitwarden.vault.CipherListViewType +import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCardListView +import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherListView +import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockLoginListView +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertNotNull +import org.junit.jupiter.api.assertNull + +class CipherListViewExtensionsTest { + + @Test + fun `login should return LoginListView when type is Login`() { + val cipherListView = createMockCipherListView( + number = 1, + type = CipherListViewType.Login(createMockLoginListView(1)), + ) + val loginListView = cipherListView.login + assertNotNull(loginListView) + } + + @Test + fun `login should return null when type is not Login`() { + val cipherListViews = listOf( + createMockCipherListView(number = 1, type = CipherListViewType.SecureNote), + createMockCipherListView( + number = 2, + type = CipherListViewType.Card(createMockCardListView(number = 2)), + ), + createMockCipherListView(number = 3, type = CipherListViewType.SshKey), + createMockCipherListView(number = 4, type = CipherListViewType.Identity), + ) + cipherListViews.forEach { assertNull(it.login) } + } + + @Test + fun `card should return CardListView when type is Card`() { + val cipherListView = createMockCipherListView( + number = 1, + type = CipherListViewType.Card(createMockCardListView(number = 1)), + ) + val cardListView = cipherListView.card + assertNotNull(cardListView) + } + + @Test + fun `card should return null when type is not Card`() { + listOf( + createMockCipherListView( + number = 1, + type = CipherListViewType.Login(createMockLoginListView(1)), + ), + createMockCipherListView(number = 2, type = CipherListViewType.SecureNote), + createMockCipherListView(number = 3, type = CipherListViewType.SshKey), + createMockCipherListView(number = 4, type = CipherListViewType.Identity), + ) + .forEach { assertNull(it.card) } + } +} diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt index 996bad15ee..9d1c871b9d 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt @@ -1,6 +1,5 @@ package com.x8bit.bitwarden.data.vault.datasource.sdk -import com.bitwarden.core.DateTime import com.bitwarden.core.DeriveKeyConnectorRequest import com.bitwarden.core.DerivePinKeyResponse import com.bitwarden.core.InitOrgCryptoRequest @@ -71,6 +70,9 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import java.security.MessageDigest +import java.time.Clock +import java.time.Instant +import java.time.ZoneOffset @Suppress("LargeClass") class VaultSdkSourceTest { @@ -1030,7 +1032,7 @@ class VaultSdkSourceTest { val totpResponse = TotpResponse("TestCode", 30u) coEvery { clientVault.generateTotp(any(), any()) } returns totpResponse - val time = DateTime.now() + val time = FIXED_CLOCK.instant() val result = vaultSdkSource.generateTotp( userId = userId, totp = "Totp", @@ -1469,3 +1471,7 @@ private val DEFAULT_FIDO_2_AUTH_REQUEST = AuthenticateFido2CredentialRequest( isUserVerificationSupported = true, selectedCipherView = createMockCipherView(number = 1), ) +private val FIXED_CLOCK: Clock = Clock.fixed( + Instant.parse("2023-10-27T12:00:00Z"), + ZoneOffset.UTC, +) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherListViewUtil.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherListViewUtil.kt new file mode 100644 index 0000000000..335f41e512 --- /dev/null +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherListViewUtil.kt @@ -0,0 +1,123 @@ +package com.x8bit.bitwarden.data.vault.datasource.sdk.model + +import com.bitwarden.vault.CardListView +import com.bitwarden.vault.CipherListView +import com.bitwarden.vault.CipherListViewType +import com.bitwarden.vault.CipherPermissions +import com.bitwarden.vault.CipherRepromptType +import com.bitwarden.vault.CopyableCipherFields +import com.bitwarden.vault.Fido2CredentialListView +import com.bitwarden.vault.LocalDataView +import com.bitwarden.vault.LoginListView +import com.bitwarden.vault.LoginUriView +import java.time.Instant +import java.time.ZonedDateTime + +/** + * Default date time used for [ZonedDateTime] properties of mock objects. + */ +private const val DEFAULT_TIMESTAMP = "2023-10-27T12:00:00Z" + +/** + * Creates a mock [CipherListView] for testing. Defaults to a Login cipher. Set [type] to override + * the default behavior. + */ +@Suppress("LongParameterList") +fun createMockCipherListView( + number: Int, + id: String = "mockId-$number", + organizationId: String? = "mockOrganizationId-$number", + folderId: String? = "mockId-$number", + type: CipherListViewType = CipherListViewType.Login( + createMockLoginListView(number = 1), + ), + reprompt: CipherRepromptType = CipherRepromptType.NONE, + name: String = "mockName-$number", + favorite: Boolean = false, + collectionIds: List = listOf("mockId-$number"), + revisionDate: Instant = Instant.parse(DEFAULT_TIMESTAMP), + creationDate: Instant = Instant.parse(DEFAULT_TIMESTAMP), + attachments: UInt = 0U, + organizationUseTotp: Boolean = false, + edit: Boolean = false, + viewPassword: Boolean = false, + permissions: CipherPermissions? = createMockSdkCipherPermissions(), + localData: LocalDataView? = null, + key: String = "mockKey-$number", + subtitle: String = "mockSubtitle-$number", + hasOldAttachments: Boolean = false, + copyableFields: List = emptyList(), + isDeleted: Boolean = false, +): CipherListView = CipherListView( + id = id, + organizationId = organizationId, + folderId = folderId, + type = type, + reprompt = reprompt, + name = name, + favorite = favorite, + collectionIds = collectionIds, + revisionDate = revisionDate, + creationDate = creationDate, + deletedDate = if (isDeleted) Instant.parse(DEFAULT_TIMESTAMP) else null, + attachments = attachments, + organizationUseTotp = organizationUseTotp, + edit = edit, + viewPassword = viewPassword, + permissions = permissions, + localData = localData, + key = key, + subtitle = subtitle, + hasOldAttachments = hasOldAttachments, + copyableFields = copyableFields, +) + +/** + * Creates a mock [LoginListView] for testing. + */ +@Suppress("LongParameterList") +fun createMockLoginListView( + number: Int, + fido2Credentials: List = listOf( + createMockFido2CredentialListView(number = 1), + ), + hasFido2: Boolean = true, + username: String = "mockUsername-$number", + totp: String? = "mockTotp-$number", + uris: List = listOf(createMockUriView(number = 1)), +): LoginListView = LoginListView( + fido2Credentials = fido2Credentials, + hasFido2 = hasFido2, + username = username, + totp = totp, + uris = uris, +) + +/** + * Creates a mock [Fido2CredentialListView] for testing. + */ +@Suppress("LongParameterList") +fun createMockFido2CredentialListView( + number: Int, + credentialId: String = "mockCredentialId-$number", + rpId: String = "mockRpId-$number", + userHandle: String = "mockUserHandle-$number", + userName: String = "mockUserName-$number", + userDisplayName: String = "mockUserDisplayName-$number", +): Fido2CredentialListView = Fido2CredentialListView( + credentialId = credentialId, + rpId = rpId, + userHandle = userHandle, + userName = userName, + userDisplayName = userDisplayName, +) + +/** + * Creates a mock [CardListView] for testing. + */ +fun createMockCardListView( + number: Int, + brand: String = "mockBrand-$number", +): CardListView = CardListView( + brand = brand, +) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerTest.kt index a40245df00..579b4b0c59 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerTest.kt @@ -6,10 +6,13 @@ import com.bitwarden.core.data.util.asFailure import com.bitwarden.core.data.util.asSuccess import com.bitwarden.data.datasource.disk.base.FakeDispatcherManager import com.bitwarden.data.manager.DispatcherManager +import com.bitwarden.vault.CipherListViewType import com.bitwarden.vault.CipherRepromptType import com.bitwarden.vault.TotpResponse import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource +import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherListView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView +import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockLoginListView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockLoginView import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem import com.x8bit.bitwarden.ui.vault.feature.verificationcode.util.createVerificationCodeItem @@ -129,4 +132,125 @@ class TotpCodeManagerTest { assertEquals(DataState.Loaded(null), awaitItem()) } } + + @Test + @Suppress("MaxLineLength") + fun `getTotpCodesForCipherListViewsStateFlow should have loaded data with a valid values passed in`() = + runTest { + val cipherListViews = listOf( + createMockCipherListView(number = 1), + ) + val totpResponse = TotpResponse("123456", 30u) + coEvery { + vaultSdkSource.generateTotpForCipherListView( + userId = any(), + cipherListView = any(), + time = any(), + ) + } returns totpResponse.asSuccess() + + val expected = createVerificationCodeItem() + + totpCodeManager.getTotpCodesForCipherListViewsStateFlow(userId, cipherListViews).test { + assertEquals(DataState.Loaded(listOf(expected)), awaitItem()) + } + } + + @Test + @Suppress("MaxLineLength") + fun `getTotpCodesForCipherListViewsStateFlow should have loaded data with empty list if no totp code is provided`() = + runTest { + val totpResponse = TotpResponse("123456", 30u) + coEvery { + vaultSdkSource.generateTotpForCipherListView( + userId = any(), + cipherListView = any(), + time = any(), + ) + } returns totpResponse.asSuccess() + + val cipherListView = createMockCipherListView( + number = 1, + type = CipherListViewType.Login( + createMockLoginListView( + number = 1, + totp = null, + ), + ), + ) + + totpCodeManager.getTotpCodesForCipherListViewsStateFlow(userId, listOf(cipherListView)) + .test { + assertEquals(DataState.Loaded(emptyList()), awaitItem()) + } + } + + @Suppress("MaxLineLength") + @Test + fun `getTotpCodesForCipherListViewsStateFlow should have loaded data with empty list if unable to generate auth code`() = + runTest { + coEvery { + vaultSdkSource.generateTotpForCipherListView( + userId = any(), + cipherListView = any(), + time = any(), + ) + } returns Exception().asFailure() + + val cipherListView = createMockCipherListView( + number = 1, + type = CipherListViewType.Login(createMockLoginListView(number = 1)), + ) + + totpCodeManager.getTotpCodesForCipherListViewsStateFlow(userId, listOf(cipherListView)).test { + assertEquals(DataState.Loaded(emptyList()), awaitItem()) + } + } + + @Test + @Suppress("MaxLineLength") + fun `getTotpCodeStateFlow from CipherListView should have loaded item with valid data passed in`() = + runTest { + val totpResponse = TotpResponse("123456", 30u) + coEvery { + vaultSdkSource.generateTotpForCipherListView( + userId = any(), + cipherListView = any(), + time = any(), + ) + } returns totpResponse.asSuccess() + + val cipherListView = createMockCipherListView( + number = 1, + reprompt = CipherRepromptType.PASSWORD, + ) + + val expected = createVerificationCodeItem().copy(hasPasswordReprompt = true) + + totpCodeManager.getTotpCodeStateFlow(userId, cipherListView).test { + assertEquals(DataState.Loaded(expected), awaitItem()) + } + } + + @Test + fun `getTotpCodeFlow from CipherListView should have null data if unable to get item`() = + runTest { + val totpResponse = TotpResponse("123456", 30u) + coEvery { + vaultSdkSource.generateTotp( + userId = any(), + totp = any(), + time = any(), + ) + } returns totpResponse.asSuccess() + + val cipherListView = createMockCipherListView( + number = 1, + type = CipherListViewType.SshKey, + ) + + totpCodeManager.getTotpCodeStateFlow(userId, cipherListView).test { + assertEquals(DataState.Loaded(null), awaitItem()) + } + } } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt index 9fafd4a20a..cfc294d4bf 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt @@ -3180,7 +3180,7 @@ class VaultRepositoryTest { ) every { - totpCodeManager.getTotpCodeStateFlow(userId = userId, any()) + totpCodeManager.getTotpCodeStateFlow(userId = userId, cipher = any()) } returns stateFlow setupDataStateFlow(userId = userId) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt index 44a341be1d..c268c2ef1a 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt @@ -2103,7 +2103,6 @@ class VaultItemScreenTest : BitwardenComposeTest() { periodSeconds = 30, timeLeftSeconds = 15, verificationCode = "123456", - totpCode = "testCode", ), ), ), @@ -3226,7 +3225,6 @@ private val DEFAULT_LOGIN: VaultItemState.ViewState.Content.ItemType.Login = periodSeconds = 30, timeLeftSeconds = 15, verificationCode = "123456", - totpCode = "testCode", ), fido2CredentialCreationDateText = null, canViewTotpCode = true, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt index 27e7147a8c..945eb5c01b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt @@ -2482,7 +2482,6 @@ class VaultItemViewModelTest : BaseViewModelTest() { periodSeconds = 30, timeLeftSeconds = 30, verificationCode = "123456", - totpCode = "mockTotp-1", ) private fun setupMockUri() { @@ -2549,8 +2548,6 @@ class VaultItemViewModelTest : BaseViewModelTest() { passwordRevisionDate = R.string.password_last_updated.asText("12/31/69 06:16 PM"), isPremiumUser = true, totpCodeItemData = TotpCodeItemData( - totpCode = "otpauth://totp/Example:alice@google.com" + - "?secret=JBSWY3DPEHPK3PXP&issuer=Example", verificationCode = "123456", timeLeftSeconds = 15, periodSeconds = 30, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/util/CipherViewExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/util/CipherViewExtensionsTest.kt index 098f459bea..7c535c9395 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/util/CipherViewExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/util/CipherViewExtensionsTest.kt @@ -48,7 +48,6 @@ class CipherViewExtensionsTest { periodSeconds = 30, timeLeftSeconds = 15, verificationCode = "123456", - totpCode = "testCode", ), clock = fixedClock, canDelete = true, @@ -82,7 +81,6 @@ class CipherViewExtensionsTest { periodSeconds = 30, timeLeftSeconds = 15, verificationCode = "123456", - totpCode = "testCode", ), clock = fixedClock, canDelete = true, @@ -122,7 +120,6 @@ class CipherViewExtensionsTest { periodSeconds = 30, timeLeftSeconds = 15, verificationCode = "123456", - totpCode = "testCode", ), clock = fixedClock, canDelete = true, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/util/VaultItemTestUtil.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/util/VaultItemTestUtil.kt index b2007b802b..61d2c43951 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/util/VaultItemTestUtil.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/util/VaultItemTestUtil.kt @@ -278,7 +278,6 @@ fun createLoginContent(isEmpty: Boolean): VaultItemState.ViewState.Content.ItemT periodSeconds = 30, timeLeftSeconds = 15, verificationCode = "123456", - totpCode = "testCode", ) .takeUnless { isEmpty }, fido2CredentialCreationDateText = R.string.created_x diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/util/VerificationCodeDataUtil.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/util/VerificationCodeDataUtil.kt index ba8fbe4014..8b0b980b71 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/util/VerificationCodeDataUtil.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/verificationcode/util/VerificationCodeDataUtil.kt @@ -6,7 +6,6 @@ import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem fun createVerificationCodeItem(number: Int = 1) = VerificationCodeItem( code = "123456", - totpCode = "mockTotp-$number", periodSeconds = 30, id = "mockId-$number", issueTime = 1698408000000,