diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt index 1e40afe8c..1a011a045 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt @@ -109,7 +109,7 @@ interface VaultRepository : CipherManager, VaultLockManager { * Unlike [syncIfNecessary], this will always perform the requested sync and should only be * utilized in cases where the user specifically requested the action. */ - fun sync() + fun sync(forced: Boolean = false) /** * Checks if conditions have been met to perform a sync request and, if so, syncs the vault diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt index 7c60bbcec..0c7edc190 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt @@ -345,7 +345,7 @@ class VaultRepositoryImpl( } } - override fun sync() { + override fun sync(forced: Boolean) { val userId = activeUserId ?: return if (!syncJob.isCompleted) return mutableCiphersStateFlow.updateToPendingOrLoading() @@ -353,7 +353,7 @@ class VaultRepositoryImpl( mutableFoldersStateFlow.updateToPendingOrLoading() mutableCollectionsStateFlow.updateToPendingOrLoading() mutableSendDataStateFlow.updateToPendingOrLoading() - syncJob = ioScope.launch { syncInternal(userId) } + syncJob = ioScope.launch { syncInternal(userId = userId, forced = forced) } } @Suppress("MagicNumber") @@ -374,10 +374,9 @@ class VaultRepositoryImpl( } override suspend fun syncForResult(): SyncVaultDataResult { - val userId = activeUserId - ?: return SyncVaultDataResult.Error(throwable = null) + val userId = activeUserId ?: return SyncVaultDataResult.Error(throwable = null) syncJob = ioScope - .async { syncInternal(userId) } + .async { syncInternal(userId = userId, forced = false) } .also { return try { it.await() @@ -1339,51 +1338,53 @@ class VaultRepositoryImpl( //endregion Push Notification helpers @Suppress("LongMethod") - private suspend fun syncInternal(userId: String): SyncVaultDataResult { - val lastSyncInstant = settingsDiskSource - .getLastSyncTime(userId = userId) - ?.toEpochMilli() - ?: 0 + private suspend fun syncInternal( + userId: String, + forced: Boolean, + ): SyncVaultDataResult { + if (!forced) { + // Skip this check if we are forcing the request. + val lastSyncInstant = settingsDiskSource + .getLastSyncTime(userId = userId) + ?.toEpochMilli() + ?: 0 + val lastDatabaseSchemeChangeInstant = databaseSchemeManager + .lastDatabaseSchemeChangeInstant + ?.toEpochMilli() + ?: 0 + syncService + .getAccountRevisionDateMillis() + .fold( + onSuccess = { serverRevisionDate -> + if (serverRevisionDate < lastSyncInstant && + lastDatabaseSchemeChangeInstant < lastSyncInstant + ) { + // We can skip the actual sync call if there is no new data or database + // scheme changes since the last sync. + vaultDiskSource.resyncVaultData(userId = userId) + settingsDiskSource.storeLastSyncTime( + userId = userId, + lastSyncTime = clock.instant(), + ) + val itemsAvailable = vaultDiskSource + .getCiphers(userId) + .firstOrNull() + ?.isNotEmpty() == true + return SyncVaultDataResult.Success(itemsAvailable = itemsAvailable) + } + }, + onFailure = { + updateVaultStateFlowsToError(throwable = it) + return SyncVaultDataResult.Error(throwable = it) + }, + ) + } - val lastDatabaseSchemeChangeInstant = databaseSchemeManager - .lastDatabaseSchemeChangeInstant - ?.toEpochMilli() - ?: 0 - - syncService - .getAccountRevisionDateMillis() - .fold( - onSuccess = { serverRevisionDate -> - if (serverRevisionDate < lastSyncInstant && - lastDatabaseSchemeChangeInstant < lastSyncInstant - ) { - // We can skip the actual sync call if there is no new data or database - // scheme changes since the last sync. - vaultDiskSource.resyncVaultData(userId) - settingsDiskSource.storeLastSyncTime( - userId = userId, - lastSyncTime = clock.instant(), - ) - val itemsAvailable = vaultDiskSource - .getCiphers(userId) - .firstOrNull() - ?.isNotEmpty() - ?: false - return SyncVaultDataResult.Success(itemsAvailable = itemsAvailable) - } - }, - onFailure = { - updateVaultStateFlowsToError(it) - return SyncVaultDataResult.Error(it) - }, - ) - - syncService + return syncService .sync() .fold( onSuccess = { syncResponse -> - val localSecurityStamp = - authDiskSource.userState?.activeAccount?.profile?.stamp + val localSecurityStamp = authDiskSource.userState?.activeAccount?.profile?.stamp val serverSecurityStamp = syncResponse.profile.securityStamp // Log the user out if the stamps do not match @@ -1395,11 +1396,9 @@ class VaultRepositoryImpl( } // Update user information with additional information from sync response - authDiskSource.userState = authDiskSource - .userState - ?.toUpdatedUserStateJson( - syncResponse = syncResponse, - ) + authDiskSource.userState = authDiskSource.userState?.toUpdatedUserStateJson( + syncResponse = syncResponse, + ) unlockVaultForOrganizationsIfNecessary(syncResponse = syncResponse) storeProfileData(syncResponse = syncResponse) @@ -1414,12 +1413,12 @@ class VaultRepositoryImpl( lastSyncTime = clock.instant(), ) vaultDiskSource.replaceVaultData(userId = userId, vault = syncResponse) - val itemsAvailable = syncResponse.ciphers?.isNotEmpty() ?: false - return SyncVaultDataResult.Success(itemsAvailable = itemsAvailable) + val itemsAvailable = syncResponse.ciphers?.isNotEmpty() == true + SyncVaultDataResult.Success(itemsAvailable = itemsAvailable) }, onFailure = { throwable -> - updateVaultStateFlowsToError(throwable) - return SyncVaultDataResult.Error(throwable) + updateVaultStateFlowsToError(throwable = throwable) + SyncVaultDataResult.Error(throwable = throwable) }, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherViewModel.kt index 1ee361d74..887725235 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherViewModel.kt @@ -99,7 +99,7 @@ class OtherViewModel @Inject constructor( mutableStateFlow.update { it.copy(dialogState = OtherState.DialogState.Loading(R.string.syncing.asText())) } - vaultRepo.sync() + vaultRepo.sync(forced = true) } private fun handleInternalAction(action: OtherAction.Internal) { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt index 1bbb8ecca..3d82049b3 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt @@ -255,7 +255,7 @@ class SendViewModel @Inject constructor( private fun handleRefreshClick() { // No need to update the view state, the vault repo will emit a new state during this time. - vaultRepo.sync() + vaultRepo.sync(forced = true) } private fun handleSearchClick() { @@ -266,7 +266,7 @@ class SendViewModel @Inject constructor( mutableStateFlow.update { it.copy(dialogState = SendState.DialogState.Loading(R.string.syncing.asText())) } - vaultRepo.sync() + vaultRepo.sync(forced = true) } private fun handleCopyClick(action: SendAction.CopyClick) { @@ -321,7 +321,7 @@ class SendViewModel @Inject constructor( mutableStateFlow.update { it.copy(isRefreshing = true) } // The Pull-To-Refresh composable is already in the refreshing state. // We will reset that state when sendDataStateFlow emits later on. - vaultRepo.sync() + vaultRepo.sync(forced = true) } } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt index 250f2562e..3014af072 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt @@ -255,7 +255,7 @@ class VaultItemViewModel @Inject constructor( private fun handleRefreshClick() { // No need to update the view state, the vault repo will emit a new state during this time - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleCopyCustomHiddenFieldClick( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt index 57d289093..cd144a490 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt @@ -300,14 +300,14 @@ class VaultItemListingViewModel @Inject constructor( } private fun handleRefreshClick() { - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleRefreshPull() { mutableStateFlow.update { it.copy(isRefreshing = true) } // The Pull-To-Refresh composable is already in the refreshing state. // We will reset that state when sendDataStateFlow emits later on. - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleConfirmOverwriteExistingPasskeyClick( @@ -877,7 +877,7 @@ class VaultItemListingViewModel @Inject constructor( ), ) } - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleSearchIconClick() { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt index dff036ac9..a3ed762b4 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt @@ -299,7 +299,7 @@ class VaultViewModel @Inject constructor( mutableStateFlow.update { it.copy(dialog = VaultState.DialogState.Syncing) } - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleLockClick() { @@ -346,7 +346,7 @@ class VaultViewModel @Inject constructor( } private fun handleTryAgainClick() { - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleDialogDismiss() { @@ -359,7 +359,7 @@ class VaultViewModel @Inject constructor( mutableStateFlow.update { it.copy(isRefreshing = true) } // The Pull-To-Refresh composable is already in the refreshing state. // We will reset that state when sendDataStateFlow emits later on. - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleOverflowOptionClick(action: VaultAction.OverflowOptionClick) { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt index 1b57c5746..261894f4d 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt @@ -120,14 +120,14 @@ class VerificationCodeViewModel @Inject constructor( } private fun handleRefreshClick() { - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleRefreshPull() { mutableStateFlow.update { it.copy(isRefreshing = true) } // The Pull-To-Refresh composable is already in the refreshing state. // We will reset that state when sendDataStateFlow emits later on. - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleSearchIconClick() { @@ -144,7 +144,7 @@ class VerificationCodeViewModel @Inject constructor( ), ) } - vaultRepository.sync() + vaultRepository.sync(forced = true) } private fun handleInternalAction(action: VerificationCodeAction.Internal) { diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt index 1206f4369..bf0ab478a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt @@ -816,6 +816,21 @@ class VaultRepositoryTest { coVerify(exactly = 0) { syncService.sync() } } + @Test + fun `sync with forced should skip checks and call the syncService sync`() { + fakeAuthDiskSource.userState = MOCK_USER_STATE + coEvery { syncService.sync() } returns Throwable("failure").asFailure() + + vaultRepository.sync(forced = true) + + coVerify(exactly = 0) { + syncService.getAccountRevisionDateMillis() + } + coVerify(exactly = 1) { + syncService.sync() + } + } + @Suppress("MaxLineLength") @Test fun `sync with syncService Success should unlock the vault for orgs if necessary and update AuthDiskSource and VaultDiskSource`() = diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt index dd528c56f..a0ec37d1d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt @@ -100,7 +100,6 @@ class SearchViewModelTest : BaseViewModelTest() { private val vaultRepository: VaultRepository = mockk { every { vaultFilterType } returns VaultFilterType.AllVaults every { vaultDataStateFlow } returns mutableVaultDataStateFlow - every { sync() } just runs } private val mutableUserStateFlow = MutableStateFlow(DEFAULT_USER_STATE) private val authRepository: AuthRepository = mockk { diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherViewModelTest.kt index 6d13125e1..19dc4beac 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/other/OtherViewModelTest.kt @@ -149,7 +149,7 @@ class OtherViewModelTest : BaseViewModelTest() { @Test fun `on SyncNowButtonClick should sync repo`() = runTest { - every { vaultRepository.sync() } just runs + every { vaultRepository.sync(forced = true) } just runs val viewModel = createViewModel() viewModel.stateFlow.test { assertEquals(DEFAULT_STATE, awaitItem()) @@ -163,7 +163,7 @@ class OtherViewModelTest : BaseViewModelTest() { awaitItem(), ) } - verify { vaultRepository.sync() } + verify { vaultRepository.sync(forced = true) } } @Test diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt index 1eb8972ba..1ddbe849a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt @@ -105,12 +105,12 @@ class SendViewModelTest : BaseViewModelTest() { @Test fun `RefreshClick should call sync`() { val viewModel = createViewModel() - every { vaultRepo.sync() } just runs + every { vaultRepo.sync(forced = true) } just runs viewModel.trySendAction(SendAction.RefreshClick) verify { - vaultRepo.sync() + vaultRepo.sync(forced = true) } } @@ -223,7 +223,7 @@ class SendViewModelTest : BaseViewModelTest() { @Test fun `SyncClick should call sync`() { val viewModel = createViewModel() - every { vaultRepo.sync() } just runs + every { vaultRepo.sync(forced = true) } just runs viewModel.trySendAction(SendAction.SyncClick) @@ -234,7 +234,7 @@ class SendViewModelTest : BaseViewModelTest() { viewModel.stateFlow.value, ) verify { - vaultRepo.sync() + vaultRepo.sync(forced = true) } } @@ -419,13 +419,13 @@ class SendViewModelTest : BaseViewModelTest() { @Test fun `RefreshPull should call vault repository sync`() { - every { vaultRepo.sync() } just runs + every { vaultRepo.sync(forced = true) } just runs val viewModel = createViewModel() viewModel.trySendAction(SendAction.RefreshPull) verify(exactly = 1) { - vaultRepo.sync() + vaultRepo.sync(forced = true) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt index dff6740ea..3e3dcf733 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt @@ -950,13 +950,13 @@ class VaultItemViewModelTest : BaseViewModelTest() { @Test fun `on RefreshClick should sync`() = runTest { - every { vaultRepo.sync() } just runs + every { vaultRepo.sync(forced = true) } just runs val viewModel = createViewModel(state = DEFAULT_STATE) viewModel.trySendAction(VaultItemAction.Common.RefreshClick) verify(exactly = 1) { - vaultRepo.sync() + vaultRepo.sync(forced = true) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt index a7769df7a..86644e763 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt @@ -135,7 +135,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { every { vaultFilterType } returns VaultFilterType.AllVaults every { vaultDataStateFlow } returns mutableVaultDataStateFlow every { lockVault(any()) } just runs - every { sync() } just runs + every { sync(forced = true) } just runs coEvery { getDecryptedFido2CredentialAutofillViews(any()) } returns DecryptFido2CredentialAutofillViewResult.Error @@ -351,7 +351,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { viewModel.stateFlow.value, ) verify(exactly = 1) { - vaultRepository.sync() + vaultRepository.sync(forced = true) } } @@ -1029,7 +1029,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fun `RefreshClick should sync`() = runTest { val viewModel = createVaultItemListingViewModel() viewModel.trySendAction(VaultItemListingsAction.RefreshClick) - verify { vaultRepository.sync() } + verify { vaultRepository.sync(forced = true) } } @Test @@ -2127,7 +2127,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { viewModel.trySendAction(VaultItemListingsAction.RefreshPull) verify(exactly = 1) { - vaultRepository.sync() + vaultRepository.sync(forced = true) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt index f5d4fda3f..fc76e7398 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt @@ -120,7 +120,7 @@ class VaultViewModelTest : BaseViewModelTest() { mockk { every { vaultFilterType = any() } just runs every { vaultDataStateFlow } returns mutableVaultDataStateFlow - every { sync() } just runs + every { sync(forced = any()) } just runs every { syncIfNecessary() } just runs every { lockVaultForCurrentUser() } just runs every { lockVault(any()) } just runs @@ -478,7 +478,7 @@ class VaultViewModelTest : BaseViewModelTest() { viewModel.stateFlow.value, ) verify { - vaultRepository.sync() + vaultRepository.sync(forced = true) } } @@ -1323,7 +1323,7 @@ class VaultViewModelTest : BaseViewModelTest() { viewModel.trySendAction(VaultAction.TryAgainClick) - verify { vaultRepository.sync() } + verify { vaultRepository.sync(forced = true) } } @Test @@ -1365,7 +1365,7 @@ class VaultViewModelTest : BaseViewModelTest() { viewModel.trySendAction(VaultAction.RefreshPull) verify(exactly = 1) { - vaultRepository.sync() + vaultRepository.sync(forced = true) } } @@ -1830,6 +1830,7 @@ class VaultViewModelTest : BaseViewModelTest() { snackbarRelayManager.clearRelayBuffer(SnackbarRelay.MY_VAULT_RELAY) } } + private fun createViewModel(): VaultViewModel = VaultViewModel( authRepository = authRepository, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt index a6912d6b1..ef1423a98 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt @@ -47,7 +47,7 @@ class VerificationCodeViewModelTest : BaseViewModelTest() { private val vaultRepository: VaultRepository = mockk { every { vaultFilterType } returns VaultFilterType.AllVaults every { getAuthCodesFlow() } returns mutableAuthCodeFlow.asStateFlow() - every { sync() } just runs + every { sync(forced = true) } just runs } private val environmentRepository: EnvironmentRepository = mockk { @@ -140,7 +140,7 @@ class VerificationCodeViewModelTest : BaseViewModelTest() { fun `RefreshClick should sync`() = runTest { val viewModel = createViewModel() viewModel.trySendAction(VerificationCodeAction.RefreshClick) - verify { vaultRepository.sync() } + verify { vaultRepository.sync(forced = true) } } @Test @@ -167,7 +167,7 @@ class VerificationCodeViewModelTest : BaseViewModelTest() { viewModel.stateFlow.value, ) verify(exactly = 1) { - vaultRepository.sync() + vaultRepository.sync(forced = true) } } @@ -456,7 +456,7 @@ class VerificationCodeViewModelTest : BaseViewModelTest() { viewModel.trySendAction(VerificationCodeAction.RefreshPull) verify(exactly = 1) { - vaultRepository.sync() + vaultRepository.sync(forced = true) } }