PM-19289 propagating remaining vault result errors. (#4881)

This commit is contained in:
Dave Severns authored and GitHub committed 2025-03-18 13:51:30 +00:00
1 parent ad8d8d271a
commit ef3b7730d0
21 files changed
+74 -55

No files matched your search

@@ -267,7 +267,7 @@ class Fido2ProviderProcessorImpl(
val result = vaultRepository
.getDecryptedFido2CredentialAutofillViews(cipherViews)
return when (result) {
DecryptFido2CredentialAutofillViewResult.Error -> {
is DecryptFido2CredentialAutofillViewResult.Error -> {
throw GetCredentialUnknownException("Error decrypting credentials.")
}
@@ -411,7 +411,7 @@ class CipherManagerImpl(
)
.fold(
onSuccess = { DownloadAttachmentResult.Success(file = it) },
onFailure = { DownloadAttachmentResult.Failure },
onFailure = { DownloadAttachmentResult.Failure(error = it) },
)
private suspend fun downloadAttachmentForResult(
@@ -511,11 +511,13 @@ class VaultRepositoryImpl(
): DecryptFido2CredentialAutofillViewResult {
return vaultSdkSource
.decryptFido2CredentialAutofillViews(
userId = activeUserId ?: return DecryptFido2CredentialAutofillViewResult.Error,
userId = activeUserId ?: return DecryptFido2CredentialAutofillViewResult.Error(
error = NoActiveUserException(),
),
cipherViews = cipherViewList.toTypedArray(),
)
.fold(
onFailure = { DecryptFido2CredentialAutofillViewResult.Error },
onFailure = { DecryptFido2CredentialAutofillViewResult.Error(error = it) },
onSuccess = { DecryptFido2CredentialAutofillViewResult.Success(it) },
)
}
@@ -780,7 +782,8 @@ class VaultRepositoryImpl(
totpCode: String,
time: DateTime,
): GenerateTotpResult {
val userId = activeUserId ?: return GenerateTotpResult.Error
val userId = activeUserId
?: return GenerateTotpResult.Error(error = NoActiveUserException())
return vaultSdkSource.generateTotp(
time = time,
userId = userId,
@@ -793,7 +796,7 @@ class VaultRepositoryImpl(
periodSeconds = it.period.toInt(),
)
},
onFailure = { GenerateTotpResult.Error },
onFailure = { GenerateTotpResult.Error(error = it) },
)
}
@@ -900,7 +903,8 @@ class VaultRepositoryImpl(
}
override suspend fun exportVaultDataToString(format: ExportFormat): ExportVaultDataResult {
val userId = activeUserId ?: return ExportVaultDataResult.Error
val userId = activeUserId
?: return ExportVaultDataResult.Error(error = NoActiveUserException())
val folders = vaultDiskSource
.getFolders(userId)
.firstOrNull()
@@ -923,7 +927,7 @@ class VaultRepositoryImpl(
)
.fold(
onSuccess = { ExportVaultDataResult.Success(it) },
onFailure = { ExportVaultDataResult.Error },
onFailure = { ExportVaultDataResult.Error(error = it) },
)
}
@@ -16,5 +16,5 @@ sealed class DecryptFido2CredentialAutofillViewResult {
/**
* Generic error while decrypting credentials.
*/
data object Error : DecryptFido2CredentialAutofillViewResult()
data class Error(val error: Throwable) : DecryptFido2CredentialAutofillViewResult()
}
@@ -14,5 +14,5 @@ sealed class DownloadAttachmentResult {
/**
* The attachment could not be downloaded.
*/
data object Failure : DownloadAttachmentResult()
data class Failure(val error: Throwable) : DownloadAttachmentResult()
}
@@ -14,5 +14,5 @@ sealed class ExportVaultDataResult {
/**
* There was an error converting the vault data.
*/
data object Error : ExportVaultDataResult()
data class Error(val error: Throwable) : ExportVaultDataResult()
}
@@ -16,5 +16,5 @@ sealed class GenerateTotpResult {
/**
* An error occurred while generating the code.
*/
data object Error : GenerateTotpResult()
data class Error(val error: Throwable) : GenerateTotpResult()
}
@@ -13,5 +13,5 @@ sealed class TotpCodeResult {
/**
* There was an error scanning the code.
*/
data object CodeScanningError : TotpCodeResult()
data class CodeScanningError(val error: Throwable? = null) : TotpCodeResult()
}
@@ -334,6 +334,7 @@ class ExportVaultViewModel @Inject constructor(
is ExportVaultDataResult.Error -> {
updateStateWithError(
message = R.string.export_vault_failure.asText(),
error = result.error,
)
}
@@ -1742,7 +1742,7 @@ class VaultAddEditViewModel @Inject constructor(
}
private fun handleVaultTotpCodeReceive(action: VaultAddEditAction.Internal.TotpCodeReceive) {
when (action.totpResult) {
when (val result = action.totpResult) {
is TotpCodeResult.Success -> {
sendEvent(
event = VaultAddEditEvent.ShowToast(
@@ -1751,15 +1751,16 @@ class VaultAddEditViewModel @Inject constructor(
)
updateLoginContent { loginType ->
loginType.copy(totp = action.totpResult.code)
loginType.copy(totp = result.code)
}
}
TotpCodeResult.CodeScanningError -> {
is TotpCodeResult.CodeScanningError -> {
showDialog(
dialogState = VaultAddEditState.DialogState.Generic(
title = R.string.an_error_has_occurred.asText(),
message = R.string.authenticator_key_read_error.asText(),
error = result.error,
),
)
}
@@ -1295,10 +1295,11 @@ class VaultItemViewModel @Inject constructor(
action: VaultItemAction.Internal.AttachmentDecryptReceive,
) {
when (val result = action.result) {
DownloadAttachmentResult.Failure -> {
is DownloadAttachmentResult.Failure -> {
updateDialogState(
VaultItemState.DialogState.Generic(
message = R.string.unable_to_download_file.asText(),
error = result.error,
),
)
}
@@ -51,7 +51,7 @@ class QrCodeScanViewModel @Inject constructor(
val scannedCode = action.qrCode
if (scannedCode.isBlank() || !scannedCode.startsWith(TOTP_CODE_PREFIX)) {
vaultRepository.emitTotpCodeResult(TotpCodeResult.CodeScanningError)
vaultRepository.emitTotpCodeResult(TotpCodeResult.CodeScanningError())
sendEvent(QrCodeScanEvent.NavigateBack)
return
}
@@ -59,14 +59,14 @@ class QrCodeScanViewModel @Inject constructor(
val scannedCodeUri = Uri.parse(scannedCode)
val secretValue = scannedCodeUri.getQueryParameter(SECRET)
if (secretValue == null || !secretValue.isBase32()) {
vaultRepository.emitTotpCodeResult(TotpCodeResult.CodeScanningError)
vaultRepository.emitTotpCodeResult(TotpCodeResult.CodeScanningError())
sendEvent(QrCodeScanEvent.NavigateBack)
return
}
val values = scannedCodeUri.queryParameterNames
if (!areParametersValid(scannedCode, values)) {
result = TotpCodeResult.CodeScanningError
result = TotpCodeResult.CodeScanningError()
}
vaultRepository.emitTotpCodeResult(result)
@@ -557,7 +557,7 @@ class Fido2ProviderProcessorTest {
every { callback.onError(capture(captureSlot)) } just runs
coEvery {
vaultRepository.getDecryptedFido2CredentialAutofillViews(any())
} returns DecryptFido2CredentialAutofillViewResult.Error
} returns DecryptFido2CredentialAutofillViewResult.Error(error = Throwable("Ooops"))
coEvery {
vaultRepository.silentlyDiscoverCredentials(
userId = DEFAULT_USER_STATE.activeUserId,
@@ -100,6 +100,10 @@ class CipherManagerTest {
every {
anyConstructed<NoActiveUserException>() == any<NoActiveUserException>()
} returns true
mockkConstructor(IllegalStateException::class)
every {
anyConstructed<IllegalStateException>() == any<IllegalStateException>()
} returns true
}
@AfterEach
@@ -107,6 +111,7 @@ class CipherManagerTest {
unmockkStatic(Uri::class, Instant::class)
unmockkStatic(Cipher::toEncryptedNetworkCipherResponse)
unmockkConstructor(NoActiveUserException::class)
unmockkConstructor(IllegalStateException::class)
}
@Test
@@ -1714,7 +1719,7 @@ class CipherManagerTest {
} returns cipher.asSuccess()
assertEquals(
DownloadAttachmentResult.Failure,
DownloadAttachmentResult.Failure(IllegalStateException()),
cipherManager.downloadAttachment(
cipherView = cipherView,
attachmentId = attachmentId,
@@ -1752,13 +1757,14 @@ class CipherManagerTest {
cipherView = cipherView,
)
} returns cipher.asSuccess()
val error = Throwable()
coEvery {
ciphersService.getCipherAttachment(cipherId = any(), attachmentId = any())
} returns Throwable().asFailure()
} returns error.asFailure()
assertEquals(
DownloadAttachmentResult.Failure,
DownloadAttachmentResult.Failure(error = error),
cipherManager.downloadAttachment(
cipherView = cipherView,
attachmentId = attachmentId,
@@ -1805,7 +1811,7 @@ class CipherManagerTest {
} returns response.asSuccess()
assertEquals(
DownloadAttachmentResult.Failure,
DownloadAttachmentResult.Failure(error = IllegalStateException()),
cipherManager.downloadAttachment(
cipherView = cipherView,
attachmentId = attachmentId,
@@ -1857,7 +1863,7 @@ class CipherManagerTest {
} returns DownloadResult.Failure
assertEquals(
DownloadAttachmentResult.Failure,
DownloadAttachmentResult.Failure(IllegalStateException()),
cipherManager.downloadAttachment(
cipherView = cipherView,
attachmentId = attachmentId,
@@ -1912,7 +1918,7 @@ class CipherManagerTest {
coEvery {
fileManager.downloadFileToCache(url = any())
} returns DownloadResult.Success(file)
val error = Throwable("Fail")
coEvery {
vaultSdkSource.decryptFile(
userId = MOCK_USER_STATE.activeUserId,
@@ -1921,10 +1927,10 @@ class CipherManagerTest {
encryptedFilePath = "path/to/encrypted/file",
decryptedFilePath = "path/to/encrypted/file_decrypted",
)
} returns Throwable().asFailure()
} returns error.asFailure()
assertEquals(
DownloadAttachmentResult.Failure,
DownloadAttachmentResult.Failure(error = error),
cipherManager.downloadAttachment(
cipherView = cipherView,
attachmentId = attachmentId,
@@ -2707,7 +2707,7 @@ class VaultRepositoryTest {
)
assertEquals(
GenerateTotpResult.Error,
GenerateTotpResult.Error(error = NoActiveUserException()),
result,
)
}
@@ -4410,12 +4410,12 @@ class VaultRepositoryTest {
coEvery {
vaultDiskSource.getFolders(userId)
} returns flowOf(listOf(createMockFolder(1)))
val error = Throwable("Fail")
coEvery {
vaultSdkSource.exportVaultDataToString(userId, any(), any(), format)
} returns Throwable("Fail").asFailure()
} returns error.asFailure()
val expected = ExportVaultDataResult.Error
val expected = ExportVaultDataResult.Error(error = error)
val result = vaultRepository.exportVaultDataToString(format = format)
assertEquals(
@@ -4429,7 +4429,9 @@ class VaultRepositoryTest {
runTest {
fakeAuthDiskSource.userState = null
val expected = DecryptFido2CredentialAutofillViewResult.Error
val expected = DecryptFido2CredentialAutofillViewResult.Error(
error = NoActiveUserException(),
)
val result = vaultRepository
.getDecryptedFido2CredentialAutofillViews(
cipherViewList = listOf(createMockCipherView(number = 1)),
@@ -4449,18 +4451,19 @@ class VaultRepositoryTest {
runTest {
fakeAuthDiskSource.userState = MOCK_USER_STATE
val cipherViewList = listOf(createMockCipherView(number = 1))
val error = Throwable()
coEvery {
vaultSdkSource.decryptFido2CredentialAutofillViews(
userId = MOCK_USER_STATE.activeUserId,
cipherViews = cipherViewList.toTypedArray(),
)
} returns Throwable().asFailure()
} returns error.asFailure()
val result = vaultRepository.getDecryptedFido2CredentialAutofillViews(
cipherViewList = cipherViewList,
)
assertEquals(DecryptFido2CredentialAutofillViewResult.Error, result)
assertEquals(DecryptFido2CredentialAutofillViewResult.Error(error = error), result)
coVerify {
vaultSdkSource.decryptFido2CredentialAutofillViews(
userId = MOCK_USER_STATE.activeUserId,
@@ -918,10 +918,10 @@ class SearchViewModelTest : BaseViewModelTest() {
fun `OverflowOptionClick Vault CopyTotpClick with GenerateTotpCode failure should not call setText on the ClipboardManager`() =
runTest {
val totpCode = "totpCode"
val error = Throwable("Fail")
coEvery {
vaultRepository.generateTotp(totpCode, clock.instant())
} returns GenerateTotpResult.Error
} returns GenerateTotpResult.Error(error = error)
val viewModel = createViewModel()
viewModel.trySendAction(
@@ -538,10 +538,10 @@ class ExportVaultViewModelTest : BaseViewModelTest() {
@Test
fun `ReceiveExportVaultDataToStringResult should update state to error if result is error`() {
val viewModel = createViewModel()
val error = Throwable("Fail")
viewModel.trySendAction(
ExportVaultAction.Internal.ReceiveExportVaultDataToStringResult(
result = ExportVaultDataResult.Error,
result = ExportVaultDataResult.Error(error = error),
),
)
@@ -550,6 +550,7 @@ class ExportVaultViewModelTest : BaseViewModelTest() {
dialogState = ExportVaultState.DialogState.Error(
title = R.string.an_error_has_occurred.asText(),
message = R.string.export_vault_failure.asText(),
error = error,
),
),
viewModel.stateFlow.value,
@@ -1707,10 +1707,10 @@ class VaultItemViewModelTest : BaseViewModelTest() {
url = "https://example.com",
title = "test.mp4",
)
val error = Throwable("Fail")
coEvery {
vaultRepo.downloadAttachment(any(), any())
} returns DownloadAttachmentResult.Failure
} returns DownloadAttachmentResult.Failure(error = error)
viewModel.stateFlow.test {
assertEquals(
@@ -1731,6 +1731,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
loginState.copy(
dialog = VaultItemState.DialogState.Generic(
R.string.unable_to_download_file.asText(),
error = error,
),
),
awaitItem(),
@@ -139,6 +139,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
}
private val mutableVaultDataStateFlow =
MutableStateFlow<DataState<VaultData>>(DataState.Loading)
private val defaultError = Throwable("Fail")
private val vaultRepository: VaultRepository = mockk {
every { vaultFilterType } returns VaultFilterType.AllVaults
every { vaultDataStateFlow } returns mutableVaultDataStateFlow
@@ -146,7 +147,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
every { sync(forced = any()) } just runs
coEvery {
getDecryptedFido2CredentialAutofillViews(any())
} returns DecryptFido2CredentialAutofillViewResult.Error
} returns DecryptFido2CredentialAutofillViewResult.Error(error = defaultError)
}
private val environmentRepository: EnvironmentRepository = mockk {
every { environment } returns Environment.Us
@@ -422,7 +423,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
vaultRepository.getDecryptedFido2CredentialAutofillViews(
cipherViewList = listOf(cipherView),
)
} returns DecryptFido2CredentialAutofillViewResult.Error
} returns DecryptFido2CredentialAutofillViewResult.Error(error = defaultError)
specialCircumstanceManager.specialCircumstance = SpecialCircumstance.AutofillSelection(
autofillSelectionData = AutofillSelectionData(
type = AutofillSelectionData.Type.LOGIN,
@@ -469,7 +470,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
vaultRepository.getDecryptedFido2CredentialAutofillViews(
cipherViewList = listOf(cipherView),
)
} returns DecryptFido2CredentialAutofillViewResult.Error
} returns DecryptFido2CredentialAutofillViewResult.Error(error = defaultError)
specialCircumstanceManager.specialCircumstance =
SpecialCircumstance.AutofillSelection(
autofillSelectionData = AutofillSelectionData(
@@ -1491,7 +1492,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
coEvery {
vaultRepository.generateTotp(totpCode, clock.instant())
} returns GenerateTotpResult.Error
} returns GenerateTotpResult.Error(error = defaultError)
val viewModel = createVaultItemListingViewModel()
viewModel.trySendAction(
@@ -117,7 +117,7 @@ class QrCodeScanViewModelTest : BaseViewModelTest() {
setupMockUri(algorithm = "SHA-224")
val viewModel = createViewModel()
val result = TotpCodeResult.CodeScanningError
val result = TotpCodeResult.CodeScanningError()
val invalidCode = "otpauth://totp/Test:me?secret=JBSWY3dpeHPK3PXP&algorithm=sha224"
viewModel.eventFlow.test {
@@ -133,7 +133,7 @@ class QrCodeScanViewModelTest : BaseViewModelTest() {
setupMockUri(digits = "11")
val viewModel = createViewModel()
val result = TotpCodeResult.CodeScanningError
val result = TotpCodeResult.CodeScanningError()
val invalidCode = "otpauth://totp/Test:me?secret=JBSWY3dpeHPK3PXP&digits=11"
viewModel.eventFlow.test {
@@ -149,7 +149,7 @@ class QrCodeScanViewModelTest : BaseViewModelTest() {
setupMockUri(period = "0")
val viewModel = createViewModel()
val result = TotpCodeResult.CodeScanningError
val result = TotpCodeResult.CodeScanningError()
val invalidCode = "otpauth://totp/Test:me?secret=JBSWY3dpeHPK3PXP&period=0"
viewModel.eventFlow.test {
@@ -164,7 +164,7 @@ class QrCodeScanViewModelTest : BaseViewModelTest() {
fun `QrCodeScan should emit failure result without correct prefix`() = runTest {
val viewModel = createViewModel()
val result = TotpCodeResult.CodeScanningError
val result = TotpCodeResult.CodeScanningError()
val invalidCode = "nototpauth://totp/Test:me?secret=JBSWY3dpeHPK3PXP"
viewModel.eventFlow.test {
@@ -180,7 +180,7 @@ class QrCodeScanViewModelTest : BaseViewModelTest() {
setupMockUri(secret = "JBSWY3dpeHPK3PXP1")
val viewModel = createViewModel()
val result = TotpCodeResult.CodeScanningError
val result = TotpCodeResult.CodeScanningError()
val invalidCode = "otpauth://totp/Test:me?secret=JBSWY3dpeHPK3PXP1"
viewModel.eventFlow.test {
@@ -196,7 +196,7 @@ class QrCodeScanViewModelTest : BaseViewModelTest() {
setupMockUri(secret = null)
val viewModel = createViewModel()
val result = TotpCodeResult.CodeScanningError
val result = TotpCodeResult.CodeScanningError()
val invalidCode = "otpauth://totp/Test:me"
viewModel.eventFlow.test {
@@ -212,7 +212,7 @@ class QrCodeScanViewModelTest : BaseViewModelTest() {
setupMockUri(secret = "")
val viewModel = createViewModel()
val result = TotpCodeResult.CodeScanningError
val result = TotpCodeResult.CodeScanningError()
val invalidCode = "otpauth://totp/Test:me?secret= "
viewModel.eventFlow.test {
@@ -226,7 +226,7 @@ class QrCodeScanViewModelTest : BaseViewModelTest() {
@Test
fun `QrCodeScan should emit failure result and NavigateBack if code is empty`() = runTest {
val viewModel = createViewModel()
val result = TotpCodeResult.CodeScanningError
val result = TotpCodeResult.CodeScanningError()
val invalidCode = ""
viewModel.eventFlow.test {
@@ -1481,7 +1481,7 @@ class VaultViewModelTest : BaseViewModelTest() {
coEvery {
vaultRepository.generateTotp(totpCode, clock.instant())
} returns GenerateTotpResult.Error
} returns GenerateTotpResult.Error(error = Throwable("Fail"))
val viewModel = createViewModel()
viewModel.trySendAction(