mirror of
https://github.com/bitwarden/android.git
synced 2025-02-17 04:19:54 +03:00
Adding the Repository folder calls. (#813)
This commit is contained in:
parent
fa551fa6ab
commit
3be37766e2
15 changed files with 654 additions and 2 deletions
|
@ -39,6 +39,11 @@ interface VaultDiskSource {
|
|||
*/
|
||||
fun getDomains(userId: String): Flow<SyncResponseJson.Domains>
|
||||
|
||||
/**
|
||||
* Deletes a folder from the data source for the given [userId] and [folderId].
|
||||
*/
|
||||
suspend fun deleteFolder(userId: String, folderId: String)
|
||||
|
||||
/**
|
||||
* Saves a folder to the data source for the given [userId].
|
||||
*/
|
||||
|
|
|
@ -114,6 +114,10 @@ class VaultDiskSourceImpl(
|
|||
json.decodeFromString<SyncResponseJson.Domains>(entity.domainsJson)
|
||||
}
|
||||
|
||||
override suspend fun deleteFolder(userId: String, folderId: String) {
|
||||
foldersDao.deleteFolder(userId = userId, folderId = folderId)
|
||||
}
|
||||
|
||||
override suspend fun saveFolder(userId: String, folder: SyncResponseJson.Folder) {
|
||||
foldersDao.insertFolder(
|
||||
folder = FolderEntity(
|
||||
|
|
|
@ -229,6 +229,18 @@ interface VaultSdkSource {
|
|||
sendList: List<Send>,
|
||||
): Result<List<SendView>>
|
||||
|
||||
/**
|
||||
* Encrypts a [FolderView] for the user with the given [userId], returning a [Folder] wrapped
|
||||
* in a [Result].
|
||||
*
|
||||
* This should only be called after a successful call to [initializeCrypto] for the associated
|
||||
* user.
|
||||
*/
|
||||
suspend fun encryptFolder(
|
||||
userId: String,
|
||||
folder: FolderView,
|
||||
): Result<Folder>
|
||||
|
||||
/**
|
||||
* Decrypts a [Folder] for the user with the given [userId], returning a [FolderView] wrapped
|
||||
* in a [Result].
|
||||
|
|
|
@ -240,6 +240,17 @@ class VaultSdkSourceImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun encryptFolder(
|
||||
userId: String,
|
||||
folder: FolderView,
|
||||
): Result<Folder> =
|
||||
runCatching {
|
||||
getClient(userId = userId)
|
||||
.vault()
|
||||
.folders()
|
||||
.encrypt(folder)
|
||||
}
|
||||
|
||||
override suspend fun decryptFolder(
|
||||
userId: String,
|
||||
folder: Folder,
|
||||
|
|
|
@ -13,9 +13,11 @@ import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
|||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateAttachmentResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteAttachmentResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteFolderResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
|
@ -25,6 +27,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
|||
import com.x8bit.bitwarden.data.vault.repository.model.ShareCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateFolderResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
|
@ -283,4 +286,19 @@ interface VaultRepository : VaultLockManager {
|
|||
attachmentId: String,
|
||||
cipherView: CipherView,
|
||||
): DeleteAttachmentResult
|
||||
|
||||
/**
|
||||
* Attempt to create a folder.
|
||||
*/
|
||||
suspend fun createFolder(folderView: FolderView): CreateFolderResult
|
||||
|
||||
/**
|
||||
* Attempt to delete a folder.
|
||||
*/
|
||||
suspend fun deleteFolder(folderId: String): DeleteFolderResult
|
||||
|
||||
/**
|
||||
* Attempt to update a folder.
|
||||
*/
|
||||
suspend fun updateFolder(folderId: String, folderView: FolderView): UpdateFolderResult
|
||||
}
|
||||
|
|
|
@ -31,8 +31,10 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonReq
|
|||
import com.x8bit.bitwarden.data.vault.datasource.network.model.ShareCipherJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateCipherResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateFolderResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateSendResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.CiphersService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.FolderService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SendsService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
|
@ -42,9 +44,11 @@ import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
|||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateAttachmentResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteAttachmentResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteFolderResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
|
@ -54,6 +58,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
|||
import com.x8bit.bitwarden.data.vault.repository.model.ShareCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateFolderResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
|
@ -61,10 +66,12 @@ import com.x8bit.bitwarden.data.vault.repository.util.sortAlphabetically
|
|||
import com.x8bit.bitwarden.data.vault.repository.util.toDomainsData
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkCipher
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkCipherResponse
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkFolder
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkSend
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipherList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCollectionList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolder
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolderList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkSend
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkSendList
|
||||
|
@ -80,6 +87,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
@ -107,6 +115,7 @@ class VaultRepositoryImpl(
|
|||
private val syncService: SyncService,
|
||||
private val ciphersService: CiphersService,
|
||||
private val sendsService: SendsService,
|
||||
private val folderService: FolderService,
|
||||
private val vaultDiskSource: VaultDiskSource,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
|
@ -917,6 +926,95 @@ class VaultRepositoryImpl(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun createFolder(folderView: FolderView): CreateFolderResult {
|
||||
val userId = activeUserId ?: return CreateFolderResult.Error
|
||||
return vaultSdkSource
|
||||
.encryptFolder(
|
||||
userId = userId,
|
||||
folder = folderView,
|
||||
)
|
||||
.flatMap { folder ->
|
||||
folderService
|
||||
.createFolder(
|
||||
body = folder.toEncryptedNetworkFolder(),
|
||||
)
|
||||
}
|
||||
.onSuccess { vaultDiskSource.saveFolder(userId = userId, folder = it) }
|
||||
.flatMap { vaultSdkSource.decryptFolder(userId, it.toEncryptedSdkFolder()) }
|
||||
.fold(
|
||||
onSuccess = { CreateFolderResult.Success(folderView = it) },
|
||||
onFailure = { CreateFolderResult.Error },
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun updateFolder(
|
||||
folderId: String,
|
||||
folderView: FolderView,
|
||||
): UpdateFolderResult {
|
||||
val userId = activeUserId ?: return UpdateFolderResult.Error(null)
|
||||
return vaultSdkSource
|
||||
.encryptFolder(
|
||||
userId = userId,
|
||||
folder = folderView,
|
||||
)
|
||||
.flatMap { folder ->
|
||||
folderService
|
||||
.updateFolder(
|
||||
folderId = folder.id.toString(),
|
||||
body = folder.toEncryptedNetworkFolder(),
|
||||
)
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { response ->
|
||||
when (response) {
|
||||
is UpdateFolderResponseJson.Success -> {
|
||||
vaultDiskSource.saveFolder(userId, response.folder)
|
||||
vaultSdkSource
|
||||
.decryptFolder(
|
||||
userId,
|
||||
response.folder.toEncryptedSdkFolder(),
|
||||
)
|
||||
.fold(
|
||||
onSuccess = { UpdateFolderResult.Success(it) },
|
||||
onFailure = { UpdateFolderResult.Error(errorMessage = null) },
|
||||
)
|
||||
}
|
||||
|
||||
is UpdateFolderResponseJson.Invalid -> {
|
||||
UpdateFolderResult.Error(response.message)
|
||||
}
|
||||
}
|
||||
},
|
||||
onFailure = { UpdateFolderResult.Error(it.message) },
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun deleteFolder(folderId: String): DeleteFolderResult {
|
||||
val userId = activeUserId ?: return DeleteFolderResult.Error
|
||||
return folderService
|
||||
.deleteFolder(
|
||||
folderId = folderId,
|
||||
)
|
||||
.onSuccess {
|
||||
clearFolderIdFromCiphers(folderId, userId)
|
||||
vaultDiskSource.deleteFolder(userId, folderId)
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { DeleteFolderResult.Success },
|
||||
onFailure = { DeleteFolderResult.Error },
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun clearFolderIdFromCiphers(folderId: String, userId: String) {
|
||||
vaultDiskSource.getCiphers(userId).firstOrNull()?.forEach {
|
||||
if (it.folderId == folderId) {
|
||||
vaultDiskSource.saveCipher(
|
||||
userId, it.copy(folderId = null),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given [userId] has an associated encrypted PIN key but not a pin-protected user
|
||||
* key. This indicates a scenario in which a user has requested PIN unlocking but requires
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
|||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.CiphersService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.FolderService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SendsService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
|
@ -33,6 +34,7 @@ object VaultRepositoryModule {
|
|||
syncService: SyncService,
|
||||
sendsService: SendsService,
|
||||
ciphersService: CiphersService,
|
||||
folderService: FolderService,
|
||||
vaultDiskSource: VaultDiskSource,
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
authDiskSource: AuthDiskSource,
|
||||
|
@ -46,6 +48,7 @@ object VaultRepositoryModule {
|
|||
syncService = syncService,
|
||||
sendsService = sendsService,
|
||||
ciphersService = ciphersService,
|
||||
folderService = folderService,
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
authDiskSource = authDiskSource,
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.bitwarden.core.FolderView
|
||||
|
||||
/**
|
||||
* Models result of creating a folder.
|
||||
*/
|
||||
sealed class CreateFolderResult {
|
||||
|
||||
/**
|
||||
* Folder created successfully.
|
||||
*/
|
||||
data class Success(val folderView: FolderView) : CreateFolderResult()
|
||||
|
||||
/**
|
||||
* Generic error while creating a folder.
|
||||
*/
|
||||
data object Error : CreateFolderResult()
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
/**
|
||||
* Models result of deleting a folder.
|
||||
*/
|
||||
sealed class DeleteFolderResult {
|
||||
|
||||
/**
|
||||
* Folder deleted successfully.
|
||||
*/
|
||||
data object Success : DeleteFolderResult()
|
||||
|
||||
/**
|
||||
* Generic error while deleting a folder.
|
||||
*/
|
||||
data object Error : DeleteFolderResult()
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.bitwarden.core.FolderView
|
||||
|
||||
/**
|
||||
* Models result of updating a folder.
|
||||
*/
|
||||
sealed class UpdateFolderResult {
|
||||
|
||||
/**
|
||||
* Folder updated successfully.
|
||||
*/
|
||||
data class Success(val folderView: FolderView) : UpdateFolderResult()
|
||||
|
||||
/**
|
||||
* Generic error while updating a folder. The optional [errorMessage]
|
||||
* may be displayed directly in the UI when present.
|
||||
*/
|
||||
data class Error(val errorMessage: String?) : UpdateFolderResult()
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.vault.repository.util
|
|||
|
||||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -29,3 +30,10 @@ fun SyncResponseJson.Folder.toEncryptedSdkFolder(): Folder =
|
|||
@JvmName("toAlphabeticallySortedFolderList")
|
||||
fun List<FolderView>.sortAlphabetically(): List<FolderView> =
|
||||
this.sortedBy { it.name.uppercase(Locale.getDefault()) }
|
||||
|
||||
/**
|
||||
* Converts a Bitwarden SDK [Folder] objects to a corresponding
|
||||
* [SyncResponseJson.Folder] object.
|
||||
*/
|
||||
fun Folder.toEncryptedNetworkFolder(): FolderJsonRequest =
|
||||
FolderJsonRequest(name = name)
|
||||
|
|
|
@ -138,6 +138,18 @@ class VaultDiskSourceTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DeleteFolder should call deleteFolder`() = runTest {
|
||||
assertFalse(foldersDao.deleteFolderCalled)
|
||||
vaultDiskSource.saveFolder(USER_ID, FOLDER_1)
|
||||
assertEquals(1, foldersDao.storedFolders.size)
|
||||
|
||||
vaultDiskSource.deleteFolder(USER_ID, FOLDER_1.id)
|
||||
|
||||
assertTrue(foldersDao.deleteFolderCalled)
|
||||
assertEquals(emptyList<FolderEntity>(), foldersDao.storedFolders)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `saveFolder should call insertFolder`() = runTest {
|
||||
assertFalse(foldersDao.insertFolderCalled)
|
||||
|
|
|
@ -581,6 +581,34 @@ class VaultSdkSourceTest {
|
|||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `encryptFolder should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val userId = "userId"
|
||||
val expectedResult = mockk<Folder>()
|
||||
val mockFolder = mockk<FolderView>()
|
||||
coEvery {
|
||||
clientVault.folders().encrypt(
|
||||
folder = mockFolder,
|
||||
)
|
||||
} returns expectedResult
|
||||
|
||||
val result = vaultSdkSource.encryptFolder(
|
||||
userId = userId,
|
||||
folder = mockFolder,
|
||||
)
|
||||
assertEquals(
|
||||
expectedResult.asSuccess(),
|
||||
result,
|
||||
)
|
||||
|
||||
coVerify {
|
||||
clientVault.folders().encrypt(
|
||||
folder = mockFolder,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Folder decrypt should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val userId = "userId"
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.bitwarden.core.Cipher
|
|||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.CollectionView
|
||||
import com.bitwarden.core.DateTime
|
||||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.core.InitOrgCryptoRequest
|
||||
import com.bitwarden.core.InitUserCryptoMethod
|
||||
|
@ -29,11 +30,13 @@ import com.x8bit.bitwarden.data.platform.util.asSuccess
|
|||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FileUploadType
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendFileResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.ShareCipherJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateCipherResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateFolderResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateSendResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockAttachmentEncryptResult
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockAttachmentJsonResponse
|
||||
|
@ -48,6 +51,7 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSend
|
|||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSendJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSyncResponse
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.CiphersService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.FolderService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SendsService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
|
@ -67,9 +71,11 @@ import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
|||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateAttachmentResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteAttachmentResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteFolderResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
|
@ -78,6 +84,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.RestoreCipherResult
|
|||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ShareCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateFolderResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
|
||||
|
@ -88,6 +95,7 @@ import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkCipherRe
|
|||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipherList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCollectionList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolder
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolderList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkSendList
|
||||
import com.x8bit.bitwarden.ui.vault.feature.verificationcode.util.createVerificationCodeItem
|
||||
|
@ -113,6 +121,7 @@ import java.net.UnknownHostException
|
|||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
@Suppress("LargeClass")
|
||||
|
@ -128,6 +137,7 @@ class VaultRepositoryTest {
|
|||
private val syncService: SyncService = mockk()
|
||||
private val sendsService: SendsService = mockk()
|
||||
private val ciphersService: CiphersService = mockk()
|
||||
private val folderService: FolderService = mockk()
|
||||
private val vaultDiskSource: VaultDiskSource = mockk()
|
||||
private val totpCodeManager: TotpCodeManager = mockk()
|
||||
private val vaultSdkSource: VaultSdkSource = mockk {
|
||||
|
@ -151,6 +161,7 @@ class VaultRepositoryTest {
|
|||
syncService = syncService,
|
||||
sendsService = sendsService,
|
||||
ciphersService = ciphersService,
|
||||
folderService = folderService,
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
|
@ -3137,6 +3148,375 @@ class VaultRepositoryTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deleteFolder with no active user should return DeleteFolderResult failure`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.deleteFolder("Test")
|
||||
|
||||
assertEquals(
|
||||
DeleteFolderResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `DeleteFolder with folderService Delete failure should return DeleteFolderResult Failure`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val folderId = "mockId-1"
|
||||
coEvery { folderService.deleteFolder(folderId) } returns Throwable("fail").asFailure()
|
||||
|
||||
val result = vaultRepository.deleteFolder(folderId)
|
||||
assertEquals(DeleteFolderResult.Error, result)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `DeleteFolder with folderService Delete success should return DeleteFolderResult Success and update ciphers`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val folderId = "mockFolderId-1"
|
||||
coEvery { folderService.deleteFolder(folderId) } returns Unit.asSuccess()
|
||||
coEvery {
|
||||
vaultDiskSource.deleteFolder(
|
||||
MOCK_USER_STATE.activeUserId,
|
||||
folderId,
|
||||
)
|
||||
} just runs
|
||||
|
||||
val mockCipher = createMockCipher(1)
|
||||
|
||||
val mutableCiphersStateFlow =
|
||||
MutableStateFlow(
|
||||
listOf(
|
||||
mockCipher,
|
||||
createMockCipher(2),
|
||||
),
|
||||
)
|
||||
|
||||
coEvery {
|
||||
vaultDiskSource.getCiphers(MOCK_USER_STATE.activeUserId)
|
||||
} returns mutableCiphersStateFlow
|
||||
|
||||
coEvery {
|
||||
vaultDiskSource.saveCipher(
|
||||
MOCK_USER_STATE.activeUserId,
|
||||
mockCipher.copy(
|
||||
folderId = null,
|
||||
),
|
||||
)
|
||||
} just runs
|
||||
|
||||
val result = vaultRepository.deleteFolder(folderId)
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
vaultDiskSource.saveCipher(
|
||||
MOCK_USER_STATE.activeUserId,
|
||||
mockCipher.copy(
|
||||
folderId = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(DeleteFolderResult.Success, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createFolder with no active user should return CreateFolderResult failure`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.createFolder(mockk())
|
||||
|
||||
assertEquals(
|
||||
CreateFolderResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `createFolder with folderService Delete failure should return DeleteFolderResult Failure`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val folderId = "mockId-1"
|
||||
coEvery { folderService.deleteFolder(folderId) } returns Throwable("fail").asFailure()
|
||||
|
||||
val result = vaultRepository.deleteFolder(folderId)
|
||||
assertEquals(DeleteFolderResult.Error, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createFolder with encryptFolder failure should return CreateFolderResult failure`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val folderView = FolderView(
|
||||
id = null,
|
||||
name = "TestName",
|
||||
revisionDate = DateTime.now(),
|
||||
)
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.encryptFolder(
|
||||
userId = MOCK_USER_STATE.activeUserId,
|
||||
folder = folderView,
|
||||
)
|
||||
} returns IllegalStateException().asFailure()
|
||||
|
||||
val result = vaultRepository.createFolder(folderView)
|
||||
assertEquals(CreateFolderResult.Error, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createFolder with folderService failure should return CreateFolderResult failure`() =
|
||||
runTest {
|
||||
val date = DateTime.now()
|
||||
val testFolderName = "TestName"
|
||||
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val folderView = FolderView(
|
||||
id = null,
|
||||
name = testFolderName,
|
||||
revisionDate = date,
|
||||
)
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.encryptFolder(
|
||||
userId = MOCK_USER_STATE.activeUserId,
|
||||
folder = folderView,
|
||||
)
|
||||
} returns Folder(id = null, name = testFolderName, revisionDate = date).asSuccess()
|
||||
|
||||
coEvery {
|
||||
folderService.createFolder(
|
||||
body = FolderJsonRequest(testFolderName),
|
||||
)
|
||||
} returns IllegalStateException().asFailure()
|
||||
|
||||
val result = vaultRepository.createFolder(folderView)
|
||||
assertEquals(CreateFolderResult.Error, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createFolder with folderService createFolder should return CreateFolderResult success`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val date = DateTime.now()
|
||||
val testFolderName = "TestName"
|
||||
|
||||
val folderView = FolderView(
|
||||
id = null,
|
||||
name = testFolderName,
|
||||
revisionDate = date,
|
||||
)
|
||||
|
||||
val networkFolder = SyncResponseJson.Folder(
|
||||
id = "1",
|
||||
name = testFolderName,
|
||||
revisionDate = ZonedDateTime.now(),
|
||||
)
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.encryptFolder(
|
||||
userId = MOCK_USER_STATE.activeUserId,
|
||||
folder = folderView,
|
||||
)
|
||||
} returns Folder(id = null, name = testFolderName, revisionDate = date).asSuccess()
|
||||
|
||||
coEvery {
|
||||
folderService.createFolder(
|
||||
body = FolderJsonRequest(testFolderName),
|
||||
)
|
||||
} returns networkFolder.asSuccess()
|
||||
|
||||
coEvery {
|
||||
vaultDiskSource.saveFolder(
|
||||
MOCK_USER_STATE.activeUserId,
|
||||
networkFolder,
|
||||
)
|
||||
} just runs
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolder(
|
||||
MOCK_USER_STATE.activeUserId,
|
||||
networkFolder.toEncryptedSdkFolder(),
|
||||
)
|
||||
} returns folderView.asSuccess()
|
||||
|
||||
val result = vaultRepository.createFolder(folderView)
|
||||
assertEquals(CreateFolderResult.Success(folderView), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateFolder with no active user should return UpdateFolderResult failure`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.updateFolder("Test", mockk())
|
||||
|
||||
assertEquals(
|
||||
UpdateFolderResult.Error(null),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateFolder with encryptFolder failure should return UpdateFolderResult failure`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val folderId = "testId"
|
||||
val folderView = FolderView(
|
||||
id = folderId,
|
||||
name = "TestName",
|
||||
revisionDate = DateTime.now(),
|
||||
)
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.encryptFolder(
|
||||
userId = MOCK_USER_STATE.activeUserId,
|
||||
folder = folderView,
|
||||
)
|
||||
} returns IllegalStateException().asFailure()
|
||||
|
||||
val result = vaultRepository.updateFolder(folderId, folderView)
|
||||
|
||||
assertEquals(UpdateFolderResult.Error(errorMessage = null), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateFolder with folderService failure should return UpdateFolderResult failure`() =
|
||||
runTest {
|
||||
val date = DateTime.now()
|
||||
val testFolderName = "TestName"
|
||||
val folderId = "testId"
|
||||
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val folderView = FolderView(
|
||||
id = folderId,
|
||||
name = testFolderName,
|
||||
revisionDate = date,
|
||||
)
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.encryptFolder(
|
||||
userId = MOCK_USER_STATE.activeUserId,
|
||||
folder = folderView,
|
||||
)
|
||||
} returns Folder(id = folderId, name = testFolderName, revisionDate = date).asSuccess()
|
||||
|
||||
coEvery {
|
||||
folderService.updateFolder(
|
||||
folderId = folderId,
|
||||
body = FolderJsonRequest(testFolderName),
|
||||
)
|
||||
} returns IllegalStateException().asFailure()
|
||||
|
||||
val result = vaultRepository.updateFolder(folderId, folderView)
|
||||
assertEquals(UpdateFolderResult.Error(errorMessage = null), result)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `updateFolder with folderService updateFolder Invalid response should return UpdateFolderResult Error with a non-null message`() =
|
||||
runTest {
|
||||
val date = DateTime.now()
|
||||
val testFolderName = "TestName"
|
||||
val folderId = "testId"
|
||||
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val folderView = FolderView(
|
||||
id = folderId,
|
||||
name = testFolderName,
|
||||
revisionDate = date,
|
||||
)
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.encryptFolder(
|
||||
userId = MOCK_USER_STATE.activeUserId,
|
||||
folder = folderView,
|
||||
)
|
||||
} returns Folder(id = folderId, name = testFolderName, revisionDate = date).asSuccess()
|
||||
|
||||
coEvery {
|
||||
folderService.updateFolder(
|
||||
folderId = folderId,
|
||||
body = FolderJsonRequest(testFolderName),
|
||||
)
|
||||
} returns UpdateFolderResponseJson
|
||||
.Invalid(
|
||||
message = "You do not have permission to edit this.",
|
||||
validationErrors = null,
|
||||
)
|
||||
.asSuccess()
|
||||
|
||||
val result = vaultRepository.updateFolder(folderId, folderView)
|
||||
assertEquals(
|
||||
UpdateFolderResult.Error(
|
||||
errorMessage = "You do not have permission to edit this.",
|
||||
),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `updateFolder with folderService updateFolder success should return UpdateFolderResult success`() =
|
||||
runTest {
|
||||
val date = DateTime.now()
|
||||
val testFolderName = "TestName"
|
||||
val folderId = "testId"
|
||||
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
val folderView = FolderView(
|
||||
id = folderId,
|
||||
name = testFolderName,
|
||||
revisionDate = date,
|
||||
)
|
||||
|
||||
val networkFolder = SyncResponseJson.Folder(
|
||||
id = "1",
|
||||
name = testFolderName,
|
||||
revisionDate = ZonedDateTime.now(),
|
||||
)
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.encryptFolder(
|
||||
userId = MOCK_USER_STATE.activeUserId,
|
||||
folder = folderView,
|
||||
)
|
||||
} returns Folder(id = folderId, name = testFolderName, revisionDate = date).asSuccess()
|
||||
|
||||
coEvery {
|
||||
folderService.updateFolder(
|
||||
folderId = folderId,
|
||||
body = FolderJsonRequest(testFolderName),
|
||||
)
|
||||
} returns UpdateFolderResponseJson
|
||||
.Success(folder = networkFolder)
|
||||
.asSuccess()
|
||||
|
||||
coEvery {
|
||||
vaultDiskSource.saveFolder(
|
||||
MOCK_USER_STATE.activeUserId,
|
||||
networkFolder,
|
||||
)
|
||||
} just runs
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolder(
|
||||
MOCK_USER_STATE.activeUserId,
|
||||
networkFolder.toEncryptedSdkFolder(),
|
||||
)
|
||||
} returns folderView.asSuccess()
|
||||
|
||||
val result = vaultRepository.updateFolder(folderId, folderView)
|
||||
assertEquals(UpdateFolderResult.Success(folderView), result)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getAuthCodeFlow with no active user should emit an error`() = runTest {
|
||||
|
@ -3169,7 +3549,10 @@ class VaultRepositoryTest {
|
|||
} just runs
|
||||
|
||||
every {
|
||||
settingsDiskSource.storeLastSyncTime(MOCK_USER_STATE.activeUserId, clock.instant())
|
||||
settingsDiskSource.storeLastSyncTime(
|
||||
MOCK_USER_STATE.activeUserId,
|
||||
clock.instant(),
|
||||
)
|
||||
} just runs
|
||||
|
||||
val stateFlow = MutableStateFlow<DataState<VerificationCodeItem?>>(
|
||||
|
@ -3233,7 +3616,10 @@ class VaultRepositoryTest {
|
|||
)
|
||||
} just runs
|
||||
every {
|
||||
settingsDiskSource.storeLastSyncTime(MOCK_USER_STATE.activeUserId, clock.instant())
|
||||
settingsDiskSource.storeLastSyncTime(
|
||||
MOCK_USER_STATE.activeUserId,
|
||||
clock.instant(),
|
||||
)
|
||||
} just runs
|
||||
|
||||
val stateFlow = MutableStateFlow<DataState<List<VerificationCodeItem>>>(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.util
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFolder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFolder
|
||||
|
@ -58,4 +59,14 @@ class VaultSdkFolderExtensionsTest {
|
|||
list.sortAlphabetically(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toEncryptedNetworkFolder should convert a SdkFolder to a NetworkFolder`() {
|
||||
val sdkFolder = createMockSdkFolder(number = 1)
|
||||
val syncFolder = sdkFolder.toEncryptedNetworkFolder()
|
||||
assertEquals(
|
||||
FolderJsonRequest(sdkFolder.name),
|
||||
syncFolder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue