From b20eece3aab1321674d0eef471feee760a83a08b Mon Sep 17 00:00:00 2001 From: David Perez Date: Mon, 17 Mar 2025 12:47:07 -0500 Subject: [PATCH] PM-19283: Propagate error from email token and known device flows (#4879) --- .../auth/repository/AuthRepositoryImpl.kt | 10 ++--- .../auth/repository/model/EmailTokenResult.kt | 5 ++- .../repository/model/KnownDeviceResult.kt | 4 +- .../com/x8bit/bitwarden/MainViewModelTest.kt | 14 ++----- .../auth/repository/AuthRepositoryTest.kt | 38 +++++++++---------- .../auth/feature/login/LoginViewModelTest.kt | 2 +- 6 files changed, 35 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt index ae4e264e83..602e6b0c04 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt @@ -1218,8 +1218,8 @@ class AuthRepositoryImpl( deviceId = authDiskSource.uniqueAppId, ) .fold( - onFailure = { KnownDeviceResult.Error }, - onSuccess = { KnownDeviceResult.Success(it) }, + onFailure = { KnownDeviceResult.Error(error = it) }, + onSuccess = { KnownDeviceResult.Success(isKnownDevice = it) }, ) override suspend fun getPasswordBreachCount(password: String): BreachCountResult = @@ -1362,15 +1362,13 @@ class AuthRepositoryImpl( when (val json = it) { VerifyEmailTokenResponseJson.Valid -> EmailTokenResult.Success is VerifyEmailTokenResponseJson.Invalid -> { - EmailTokenResult.Error(json.message) + EmailTokenResult.Error(message = json.message, error = null) } VerifyEmailTokenResponseJson.TokenExpired -> EmailTokenResult.Expired } }, - onFailure = { - EmailTokenResult.Error(message = null) - }, + onFailure = { EmailTokenResult.Error(message = null, error = it) }, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/EmailTokenResult.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/EmailTokenResult.kt index 3989045367..df219cc4a3 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/EmailTokenResult.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/EmailTokenResult.kt @@ -18,5 +18,8 @@ sealed class EmailTokenResult { /** * There was an error validating the token. */ - data class Error(val message: String?) : EmailTokenResult() + data class Error( + val message: String?, + val error: Throwable?, + ) : EmailTokenResult() } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/KnownDeviceResult.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/KnownDeviceResult.kt index 05ae8796ea..5613f8021b 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/KnownDeviceResult.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/KnownDeviceResult.kt @@ -12,5 +12,7 @@ sealed class KnownDeviceResult { /** * There was an error determining if this is a known device. */ - data object Error : KnownDeviceResult() + data class Error( + val error: Throwable, + ) : KnownDeviceResult() } diff --git a/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt index 8892070009..772854e647 100644 --- a/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt @@ -558,11 +558,8 @@ class MainViewModelTest : BaseViewModelTest() { ) every { authRepository.activeUserId } returns null coEvery { - authRepository.validateEmailToken( - intentEmail, - token, - ) - } returns EmailTokenResult.Error(message = null) + authRepository.validateEmailToken(email = intentEmail, token = token) + } returns EmailTokenResult.Error(message = null, error = Throwable("Fail!")) viewModel.eventFlow.test { // We skip the first 2 events because they are the default appTheme and appLanguage @@ -595,11 +592,8 @@ class MainViewModelTest : BaseViewModelTest() { val expectedMessage = "expectedMessage" coEvery { - authRepository.validateEmailToken( - intentEmail, - token, - ) - } returns EmailTokenResult.Error(message = expectedMessage) + authRepository.validateEmailToken(email = intentEmail, token = token) + } returns EmailTokenResult.Error(message = expectedMessage, error = null) viewModel.eventFlow.test { // We skip the first 2 events because they are the default appTheme and appLanguage diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index d5cb87e824..527d352eff 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -5662,16 +5662,17 @@ class AuthRepositoryTest { @Test fun `getIsKnownDevice should return failure when service returns failure`() = runTest { + val error = Throwable("Fail!") coEvery { devicesService.getIsKnownDevice(EMAIL, UNIQUE_APP_ID) - } returns Throwable("Fail").asFailure() + } returns error.asFailure() val result = repository.getIsKnownDevice(EMAIL) coVerify(exactly = 1) { devicesService.getIsKnownDevice(EMAIL, UNIQUE_APP_ID) } - assertEquals(KnownDeviceResult.Error, result) + assertEquals(KnownDeviceResult.Error(error = error), result) } @Test @@ -6286,39 +6287,38 @@ class AuthRepositoryTest { runTest { val errorMessage = "I haven't heard of second breakfast." coEvery { - identityService - .verifyEmailRegistrationToken( - body = VerifyEmailTokenRequestJson( - email = EMAIL, - token = EMAIL_VERIFICATION_TOKEN, - ), - ) + identityService.verifyEmailRegistrationToken( + body = VerifyEmailTokenRequestJson( + email = EMAIL, + token = EMAIL_VERIFICATION_TOKEN, + ), + ) } returns VerifyEmailTokenResponseJson.Invalid(message = errorMessage).asSuccess() val emailTokenResult = repository.validateEmailToken(EMAIL, EMAIL_VERIFICATION_TOKEN) assertEquals( - EmailTokenResult.Error(message = errorMessage), + EmailTokenResult.Error(message = errorMessage, error = null), emailTokenResult, ) } @Test fun `validateEmailToken should return error result when service returns failure`() = runTest { + val error = Throwable("Fail!") coEvery { - identityService - .verifyEmailRegistrationToken( - body = VerifyEmailTokenRequestJson( - email = EMAIL, - token = EMAIL_VERIFICATION_TOKEN, - ), - ) - } returns Exception().asFailure() + identityService.verifyEmailRegistrationToken( + body = VerifyEmailTokenRequestJson( + email = EMAIL, + token = EMAIL_VERIFICATION_TOKEN, + ), + ) + } returns error.asFailure() val emailTokenResult = repository.validateEmailToken(EMAIL, EMAIL_VERIFICATION_TOKEN) assertEquals( - EmailTokenResult.Error(message = null), + EmailTokenResult.Error(message = null, error = error), emailTokenResult, ) } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt index 647e5d154b..514767f2ad 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt @@ -177,7 +177,7 @@ class LoginViewModelTest : BaseViewModelTest() { fun `should have default state when isKnownDevice returns error`() = runTest { coEvery { authRepository.getIsKnownDevice(EMAIL) - } returns KnownDeviceResult.Error + } returns KnownDeviceResult.Error(error = Throwable("Fail!")) val viewModel = createViewModel() viewModel.stateFlow.test {