diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt index a52bdf042b..6f0e9caccd 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt @@ -1,6 +1,5 @@ package com.x8bit.bitwarden.data.vault.repository -import com.bitwarden.core.DateTime import com.bitwarden.core.data.repository.model.DataState import com.bitwarden.exporters.ExportFormat import com.bitwarden.fido.Fido2CredentialAutofillView @@ -24,6 +23,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import java.time.Instant import javax.crypto.Cipher /** @@ -135,7 +135,7 @@ interface VaultRepository : /** * Attempt to get the verification code and the period. */ - suspend fun generateTotp(cipherId: String, time: DateTime): GenerateTotpResult + suspend fun generateTotp(cipherId: String, time: Instant): GenerateTotpResult /** * Attempt to get the user's vault data for export. diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt index 0a9ee3c8fd..ceb636e7af 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt @@ -1,6 +1,5 @@ package com.x8bit.bitwarden.data.vault.repository -import com.bitwarden.core.DateTime import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.data.manager.dispatcher.DispatcherManager import com.bitwarden.core.data.repository.error.MissingPropertyException @@ -63,6 +62,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import timber.log.Timber import java.security.GeneralSecurityException +import java.time.Instant import javax.crypto.Cipher /** @@ -408,7 +408,7 @@ class VaultRepositoryImpl( override suspend fun generateTotp( cipherId: String, - time: DateTime, + time: Instant, ): GenerateTotpResult { val userId = activeUserId ?: return GenerateTotpResult.Error(error = NoActiveUserException()) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditViewModel.kt index c5aea7b585..4c9449f03c 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditViewModel.kt @@ -3,7 +3,6 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.folders.addedit import android.os.Parcelable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope -import com.bitwarden.core.DateTime import com.bitwarden.core.data.repository.model.DataState import com.bitwarden.ui.platform.base.BaseViewModel import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData @@ -25,6 +24,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize +import java.time.Clock import javax.inject.Inject private const val KEY_STATE = "state" @@ -37,6 +37,7 @@ private const val KEY_STATE = "state" @Suppress("TooManyFunctions", "LargeClass") class FolderAddEditViewModel @Inject constructor( savedStateHandle: SavedStateHandle, + private val clock: Clock, private val vaultRepository: VaultRepository, private val relayManager: SnackbarRelayManager, ) : BaseViewModel( @@ -124,7 +125,7 @@ class FolderAddEditViewModel @Inject constructor( .orEmpty() + content.folderName, id = folderAddEditType.folderId, - revisionDate = DateTime.now(), + revisionDate = clock.instant(), ), ) sendAction(FolderAddEditAction.Internal.CreateFolderResultReceive(result)) @@ -136,7 +137,7 @@ class FolderAddEditViewModel @Inject constructor( FolderView( name = content.folderName, id = folderAddEditType.folderId, - revisionDate = DateTime.now(), + revisionDate = clock.instant(), ), ) sendAction(FolderAddEditAction.Internal.UpdateFolderResultReceive(result)) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt index 248b25c410..c1841480a8 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt @@ -5,7 +5,6 @@ import androidx.credentials.CreatePublicKeyCredentialRequest import androidx.credentials.provider.CallingAppInfo import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope -import com.bitwarden.core.DateTime import com.bitwarden.core.data.manager.toast.ToastManager import com.bitwarden.core.data.repository.model.DataState import com.bitwarden.core.data.repository.util.takeUntilLoaded @@ -415,7 +414,7 @@ class VaultAddEditViewModel @Inject constructor( handleCreatePublicKeyCredentialRequest( request = createPublicKeyCredentialRequest, callingAppInfo = this.callingAppInfo, - cipherView = content.toCipherView(), + cipherView = content.toCipherView(clock = clock), ) return@onContent } @@ -431,7 +430,7 @@ class VaultAddEditViewModel @Inject constructor( is VaultAddEditType.EditItem -> { val result = vaultRepository.updateCipher( cipherId = vaultAddEditType.vaultItemId, - cipherView = content.toCipherView(), + cipherView = content.toCipherView(clock = clock), ) sendAction(VaultAddEditAction.Internal.UpdateCipherResultReceive(result)) } @@ -610,7 +609,7 @@ class VaultAddEditViewModel @Inject constructor( handleCreatePublicKeyCredentialRequest( request = createPublicKeyCredentialRequest, callingAppInfo = request.callingAppInfo, - cipherView = content.toCipherView(), + cipherView = content.toCipherView(clock = clock), ) } } @@ -641,7 +640,7 @@ class VaultAddEditViewModel @Inject constructor( handleCreatePublicKeyCredentialRequest( request = createPublicKeyCredentialRequest, callingAppInfo = request.callingAppInfo, - cipherView = content.toCipherView(), + cipherView = content.toCipherView(clock = clock), ) } } @@ -817,7 +816,7 @@ class VaultAddEditViewModel @Inject constructor( FolderView( name = action.newFolderName, id = null, - revisionDate = DateTime.now(), + revisionDate = clock.instant(), ), ) sendAction(VaultAddEditAction.Internal.AddFolderResultReceive(result = result)) @@ -2191,11 +2190,11 @@ class VaultAddEditViewModel @Inject constructor( ?.map { it.id } ?.let { vaultRepository.createCipherInOrganization( - cipherView = toCipherView(), + cipherView = toCipherView(clock = clock), collectionIds = it, ) } - ?: vaultRepository.createCipher(cipherView = toCipherView()) + ?: vaultRepository.createCipher(cipherView = toCipherView(clock = clock)) } private fun List.toUpdatedOwners( diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensions.kt index 21f5220421..0201ac0167 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensions.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensions.kt @@ -2,7 +2,6 @@ package com.x8bit.bitwarden.ui.vault.feature.vault.util -import com.bitwarden.core.DateTime import com.bitwarden.ui.platform.base.util.orNullIfBlank import com.bitwarden.vault.CardView import com.bitwarden.vault.CipherRepromptType @@ -23,12 +22,13 @@ import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth import com.x8bit.bitwarden.ui.vault.model.VaultIdentityTitle import com.x8bit.bitwarden.ui.vault.util.stringLongNameOrNull +import java.time.Clock import java.time.Instant /** * Transforms [VaultAddEditState.ViewState.Content] into [CipherView]. */ -fun VaultAddEditState.ViewState.Content.toCipherView(): CipherView = +fun VaultAddEditState.ViewState.Content.toCipherView(clock: Clock): CipherView = CipherView( // Pulled from original cipher when editing, otherwise uses defaults id = common.originalCipher?.id, @@ -39,18 +39,18 @@ fun VaultAddEditState.ViewState.Content.toCipherView(): CipherView = localData = common.originalCipher?.localData, attachments = common.originalCipher?.attachments, organizationUseTotp = common.originalCipher?.organizationUseTotp ?: false, - passwordHistory = toPasswordHistory(), + passwordHistory = toPasswordHistory(clock = clock), permissions = common.originalCipher?.permissions, - creationDate = common.originalCipher?.creationDate ?: Instant.now(), + creationDate = common.originalCipher?.creationDate ?: clock.instant(), deletedDate = common.originalCipher?.deletedDate, - revisionDate = common.originalCipher?.revisionDate ?: Instant.now(), + revisionDate = common.originalCipher?.revisionDate ?: clock.instant(), archivedDate = common.originalCipher?.archivedDate, // Type specific section type = type.toCipherType(), identity = type.toIdentityView(), secureNote = type.toSecureNotesView(), - login = type.toLoginView(common = common), + login = type.toLoginView(common = common, clock = clock), card = type.toCardView(), sshKey = type.toSshKeyView(), @@ -134,21 +134,23 @@ private fun VaultAddEditState.ViewState.Content.ItemType.toIdentityView(): Ident } @Suppress("MagicNumber") -private fun VaultAddEditState.ViewState.Content.toPasswordHistory(): List? { +private fun VaultAddEditState.ViewState.Content.toPasswordHistory( + clock: Clock, +): List? { val loginCipher = type as? VaultAddEditState.ViewState.Content.ItemType.Login val oldPassword = common.originalCipher?.login?.password - val timestamp = Instant.now() + val timestamp = clock.instant() val newPasswordHistory = getPasswordHistory( - loginCipher, - oldPassword, - timestamp, + loginCipher = loginCipher, + oldPassword = oldPassword, + timestamp = timestamp, ) val newHiddenFieldHistory = getHiddenFieldHistory( - timestamp, - common.originalCipher?.fields, - common.customFieldData, + timestamp = timestamp, + oldFields = common.originalCipher?.fields, + newFields = common.customFieldData, ) return listOf( @@ -204,12 +206,16 @@ private fun getHiddenFieldHistory( private fun VaultAddEditState.ViewState.Content.ItemType.toLoginView( common: VaultAddEditState.ViewState.Content.Common, + clock: Clock, ): LoginView? = (this as? VaultAddEditState.ViewState.Content.ItemType.Login)?.let { LoginView( username = it.username.orNullIfBlank(), password = it.password.orNullIfBlank(), - passwordRevisionDate = it.getRevisionDate(common.originalCipher), + passwordRevisionDate = it.getRevisionDate( + originalCipher = common.originalCipher, + clock = clock, + ), uris = it.uriList.toLoginUriView(), totp = it.totp, autofillOnPageLoad = common.originalCipher?.login?.autofillOnPageLoad, @@ -280,12 +286,13 @@ private fun List?.toLoginUriView(): List? = private fun VaultAddEditState.ViewState.Content.ItemType.Login.getRevisionDate( originalCipher: CipherView?, -): DateTime? { + clock: Clock, +): Instant? { val isOriginalPasswordNull = originalCipher?.login?.password.isNullOrEmpty() val hasPasswordHistory = originalCipher?.passwordHistory?.any() ?: false val isOriginalAndNewPasswordEqual = originalCipher?.login?.password == this.password return if ((!isOriginalPasswordNull || hasPasswordHistory) && !isOriginalAndNewPasswordEqual) { - Instant.now() + clock.instant() } else { originalCipher?.login?.passwordRevisionDate } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/PushDiskSourceTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/PushDiskSourceTest.kt index c3cdd5b1c5..cc344f2f84 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/PushDiskSourceTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/datasource/disk/PushDiskSourceTest.kt @@ -47,7 +47,7 @@ class PushDiskSourceTest { ) pushDiskSource.storeLastPushTokenRegistrationDate( userId = userId, - registrationDate = ZonedDateTime.now(), + registrationDate = ZonedDateTime.parse("2023-10-27T12:00:00Z"), ) pushDiskSource.clearData(userId = userId) @@ -138,7 +138,7 @@ class PushDiskSourceTest { fun `storeLastPushTokenRegistrationDate for null values should clear SharedPreferences`() { val lastPushTokenBaseKey = "bwPreferencesStorage:pushLastRegistrationDate" val mockUserId = "mockUserId" - val mockLastPushTokenRegistration = ZonedDateTime.now() + val mockLastPushTokenRegistration = ZonedDateTime.parse("2023-10-27T12:00:00Z") val lastPushTokenKey = "${lastPushTokenBaseKey}_$mockUserId" fakeSharedPreferences.edit { putLong(lastPushTokenKey, mockLastPushTokenRegistration.toEpochSecond()) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt index ef03d49101..b4969b1f45 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt @@ -506,7 +506,7 @@ private val USER_1_TOTP_CIPHER = mockk { private val USER_1_DELETED_TOTP_CIPHER = mockk { every { login?.totp } returns "encryptedTotp1Deleted" every { login?.username } returns "username" - every { deletedDate } returns ZonedDateTime.now() + every { deletedDate } returns ZonedDateTime.parse("2023-10-27T12:00:00Z") every { name } returns "cipher1" } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialViewUtil.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialViewUtil.kt index 71d6726d50..bc0ad1f47e 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialViewUtil.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialViewUtil.kt @@ -20,7 +20,7 @@ fun createMockFido2CredentialView(number: Int): Fido2CredentialView = Fido2Crede rpName = "mockRpName-$number", userDisplayName = "mockUserDisplayName-$number", discoverable = "mockDiscoverable-$number", - creationDate = Instant.now(), + creationDate = Instant.parse("2023-10-27T12:00:00Z"), ) /** diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt index b055520251..b43801e608 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt @@ -933,7 +933,6 @@ class CipherManagerTest { ) .toEncryptedNetworkCipherResponse() } returns createMockCipher(number = 1) - val fixedInstant = Instant.parse("2021-01-01T00:00:00Z") val userId = "mockId-1" val cipherId = "mockId-1" val attachmentId = "mockId-1" @@ -960,8 +959,6 @@ class CipherManagerTest { ) } just runs val cipherView = createMockCipherView(number = 1) - mockkStatic(Instant::class) - every { Instant.now() } returns fixedInstant val result = cipherManager.deleteCipherAttachment( cipherId = cipherId, @@ -970,7 +967,6 @@ class CipherManagerTest { ) assertEquals(DeleteAttachmentResult.Success, result) - unmockkStatic(Instant::class) unmockkStatic(Cipher::toEncryptedNetworkCipherResponse) } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/FolderManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/FolderManagerTest.kt index 4fb950034e..d6a2a332e2 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/FolderManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/FolderManagerTest.kt @@ -115,7 +115,7 @@ class FolderManagerTest { val folderView = FolderView( id = null, name = "TestName", - revisionDate = Instant.now(FIXED_CLOCK), + revisionDate = FIXED_CLOCK.instant(), ) val error = IllegalStateException() @@ -130,7 +130,7 @@ class FolderManagerTest { @Test fun `createFolder with folderService failure should return CreateFolderResult failure`() = runTest { - val date = Instant.now(FIXED_CLOCK) + val date = FIXED_CLOCK.instant() val testFolderName = "TestName" fakeAuthDiskSource.userState = MOCK_USER_STATE @@ -157,7 +157,7 @@ class FolderManagerTest { fun `createFolder with folderService createFolder should return CreateFolderResult success`() = runTest { fakeAuthDiskSource.userState = MOCK_USER_STATE - val date = Instant.now(FIXED_CLOCK) + val date = FIXED_CLOCK.instant() val testFolderName = "TestName" val folderView = FolderView( id = null, @@ -270,7 +270,7 @@ class FolderManagerTest { val folderView = FolderView( id = folderId, name = "TestName", - revisionDate = Instant.now(FIXED_CLOCK), + revisionDate = FIXED_CLOCK.instant(), ) val error = IllegalStateException() @@ -286,7 +286,7 @@ class FolderManagerTest { @Test fun `updateFolder with folderService failure should return UpdateFolderResult failure`() = runTest { - val date = Instant.now(FIXED_CLOCK) + val date = FIXED_CLOCK.instant() val testFolderName = "TestName" val folderId = "testId" @@ -317,7 +317,7 @@ class FolderManagerTest { @Test fun `updateFolder with folderService updateFolder Invalid response should return UpdateFolderResult Error with a non-null message`() = runTest { - val date = Instant.now(FIXED_CLOCK) + val date = FIXED_CLOCK.instant() val testFolderName = "TestName" val folderId = "testId" @@ -358,7 +358,7 @@ class FolderManagerTest { @Test fun `updateFolder with folderService updateFolder success should return UpdateFolderResult success`() = runTest { - val date = Instant.now(FIXED_CLOCK) + val date = FIXED_CLOCK.instant() val testFolderName = "TestName" val folderId = "testId" diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt index 686801d3e6..588f23570e 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt @@ -2,7 +2,6 @@ package com.x8bit.bitwarden.data.vault.repository import app.cash.turbine.test import com.bitwarden.collections.CollectionView -import com.bitwarden.core.DateTime import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.MasterPasswordUnlockData import com.bitwarden.core.data.manager.dispatcher.DispatcherManager @@ -77,6 +76,7 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import java.security.GeneralSecurityException +import java.time.Instant import java.time.ZonedDateTime import javax.crypto.BadPaddingException import javax.crypto.Cipher @@ -996,7 +996,7 @@ class VaultRepositoryTest { val result = vaultRepository.generateTotp( cipherId = "totpCode", - time = DateTime.now(), + time = Instant.parse("2023-10-27T12:00:00Z"), ) assertEquals( @@ -1023,7 +1023,7 @@ class VaultRepositoryTest { val result = vaultRepository.generateTotp( cipherId = "mockId-1", - time = DateTime.now(), + time = Instant.parse("2023-10-27T12:00:00Z"), ) assertEquals( diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsViewModelTest.kt index 22f25fcbe1..c76b271a7a 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/pendingrequests/PendingRequestsViewModelTest.kt @@ -263,7 +263,7 @@ class PendingRequestsViewModelTest : BaseViewModelTest() { ipAddress = "192.168.0.2", key = "publicKey", masterPasswordHash = "verySecureHash", - creationDate = ZonedDateTime.now().minusMinutes(5), + creationDate = ZonedDateTime.now(fixedClock).minusMinutes(5), responseDate = null, requestApproved = false, originUrl = "www.bitwarden.com", @@ -276,7 +276,7 @@ class PendingRequestsViewModelTest : BaseViewModelTest() { ipAddress = "192.168.0.3", key = "publicKey", masterPasswordHash = "verySecureHash", - creationDate = ZonedDateTime.now(), + creationDate = ZonedDateTime.now(fixedClock), responseDate = null, requestApproved = false, originUrl = "www.bitwarden.com", @@ -291,7 +291,7 @@ class PendingRequestsViewModelTest : BaseViewModelTest() { ) } returns AuthRequestResult.Success( authRequest1.copy( - responseDate = ZonedDateTime.now(), + responseDate = ZonedDateTime.now(fixedClock), ), ) coEvery { @@ -303,7 +303,7 @@ class PendingRequestsViewModelTest : BaseViewModelTest() { ) } returns AuthRequestResult.Success( authRequest2.copy( - responseDate = ZonedDateTime.now(), + responseDate = ZonedDateTime.now(fixedClock), ), ) val viewModel = createViewModel() diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/FoldersViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/FoldersViewModelTest.kt index 4d197c6b24..b99ef98ec3 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/FoldersViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/FoldersViewModelTest.kt @@ -1,7 +1,6 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.folders import app.cash.turbine.test -import com.bitwarden.core.DateTime import com.bitwarden.core.data.repository.model.DataState import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow import com.bitwarden.ui.platform.base.BaseViewModelTest @@ -20,6 +19,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test +import java.time.Instant class FoldersViewModelTest : BaseViewModelTest() { @@ -195,5 +195,9 @@ class FoldersViewModelTest : BaseViewModelTest() { ) } -private val DEFAULT_FOLDER_VIEW = FolderView("1", "test", revisionDate = DateTime.now()) +private val DEFAULT_FOLDER_VIEW = FolderView( + id = "1", + name = "test", + revisionDate = Instant.parse("2025-04-11T10:15:30.00Z"), +) private val DEFAULT__DISPLAY_FOLDER = FolderDisplayItem("1", "test") diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditViewModelTest.kt index 5e6ee9eed2..9476878bcc 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/folders/addedit/FolderAddEditViewModelTest.kt @@ -2,7 +2,6 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.folders.addedit import androidx.lifecycle.SavedStateHandle import app.cash.turbine.test -import com.bitwarden.core.DateTime import com.bitwarden.core.data.repository.model.DataState import com.bitwarden.ui.platform.base.BaseViewModelTest import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData @@ -32,7 +31,9 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import java.time.Clock import java.time.Instant +import java.time.ZoneOffset @Suppress("LargeClass") class FolderAddEditViewModelTest : BaseViewModelTest() { @@ -120,9 +121,9 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { mutableFoldersStateFlow.value = DataState.Loaded( FolderView( - DEFAULT_EDIT_ITEM_ID, - DEFAULT_FOLDER_NAME, - DateTime.now(), + id = DEFAULT_EDIT_ITEM_ID, + name = DEFAULT_FOLDER_NAME, + revisionDate = FIXED_CLOCK.instant(), ), ) @@ -174,9 +175,9 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { mutableFoldersStateFlow.value = DataState.Loaded( FolderView( - DEFAULT_EDIT_ITEM_ID, - DEFAULT_FOLDER_NAME, - DateTime.now(), + id = DEFAULT_EDIT_ITEM_ID, + name = DEFAULT_FOLDER_NAME, + revisionDate = FIXED_CLOCK.instant(), ), ) @@ -253,9 +254,9 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { mutableFoldersStateFlow.value = DataState.Loaded( FolderView( - DEFAULT_EDIT_ITEM_ID, - DEFAULT_FOLDER_NAME, - DateTime.now(), + id = DEFAULT_EDIT_ITEM_ID, + name = DEFAULT_FOLDER_NAME, + revisionDate = FIXED_CLOCK.instant(), ), ) @@ -350,8 +351,6 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { @Test fun `in add mode, SaveClick createFolder with no parentFolderNamePresent should just create folder with entered name`() = runTest { - mockkStatic(DateTime::class) - every { DateTime.now() } returns Instant.MIN val viewModel = createViewModel( createSavedStateHandleWithState( @@ -375,7 +374,7 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { folderView = FolderView( name = DEFAULT_FOLDER_NAME, id = null, - revisionDate = Instant.MIN, + revisionDate = FIXED_CLOCK.instant(), ), ) } @@ -385,15 +384,12 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { relay = SnackbarRelay.FOLDER_CREATED, ) } - unmockkStatic(DateTime::class) } @Suppress("MaxLineLength") @Test fun `in add mode, SaveClick createFolder with a parentFolderNamePresent should prepend the parent folder to the entered name`() = runTest { - mockkStatic(DateTime::class) - every { DateTime.now() } returns Instant.MIN val parentFolderName = "parent/folder" val viewModel = createViewModel( @@ -418,7 +414,7 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { folderView = FolderView( name = "$parentFolderName/$DEFAULT_FOLDER_NAME", id = null, - revisionDate = Instant.MIN, + revisionDate = FIXED_CLOCK.instant(), ), ) } @@ -428,7 +424,6 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { relay = SnackbarRelay.FOLDER_CREATED, ) } - unmockkStatic(DateTime::class) } @Test @@ -497,9 +492,9 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { mutableFoldersStateFlow.value = DataState.Loaded( FolderView( - DEFAULT_EDIT_ITEM_ID, - DEFAULT_FOLDER_NAME, - DateTime.now(), + id = DEFAULT_EDIT_ITEM_ID, + name = DEFAULT_FOLDER_NAME, + revisionDate = FIXED_CLOCK.instant(), ), ) @@ -542,9 +537,9 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { mutableFoldersStateFlow.value = DataState.Loaded( FolderView( - DEFAULT_EDIT_ITEM_ID, - DEFAULT_FOLDER_NAME, - DateTime.now(), + id = DEFAULT_EDIT_ITEM_ID, + name = DEFAULT_FOLDER_NAME, + revisionDate = FIXED_CLOCK.instant(), ), ) @@ -640,9 +635,9 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { mutableFoldersStateFlow.tryEmit( DataState.Loaded( FolderView( - DEFAULT_EDIT_ITEM_ID, - DEFAULT_FOLDER_NAME, - revisionDate = DateTime.now(), + id = DEFAULT_EDIT_ITEM_ID, + name = DEFAULT_FOLDER_NAME, + revisionDate = FIXED_CLOCK.instant(), ), ), ) @@ -738,9 +733,9 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { mutableFoldersStateFlow.tryEmit( value = DataState.Pending( FolderView( - DEFAULT_EDIT_ITEM_ID, - DEFAULT_FOLDER_NAME, - revisionDate = DateTime.now(), + id = DEFAULT_EDIT_ITEM_ID, + name = DEFAULT_FOLDER_NAME, + revisionDate = FIXED_CLOCK.instant(), ), ), ) @@ -793,6 +788,7 @@ class FolderAddEditViewModelTest : BaseViewModelTest() { savedStateHandle: SavedStateHandle = createSavedStateHandleWithState(), ): FolderAddEditViewModel = FolderAddEditViewModel( savedStateHandle = savedStateHandle, + clock = FIXED_CLOCK, vaultRepository = vaultRepository, relayManager = relayManager, ) @@ -805,5 +801,10 @@ private val DEFAULT_STATE = FolderAddEditState( parentFolderName = null, ) +private val FIXED_CLOCK = Clock.fixed( + Instant.parse("2025-04-11T10:15:30.00Z"), + ZoneOffset.UTC, +) + private const val DEFAULT_EDIT_ITEM_ID = "edit_id" private const val DEFAULT_FOLDER_NAME = "test_name" diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/generator/passwordhistory/PasswordHistoryViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/generator/passwordhistory/PasswordHistoryViewModelTest.kt index 0e0af360ef..3ac421a626 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/generator/passwordhistory/PasswordHistoryViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/generator/passwordhistory/PasswordHistoryViewModelTest.kt @@ -187,7 +187,7 @@ class PasswordHistoryViewModelTest : BaseViewModelTest() { fun `when password history updates the state updates correctly`() = runTest { val viewModel = createViewModel() - val passwordHistoryView = PasswordHistoryView("password", Instant.now()) + val passwordHistoryView = PasswordHistoryView("password", fixedClock.instant()) fakeGeneratorRepository.storePasswordHistory(passwordHistoryView) val expectedState = createPasswordHistoryState( @@ -241,7 +241,7 @@ class PasswordHistoryViewModelTest : BaseViewModelTest() { fun `PasswordClearClick action should update to Empty ViewState`() = runTest { val viewModel = createViewModel() - val passwordHistoryView = PasswordHistoryView("password", Instant.now()) + val passwordHistoryView = PasswordHistoryView("password", fixedClock.instant()) fakeGeneratorRepository.storePasswordHistory(passwordHistoryView) viewModel.trySendAction(PasswordHistoryAction.PasswordClearClick) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt index dae550e885..8f65a64c46 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt @@ -7,7 +7,6 @@ import androidx.credentials.provider.ProviderCreateCredentialRequest import androidx.lifecycle.SavedStateHandle import app.cash.turbine.test import com.bitwarden.collections.CollectionView -import com.bitwarden.core.DateTime import com.bitwarden.core.data.manager.dispatcher.FakeDispatcherManager import com.bitwarden.core.data.manager.toast.ToastManager import com.bitwarden.core.data.repository.model.DataState @@ -3363,14 +3362,11 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { @Test fun `AddNewFolder action calls create folder from vault repository`() = runTest { - mockkStatic(DateTime::class) - every { DateTime.now() } returns Instant.MIN - val folderName = "folderName" val expectedFolderResult = FolderView( id = "123", name = folderName, - revisionDate = DateTime.now(), + revisionDate = fixedClock.instant(), ) coEvery { vaultRepository.createFolder(any()) @@ -3381,23 +3377,20 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { FolderView( name = folderName, id = null, - revisionDate = Instant.MIN, + revisionDate = fixedClock.instant(), ), ) } - - unmockkStatic(DateTime::class) } @Test fun `AddNewFolder updates dialog states and selected folder id on success`() = runTest { - val folderId = "123" val folderName = "folderName" val expectedFolderResult = FolderView( id = folderId, name = folderName, - revisionDate = DateTime.now(), + revisionDate = fixedClock.instant(), ) coEvery { vaultRepository.createFolder(any()) @@ -3433,7 +3426,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { data = listOf( FolderView( name = "folder", - revisionDate = DateTime.now(), + revisionDate = fixedClock.instant(), id = null, ), ), diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensionsTest.kt index 80716b2980..e390588587 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensionsTest.kt @@ -21,30 +21,19 @@ import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth import com.x8bit.bitwarden.ui.vault.model.VaultIdentityTitle import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType -import io.mockk.every -import io.mockk.mockkStatic -import io.mockk.unmockkStatic -import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotEquals import org.junit.jupiter.api.Test +import java.time.Clock import java.time.Instant +import java.time.ZoneOffset @Suppress("LargeClass") class VaultAddItemStateExtensionsTest { - @AfterEach - fun tearDown() { - // Some individual tests call mockkStatic so we will make sure this is always undone. - unmockkStatic(Instant::class) - } - @Suppress("MaxLineLength") @Test fun `toCipherView should transform Login ItemType to CipherView`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MIN - val loginItemType = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common( name = "mockName-1", @@ -71,7 +60,7 @@ class VaultAddItemStateExtensionsTest { ), ) - val result = loginItemType.toCipherView() + val result = loginItemType.toCipherView(clock = FIXED_CLOCK) assertEquals( CipherView( @@ -111,9 +100,9 @@ class VaultAddItemStateExtensionsTest { fields = emptyList(), passwordHistory = null, permissions = null, - creationDate = Instant.MIN, + creationDate = FIXED_CLOCK.instant(), deletedDate = null, - revisionDate = Instant.MIN, + revisionDate = FIXED_CLOCK.instant(), archivedDate = null, sshKey = null, ), @@ -124,8 +113,6 @@ class VaultAddItemStateExtensionsTest { @Suppress("MaxLineLength") @Test fun `toCipherView should transform Login ItemType to CipherView with original cipher`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MIN val cipherView = DEFAULT_LOGIN_CIPHER_VIEW val viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common( @@ -158,7 +145,7 @@ class VaultAddItemStateExtensionsTest { ), ) - val result = viewState.toCipherView() + val result = viewState.toCipherView(clock = FIXED_CLOCK) assertEquals( @Suppress("MaxLineLength") @@ -171,7 +158,7 @@ class VaultAddItemStateExtensionsTest { login = LoginView( username = "mockUsername-1", password = "mockPassword-1", - passwordRevisionDate = Instant.MIN, + passwordRevisionDate = FIXED_CLOCK.instant(), uris = listOf( LoginUriView( uri = "mockUri-1", @@ -214,15 +201,15 @@ class VaultAddItemStateExtensionsTest { passwordHistory = listOf( PasswordHistoryView( password = "old_password", - lastUsedDate = Instant.MIN, + lastUsedDate = FIXED_CLOCK.instant(), ), PasswordHistoryView( password = "password", - lastUsedDate = Instant.MIN, + lastUsedDate = FIXED_CLOCK.instant(), ), PasswordHistoryView( password = "hidden: value", - lastUsedDate = Instant.MIN, + lastUsedDate = FIXED_CLOCK.instant(), ), ), ), @@ -232,8 +219,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toCipherView should transform SecureNotes ItemType to CipherView`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MIN val viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common( name = "mockName-1", @@ -252,7 +237,7 @@ class VaultAddItemStateExtensionsTest { type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, ) - val result = viewState.toCipherView() + val result = viewState.toCipherView(clock = FIXED_CLOCK) assertEquals( CipherView( @@ -297,9 +282,9 @@ class VaultAddItemStateExtensionsTest { ), passwordHistory = null, permissions = null, - creationDate = Instant.MIN, + creationDate = FIXED_CLOCK.instant(), deletedDate = null, - revisionDate = Instant.MIN, + revisionDate = FIXED_CLOCK.instant(), archivedDate = null, sshKey = null, ), @@ -309,8 +294,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toCipherView should transform SecureNotes ItemType to CipherView with original cipher`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MIN val cipherView = DEFAULT_SECURE_NOTES_CIPHER_VIEW.copy(passwordHistory = null) val viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common( @@ -327,7 +310,7 @@ class VaultAddItemStateExtensionsTest { type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, ) - val result = viewState.toCipherView() + val result = viewState.toCipherView(clock = FIXED_CLOCK) assertEquals( cipherView.copy( @@ -342,7 +325,7 @@ class VaultAddItemStateExtensionsTest { passwordHistory = listOf( PasswordHistoryView( password = "hidden: value", - lastUsedDate = Instant.MIN, + lastUsedDate = FIXED_CLOCK.instant(), ), ), ), @@ -352,8 +335,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toCipherView should transform Identity ItemType to CipherView`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MIN val viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common( name = "mockName-1", @@ -386,7 +367,7 @@ class VaultAddItemStateExtensionsTest { ), ) - val result = viewState.toCipherView() + val result = viewState.toCipherView(clock = FIXED_CLOCK) assertEquals( CipherView( @@ -431,9 +412,9 @@ class VaultAddItemStateExtensionsTest { fields = emptyList(), passwordHistory = null, permissions = null, - creationDate = Instant.MIN, + creationDate = FIXED_CLOCK.instant(), deletedDate = null, - revisionDate = Instant.MIN, + revisionDate = FIXED_CLOCK.instant(), archivedDate = null, sshKey = null, ), @@ -443,8 +424,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toCipherView should transform Identity ItemType to CipherView with original cipher`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MIN val cipherView = DEFAULT_IDENTITY_CIPHER_VIEW val viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common( @@ -489,7 +468,7 @@ class VaultAddItemStateExtensionsTest { ), ) - val result = viewState.toCipherView() + val result = viewState.toCipherView(clock = FIXED_CLOCK) assertEquals( @Suppress("MaxLineLength") @@ -550,11 +529,11 @@ class VaultAddItemStateExtensionsTest { passwordHistory = listOf( PasswordHistoryView( password = "old_password", - lastUsedDate = Instant.MIN, + lastUsedDate = FIXED_CLOCK.instant(), ), PasswordHistoryView( password = "hidden: value", - lastUsedDate = Instant.MIN, + lastUsedDate = FIXED_CLOCK.instant(), ), ), ), @@ -564,8 +543,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toCipherView should transform Card ItemType to CipherView`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MIN val viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common( name = "mockName-1", @@ -586,7 +563,7 @@ class VaultAddItemStateExtensionsTest { ), ) - val result = viewState.toCipherView() + val result = viewState.toCipherView(clock = FIXED_CLOCK) assertEquals( CipherView( @@ -619,9 +596,9 @@ class VaultAddItemStateExtensionsTest { fields = emptyList(), passwordHistory = null, permissions = null, - creationDate = Instant.MIN, + creationDate = FIXED_CLOCK.instant(), deletedDate = null, - revisionDate = Instant.MIN, + revisionDate = FIXED_CLOCK.instant(), archivedDate = null, sshKey = null, ), @@ -631,8 +608,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toCipherView should transform Card ItemType to CipherView with original cipher`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MIN val cipherView = DEFAULT_CARD_CIPHER_VIEW val viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common( @@ -665,7 +640,7 @@ class VaultAddItemStateExtensionsTest { ), ) - val result = viewState.toCipherView() + val result = viewState.toCipherView(clock = FIXED_CLOCK) assertEquals( cipherView.copy( @@ -704,11 +679,11 @@ class VaultAddItemStateExtensionsTest { passwordHistory = listOf( PasswordHistoryView( password = "old_password", - lastUsedDate = Instant.MIN, + lastUsedDate = FIXED_CLOCK.instant(), ), PasswordHistoryView( password = "hidden: value", - lastUsedDate = Instant.MIN, + lastUsedDate = FIXED_CLOCK.instant(), ), ), ), @@ -718,8 +693,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toCipherView should transform SSH Key ItemType to CipherView`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MIN val viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common( name = "mockName-1", @@ -737,7 +710,7 @@ class VaultAddItemStateExtensionsTest { ), ) - val result = viewState.toCipherView() + val result = viewState.toCipherView(clock = FIXED_CLOCK) assertEquals( CipherView( @@ -763,9 +736,9 @@ class VaultAddItemStateExtensionsTest { fields = emptyList(), passwordHistory = null, permissions = null, - creationDate = Instant.MIN, + creationDate = FIXED_CLOCK.instant(), deletedDate = null, - revisionDate = Instant.MIN, + revisionDate = FIXED_CLOCK.instant(), archivedDate = null, sshKey = SshKeyView( publicKey = "mockPublicKey-1", @@ -787,7 +760,7 @@ class VaultAddItemStateExtensionsTest { login = LoginView( username = "mockUsername-1", password = "mockPassword-1", - passwordRevisionDate = Instant.MIN, + passwordRevisionDate = FIXED_CLOCK.instant(), uris = null, totp = null, autofillOnPageLoad = false, @@ -811,7 +784,7 @@ class VaultAddItemStateExtensionsTest { ), ) - val result = viewState.toCipherView() + val result = viewState.toCipherView(clock = FIXED_CLOCK) assertEquals( cipherView.copy( @@ -822,7 +795,7 @@ class VaultAddItemStateExtensionsTest { totp = null, fido2Credentials = null, uris = null, - passwordRevisionDate = Instant.MIN, + passwordRevisionDate = FIXED_CLOCK.instant(), autofillOnPageLoad = false, ), ), @@ -832,9 +805,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toLoginView should update revision date when password differs`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MAX - val cipherView = DEFAULT_LOGIN_CIPHER_VIEW val viewState = VaultAddEditState.ViewState.Content( @@ -851,8 +821,14 @@ class VaultAddItemStateExtensionsTest { password = "mockPassword-1", ), ) + val futureClock = Clock.fixed( + Instant.parse("2023-11-27T12:00:00Z"), + ZoneOffset.UTC, + ) - val result = viewState.toCipherView() + // We need to pass in a future clock to make sure that when the + // revision date is updated it is updated to a new time + val result = viewState.toCipherView(clock = futureClock) assertNotEquals( viewState.common.originalCipher?.login?.passwordRevisionDate, @@ -862,9 +838,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toLoginView should keep revision date when password is equal`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MAX - val cipherView = DEFAULT_LOGIN_CIPHER_VIEW val viewState = VaultAddEditState.ViewState.Content( @@ -881,8 +854,14 @@ class VaultAddItemStateExtensionsTest { password = cipherView.login?.password ?: "", ), ) + val futureClock = Clock.fixed( + Instant.parse("2023-11-27T12:00:00Z"), + ZoneOffset.UTC, + ) - val result = viewState.toCipherView() + // We need to pass in a future clock to make sure that if the + // revision date were to be updated it would be updated to a new time + val result = viewState.toCipherView(clock = futureClock) assertEquals( viewState.common.originalCipher?.login?.passwordRevisionDate, @@ -892,9 +871,6 @@ class VaultAddItemStateExtensionsTest { @Test fun `toLoginView should not update revision date when password is null and has no history`() { - mockkStatic(Instant::class) - every { Instant.now() } returns Instant.MAX - val cipherView = DEFAULT_LOGIN_CIPHER_VIEW.copy( passwordHistory = null, login = DEFAULT_LOGIN_CIPHER_VIEW.login?.copy(password = null), @@ -914,8 +890,14 @@ class VaultAddItemStateExtensionsTest { password = "updated password", ), ) + val futureClock = Clock.fixed( + Instant.parse("2023-11-27T12:00:00Z"), + ZoneOffset.UTC, + ) - val result = viewState.toCipherView() + // We need to pass in a future clock to make sure that if the + // revision date were to be updated it would be updated to a new time + val result = viewState.toCipherView(clock = futureClock) assertEquals( viewState.common.originalCipher?.login?.passwordRevisionDate, @@ -924,6 +906,11 @@ class VaultAddItemStateExtensionsTest { } } +private val FIXED_CLOCK: Clock = Clock.fixed( + Instant.parse("2023-10-27T12:00:00Z"), + ZoneOffset.UTC, +) + private val DEFAULT_BASE_CIPHER_VIEW: CipherView = CipherView( id = "id1234", organizationId = null, @@ -979,13 +966,13 @@ private val DEFAULT_BASE_CIPHER_VIEW: CipherView = CipherView( passwordHistory = listOf( PasswordHistoryView( password = "old_password", - lastUsedDate = Instant.MIN, + lastUsedDate = FIXED_CLOCK.instant(), ), ), permissions = null, - creationDate = Instant.MIN, + creationDate = FIXED_CLOCK.instant(), deletedDate = null, - revisionDate = Instant.MIN, + revisionDate = FIXED_CLOCK.instant(), archivedDate = null, sshKey = null, ) @@ -995,7 +982,7 @@ private val DEFAULT_LOGIN_CIPHER_VIEW: CipherView = DEFAULT_BASE_CIPHER_VIEW.cop login = LoginView( username = "username", password = "password", - passwordRevisionDate = Instant.MIN, + passwordRevisionDate = FIXED_CLOCK.instant(), uris = listOf( LoginUriView( uri = "www.example.com", diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/datasource/sdk/AuthenticatorSdkSource.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/datasource/sdk/AuthenticatorSdkSource.kt index 6500c87451..f9c2455579 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/datasource/sdk/AuthenticatorSdkSource.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/datasource/sdk/AuthenticatorSdkSource.kt @@ -1,7 +1,7 @@ package com.bitwarden.authenticator.data.authenticator.datasource.sdk -import com.bitwarden.core.DateTime import com.bitwarden.vault.TotpResponse +import java.time.Instant /** * Source of authenticator information from the Bitwarden SDK. @@ -13,7 +13,7 @@ interface AuthenticatorSdkSource { */ suspend fun generateTotp( totp: String, - time: DateTime, + time: Instant, ): Result /** diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/datasource/sdk/AuthenticatorSdkSourceImpl.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/datasource/sdk/AuthenticatorSdkSourceImpl.kt index d6991fd9cc..3c62f53780 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/datasource/sdk/AuthenticatorSdkSourceImpl.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/datasource/sdk/AuthenticatorSdkSourceImpl.kt @@ -1,10 +1,10 @@ package com.bitwarden.authenticator.data.authenticator.datasource.sdk import com.bitwarden.authenticator.data.platform.manager.SdkClientManager -import com.bitwarden.core.DateTime import com.bitwarden.generators.PasswordGeneratorRequest import com.bitwarden.sdk.Client import com.bitwarden.vault.TotpResponse +import java.time.Instant import javax.inject.Inject /** @@ -16,7 +16,7 @@ class AuthenticatorSdkSourceImpl @Inject constructor( override suspend fun generateTotp( totp: String, - time: DateTime, + time: Instant, ): Result = runCatching { getClient() .vault() diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/manager/TotpCodeManagerImpl.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/manager/TotpCodeManagerImpl.kt index 984ab3ae57..f38bba3e0e 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/manager/TotpCodeManagerImpl.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/authenticator/manager/TotpCodeManagerImpl.kt @@ -3,7 +3,6 @@ package com.bitwarden.authenticator.data.authenticator.manager import com.bitwarden.authenticator.data.authenticator.datasource.sdk.AuthenticatorSdkSource import com.bitwarden.authenticator.data.authenticator.manager.model.VerificationCodeItem import com.bitwarden.authenticator.data.authenticator.repository.model.AuthenticatorItem -import com.bitwarden.core.DateTime import kotlinx.coroutines.cancel import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.delay @@ -46,7 +45,7 @@ class TotpCodeManagerImpl @Inject constructor( // If the item is expired or we haven't generated our first item, // generate a new code using the SDK: item = authenticatorSdkSource - .generateTotp(otpUri, DateTime.now()) + .generateTotp(otpUri, clock.instant()) .getOrNull() ?.let { response -> VerificationCodeItem(