mirror of
https://github.com/bitwarden/android.git
synced 2025-02-16 11:59:57 +03:00
BIT-1128: Expose vault data (#227)
This commit is contained in:
parent
a4c34a8704
commit
a43a541719
13 changed files with 507 additions and 48 deletions
|
@ -1,6 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.network.util
|
||||
|
||||
import okio.ByteString.Companion.decodeBase64
|
||||
import java.net.UnknownHostException
|
||||
import java.nio.charset.Charset
|
||||
import java.util.Base64
|
||||
|
||||
|
@ -32,3 +33,11 @@ fun String.base64UrlDecodeOrNull(): String? =
|
|||
.replace("_", "/")
|
||||
.decodeBase64()
|
||||
?.string(Charset.defaultCharset())
|
||||
|
||||
/**
|
||||
* Returns true if the throwable represents a no network error.
|
||||
*/
|
||||
fun Throwable?.isNoConnectionError(): Boolean {
|
||||
return this is UnknownHostException ||
|
||||
this?.cause?.isNoConnectionError() ?: false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package com.x8bit.bitwarden.data.platform.repository.model
|
||||
|
||||
/**
|
||||
* A data state that can be used as a template for data in the repository layer.
|
||||
*/
|
||||
sealed class DataState<out T> {
|
||||
|
||||
/**
|
||||
* Data that is being wrapped by [DataState].
|
||||
*/
|
||||
abstract val data: T?
|
||||
|
||||
/**
|
||||
* Loading state that has no data is available.
|
||||
*/
|
||||
data object Loading : DataState<Nothing>() {
|
||||
override val data: Nothing? get() = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Loaded state that has data available.
|
||||
*/
|
||||
data class Loaded<T>(
|
||||
override val data: T,
|
||||
) : DataState<T>()
|
||||
|
||||
/**
|
||||
* Pending state that has data available.
|
||||
*/
|
||||
data class Pending<T>(
|
||||
override val data: T,
|
||||
) : DataState<T>()
|
||||
|
||||
/**
|
||||
* Error state that may have data available.
|
||||
*/
|
||||
data class Error<T>(
|
||||
val error: Throwable,
|
||||
override val data: T? = null,
|
||||
) : DataState<T>()
|
||||
|
||||
/**
|
||||
* No network state that may have data is available.
|
||||
*/
|
||||
data class NoNetwork<T>(
|
||||
override val data: T? = null,
|
||||
) : DataState<T>()
|
||||
}
|
|
@ -1,12 +1,25 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Responsible for managing vault data inside the network layer.
|
||||
*/
|
||||
interface VaultRepository {
|
||||
|
||||
/**
|
||||
* Flow that represents the current vault data.
|
||||
*/
|
||||
val vaultDataStateFlow: StateFlow<DataState<VaultData>>
|
||||
|
||||
/**
|
||||
* Clear the in memory vault data.
|
||||
*/
|
||||
fun clearVaultData()
|
||||
|
||||
/**
|
||||
* Attempt to sync the vault data.
|
||||
*/
|
||||
|
|
|
@ -3,15 +3,24 @@ package com.x8bit.bitwarden.data.vault.repository
|
|||
import com.bitwarden.core.InitCryptoRequest
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.isNoConnectionError
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.util.flatMap
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipherList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolderList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
|
@ -28,8 +37,23 @@ class VaultRepositoryImpl constructor(
|
|||
|
||||
private var syncJob: Job = Job().apply { complete() }
|
||||
|
||||
private val vaultDataMutableStateFlow =
|
||||
MutableStateFlow<DataState<VaultData>>(DataState.Loading)
|
||||
|
||||
override val vaultDataStateFlow: StateFlow<DataState<VaultData>>
|
||||
get() = vaultDataMutableStateFlow.asStateFlow()
|
||||
|
||||
override fun clearVaultData() {
|
||||
vaultDataMutableStateFlow.update { DataState.Loading }
|
||||
}
|
||||
|
||||
override fun sync() {
|
||||
if (!syncJob.isCompleted) return
|
||||
vaultDataMutableStateFlow.value.data?.let { data ->
|
||||
vaultDataMutableStateFlow.update {
|
||||
DataState.Pending(data = data)
|
||||
}
|
||||
}
|
||||
syncJob = scope.launch {
|
||||
syncService
|
||||
.sync()
|
||||
|
@ -39,20 +63,23 @@ class VaultRepositoryImpl constructor(
|
|||
userKey = syncResponse.profile?.key,
|
||||
privateKey = syncResponse.profile?.privateKey,
|
||||
)
|
||||
// TODO transform into domain object consumable by VaultViewModel BIT-205.
|
||||
syncResponse.ciphers?.let { networkCiphers ->
|
||||
vaultSdkSource.decryptCipherList(
|
||||
cipherList = networkCiphers.toEncryptedSdkCipherList(),
|
||||
)
|
||||
}
|
||||
syncResponse.folders?.let { networkFolders ->
|
||||
vaultSdkSource.decryptFolderList(
|
||||
folderList = networkFolders.toEncryptedSdkFolderList(),
|
||||
)
|
||||
}
|
||||
decryptSyncResponseAndUpdateVaultDataState(
|
||||
syncResponse = syncResponse,
|
||||
)
|
||||
},
|
||||
onFailure = {
|
||||
// TODO handle failure BIT-205.
|
||||
onFailure = { throwable ->
|
||||
vaultDataMutableStateFlow.update {
|
||||
if (throwable.isNoConnectionError()) {
|
||||
DataState.NoNetwork(
|
||||
data = it.data,
|
||||
)
|
||||
} else {
|
||||
DataState.Error(
|
||||
error = throwable,
|
||||
data = it.data,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -108,4 +135,34 @@ class VaultRepositoryImpl constructor(
|
|||
onSuccess = { it.toVaultUnlockResult() },
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun decryptSyncResponseAndUpdateVaultDataState(syncResponse: SyncResponseJson) {
|
||||
val newState = vaultSdkSource
|
||||
.decryptCipherList(
|
||||
cipherList = (syncResponse.ciphers ?: emptyList())
|
||||
.toEncryptedSdkCipherList(),
|
||||
)
|
||||
.flatMap { decryptedCipherList ->
|
||||
vaultSdkSource
|
||||
.decryptFolderList(
|
||||
folderList = (syncResponse.folders ?: emptyList())
|
||||
.toEncryptedSdkFolderList(),
|
||||
)
|
||||
.map { decryptedFolderList ->
|
||||
decryptedCipherList to decryptedFolderList
|
||||
}
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { (decryptedCipherList, decryptedFolderList) ->
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = decryptedCipherList,
|
||||
folderViewList = decryptedFolderList,
|
||||
),
|
||||
)
|
||||
},
|
||||
onFailure = { DataState.Error(error = it) },
|
||||
)
|
||||
vaultDataMutableStateFlow.update { newState }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.bitwarden.core.CipherListView
|
||||
import com.bitwarden.core.FolderView
|
||||
|
||||
/**
|
||||
* Represents decrypted vault data.
|
||||
*
|
||||
* @param cipherListViewList List of decrypted ciphers.
|
||||
* @param folderViewList List of decrypted folders.
|
||||
*/
|
||||
data class VaultData(
|
||||
val cipherListViewList: List<CipherListView>,
|
||||
val folderViewList: List<FolderView>,
|
||||
)
|
|
@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.platform.datasource.network.util
|
|||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.net.UnknownHostException
|
||||
|
||||
class NetworkUtilsTest {
|
||||
@Test
|
||||
|
@ -40,4 +41,20 @@ class NetworkUtilsTest {
|
|||
"*.*".base64UrlDecodeOrNull(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isNoConnectionError should return return true for UnknownHostException`() {
|
||||
assertEquals(
|
||||
true,
|
||||
UnknownHostException().isNoConnectionError(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isNoConnectionError should return return false for not UnknownHostException`() {
|
||||
assertEquals(
|
||||
false,
|
||||
IllegalStateException().isNoConnectionError(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
|
||||
import com.bitwarden.core.CipherListView
|
||||
import com.bitwarden.core.CipherRepromptType
|
||||
import com.bitwarden.core.CipherType
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Create a mock [CipherListView] with a given [number].
|
||||
*/
|
||||
fun createMockCipherListView(number: Int): CipherListView =
|
||||
CipherListView(
|
||||
id = "mockId-$number",
|
||||
organizationId = "mockOrganizationId-$number",
|
||||
folderId = "mockFolderId-$number",
|
||||
collectionIds = listOf("mockCollectionId-$number"),
|
||||
name = "mockName-$number",
|
||||
type = CipherType.LOGIN,
|
||||
creationDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
deletedDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
revisionDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
attachments = 1U,
|
||||
favorite = false,
|
||||
reprompt = CipherRepromptType.NONE,
|
||||
edit = false,
|
||||
viewPassword = false,
|
||||
subTitle = "",
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
|
||||
import com.bitwarden.core.FolderView
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Create a mock [FolderView] with a given [number].
|
||||
*/
|
||||
fun createMockFolderView(number: Int): FolderView =
|
||||
FolderView(
|
||||
id = "mockId-$number",
|
||||
name = "mockName-$number",
|
||||
revisionDate = LocalDateTime
|
||||
.parse("2023-10-27T12:00:00")
|
||||
.toInstant(ZoneOffset.UTC),
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
|
||||
import com.bitwarden.core.Attachment
|
||||
import com.bitwarden.core.Card
|
|
@ -1,4 +1,4 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
|
||||
import com.bitwarden.core.Folder
|
||||
import java.time.LocalDateTime
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.InitCryptoRequest
|
||||
import com.bitwarden.core.Kdf
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
|
||||
|
@ -8,12 +9,18 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
|
|||
import com.x8bit.bitwarden.data.auth.util.KdfParamsConstants.DEFAULT_PBKDF2_ITERATIONS
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSyncResponse
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkFolder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFolder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherListView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
|
@ -21,6 +28,7 @@ import io.mockk.mockk
|
|||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.net.UnknownHostException
|
||||
|
||||
class VaultRepositoryTest {
|
||||
|
||||
|
@ -36,27 +44,228 @@ class VaultRepositoryTest {
|
|||
)
|
||||
|
||||
@Test
|
||||
fun `sync when syncService Success should update AuthDiskSource with keys`() = runTest {
|
||||
coEvery { syncService.sync() } returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns mockk()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns mockk()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
fun `sync with syncService Success should update AuthDiskSource and vaultDataStateFlow`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sync()
|
||||
vaultRepository.sync()
|
||||
|
||||
fakeAuthDiskSource.assertUserKey(
|
||||
userId = "mockUserId",
|
||||
userKey = "mockKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.assertPrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
)
|
||||
}
|
||||
fakeAuthDiskSource.assertUserKey(
|
||||
userId = "mockUserId",
|
||||
userKey = "mockKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.assertPrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
)
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
vaultRepository.vaultDataStateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with data should update vaultDataStateFlow to Pending before service sync`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.vaultDataStateFlow.test {
|
||||
assertEquals(
|
||||
DataState.Loading,
|
||||
awaitItem(),
|
||||
)
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Pending(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with decryptCipherList Failure should update vaultDataStateFlow with Error`() =
|
||||
runTest {
|
||||
val mockException = IllegalStateException()
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns mockException.asFailure()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sync()
|
||||
|
||||
assertEquals(
|
||||
DataState.Error<VaultData>(error = mockException),
|
||||
vaultRepository.vaultDataStateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with decryptFolderList Failure should update vaultDataStateFlow with Error`() =
|
||||
runTest {
|
||||
val mockException = IllegalStateException()
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns mockException.asFailure()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sync()
|
||||
|
||||
assertEquals(
|
||||
DataState.Error<VaultData>(error = mockException),
|
||||
vaultRepository.vaultDataStateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with syncService Failure should update vaultDataStateFlow with an Error`() =
|
||||
runTest {
|
||||
val mockException = IllegalStateException(
|
||||
"sad",
|
||||
)
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns mockException.asFailure()
|
||||
|
||||
vaultRepository.sync()
|
||||
|
||||
assertEquals(
|
||||
DataState.Error(
|
||||
error = mockException,
|
||||
data = null,
|
||||
),
|
||||
vaultRepository.vaultDataStateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with NoNetwork should update vaultDataStateFlow to NoNetwork`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns UnknownHostException().asFailure()
|
||||
|
||||
vaultRepository.sync()
|
||||
|
||||
assertEquals(
|
||||
DataState.NoNetwork(
|
||||
data = null,
|
||||
),
|
||||
vaultRepository.vaultDataStateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with NoNetwork data should update vaultDataStateFlow to NoNetwork with data`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.vaultDataStateFlow.test {
|
||||
assertEquals(
|
||||
DataState.Loading,
|
||||
awaitItem(),
|
||||
)
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns UnknownHostException().asFailure()
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Pending(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
DataState.NoNetwork(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVaultAndSync with initializeCrypto Success should sync and return Success`() =
|
||||
|
@ -66,10 +275,10 @@ class VaultRepositoryTest {
|
|||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns mockk()
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns mockk()
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
|
@ -233,6 +442,45 @@ class VaultRepositoryTest {
|
|||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clearVaultData should update the vaultDataStateFlow to Loading`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherListView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.vaultDataStateFlow.test {
|
||||
assertEquals(
|
||||
DataState.Loading,
|
||||
awaitItem(),
|
||||
)
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = VaultData(
|
||||
cipherListViewList = listOf(createMockCipherListView(number = 1)),
|
||||
folderViewList = listOf(createMockFolderView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
vaultRepository.clearVaultData()
|
||||
|
||||
assertEquals(
|
||||
DataState.Loading,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val MOCK_USER_STATE = UserStateJson(
|
||||
|
|
|
@ -17,15 +17,15 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockLogin
|
|||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPasswordHistory
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSecureNote
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockUri
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkAttachment
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCard
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkField
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkIdentity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkLogin
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkPasswordHistory
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkSecureNote
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkUri
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkAttachment
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCard
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkField
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkIdentity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkLogin
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkPasswordHistory
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkSecureNote
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkUri
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.util
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFolder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkFolder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFolder
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue