mirror of
https://github.com/bitwarden/android.git
synced 2026-10-22 11:02:51 -05:00
Updated RootNavViewModel and added tests
This commit is contained in:
4 files changed
+413
-42
No files matched your search
+19
-25
@@ -2,7 +2,6 @@ package com.x8bit.bitwarden.ui.platform.feature.rootnav
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.network.model.OrganizationType
|
||||
import com.bitwarden.network.util.parseJwtTokenDataOrNull
|
||||
import com.bitwarden.ui.platform.base.BaseViewModel
|
||||
@@ -18,11 +17,8 @@ import com.x8bit.bitwarden.data.credentials.model.CreateCredentialRequest
|
||||
import com.x8bit.bitwarden.data.credentials.model.Fido2CredentialAssertionRequest
|
||||
import com.x8bit.bitwarden.data.credentials.model.GetCredentialsRequest
|
||||
import com.x8bit.bitwarden.data.credentials.model.ProviderGetPasswordCredentialRequest
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.model.SendItemType
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@@ -40,10 +36,7 @@ import javax.inject.Inject
|
||||
class RootNavViewModel @Inject constructor(
|
||||
private val authRepository: AuthRepository,
|
||||
specialCircumstanceManager: SpecialCircumstanceManager,
|
||||
private val policyManager: PolicyManager,
|
||||
private val connectionManager: NetworkConnectionManager,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
private val vaultRepository: VaultRepository,
|
||||
vaultRepository: VaultRepository,
|
||||
) : BaseViewModel<RootNavState, Unit, RootNavAction>(
|
||||
initialState = RootNavState.Splash,
|
||||
) {
|
||||
@@ -123,28 +116,27 @@ class RootNavViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
userState.activeAccount.isVaultUnlocked &&
|
||||
action.shouldMigratePersonalVault &&
|
||||
featureFlagManager.getFeatureFlag(FlagKey.MigrateMyVaultToMyItems) &&
|
||||
connectionManager.isNetworkConnected &&
|
||||
specialCircumstance == null -> {
|
||||
specialCircumstance is SpecialCircumstance.AutofillSave -> {
|
||||
RootNavState.VaultUnlockedForAutofillSave(
|
||||
autofillSaveItem = specialCircumstance.autofillSaveItem,
|
||||
)
|
||||
}
|
||||
|
||||
userState.activeAccount.isVaultUnlocked &&
|
||||
specialCircumstance is SpecialCircumstance.AutofillSelection -> {
|
||||
RootNavState.VaultUnlockedForAutofillSelection(
|
||||
activeUserId = userState.activeAccount.userId,
|
||||
type = specialCircumstance.autofillSelectionData.type,
|
||||
)
|
||||
}
|
||||
|
||||
userState.activeAccount.isVaultUnlocked &&
|
||||
action.shouldMigratePersonalVault -> {
|
||||
RootNavState.MigrateToMyItems
|
||||
}
|
||||
|
||||
userState.activeAccount.isVaultUnlocked -> {
|
||||
when (specialCircumstance) {
|
||||
is SpecialCircumstance.AutofillSave -> {
|
||||
RootNavState.VaultUnlockedForAutofillSave(
|
||||
autofillSaveItem = specialCircumstance.autofillSaveItem,
|
||||
)
|
||||
}
|
||||
|
||||
is SpecialCircumstance.AutofillSelection -> {
|
||||
RootNavState.VaultUnlockedForAutofillSelection(
|
||||
activeUserId = userState.activeAccount.userId,
|
||||
type = specialCircumstance.autofillSelectionData.type,
|
||||
)
|
||||
}
|
||||
|
||||
is SpecialCircumstance.AddTotpLoginItem -> {
|
||||
RootNavState.VaultUnlockedForNewTotp(
|
||||
activeUserId = userState.activeAccount.userId,
|
||||
@@ -227,6 +219,8 @@ class RootNavViewModel @Inject constructor(
|
||||
|
||||
is SpecialCircumstance.CredentialExchangeExport,
|
||||
is SpecialCircumstance.RegistrationEvent,
|
||||
is SpecialCircumstance.AutofillSave,
|
||||
is SpecialCircumstance.AutofillSelection,
|
||||
-> {
|
||||
throw IllegalStateException(
|
||||
"Special circumstance should have been already handled.",
|
||||
|
||||
@@ -5,11 +5,13 @@ import app.cash.turbine.turbineScope
|
||||
import com.bitwarden.collections.CollectionView
|
||||
import com.bitwarden.core.InitOrgCryptoRequest
|
||||
import com.bitwarden.core.data.manager.dispatcher.FakeDispatcherManager
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.repository.error.MissingPropertyException
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.core.data.util.asFailure
|
||||
import com.bitwarden.core.data.util.asSuccess
|
||||
import com.bitwarden.network.model.PolicyTypeJson
|
||||
import com.bitwarden.network.model.SyncResponseJson
|
||||
import com.bitwarden.network.model.createMockCipher
|
||||
import com.bitwarden.network.model.createMockCollection
|
||||
@@ -35,7 +37,10 @@ import com.x8bit.bitwarden.data.auth.repository.model.LogoutReason
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
|
||||
import com.x8bit.bitwarden.data.platform.manager.DatabaseSchemeManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PushManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
@@ -140,6 +145,15 @@ class VaultSyncManagerTest {
|
||||
private val databaseSchemeManager: DatabaseSchemeManager = mockk {
|
||||
every { databaseSchemeChangeFlow } returns mutableDatabaseSchemeChangeFlow
|
||||
}
|
||||
private val policyManager: PolicyManager = mockk<PolicyManager> {
|
||||
every { getActivePolicies(any()) } returns emptyList()
|
||||
}
|
||||
private val mockFeatureFlagManager = mockk<FeatureFlagManager>(relaxed = true) {
|
||||
every { getFeatureFlag(FlagKey.MigrateMyVaultToMyItems) } returns true
|
||||
}
|
||||
private val connectionManager: NetworkConnectionManager = mockk<NetworkConnectionManager> {
|
||||
every { isNetworkConnected } returns true
|
||||
}
|
||||
|
||||
private val vaultSyncManager: VaultSyncManager = VaultSyncManagerImpl(
|
||||
syncService = syncService,
|
||||
@@ -150,6 +164,9 @@ class VaultSyncManagerTest {
|
||||
userLogoutManager = userLogoutManager,
|
||||
userStateManager = userStateManager,
|
||||
vaultLockManager = vaultLockManager,
|
||||
policyManager = policyManager,
|
||||
featureFlagManager = mockFeatureFlagManager,
|
||||
connectionManager = connectionManager,
|
||||
clock = clock,
|
||||
databaseSchemeManager = databaseSchemeManager,
|
||||
pushManager = pushManager,
|
||||
@@ -1249,6 +1266,250 @@ class VaultSyncManagerTest {
|
||||
coVerify(exactly = 0) { syncService.sync() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigratePersonalVaultFlow should initially emit false`() = runTest {
|
||||
assertEquals(false, vaultSyncManager.shouldMigratePersonalVaultFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigratePersonalVaultFlow should emit true after sync when all conditions are met`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
setVaultToUnlocked(userId)
|
||||
|
||||
// Setup conditions for migration
|
||||
val mockPolicy = createMockPolicy(number = 1, type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
every {
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
} returns listOf(mockPolicy)
|
||||
coEvery {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
|
||||
// Setup sync with personal ciphers (organizationId = null)
|
||||
val personalCipher = createMockCipher(number = 1).copy(organizationId = null)
|
||||
val syncResponse =
|
||||
createMockSyncResponse(number = 1).copy(ciphers = listOf(personalCipher))
|
||||
coEvery { syncService.sync() } returns syncResponse.asSuccess()
|
||||
coEvery { vaultDiskSource.replaceVaultData(userId = userId, vault = any()) } just runs
|
||||
|
||||
setupEmptyDecryptionResults(userId)
|
||||
setupVaultDiskSourceFlows(userId)
|
||||
|
||||
vaultSyncManager.shouldMigratePersonalVaultFlow.test {
|
||||
assertEquals(false, awaitItem())
|
||||
|
||||
vaultSyncManager.sync()
|
||||
|
||||
assertEquals(true, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigratePersonalVaultFlow should emit false when no personal ownership policy`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
setVaultToUnlocked(userId)
|
||||
|
||||
every {
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
} returns emptyList()
|
||||
|
||||
val personalCipher = createMockCipher(number = 1).copy(organizationId = null)
|
||||
val syncResponse =
|
||||
createMockSyncResponse(number = 1).copy(ciphers = listOf(personalCipher))
|
||||
coEvery { syncService.sync() } returns syncResponse.asSuccess()
|
||||
coEvery { vaultDiskSource.replaceVaultData(userId = userId, vault = any()) } just runs
|
||||
coEvery {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
|
||||
setupEmptyDecryptionResults(userId)
|
||||
setupVaultDiskSourceFlows(userId)
|
||||
|
||||
vaultSyncManager.shouldMigratePersonalVaultFlow.test {
|
||||
assertEquals(false, awaitItem())
|
||||
|
||||
vaultSyncManager.sync()
|
||||
|
||||
// Should still be false since no policy
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigratePersonalVaultFlow should emit false when feature flag is disabled`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
setVaultToUnlocked(userId)
|
||||
|
||||
val mockPolicy = createMockPolicy(number = 1, type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
every {
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
} returns listOf(mockPolicy)
|
||||
|
||||
every {
|
||||
mockFeatureFlagManager.getFeatureFlag(FlagKey.MigrateMyVaultToMyItems)
|
||||
} returns false
|
||||
|
||||
val personalCipher = createMockCipher(number = 1).copy(organizationId = null)
|
||||
val syncResponse =
|
||||
createMockSyncResponse(number = 1).copy(ciphers = listOf(personalCipher))
|
||||
coEvery { syncService.sync() } returns syncResponse.asSuccess()
|
||||
coEvery { vaultDiskSource.replaceVaultData(userId = userId, vault = any()) } just runs
|
||||
coEvery {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
|
||||
setupEmptyDecryptionResults(userId)
|
||||
setupVaultDiskSourceFlows(userId)
|
||||
|
||||
vaultSyncManager.shouldMigratePersonalVaultFlow.test {
|
||||
assertEquals(false, awaitItem())
|
||||
|
||||
vaultSyncManager.sync()
|
||||
|
||||
// Should still be false since feature flag is disabled
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigratePersonalVaultFlow should emit false when no network connection`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
setVaultToUnlocked(userId)
|
||||
|
||||
val mockPolicy = createMockPolicy(number = 1, type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
every {
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
} returns listOf(mockPolicy)
|
||||
|
||||
every { connectionManager.isNetworkConnected } returns false
|
||||
|
||||
val personalCipher = createMockCipher(number = 1).copy(organizationId = null)
|
||||
val syncResponse =
|
||||
createMockSyncResponse(number = 1).copy(ciphers = listOf(personalCipher))
|
||||
coEvery { syncService.sync() } returns syncResponse.asSuccess()
|
||||
coEvery { vaultDiskSource.replaceVaultData(userId = userId, vault = any()) } just runs
|
||||
coEvery {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
|
||||
setupEmptyDecryptionResults(userId)
|
||||
setupVaultDiskSourceFlows(userId)
|
||||
|
||||
vaultSyncManager.shouldMigratePersonalVaultFlow.test {
|
||||
assertEquals(false, awaitItem())
|
||||
|
||||
vaultSyncManager.sync()
|
||||
|
||||
// Should still be false since no network
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigratePersonalVaultFlow should emit false when no personal ciphers exist`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
setVaultToUnlocked(userId)
|
||||
|
||||
val mockPolicy = createMockPolicy(number = 1, type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
every {
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
} returns listOf(mockPolicy)
|
||||
|
||||
// All ciphers belong to organizations
|
||||
val orgCipher = createMockCipher(number = 1).copy(organizationId = "org-id")
|
||||
val syncResponse = createMockSyncResponse(number = 1).copy(ciphers = listOf(orgCipher))
|
||||
coEvery { syncService.sync() } returns syncResponse.asSuccess()
|
||||
coEvery { vaultDiskSource.replaceVaultData(userId = userId, vault = any()) } just runs
|
||||
coEvery {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
|
||||
setupEmptyDecryptionResults(userId)
|
||||
setupVaultDiskSourceFlows(userId)
|
||||
|
||||
vaultSyncManager.shouldMigratePersonalVaultFlow.test {
|
||||
assertEquals(false, awaitItem())
|
||||
|
||||
vaultSyncManager.sync()
|
||||
|
||||
// Should still be false since no personal ciphers
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldMigratePersonalVaultFlow should emit false when cipher list is null`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
setVaultToUnlocked(userId)
|
||||
|
||||
val mockPolicy = createMockPolicy(number = 1, type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
every {
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
} returns listOf(mockPolicy)
|
||||
|
||||
val syncResponse = createMockSyncResponse(number = 1).copy(ciphers = null)
|
||||
coEvery { syncService.sync() } returns syncResponse.asSuccess()
|
||||
coEvery { vaultDiskSource.replaceVaultData(userId = userId, vault = any()) } just runs
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(
|
||||
organizationKeys = createMockOrganizationKeys(number = 1),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
|
||||
setupEmptyDecryptionResults(userId)
|
||||
setupVaultDiskSourceFlows(userId)
|
||||
|
||||
vaultSyncManager.shouldMigratePersonalVaultFlow.test {
|
||||
assertEquals(false, awaitItem())
|
||||
|
||||
vaultSyncManager.sync()
|
||||
|
||||
// Should still be false since ciphers is null
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
//region Helper functions
|
||||
|
||||
/**
|
||||
|
||||
+52
@@ -28,6 +28,7 @@ import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.CompleteRegistrationData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.model.SendItemType
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
@@ -61,6 +62,11 @@ class RootNavViewModelTest : BaseViewModelTest() {
|
||||
dispatcherManager = FakeDispatcherManager(),
|
||||
)
|
||||
|
||||
private val mutableShouldMigratePersonalVaultFlow = MutableStateFlow(false)
|
||||
private val vaultRepository = mockk<VaultRepository> {
|
||||
every { shouldMigratePersonalVaultFlow } returns mutableShouldMigratePersonalVaultFlow
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockkStatic(::parseJwtTokenDataOrNull)
|
||||
@@ -1579,10 +1585,56 @@ class RootNavViewModelTest : BaseViewModelTest() {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when shouldMigratePersonalVaultFlow emits true the nav state should be MigrateToMyItems`() {
|
||||
mutableShouldMigratePersonalVaultFlow.value = true
|
||||
mutableUserStateFlow.tryEmit(MOCK_VAULT_UNLOCKED_USER_STATE)
|
||||
val viewModel = createViewModel()
|
||||
|
||||
assertEquals(
|
||||
RootNavState.MigrateToMyItems,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when vault is locked MigrateToMyItems should not show even if flow emits true`() {
|
||||
mutableShouldMigratePersonalVaultFlow.value = true
|
||||
|
||||
// Vault is locked
|
||||
mutableUserStateFlow.tryEmit(
|
||||
MOCK_VAULT_UNLOCKED_USER_STATE.copy(
|
||||
accounts = listOf(
|
||||
MOCK_VAULT_UNLOCKED_USER_STATE.activeAccount.copy(isVaultUnlocked = false),
|
||||
),
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
|
||||
assertEquals(
|
||||
RootNavState.VaultLocked,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when shouldMigratePersonalVaultFlow emits false MigrateToMyItems should not show`() {
|
||||
mutableShouldMigratePersonalVaultFlow.value = false
|
||||
mutableUserStateFlow.tryEmit(MOCK_VAULT_UNLOCKED_USER_STATE)
|
||||
val viewModel = createViewModel()
|
||||
|
||||
assertEquals(
|
||||
RootNavState.VaultUnlocked("activeUserId"),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createViewModel(): RootNavViewModel =
|
||||
RootNavViewModel(
|
||||
authRepository = authRepository,
|
||||
specialCircumstanceManager = specialCircumstanceManager,
|
||||
vaultRepository = vaultRepository,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+81
-17
@@ -2,29 +2,35 @@ package com.x8bit.bitwarden.ui.vault.feature.migratetomyitems
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.data.repository.model.Environment
|
||||
import com.bitwarden.network.model.OrganizationType
|
||||
import com.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.Organization
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class MigrateToMyItemsViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockkStatic(SavedStateHandle::toMigrateToMyItemsArgs)
|
||||
private val mutableUserStateFlow = MutableStateFlow<UserState?>(MOCK_USER_STATE)
|
||||
private val authRepository = mockk<AuthRepository> {
|
||||
every { userStateFlow } returns mutableUserStateFlow
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
unmockkStatic(SavedStateHandle::toMigrateToMyItemsArgs)
|
||||
private val policyManager = mockk<PolicyManager> {
|
||||
every { getPersonalOwnershipPolicyOrganizationId() } returns ORGANIZATION_ID
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -150,18 +156,76 @@ class MigrateToMyItemsViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial state should handle null organization ID`() {
|
||||
every { policyManager.getPersonalOwnershipPolicyOrganizationId() } returns null
|
||||
val viewModel = createViewModel()
|
||||
|
||||
assertEquals("", viewModel.stateFlow.value.organizationId)
|
||||
assertEquals("", viewModel.stateFlow.value.organizationName)
|
||||
assertNull(viewModel.stateFlow.value.dialog)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial state should handle null organization name`() {
|
||||
mutableUserStateFlow.value = MOCK_USER_STATE.copy(
|
||||
accounts = listOf(
|
||||
MOCK_USER_STATE.activeAccount.copy(organizations = emptyList()),
|
||||
),
|
||||
)
|
||||
val viewModel = createViewModel()
|
||||
|
||||
assertEquals(ORGANIZATION_ID, viewModel.stateFlow.value.organizationId)
|
||||
assertEquals("", viewModel.stateFlow.value.organizationName)
|
||||
assertNull(viewModel.stateFlow.value.dialog)
|
||||
}
|
||||
|
||||
private fun createViewModel(
|
||||
savedStateHandle: SavedStateHandle = SavedStateHandle(),
|
||||
): MigrateToMyItemsViewModel {
|
||||
every { savedStateHandle.toMigrateToMyItemsArgs() } returns MigrateToMyItemsArgs(
|
||||
organizationId = ORGANIZATION_ID,
|
||||
organizationName = ORGANIZATION_NAME,
|
||||
)
|
||||
return MigrateToMyItemsViewModel(
|
||||
): MigrateToMyItemsViewModel =
|
||||
MigrateToMyItemsViewModel(
|
||||
authRepository = authRepository,
|
||||
policyManager = policyManager,
|
||||
savedStateHandle = savedStateHandle,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val ORGANIZATION_ID = "test-organization-id"
|
||||
private const val ORGANIZATION_NAME = "Test Organization"
|
||||
|
||||
private val MOCK_USER_STATE = UserState(
|
||||
activeUserId = "activeUserId",
|
||||
accounts = listOf(
|
||||
UserState.Account(
|
||||
userId = "activeUserId",
|
||||
name = "Test User",
|
||||
email = "test@example.com",
|
||||
avatarColorHex = "#FF0000",
|
||||
environment = Environment.Us,
|
||||
isPremium = false,
|
||||
isLoggedIn = true,
|
||||
isVaultUnlocked = true,
|
||||
needsPasswordReset = false,
|
||||
needsMasterPassword = false,
|
||||
hasMasterPassword = true,
|
||||
trustedDevice = null,
|
||||
organizations = listOf(
|
||||
Organization(
|
||||
id = ORGANIZATION_ID,
|
||||
name = ORGANIZATION_NAME,
|
||||
shouldManageResetPassword = false,
|
||||
shouldUseKeyConnector = false,
|
||||
role = OrganizationType.USER,
|
||||
keyConnectorUrl = null,
|
||||
userIsClaimedByOrganization = false,
|
||||
),
|
||||
),
|
||||
isBiometricsEnabled = false,
|
||||
vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
|
||||
isUsingKeyConnector = false,
|
||||
onboardingStatus = OnboardingStatus.COMPLETE,
|
||||
firstTimeState = FirstTimeState(showImportLoginsCard = false),
|
||||
isExportable = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
Reference in new issue
Block a user