From f8ff7c225dbd14f4665b853d98bbe9e8e5787ea4 Mon Sep 17 00:00:00 2001 From: David Perez Date: Wed, 10 Jan 2024 13:31:31 -0600 Subject: [PATCH] Add support for retrieving a single send by ID from the VaultRepository (#564) --- .../data/vault/repository/VaultRepository.kt | 6 ++ .../vault/repository/VaultRepositoryImpl.kt | 13 ++++ .../vault/repository/VaultRepositoryTest.kt | 70 +++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt index 1f359ac29..87c3bbfb2 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt @@ -93,6 +93,12 @@ interface VaultRepository : VaultLockManager { */ fun getVaultFolderStateFlow(folderId: String): StateFlow> + /** + * Flow that represents the data for a specific send as found by ID. This may emit `null` if + * the send cannot be found. + */ + fun getSendStateFlow(sendId: String): StateFlow> + /** * Emits the totp code result flow to listeners. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt index ec6555bea..1e20f4b5e 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt @@ -263,6 +263,19 @@ class VaultRepositoryImpl( initialValue = DataState.Loading, ) + override fun getSendStateFlow(sendId: String): StateFlow> = + sendDataStateFlow + .map { dataState -> + dataState.map { sendData -> + sendData.sendViewList.find { it.id == sendId } + } + } + .stateIn( + scope = unconfinedScope, + started = SharingStarted.Lazily, + initialValue = DataState.Loading, + ) + override fun emitTotpCodeResult(totpCodeResult: TotpCodeResult) { mutableTotpCodeResultFlow.tryEmit(totpCodeResult) } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt index 75ad8b16f..ee7080c65 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt @@ -8,6 +8,7 @@ import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.InitUserCryptoRequest import com.bitwarden.core.Kdf +import com.bitwarden.core.SendView import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource @@ -1113,6 +1114,75 @@ class VaultRepositoryTest { } } + @Test + fun `getSendStateFlow should update emit SendView when present`() = runTest { + val sendId = 1 + fakeAuthDiskSource.userState = MOCK_USER_STATE + val sendView = createMockSendView(number = sendId) + coEvery { + vaultSdkSource.decryptSendList( + userId = MOCK_USER_STATE.activeUserId, + sendList = emptyList(), + ) + } returns emptyList().asSuccess() + coEvery { + vaultSdkSource.decryptSendList( + userId = MOCK_USER_STATE.activeUserId, + sendList = listOf(createMockSdkSend(number = sendId)), + ) + } returns listOf(sendView).asSuccess() + + val sendsFlow = bufferedMutableSharedFlow>() + setupVaultDiskSourceFlows(sendsFlow = sendsFlow) + + vaultRepository.getSendStateFlow("mockId-$sendId").test { + assertEquals(DataState.Loading, awaitItem()) + sendsFlow.tryEmit(emptyList()) + assertEquals(DataState.Loaded(null), awaitItem()) + sendsFlow.tryEmit(listOf(createMockSend(number = sendId))) + assertEquals(DataState.Loaded(sendView), awaitItem()) + } + } + + @Test + fun `getSendStateFlow should update to NoNetwork when a sync fails from no network`() = + runTest { + val sendId = 1234 + fakeAuthDiskSource.userState = MOCK_USER_STATE + coEvery { syncService.sync() } returns UnknownHostException().asFailure() + setupVaultDiskSourceFlows() + + vaultRepository.getSendStateFlow("mockId-$sendId").test { + assertEquals(DataState.Loading, awaitItem()) + vaultRepository.sync() + assertEquals(DataState.NoNetwork(), awaitItem()) + } + + coVerify(exactly = 1) { + syncService.sync() + } + } + + @Test + fun `getSendStateFlow should update to Error when a sync fails generically`() = + runTest { + val sendId = 1234 + val throwable = Throwable("Fail") + fakeAuthDiskSource.userState = MOCK_USER_STATE + coEvery { syncService.sync() } returns throwable.asFailure() + setupVaultDiskSourceFlows() + + vaultRepository.getSendStateFlow("mockId-$sendId").test { + assertEquals(DataState.Loading, awaitItem()) + vaultRepository.sync() + assertEquals(DataState.Error(throwable), awaitItem()) + } + + coVerify(exactly = 1) { + syncService.sync() + } + } + @Test fun `createCipher with encryptCipher failure should return CreateCipherResult failure`() = runTest {