mirror of
https://github.com/bitwarden/android.git
synced 2025-02-16 11:59:57 +03:00
BIT-1117: Decrypt sends (#254)
This commit is contained in:
parent
db5c57664f
commit
ba5509511f
12 changed files with 582 additions and 25 deletions
|
@ -6,6 +6,8 @@ import com.bitwarden.core.CipherView
|
|||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.core.InitCryptoRequest
|
||||
import com.bitwarden.core.Send
|
||||
import com.bitwarden.core.SendView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
|
||||
/**
|
||||
|
@ -34,6 +36,16 @@ interface VaultSdkSource {
|
|||
*/
|
||||
suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherView>>
|
||||
|
||||
/**
|
||||
* Decrypts a [Send] returning a [SendView] wrapped in a [Result].
|
||||
*/
|
||||
suspend fun decryptSend(send: Send): Result<SendView>
|
||||
|
||||
/**
|
||||
* Decrypts a list of [Send]s returning a list of [SendView] wrapped in a [Result].
|
||||
*/
|
||||
suspend fun decryptSendList(sendList: List<Send>): Result<List<SendView>>
|
||||
|
||||
/**
|
||||
* Decrypts a [Folder] returning a [FolderView] wrapped in a [Result].
|
||||
*/
|
||||
|
|
|
@ -6,6 +6,8 @@ import com.bitwarden.core.CipherView
|
|||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.core.InitCryptoRequest
|
||||
import com.bitwarden.core.Send
|
||||
import com.bitwarden.core.SendView
|
||||
import com.bitwarden.sdk.BitwardenException
|
||||
import com.bitwarden.sdk.ClientCrypto
|
||||
import com.bitwarden.sdk.ClientVault
|
||||
|
@ -43,6 +45,12 @@ class VaultSdkSourceImpl(
|
|||
override suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherView>> =
|
||||
runCatching { cipherList.map { clientVault.ciphers().decrypt(it) } }
|
||||
|
||||
override suspend fun decryptSend(send: Send): Result<SendView> =
|
||||
runCatching { clientVault.sends().decrypt(send) }
|
||||
|
||||
override suspend fun decryptSendList(sendList: List<Send>): Result<List<SendView>> =
|
||||
runCatching { sendList.map { clientVault.sends().decrypt(it) } }
|
||||
|
||||
override suspend fun decryptFolder(folder: Folder): Result<FolderView> =
|
||||
runCatching { clientVault.folders().decrypt(folder) }
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
@ -16,9 +17,14 @@ interface VaultRepository {
|
|||
val vaultDataStateFlow: StateFlow<DataState<VaultData>>
|
||||
|
||||
/**
|
||||
* Clear the in memory vault data.
|
||||
* Flow that represents the current send data.
|
||||
*/
|
||||
fun clearVaultData()
|
||||
val sendDataStateFlow: StateFlow<DataState<SendData>>
|
||||
|
||||
/**
|
||||
* Clear any previously unlocked, in-memory data (vault, send, etc).
|
||||
*/
|
||||
fun clearUnlockedData()
|
||||
|
||||
/**
|
||||
* Attempt to sync the vault data.
|
||||
|
|
|
@ -10,10 +10,12 @@ 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.SendData
|
||||
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.toEncryptedSdkSendList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -46,11 +48,18 @@ class VaultRepositoryImpl constructor(
|
|||
private val vaultDataMutableStateFlow =
|
||||
MutableStateFlow<DataState<VaultData>>(DataState.Loading)
|
||||
|
||||
private val sendDataMutableStateFlow =
|
||||
MutableStateFlow<DataState<SendData>>(DataState.Loading)
|
||||
|
||||
override val vaultDataStateFlow: StateFlow<DataState<VaultData>>
|
||||
get() = vaultDataMutableStateFlow.asStateFlow()
|
||||
|
||||
override fun clearVaultData() {
|
||||
override val sendDataStateFlow: StateFlow<DataState<SendData>>
|
||||
get() = sendDataMutableStateFlow.asStateFlow()
|
||||
|
||||
override fun clearUnlockedData() {
|
||||
vaultDataMutableStateFlow.update { DataState.Loading }
|
||||
sendDataMutableStateFlow.update { DataState.Loading }
|
||||
}
|
||||
|
||||
override fun sync() {
|
||||
|
@ -60,6 +69,11 @@ class VaultRepositoryImpl constructor(
|
|||
DataState.Pending(data = data)
|
||||
}
|
||||
}
|
||||
sendDataMutableStateFlow.value.data?.let { data ->
|
||||
sendDataMutableStateFlow.update {
|
||||
DataState.Pending(data = data)
|
||||
}
|
||||
}
|
||||
syncJob = scope.launch {
|
||||
syncService
|
||||
.sync()
|
||||
|
@ -69,22 +83,19 @@ class VaultRepositoryImpl constructor(
|
|||
userKey = syncResponse.profile?.key,
|
||||
privateKey = syncResponse.profile?.privateKey,
|
||||
)
|
||||
decryptSyncResponseAndUpdateVaultDataState(
|
||||
syncResponse = syncResponse,
|
||||
)
|
||||
decryptSyncResponseAndUpdateVaultDataState(syncResponse = syncResponse)
|
||||
decryptSendsAndUpdateSendDataState(sendList = syncResponse.sends)
|
||||
},
|
||||
onFailure = { throwable ->
|
||||
vaultDataMutableStateFlow.update {
|
||||
if (throwable.isNoConnectionError()) {
|
||||
DataState.NoNetwork(
|
||||
data = it.data,
|
||||
)
|
||||
} else {
|
||||
DataState.Error(
|
||||
error = throwable,
|
||||
data = it.data,
|
||||
)
|
||||
}
|
||||
vaultDataMutableStateFlow.update { currentState ->
|
||||
throwable.toNetworkOrErrorState(
|
||||
data = currentState.data,
|
||||
)
|
||||
}
|
||||
sendDataMutableStateFlow.update { currentState ->
|
||||
throwable.toNetworkOrErrorState(
|
||||
data = currentState.data,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -148,16 +159,34 @@ class VaultRepositoryImpl constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun decryptSendsAndUpdateSendDataState(sendList: List<SyncResponseJson.Send>?) {
|
||||
val newState = vaultSdkSource
|
||||
.decryptSendList(
|
||||
sendList = sendList
|
||||
.orEmpty()
|
||||
.toEncryptedSdkSendList(),
|
||||
)
|
||||
.fold(
|
||||
onSuccess = { DataState.Loaded(data = SendData(sendViewList = it)) },
|
||||
onFailure = { DataState.Error(error = it) },
|
||||
)
|
||||
sendDataMutableStateFlow.update { newState }
|
||||
}
|
||||
|
||||
private suspend fun decryptSyncResponseAndUpdateVaultDataState(syncResponse: SyncResponseJson) {
|
||||
val newState = vaultSdkSource
|
||||
.decryptCipherList(
|
||||
cipherList = (syncResponse.ciphers ?: emptyList())
|
||||
cipherList = syncResponse
|
||||
.ciphers
|
||||
.orEmpty()
|
||||
.toEncryptedSdkCipherList(),
|
||||
)
|
||||
.flatMap { decryptedCipherList ->
|
||||
vaultSdkSource
|
||||
.decryptFolderList(
|
||||
folderList = (syncResponse.folders ?: emptyList())
|
||||
folderList = syncResponse
|
||||
.folders
|
||||
.orEmpty()
|
||||
.toEncryptedSdkFolderList(),
|
||||
)
|
||||
.map { decryptedFolderList ->
|
||||
|
@ -178,3 +207,13 @@ class VaultRepositoryImpl constructor(
|
|||
vaultDataMutableStateFlow.update { newState }
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> Throwable.toNetworkOrErrorState(data: T?): DataState<T> =
|
||||
if (isNoConnectionError()) {
|
||||
DataState.NoNetwork(data = data)
|
||||
} else {
|
||||
DataState.Error(
|
||||
error = this,
|
||||
data = data,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.bitwarden.core.SendView
|
||||
|
||||
/**
|
||||
* Represents decrypted send data.
|
||||
*
|
||||
* @param sendViewList List of decrypted sends.
|
||||
*/
|
||||
data class SendData(
|
||||
val sendViewList: List<SendView>,
|
||||
)
|
|
@ -0,0 +1,72 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.util
|
||||
|
||||
import com.bitwarden.core.Send
|
||||
import com.bitwarden.core.SendFile
|
||||
import com.bitwarden.core.SendText
|
||||
import com.bitwarden.core.SendType
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Converts a list of [SyncResponseJson.Send] objects to a list of corresponding
|
||||
* Bitwarden SDK [Send] objects.
|
||||
*/
|
||||
fun List<SyncResponseJson.Send>.toEncryptedSdkSendList(): List<Send> =
|
||||
map { it.toEncryptedSdkSend() }
|
||||
|
||||
/**
|
||||
* Converts a [SyncResponseJson.Send] object to a corresponding
|
||||
* Bitwarden SDK [Send] object.
|
||||
*/
|
||||
fun SyncResponseJson.Send.toEncryptedSdkSend(): Send =
|
||||
Send(
|
||||
id = id,
|
||||
accessId = accessId.toString(),
|
||||
name = name.toString(),
|
||||
notes = notes,
|
||||
key = key.toString(),
|
||||
password = password,
|
||||
type = type.toSdkSendType(),
|
||||
file = file.toEncryptedSdkFile(),
|
||||
text = text.toEncryptedSdkText(),
|
||||
maxAccessCount = maxAccessCount?.toUInt(),
|
||||
accessCount = accessCount.toUInt(),
|
||||
disabled = isDisabled,
|
||||
hideEmail = shouldHideEmail,
|
||||
revisionDate = revisionDate.toInstant(ZoneOffset.UTC),
|
||||
deletionDate = deletionDate.toInstant(ZoneOffset.UTC),
|
||||
expirationDate = expirationDate?.toInstant(ZoneOffset.UTC),
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts a [SyncResponseJson.Send.Text] object to a corresponding
|
||||
* Bitwarden SDK [SendText] object.
|
||||
*/
|
||||
private fun SyncResponseJson.Send.Text.toEncryptedSdkText(): SendText =
|
||||
SendText(
|
||||
text = text,
|
||||
hidden = isHidden,
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts a [SyncResponseJson.Send.File] objects to a corresponding
|
||||
* Bitwarden SDK [SendFile] object.
|
||||
*/
|
||||
private fun SyncResponseJson.Send.File.toEncryptedSdkFile(): SendFile =
|
||||
SendFile(
|
||||
id = id.toString(),
|
||||
fileName = fileName.toString(),
|
||||
size = size.toString(),
|
||||
sizeName = sizeName.toString(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts a [SendTypeJson] objects to a corresponding
|
||||
* Bitwarden SDK [SendType].
|
||||
*/
|
||||
private fun SendTypeJson.toSdkSendType(): SendType =
|
||||
when (this) {
|
||||
SendTypeJson.TEXT -> SendType.TEXT
|
||||
SendTypeJson.FILE -> SendType.FILE
|
||||
}
|
|
@ -12,7 +12,7 @@ fun createMockSend(number: Int): SyncResponseJson.Send =
|
|||
type = SendTypeJson.FILE,
|
||||
accessId = "mockAccessId-$number",
|
||||
password = "mockPassword-$number",
|
||||
file = createMockFile(number = 1),
|
||||
file = createMockFile(number = number),
|
||||
deletionDate = LocalDateTime.parse("2023-10-27T12:00:00"),
|
||||
name = "mockName-$number",
|
||||
isDisabled = false,
|
||||
|
|
|
@ -6,6 +6,8 @@ import com.bitwarden.core.CipherView
|
|||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.core.InitCryptoRequest
|
||||
import com.bitwarden.core.Send
|
||||
import com.bitwarden.core.SendView
|
||||
import com.bitwarden.sdk.BitwardenException
|
||||
import com.bitwarden.sdk.ClientCrypto
|
||||
import com.bitwarden.sdk.ClientVault
|
||||
|
@ -168,6 +170,53 @@ class VaultSdkSourceTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decryptSendList should call SDK and return correct data wrapped in a Result`() =
|
||||
runBlocking {
|
||||
val mockSend = mockk<Send>()
|
||||
val expectedResult = mockk<SendView>()
|
||||
coEvery {
|
||||
clientVault.sends().decrypt(
|
||||
send = mockSend,
|
||||
)
|
||||
} returns expectedResult
|
||||
val result = vaultSdkSource.decryptSendList(
|
||||
sendList = listOf(mockSend),
|
||||
)
|
||||
assertEquals(
|
||||
listOf(expectedResult).asSuccess(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientVault.sends().decrypt(
|
||||
send = mockSend,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decryptSend should call SDK and return correct data wrapped in a Result`() =
|
||||
runBlocking {
|
||||
val mockSend = mockk<Send>()
|
||||
val expectedResult = mockk<SendView>()
|
||||
coEvery {
|
||||
clientVault.sends().decrypt(
|
||||
send = mockSend,
|
||||
)
|
||||
} returns expectedResult
|
||||
val result = vaultSdkSource.decryptSend(
|
||||
send = mockSend,
|
||||
)
|
||||
assertEquals(
|
||||
expectedResult.asSuccess(), result,
|
||||
)
|
||||
coVerify {
|
||||
clientVault.sends().decrypt(
|
||||
send = mockSend,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Folder decrypt should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val mockFolder = mockk<Folder>()
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
|
||||
import com.bitwarden.core.SendFileView
|
||||
import com.bitwarden.core.SendTextView
|
||||
import com.bitwarden.core.SendType
|
||||
import com.bitwarden.core.SendView
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Create a mock [SendView] with a given [number].
|
||||
*/
|
||||
fun createMockSendView(number: Int): SendView =
|
||||
SendView(
|
||||
id = "mockId-$number",
|
||||
accessId = "mockAccessId-$number",
|
||||
name = "mockName-$number",
|
||||
notes = "mockNotes-$number",
|
||||
key = "mockKey-$number",
|
||||
password = "mockPassword-$number",
|
||||
type = SendType.FILE,
|
||||
file = createMockFileView(number = number),
|
||||
text = createMockTextView(number = number),
|
||||
maxAccessCount = 1u,
|
||||
accessCount = 1u,
|
||||
disabled = false,
|
||||
hideEmail = false,
|
||||
revisionDate = LocalDateTime.parse("2023-10-27T12:00:00").toInstant(ZoneOffset.UTC),
|
||||
deletionDate = LocalDateTime.parse("2023-10-27T12:00:00").toInstant(ZoneOffset.UTC),
|
||||
expirationDate = LocalDateTime.parse("2023-10-27T12:00:00").toInstant(ZoneOffset.UTC),
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SendFileView] with a given [number].
|
||||
*/
|
||||
fun createMockFileView(number: Int): SendFileView =
|
||||
SendFileView(
|
||||
fileName = "mockFileName-$number",
|
||||
size = "1",
|
||||
sizeName = "mockSizeName-$number",
|
||||
id = "mockId-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SendTextView] with a given [number].
|
||||
*/
|
||||
fun createMockTextView(number: Int): SendTextView =
|
||||
SendTextView(
|
||||
hidden = false,
|
||||
text = "mockText-$number",
|
||||
)
|
|
@ -0,0 +1,51 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
|
||||
import com.bitwarden.core.Send
|
||||
import com.bitwarden.core.SendFile
|
||||
import com.bitwarden.core.SendText
|
||||
import com.bitwarden.core.SendType
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
||||
/**
|
||||
* Create a mock [Send] with a given [number].
|
||||
*/
|
||||
fun createMockSdkSend(number: Int): Send =
|
||||
Send(
|
||||
id = "mockId-$number",
|
||||
accessId = "mockAccessId-$number",
|
||||
name = "mockName-$number",
|
||||
notes = "mockNotes-$number",
|
||||
key = "mockKey-$number",
|
||||
password = "mockPassword-$number",
|
||||
type = SendType.FILE,
|
||||
file = createMockSdkFile(number = number),
|
||||
text = createMockSdkText(number = number),
|
||||
maxAccessCount = 1u,
|
||||
accessCount = 1u,
|
||||
disabled = false,
|
||||
hideEmail = false,
|
||||
revisionDate = LocalDateTime.parse("2023-10-27T12:00:00").toInstant(ZoneOffset.UTC),
|
||||
deletionDate = LocalDateTime.parse("2023-10-27T12:00:00").toInstant(ZoneOffset.UTC),
|
||||
expirationDate = LocalDateTime.parse("2023-10-27T12:00:00").toInstant(ZoneOffset.UTC),
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SendFile] with a given [number].
|
||||
*/
|
||||
fun createMockSdkFile(number: Int): SendFile =
|
||||
SendFile(
|
||||
fileName = "mockFileName-$number",
|
||||
size = "1",
|
||||
sizeName = "mockSizeName-$number",
|
||||
id = "mockId-$number",
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [SendText] with a given [number].
|
||||
*/
|
||||
fun createMockSdkText(number: Int): SendText =
|
||||
SendText(
|
||||
hidden = false,
|
||||
text = "mockText-$number",
|
||||
)
|
|
@ -20,6 +20,9 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
|||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||
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.createMockSdkSend
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import io.mockk.awaits
|
||||
|
@ -36,6 +39,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
|
|||
import org.junit.jupiter.api.Test
|
||||
import java.net.UnknownHostException
|
||||
|
||||
@Suppress("LargeClass")
|
||||
class VaultRepositoryTest {
|
||||
|
||||
private val dispatcherManager: DispatcherManager = FakeDispatcherManager()
|
||||
|
@ -50,7 +54,7 @@ class VaultRepositoryTest {
|
|||
)
|
||||
|
||||
@Test
|
||||
fun `sync with syncService Success should update AuthDiskSource and vaultDataStateFlow`() =
|
||||
fun `sync with syncService Success should update AuthDiskSource and DataStateFlows`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
|
@ -61,6 +65,9 @@ class VaultRepositoryTest {
|
|||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sync()
|
||||
|
@ -82,6 +89,14 @@ class VaultRepositoryTest {
|
|||
),
|
||||
vaultRepository.vaultDataStateFlow.value,
|
||||
)
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = SendData(
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
vaultRepository.sendDataStateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -96,6 +111,9 @@ class VaultRepositoryTest {
|
|||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.vaultDataStateFlow.test {
|
||||
|
@ -135,6 +153,57 @@ class VaultRepositoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with data should update sendDataStateFlow to Pending before service sync`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sendDataStateFlow.test {
|
||||
assertEquals(
|
||||
DataState.Loading,
|
||||
awaitItem(),
|
||||
)
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = SendData(
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Pending(
|
||||
data = SendData(
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = SendData(
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with decryptCipherList Failure should update vaultDataStateFlow with Error`() =
|
||||
runTest {
|
||||
|
@ -148,6 +217,9 @@ class VaultRepositoryTest {
|
|||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sync()
|
||||
|
@ -171,6 +243,9 @@ class VaultRepositoryTest {
|
|||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns mockException.asFailure()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sync()
|
||||
|
@ -182,7 +257,33 @@ class VaultRepositoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `sync with syncService Failure should update vaultDataStateFlow with an Error`() =
|
||||
fun `sync with decryptSendList Failure should update sendDataStateFlow with Error`() =
|
||||
runTest {
|
||||
val mockException = IllegalStateException()
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns mockException.asFailure()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sync()
|
||||
|
||||
assertEquals(
|
||||
DataState.Error<SendData>(error = mockException),
|
||||
vaultRepository.sendDataStateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with syncService Failure should update vault and send DataStateFlow with an Error`() =
|
||||
runTest {
|
||||
val mockException = IllegalStateException(
|
||||
"sad",
|
||||
|
@ -200,10 +301,17 @@ class VaultRepositoryTest {
|
|||
),
|
||||
vaultRepository.vaultDataStateFlow.value,
|
||||
)
|
||||
assertEquals(
|
||||
DataState.Error(
|
||||
error = mockException,
|
||||
data = null,
|
||||
),
|
||||
vaultRepository.sendDataStateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with NoNetwork should update vaultDataStateFlow to NoNetwork`() =
|
||||
fun `sync with NoNetwork should update vault and send DataStateFlow to NoNetwork`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
|
@ -217,6 +325,12 @@ class VaultRepositoryTest {
|
|||
),
|
||||
vaultRepository.vaultDataStateFlow.value,
|
||||
)
|
||||
assertEquals(
|
||||
DataState.NoNetwork(
|
||||
data = null,
|
||||
),
|
||||
vaultRepository.sendDataStateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -231,6 +345,9 @@ class VaultRepositoryTest {
|
|||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.vaultDataStateFlow.test {
|
||||
|
@ -273,6 +390,60 @@ class VaultRepositoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sync with NoNetwork data should update sendDataStateFlow to NoNetwork with data`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returnsMany listOf(
|
||||
Result.success(createMockSyncResponse(number = 1)),
|
||||
UnknownHostException().asFailure(),
|
||||
)
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sendDataStateFlow.test {
|
||||
assertEquals(
|
||||
DataState.Loading,
|
||||
awaitItem(),
|
||||
)
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = SendData(
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Pending(
|
||||
data = SendData(
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
DataState.NoNetwork(
|
||||
data = SendData(
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVaultAndSync with initializeCrypto Success should sync and return Success`() =
|
||||
runTest {
|
||||
|
@ -285,6 +456,9 @@ class VaultRepositoryTest {
|
|||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
|
@ -327,6 +501,9 @@ class VaultRepositoryTest {
|
|||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
|
@ -540,7 +717,7 @@ class VaultRepositoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `clearVaultData should update the vaultDataStateFlow to Loading`() =
|
||||
fun `clearUnlockedData should update the vaultDataStateFlow to Loading`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
|
@ -551,6 +728,9 @@ class VaultRepositoryTest {
|
|||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.vaultDataStateFlow.test {
|
||||
|
@ -569,7 +749,48 @@ class VaultRepositoryTest {
|
|||
awaitItem(),
|
||||
)
|
||||
|
||||
vaultRepository.clearVaultData()
|
||||
vaultRepository.clearUnlockedData()
|
||||
|
||||
assertEquals(
|
||||
DataState.Loading,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clearUnlockedData should update the sendDataStateFlow to Loading`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(number = 1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
|
||||
} returns listOf(createMockSendView(number = 1)).asSuccess()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
vaultRepository.sendDataStateFlow.test {
|
||||
assertEquals(
|
||||
DataState.Loading,
|
||||
awaitItem(),
|
||||
)
|
||||
vaultRepository.sync()
|
||||
assertEquals(
|
||||
DataState.Loaded(
|
||||
data = SendData(
|
||||
sendViewList = listOf(createMockSendView(number = 1)),
|
||||
),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
|
||||
vaultRepository.clearUnlockedData()
|
||||
|
||||
assertEquals(
|
||||
DataState.Loading,
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.util
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSend
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkSend
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultSdkSendExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `toEncryptedSdkSend should convert a network-based Send to SDK-based Send`() {
|
||||
val syncSend = createMockSend(number = 1)
|
||||
val sdkSend = syncSend.toEncryptedSdkSend()
|
||||
assertEquals(
|
||||
createMockSdkSend(number = 1),
|
||||
sdkSend,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `toEncryptedSdkSendList should convert list of network-based Send to List of SDK-based Send`() {
|
||||
val syncSends = listOf(
|
||||
createMockSend(number = 1),
|
||||
createMockSend(number = 2),
|
||||
)
|
||||
val sdkSends = syncSends.toEncryptedSdkSendList()
|
||||
assertEquals(
|
||||
listOf(
|
||||
createMockSdkSend(number = 1),
|
||||
createMockSdkSend(number = 2),
|
||||
),
|
||||
sdkSends,
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue