From 1f8280f76d6fe5aeb50aa47b51a6724e8109ce8f Mon Sep 17 00:00:00 2001 From: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com> Date: Mon, 18 May 2026 16:48:48 -0400 Subject: [PATCH] [PM-37465] fix: Gate Plan row and Upgraded card on personal Premium (#6930) --- .../auth/datasource/disk/model/AccountJson.kt | 11 +- .../data/auth/repository/model/UserState.kt | 9 +- .../util/GetTokenResponseExtensions.kt | 3 +- .../util/UserStateJsonExtensions.kt | 7 +- .../billing/manager/PremiumStateManager.kt | 6 + .../manager/PremiumStateManagerImpl.kt | 50 +++- .../feature/settings/SettingsViewModel.kt | 49 ++-- .../com/x8bit/bitwarden/MainViewModelTest.kt | 1 + .../datasource/disk/AuthDiskSourceTest.kt | 3 +- .../auth/manager/AuthRequestManagerTest.kt | 3 +- .../data/auth/manager/AuthTokenManagerTest.kt | 3 +- .../data/auth/manager/KdfManagerTest.kt | 6 +- .../auth/manager/TrustedDeviceManagerTests.kt | 6 +- .../auth/manager/UserLogoutManagerTest.kt | 6 +- .../data/auth/manager/UserStateManagerTest.kt | 6 +- .../util/TrustDeviceResponseExtensionsTest.kt | 6 +- .../auth/repository/AuthRepositoryTest.kt | 6 +- .../util/AuthDiskSourceExtensionsTest.kt | 3 +- .../util/GetTokenResponseExtensionsTest.kt | 6 +- .../util/UserStateJsonExtensionsTest.kt | 244 +++++++++++++++--- .../manager/PremiumStateManagerImplTest.kt | 197 +++++++++++++- .../CredentialProviderProcessorTest.kt | 1 + .../manager/FirstTimeActionManagerTest.kt | 3 +- .../repository/SettingsRepositoryTest.kt | 3 +- .../repository/GeneratorRepositoryTest.kt | 3 +- .../data/vault/manager/CipherManagerTest.kt | 3 +- .../data/vault/manager/FolderManagerTest.kt | 3 +- .../data/vault/manager/SendManagerTest.kt | 3 +- .../vault/manager/VaultLockManagerTest.kt | 3 +- .../manager/VaultMigrationManagerTest.kt | 3 +- .../vault/manager/VaultSyncManagerTest.kt | 4 +- .../vault/repository/VaultRepositoryTest.kt | 3 +- .../vault/repository/model/AccountJsonUtil.kt | 5 +- .../accountsetup/SetupUnlockViewModelTest.kt | 1 + .../feature/landing/LandingViewModelTest.kt | 6 + .../auth/feature/login/LoginViewModelTest.kt | 1 + .../RemovePasswordViewModelTest.kt | 1 + .../TrustedDeviceViewModelTest.kt | 1 + .../vaultunlock/VaultUnlockViewModelTest.kt | 3 + .../feature/premium/plan/PlanViewModelTest.kt | 1 + .../feature/rootnav/RootNavViewModelTest.kt | 34 +++ .../feature/search/SearchViewModelTest.kt | 1 + .../feature/settings/SettingsScreenTest.kt | 1 + .../feature/settings/SettingsViewModelTest.kt | 48 +--- .../AccountSecurityViewModelTest.kt | 1 + .../DeleteAccountViewModelTest.kt | 1 + .../LoginApprovalViewModelTest.kt | 2 + .../exportvault/ExportVaultViewModelTest.kt | 1 + .../generator/GeneratorViewModelTest.kt | 1 + .../tools/feature/send/SendViewModelTest.kt | 1 + .../send/addedit/AddEditSendViewModelTest.kt | 1 + .../addedit/VaultAddEditViewModelTest.kt | 1 + .../addedit/util/CipherViewExtensionsTest.kt | 1 + .../attachments/AttachmentsViewModelTest.kt | 1 + .../exportitems/SelectAccountViewModelTest.kt | 1 + .../reviewexport/ReviewExportViewModelTest.kt | 1 + .../VerifyPasswordScreenTest.kt | 1 + .../VerifyPasswordViewModelTest.kt | 2 + .../feature/item/VaultItemViewModelTest.kt | 1 + .../VaultItemListingViewModelTest.kt | 1 + .../LeaveOrganizationViewModelTest.kt | 1 + .../VaultMoveToOrganizationViewModelTest.kt | 1 + .../VaultMoveToOrganizationExtensionsTest.kt | 1 + .../vault/feature/vault/VaultViewModelTest.kt | 4 + .../vault/util/UserStateExtensionsTest.kt | 10 + 65 files changed, 654 insertions(+), 147 deletions(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/model/AccountJson.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/model/AccountJson.kt index 11b5ca1e37..1860dbbf59 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/model/AccountJson.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/model/AccountJson.kt @@ -42,7 +42,11 @@ data class AccountJson( * @property name The user's name (if applicable). * @property stamp The account's security stamp (if applicable). * @property organizationId The ID of the associated organization (if applicable). - * @property hasPremium True if the user has a Premium account. + * @property hasPremiumPersonally True if the user has personal Premium (i.e., a personal + * subscription not derived from any organization membership). + * @property hasPremiumFromOrganization True if any organization the user is a member of grants + * Premium features. `null` when the value has not yet been synced (e.g., immediately after + * token-based login before the first sync completes). * @property avatarColorHex Hex color value for a user's avatar in the "#AARRGGBB" format. * @property forcePasswordResetReason Describes the reason for a forced password reset. * @property kdfType The KDF type. @@ -80,7 +84,10 @@ data class AccountJson( val avatarColorHex: String?, @SerialName("hasPremiumPersonally") - val hasPremium: Boolean?, + val hasPremiumPersonally: Boolean?, + + @SerialName("hasPremiumFromOrganization") + val hasPremiumFromOrganization: Boolean?, @SerialName("forcePasswordResetReason") val forcePasswordResetReason: ForcePasswordResetReason?, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt index cb792c09d5..8c58867d39 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt @@ -42,7 +42,12 @@ data class UserState( * @property name The user's name (if applicable). * @property avatarColorHex Hex color value for a user's avatar in the "#AARRGGBB" format. * @property environment The [Environment] associated with the user's account. - * @property isPremium `true` if the account has a Premium membership. + * @property isPremium `true` if the account has a Premium membership from any source (personal + * subscription or organization-granted). + * @property isPremiumFromSelf `true` if the account has a personal Premium subscription. This + * is `false` for users whose only Premium access is granted by an organization they are a + * member of. Use this when behavior should be gated on the user's own subscription, not on + * organization-granted Premium. * @property isLoggedIn `true` if the account is logged in, or `false` if it requires additional * authentication to view their vault. * @property isVaultUnlocked Whether the user's vault is currently unlocked. @@ -66,6 +71,7 @@ data class UserState( val avatarColorHex: String, val environment: Environment, val isPremium: Boolean, + val isPremiumFromSelf: Boolean, val isLoggedIn: Boolean, val isVaultUnlocked: Boolean, val needsPasswordReset: Boolean, @@ -113,6 +119,7 @@ data class UserState( avatarColorHex = "".toHexColorRepresentation(), environment = Environment.Us, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/GetTokenResponseExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/GetTokenResponseExtensions.kt index 90b1ce1205..e035681cdb 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/GetTokenResponseExtensions.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/GetTokenResponseExtensions.kt @@ -31,7 +31,8 @@ fun GetTokenResponseJson.Success.toUserState( stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = jwtTokenData.hasPremium, + hasPremiumPersonally = jwtTokenData.hasPremium, + hasPremiumFromOrganization = null, forcePasswordResetReason = this.toForcePasswordResetReason(), kdfType = this.kdfType, kdfIterations = this.kdfIterations, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt index 76612273de..2de5958608 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt @@ -83,7 +83,8 @@ fun UserStateJson.toUpdatedUserStateJson( .copy( avatarColorHex = syncProfile.avatarColor, stamp = syncProfile.securityStamp, - hasPremium = syncProfile.isPremium || syncProfile.isPremiumFromOrganization, + hasPremiumPersonally = syncProfile.isPremium, + hasPremiumFromOrganization = syncProfile.isPremiumFromOrganization, isTwoFactorEnabled = syncProfile.isTwoFactorEnabled, creationDate = syncProfile.creationDate, userDecryptionOptions = userDecryptionOptions, @@ -253,7 +254,9 @@ fun UserStateJson.toUserState( .settings .environmentUrlData .toEnvironmentUrlsOrDefault(), - isPremium = profile.hasPremium == true, + isPremium = profile.hasPremiumPersonally == true || + profile.hasPremiumFromOrganization == true, + isPremiumFromSelf = profile.hasPremiumPersonally == true, isLoggedIn = userAccountTokens .find { it.userId == userId } ?.isLoggedIn == true, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManager.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManager.kt index b2b37bae1b..7b0eb6bc81 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManager.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManager.kt @@ -26,6 +26,12 @@ interface PremiumStateManager { */ val isUpgradedToPremiumCardEligibleFlow: StateFlow + /** + * Emits `true` when the active user is eligible to see the Plan row in Settings, or `false` + * otherwise. + */ + val isPlanRowEligibleFlow: StateFlow + /** * Returns `true` when the in-app upgrade flow is available, or `false` otherwise. */ diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManagerImpl.kt index e9a51d1c70..dcf0f0f1e2 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManagerImpl.kt @@ -94,6 +94,32 @@ class PremiumStateManagerImpl( initialValue = false, ) + /** + * Eligibility is keyed on the user holding personal Premium (or being eligible to purchase + * it). Organization-granted Premium does not surface the Plan row, since the user has no + * personal subscription to manage. + */ + override val isPlanRowEligibleFlow: StateFlow = + combine( + authRepository.userStateFlow, + featureFlagManager.getFeatureFlagFlow(FlagKey.MobilePremiumUpgrade), + ) { userState, featureFlagEnabled -> + val activeAccount = userState?.activeAccount ?: return@combine false + val isOrgOnlyPremium = activeAccount.isPremium && !activeAccount.isPremiumFromSelf + featureFlagEnabled && !isOrgOnlyPremium + } + .stateIn( + scope = unconfinedScope, + started = SharingStarted.Eagerly, + initialValue = false, + ) + + /** + * The card surfaces only while the active user holds personal Premium. This guards against + * non-personal upgrade signals (e.g., the debug menu trigger or a stray + * `PREMIUM_STATUS_CHANGED` push for an organization grant) marking the card pending for users + * with no personal subscription to celebrate. + */ @OptIn(ExperimentalCoroutinesApi::class) override val isUpgradedToPremiumCardEligibleFlow: StateFlow = authDiskSource @@ -109,7 +135,12 @@ class PremiumStateManagerImpl( settingsDiskSource .getUpgradedToPremiumCardConsumedFlow(userId) .map { it ?: false }, - ) { isPending, isConsumed -> isPending && !isConsumed } + authRepository + .userStateFlow + .map { it?.activeAccount?.isPremiumFromSelf == true }, + ) { isPending, isConsumed, isPremiumFromSelf -> + isPending && !isConsumed && isPremiumFromSelf + } } } .distinctUntilChanged() @@ -131,23 +162,22 @@ class PremiumStateManagerImpl( } .launchIn(unconfinedScope) - // Sync-delta detection: observe the active user's premium flag transitioning false → true - // (e.g., F-Droid users without push support). NOTE: UserState.Account.isPremium is - // derived from `hasPremium = isPremium || isPremiumFromOrganization` so this path may - // also fire for organization-granted premium. The push path (above) is personal-only and - // takes precedence on flavors that support it. + // Sync-delta detection: observe the active user's personal premium flag transitioning + // false → true (e.g., F-Droid users without push support). Keyed on `isPremiumFromSelf` + // so that organization-granted premium does not trigger the personal-upgrade card. authRepository .userStateFlow .map { state -> - state?.activeAccount?.let { it.userId to it.isPremium } + state?.activeAccount?.let { it.userId to it.isPremiumFromSelf } } .distinctUntilChanged() .scanPairs() .onEach { (previous, current) -> if (current == null) return@onEach - val (currentUserId, currentIsPremium) = current - if (!currentIsPremium) return@onEach - // Same user transitioning from non-premium to premium counts as an upgrade. + val (currentUserId, currentIsPremiumFromSelf) = current + if (!currentIsPremiumFromSelf) return@onEach + // Same user transitioning from non-personal-premium to personal-premium counts as + // an upgrade. if (previous?.first == currentUserId && !previous.second) { markUpgradedToPremiumCardPending(userId = currentUserId) } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModel.kt index 7b0a3d9623..3732597afd 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModel.kt @@ -4,7 +4,6 @@ import androidx.annotation.DrawableRes import androidx.compose.material3.Text import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope -import com.bitwarden.core.data.manager.model.FlagKey import com.bitwarden.ui.platform.base.BaseViewModel import com.bitwarden.ui.platform.base.DeferredBackgroundEvent import com.bitwarden.ui.platform.resource.BitwardenDrawable @@ -13,7 +12,6 @@ import com.bitwarden.ui.util.Text import com.bitwarden.ui.util.asText import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager import com.x8bit.bitwarden.data.billing.manager.UPGRADED_TO_PREMIUM_LEARN_MORE_URL -import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance @@ -34,7 +32,6 @@ import javax.inject.Inject class SettingsViewModel @Inject constructor( specialCircumstanceManager: SpecialCircumstanceManager, firstTimeActionManager: FirstTimeActionManager, - featureFlagManager: FeatureFlagManager, private val premiumStateManager: PremiumStateManager, savedStateHandle: SavedStateHandle, ) : BaseViewModel( @@ -43,8 +40,7 @@ class SettingsViewModel @Inject constructor( securityCount = firstTimeActionManager.allSecuritySettingsBadgeCountFlow.value, autoFillCount = firstTimeActionManager.allAutofillSettingsBadgeCountFlow.value, vaultCount = firstTimeActionManager.allVaultSettingsBadgeCountFlow.value, - isMobilePremiumUpgradeEnabled = featureFlagManager - .getFeatureFlag(FlagKey.MobilePremiumUpgrade), + isPlanRowEligible = premiumStateManager.isPlanRowEligibleFlow.value, isUpgradedToPremiumCardEligible = premiumStateManager .isUpgradedToPremiumCardEligibleFlow .value, @@ -66,13 +62,9 @@ class SettingsViewModel @Inject constructor( .onEach(::sendAction) .launchIn(viewModelScope) - featureFlagManager - .getFeatureFlagFlow(FlagKey.MobilePremiumUpgrade) - .map { - SettingsAction.Internal.MobilePremiumUpgradeFlagUpdate( - isMobilePremiumUpgradeEnabled = it, - ) - } + premiumStateManager + .isPlanRowEligibleFlow + .map { SettingsAction.Internal.PlanRowEligibilityReceive(isEligible = it) } .onEach(::sendAction) .launchIn(viewModelScope) @@ -103,8 +95,8 @@ class SettingsViewModel @Inject constructor( handleSettingsNotificationCountUpdate(action) } - is SettingsAction.Internal.MobilePremiumUpgradeFlagUpdate -> { - handleMobilePremiumUpgradeFlagUpdate(action) + is SettingsAction.Internal.PlanRowEligibilityReceive -> { + handlePlanRowEligibilityReceive(action) } is SettingsAction.Internal.UpgradedToPremiumCardEligibilityReceive -> { @@ -145,15 +137,10 @@ class SettingsViewModel @Inject constructor( } } - private fun handleMobilePremiumUpgradeFlagUpdate( - action: SettingsAction.Internal.MobilePremiumUpgradeFlagUpdate, + private fun handlePlanRowEligibilityReceive( + action: SettingsAction.Internal.PlanRowEligibilityReceive, ) { - mutableStateFlow.update { - it.copy( - isMobilePremiumUpgradeEnabled = - action.isMobilePremiumUpgradeEnabled, - ) - } + mutableStateFlow.update { it.copy(isPlanRowEligible = action.isEligible) } } private fun handleSettingsClick(action: SettingsAction.SettingsClick) { @@ -197,7 +184,7 @@ data class SettingsState( private val autoFillCount: Int, private val securityCount: Int, private val vaultCount: Int, - private val isMobilePremiumUpgradeEnabled: Boolean = false, + private val isPlanRowEligible: Boolean, private val isUpgradedToPremiumCardEligible: Boolean = false, ) { val shouldShowCloseButton: Boolean = isPreAuth @@ -209,12 +196,12 @@ data class SettingsState( val shouldShowUpgradedToPremiumCard: Boolean = !isPreAuth && isUpgradedToPremiumCardEligible /** - * Whether the plan row should be shown. The row is visible when the - * mobile premium upgrade feature flag is enabled and the user is - * authenticated. + * Whether the plan row should be shown. The row is visible post-authentication when the user + * is eligible per [PremiumStateManager.isPlanRowEligibleFlow] — currently, when the in-app + * upgrade feature is enabled and the user is not relying solely on organization-granted + * Premium. */ - private val shouldShowPlanRow: Boolean = - !isPreAuth && isMobilePremiumUpgradeEnabled + private val shouldShowPlanRow: Boolean = !isPreAuth && isPlanRowEligible val settingRows: ImmutableList = Settings .entries @@ -335,10 +322,10 @@ sealed class SettingsAction { ) : Internal() /** - * Update the mobile premium upgrade feature flag state. + * Indicates that the Plan row eligibility has been updated. */ - data class MobilePremiumUpgradeFlagUpdate( - val isMobilePremiumUpgradeEnabled: Boolean, + data class PlanRowEligibilityReceive( + val isEligible: Boolean, ) : Internal() /** diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt index f2b302e8e9..0fc00f0e92 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt @@ -1416,6 +1416,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( environment = Environment.Us, avatarColorHex = "#aa00aa", isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt index 40c22714dd..730f9e3f7a 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt @@ -1570,7 +1570,8 @@ private val USER_STATE = UserStateJson( stamp = "stamp", organizationId = "organizationId", avatarColorHex = "avatarColorHex", - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = ForcePasswordResetReason.ADMIN_FORCE_PASSWORD_RESET, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/AuthRequestManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/AuthRequestManagerTest.kt index abcaaad430..e73a0968b5 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/AuthRequestManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/AuthRequestManagerTest.kt @@ -1165,7 +1165,8 @@ private val ACCOUNT: AccountJson = AccountJson( email = EMAIL, isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/AuthTokenManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/AuthTokenManagerTest.kt index f5afbdd822..30a86519bb 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/AuthTokenManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/AuthTokenManagerTest.kt @@ -177,7 +177,8 @@ private val ACCOUNT: AccountJson = AccountJson( email = EMAIL, isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/KdfManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/KdfManagerTest.kt index 104f626318..a4a43e5a78 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/KdfManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/KdfManagerTest.kt @@ -344,7 +344,8 @@ private val PROFILE_1 = AccountJson.Profile( email = EMAIL, isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, @@ -371,7 +372,8 @@ private val ACCOUNT_2 = AccountJson( email = EMAIL_2, isEmailVerified = true, name = "Bitwarden Tester 2", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/TrustedDeviceManagerTests.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/TrustedDeviceManagerTests.kt index 38a70bd16a..df41de46be 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/TrustedDeviceManagerTests.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/TrustedDeviceManagerTests.kt @@ -289,7 +289,8 @@ private val DEFAULT_ACCOUNT = AccountJson( email = "test@bitwarden.com", isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, @@ -313,7 +314,8 @@ private val UPDATED_ACCOUNT = AccountJson( email = "test@bitwarden.com", isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerTest.kt index b80a797673..00166467e2 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerTest.kt @@ -289,7 +289,8 @@ private val ACCOUNT_1 = AccountJson( email = "test@bitwarden.com", isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, @@ -316,7 +317,8 @@ private val ACCOUNT_2 = AccountJson( email = EMAIL_2, isEmailVerified = true, name = "Bitwarden Tester 2", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserStateManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserStateManagerTest.kt index 34244c4a01..6a77228696 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserStateManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserStateManagerTest.kt @@ -327,7 +327,8 @@ private val PROFILE_1 = AccountJson.Profile( email = EMAIL_1, isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, @@ -352,7 +353,8 @@ private val ACCOUNT_2 = AccountJson( email = EMAIL_2, isEmailVerified = true, name = "Bitwarden Tester 2", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/util/TrustDeviceResponseExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/util/TrustDeviceResponseExtensionsTest.kt index c1ab9f8ea7..f62a1e017b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/util/TrustDeviceResponseExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/util/TrustDeviceResponseExtensionsTest.kt @@ -71,7 +71,8 @@ private val DEFAULT_ACCOUNT = AccountJson( email = "test@bitwarden.com", isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, @@ -95,7 +96,8 @@ private val UPDATED_ACCOUNT = AccountJson( email = "test@bitwarden.com", isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index 31556243ce..54d402aea1 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -7916,7 +7916,8 @@ class AuthRepositoryTest { email = EMAIL, isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, @@ -7954,7 +7955,8 @@ class AuthRepositoryTest { email = EMAIL_2, isEmailVerified = true, name = "Bitwarden Tester 2", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt index 6e39bcb61e..280ef1b5e6 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt @@ -482,7 +482,8 @@ private val MOCK_PROFILE = AccountJson.Profile( stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = null, kdfIterations = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/GetTokenResponseExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/GetTokenResponseExtensionsTest.kt index 55c0d7de83..02fc99f7b6 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/GetTokenResponseExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/GetTokenResponseExtensionsTest.kt @@ -134,7 +134,8 @@ private val PROFILE_1 = AccountJson.Profile( email = "test@bitwarden.com", isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, @@ -159,7 +160,8 @@ private val ACCOUNT_2 = AccountJson( email = "test2@bitwarden.com", isEmailVerified = true, name = "Bitwarden Tester 2", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt index 206c53bbd5..e5605fa0ca 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt @@ -36,6 +36,8 @@ 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.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import java.time.Instant @@ -77,7 +79,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, @@ -127,7 +130,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, @@ -193,6 +197,147 @@ class UserStateJsonExtensionsTest { ) } + @Suppress("MaxLineLength") + @Test + fun `toUpdatedUserStateJson should store personal and organization-granted premium separately`() { + val originalProfile = AccountJson.Profile( + userId = "activeUserId", + email = "email", + isEmailVerified = true, + name = "name", + stamp = "stamp", + organizationId = null, + avatarColorHex = "color", + hasPremiumPersonally = false, + hasPremiumFromOrganization = false, + forcePasswordResetReason = null, + kdfType = KdfTypeJson.ARGON2_ID, + kdfIterations = 600000, + kdfMemory = 16, + kdfParallelism = 4, + userDecryptionOptions = null, + isTwoFactorEnabled = false, + creationDate = null, + ) + val originalAccount = AccountJson( + profile = originalProfile, + tokens = mockk(), + settings = mockk(), + ) + val originalState = UserStateJson( + activeUserId = "activeUserId", + accounts = mapOf("activeUserId" to originalAccount), + ) + + val orgOnlyResult = originalState.toUpdatedUserStateJson( + syncResponse = mockk { + every { profile } returns mockk { + every { id } returns "activeUserId" + every { avatarColor } returns "color" + every { securityStamp } returns "stamp" + every { isPremium } returns false + every { isPremiumFromOrganization } returns true + every { isTwoFactorEnabled } returns false + every { creationDate } returns Instant.parse("2024-09-13T01:00:00.00Z") + every { userDecryption } returns null + } + }, + ) + val orgOnlyProfile = orgOnlyResult.accounts.getValue("activeUserId").profile + assertEquals(false, orgOnlyProfile.hasPremiumPersonally) + assertEquals(true, orgOnlyProfile.hasPremiumFromOrganization) + + val personalOnlyResult = originalState.toUpdatedUserStateJson( + syncResponse = mockk { + every { profile } returns mockk { + every { id } returns "activeUserId" + every { avatarColor } returns "color" + every { securityStamp } returns "stamp" + every { isPremium } returns true + every { isPremiumFromOrganization } returns false + every { isTwoFactorEnabled } returns false + every { creationDate } returns Instant.parse("2024-09-13T01:00:00.00Z") + every { userDecryption } returns null + } + }, + ) + val personalOnlyProfile = personalOnlyResult.accounts.getValue("activeUserId").profile + assertEquals(true, personalOnlyProfile.hasPremiumPersonally) + assertEquals(false, personalOnlyProfile.hasPremiumFromOrganization) + } + + @Suppress("MaxLineLength") + @Test + fun `toUserState should derive aggregate isPremium and isPremiumFromSelf from profile premium fields`() { + val baseProfile = AccountJson.Profile( + userId = "activeUserId", + email = "email", + isEmailVerified = true, + name = "name", + stamp = "stamp", + organizationId = null, + avatarColorHex = "color", + hasPremiumPersonally = false, + hasPremiumFromOrganization = false, + forcePasswordResetReason = null, + kdfType = KdfTypeJson.ARGON2_ID, + kdfIterations = 600000, + kdfMemory = 16, + kdfParallelism = 4, + userDecryptionOptions = null, + isTwoFactorEnabled = false, + creationDate = null, + ) + + fun stateWith(hasPremiumPersonally: Boolean?, hasPremiumFromOrg: Boolean?): UserStateJson = + UserStateJson( + activeUserId = "activeUserId", + accounts = mapOf( + "activeUserId" to AccountJson( + profile = baseProfile.copy( + hasPremiumPersonally = hasPremiumPersonally, + hasPremiumFromOrganization = hasPremiumFromOrg, + ), + tokens = null, + settings = AccountJson.Settings(environmentUrlData = null), + ), + ), + ) + + fun toAccount(state: UserStateJson) = state + .toUserState( + vaultState = emptyList(), + userAccountTokens = emptyList(), + userOrganizationsList = emptyList(), + userIsUsingKeyConnectorList = emptyList(), + hasPendingAccountAddition = false, + onboardingStatus = OnboardingStatus.COMPLETE, + firstTimeState = FirstTimeState(), + isBiometricsEnabledProvider = { false }, + vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, + isDeviceTrustedProvider = { false }, + getUserPolicies = { _, _ -> emptyList() }, + ) + .accounts + .first() + + val freeAccount = toAccount(stateWith(hasPremiumPersonally = false, hasPremiumFromOrg = false)) + assertFalse(freeAccount.isPremium) + assertFalse(freeAccount.isPremiumFromSelf) + + val personalAccount = toAccount(stateWith(hasPremiumPersonally = true, hasPremiumFromOrg = false)) + assertTrue(personalAccount.isPremium) + assertTrue(personalAccount.isPremiumFromSelf) + + val orgOnlyAccount = toAccount(stateWith(hasPremiumPersonally = false, hasPremiumFromOrg = true)) + assertTrue(orgOnlyAccount.isPremium) + assertFalse(orgOnlyAccount.isPremiumFromSelf) + + val bothAccount = toAccount(stateWith(hasPremiumPersonally = true, hasPremiumFromOrg = true)) + assertTrue(bothAccount.isPremium) + assertTrue(bothAccount.isPremiumFromSelf) + } + @Test fun `toUpdatedUserStateJson should update the correct account with new information`() { val originalProfile = AccountJson.Profile( @@ -203,7 +348,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, @@ -226,6 +372,7 @@ class UserStateJsonExtensionsTest { profile = originalProfile.copy( avatarColorHex = "avatarColor", stamp = "securityStamp", + hasPremiumFromOrganization = true, isTwoFactorEnabled = false, creationDate = Instant.parse("2024-09-13T01:00:00.00Z"), ), @@ -266,7 +413,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = ForcePasswordResetReason .TDE_USER_WITHOUT_PASSWORD_HAS_PASSWORD_RESET_PERMISSION, kdfType = KdfTypeJson.ARGON2_ID, @@ -320,7 +468,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = ForcePasswordResetReason .TDE_USER_WITHOUT_PASSWORD_HAS_PASSWORD_RESET_PERMISSION, kdfType = KdfTypeJson.ARGON2_ID, @@ -393,7 +542,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, @@ -453,6 +603,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "activeAvatarColorHex", environment = Environment.Eu, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -486,7 +637,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns "activeAvatarColorHex" - every { hasPremium } returns null + every { hasPremiumPersonally } returns null + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null every { userDecryptionOptions } returns UserDecryptionOptionsJson( hasMasterPassword = true, @@ -564,6 +716,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -598,7 +751,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns null - every { hasPremium } returns true + every { hasPremiumPersonally } returns true + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null every { userDecryptionOptions } returns UserDecryptionOptionsJson( hasMasterPassword = false, @@ -672,6 +826,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -711,7 +866,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns null - every { hasPremium } returns true + every { hasPremiumPersonally } returns true + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null every { userDecryptionOptions } returns UserDecryptionOptionsJson( hasMasterPassword = false, @@ -788,6 +944,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -827,7 +984,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns null - every { hasPremium } returns true + every { hasPremiumPersonally } returns true + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null every { userDecryptionOptions } returns UserDecryptionOptionsJson( hasMasterPassword = false, @@ -904,6 +1062,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -943,7 +1102,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns null - every { hasPremium } returns true + every { hasPremiumPersonally } returns true + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null every { userDecryptionOptions } returns UserDecryptionOptionsJson( hasMasterPassword = false, @@ -1020,6 +1180,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -1066,7 +1227,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns null - every { hasPremium } returns true + every { hasPremiumPersonally } returns true + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null every { userDecryptionOptions } returns UserDecryptionOptionsJson( hasMasterPassword = false, @@ -1143,6 +1305,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -1170,7 +1333,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns null - every { hasPremium } returns false + every { hasPremiumPersonally } returns false + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null // The decryption options are what are determining the result @Suppress("MaxLineLength") @@ -1226,6 +1390,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -1253,7 +1418,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns null - every { hasPremium } returns false + every { hasPremiumPersonally } returns false + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null // The decryption options are what are determining the result every { userDecryptionOptions } returns UserDecryptionOptionsJson( @@ -1312,6 +1478,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -1352,7 +1519,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns null - every { hasPremium } returns false + every { hasPremiumPersonally } returns false + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null // The decryption options are what are determining the result @Suppress("MaxLineLength") @@ -1432,6 +1600,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -1473,7 +1642,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns null - every { hasPremium } returns true + every { hasPremiumPersonally } returns true + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null every { userDecryptionOptions } returns UserDecryptionOptionsJson( hasMasterPassword = false, @@ -1550,6 +1720,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "activeAvatarColorHex", environment = Environment.Eu, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1583,7 +1754,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns "activeAvatarColorHex" - every { hasPremium } returns null + every { hasPremiumPersonally } returns null + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null every { userDecryptionOptions } returns UserDecryptionOptionsJson( hasMasterPassword = true, @@ -1670,6 +1842,7 @@ class UserStateJsonExtensionsTest { avatarColorHex = "activeAvatarColorHex", environment = Environment.Eu, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1703,7 +1876,8 @@ class UserStateJsonExtensionsTest { every { name } returns "activeName" every { email } returns "activeEmail" every { avatarColorHex } returns "activeAvatarColorHex" - every { hasPremium } returns null + every { hasPremiumPersonally } returns null + every { hasPremiumFromOrganization } returns null every { forcePasswordResetReason } returns null every { userDecryptionOptions } returns UserDecryptionOptionsJson( hasMasterPassword = true, @@ -1788,7 +1962,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, @@ -1831,7 +2006,8 @@ class UserStateJsonExtensionsTest { profile = originalProfile.copy( avatarColorHex = "avatarColor", stamp = "securityStamp", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = false, isTwoFactorEnabled = true, creationDate = Instant.parse("2024-09-13T01:00:00.00Z"), kdfType = KdfTypeJson.PBKDF2_SHA256, @@ -1870,7 +2046,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, @@ -1918,7 +2095,8 @@ class UserStateJsonExtensionsTest { profile = originalProfile.copy( avatarColorHex = "newAvatarColor", stamp = "newSecurityStamp", - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = false, isTwoFactorEnabled = true, creationDate = Instant.parse("2024-09-13T01:00:00.00Z"), kdfType = KdfTypeJson.PBKDF2_SHA256, @@ -1951,7 +2129,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, @@ -1997,7 +2176,8 @@ class UserStateJsonExtensionsTest { profile = originalProfile.copy( avatarColorHex = "updatedAvatarColor", stamp = "updatedSecurityStamp", - hasPremium = true, + hasPremiumPersonally = false, + hasPremiumFromOrganization = true, isTwoFactorEnabled = false, creationDate = Instant.parse("2024-09-13T01:00:00.00Z"), userDecryptionOptions = UserDecryptionOptionsJson( @@ -2025,7 +2205,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.PBKDF2_SHA256, kdfIterations = 100_000, @@ -2078,6 +2259,7 @@ class UserStateJsonExtensionsTest { "activeUserId" to originalAccount.copy( profile = originalProfile.copy( kdfIterations = DEFAULT_PBKDF2_ITERATIONS, + hasPremiumFromOrganization = false, userDecryptionOptions = UserDecryptionOptionsJson( hasMasterPassword = true, masterPasswordUnlock = MasterPasswordUnlockDataJson( @@ -2111,7 +2293,8 @@ class UserStateJsonExtensionsTest { stamp = "stamp", organizationId = null, avatarColorHex = "avatarColorHex", - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, @@ -2168,7 +2351,8 @@ class UserStateJsonExtensionsTest { stamp = "securityStamp", organizationId = "orgId", avatarColorHex = "#FF0000", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 100000, @@ -2220,7 +2404,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, @@ -2238,7 +2423,8 @@ class UserStateJsonExtensionsTest { stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 500000, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManagerImplTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManagerImplTest.kt index 72e5877788..aa9dd12e4a 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManagerImplTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/billing/manager/PremiumStateManagerImplTest.kt @@ -392,6 +392,108 @@ class PremiumStateManagerImplTest { ) } + @Test + fun `isPlanRowEligibleFlow emits true for a free user when feature flag is enabled`() = + runTest { + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = false, + isPremiumFromSelf = false, + ), + ), + ) + val manager = createManager() + manager.isPlanRowEligibleFlow.test { + assertTrue(awaitItem()) + } + } + + @Test + fun `isPlanRowEligibleFlow emits true for a personal Premium user`() = runTest { + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = true, + isPremiumFromSelf = true, + ), + ), + ) + val manager = createManager() + manager.isPlanRowEligibleFlow.test { + assertTrue(awaitItem()) + } + } + + @Test + fun `isPlanRowEligibleFlow emits false for a user with only org-granted Premium`() = + runTest { + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = true, + isPremiumFromSelf = false, + ), + ), + ) + val manager = createManager() + manager.isPlanRowEligibleFlow.test { + assertFalse(awaitItem()) + } + } + + @Test + fun `isPlanRowEligibleFlow emits false when feature flag is disabled`() = runTest { + mutableMobilePremiumUpgradeFlagFlow.value = false + val manager = createManager() + manager.isPlanRowEligibleFlow.test { + assertFalse(awaitItem()) + } + } + + @Test + fun `isPlanRowEligibleFlow emits false when userState is null`() = runTest { + mutableUserStateFlow.value = null + val manager = createManager() + manager.isPlanRowEligibleFlow.test { + assertFalse(awaitItem()) + } + } + + @Test + fun `isPlanRowEligibleFlow updates as the user gains and loses org-only Premium`() = runTest { + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = false, + isPremiumFromSelf = false, + ), + ), + ) + val manager = createManager() + manager.isPlanRowEligibleFlow.test { + assertTrue(awaitItem()) + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = true, + isPremiumFromSelf = false, + ), + ), + ) + assertFalse(awaitItem()) + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = false, + isPremiumFromSelf = false, + ), + ), + ) + assertTrue(awaitItem()) + } + } + @Test fun `isUpgradedToPremiumCardEligibleFlow emits false when nothing has been observed`() = runTest { @@ -404,6 +506,16 @@ class PremiumStateManagerImplTest { @Test fun `premium-status push with isPremium=true marks the card pending and emits true`() = runTest { + // Sync has caught up to the upgrade by the time the push lands — the active user + // already holds personal Premium. + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = true, + isPremiumFromSelf = true, + ), + ), + ) val manager = createManager() manager.isUpgradedToPremiumCardEligibleFlow.test { assertFalse(awaitItem()) @@ -421,6 +533,32 @@ class PremiumStateManagerImplTest { } } + @Suppress("MaxLineLength") + @Test + fun `isUpgradedToPremiumCardEligibleFlow emits false when pending is set but the active user lacks personal Premium`() = + runTest { + // Simulates the debug-menu trigger (or a stray PREMIUM_STATUS_CHANGED push routed to + // an organization-granted user): pending is written directly to disk, but the active + // user has no personal subscription. The read-side gate must withhold the card. + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = true, + isPremiumFromSelf = false, + ), + ), + ) + fakeSettingsDiskSource.storeUpgradedToPremiumCardPending( + userId = ACTIVE_USER_ID, + isPending = true, + ) + val manager = createManager() + manager.isUpgradedToPremiumCardEligibleFlow.test { + assertFalse(awaitItem()) + expectNoEvents() + } + } + @Test fun `premium-status push with isPremium=false should not mark the card pending`() = runTest { @@ -441,19 +579,30 @@ class PremiumStateManagerImplTest { } } + @Suppress("MaxLineLength") @Test - fun `userState transition from non-Premium to Premium for the same user marks card pending`() = + fun `userState transition from non-Premium to personal Premium for the same user marks card pending`() = runTest { // Start as Free. mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( - accounts = listOf(DEFAULT_ACTIVE_ACCOUNT.copy(isPremium = false)), + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = false, + isPremiumFromSelf = false, + ), + ), ) val manager = createManager() manager.isUpgradedToPremiumCardEligibleFlow.test { assertFalse(awaitItem()) - // Transition to Premium for the same user. + // Transition to personal Premium for the same user. mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( - accounts = listOf(DEFAULT_ACTIVE_ACCOUNT.copy(isPremium = true)), + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = true, + isPremiumFromSelf = true, + ), + ), ) assertTrue(awaitItem()) fakeSettingsDiskSource.assertUpgradedToPremiumCardPending( @@ -463,6 +612,38 @@ class PremiumStateManagerImplTest { } } + @Test + fun `userState transition that only flips org-granted Premium should not mark card pending`() = + runTest { + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = false, + isPremiumFromSelf = false, + ), + ), + ) + val manager = createManager() + manager.isUpgradedToPremiumCardEligibleFlow.test { + assertFalse(awaitItem()) + // Aggregate isPremium becomes true via the user's organization, but personal + // premium stays false — the card must not arm. + mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = true, + isPremiumFromSelf = false, + ), + ), + ) + expectNoEvents() + fakeSettingsDiskSource.assertUpgradedToPremiumCardPending( + userId = ACTIVE_USER_ID, + expected = null, + ) + } + } + @Test fun `Premium account that signs in for the first time should not mark the card pending`() = runTest { @@ -472,7 +653,12 @@ class PremiumStateManagerImplTest { manager.isUpgradedToPremiumCardEligibleFlow.test { assertFalse(awaitItem()) mutableUserStateFlow.value = DEFAULT_USER_STATE.copy( - accounts = listOf(DEFAULT_ACTIVE_ACCOUNT.copy(isPremium = true)), + accounts = listOf( + DEFAULT_ACTIVE_ACCOUNT.copy( + isPremium = true, + isPremiumFromSelf = true, + ), + ), ) expectNoEvents() fakeSettingsDiskSource.assertUpgradedToPremiumCardPending( @@ -562,6 +748,7 @@ private val DEFAULT_ACTIVE_ACCOUNT = UserState.Account( avatarColorHex = "#aa00aa", environment = com.bitwarden.data.repository.model.Environment.Us, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorTest.kt index f7f079277a..08b301ac7b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorTest.kt @@ -693,6 +693,7 @@ private fun createMockAccounts(number: Int): List { avatarColorHex = "$it", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerTest.kt index e5d6cadb66..95eddc9340 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerTest.kt @@ -429,7 +429,8 @@ private val MOCK_PROFILE = AccountJson.Profile( email = "test@bitwarden.com", isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt index e4f0cde4f2..ce9865719b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt @@ -1496,7 +1496,8 @@ private val MOCK_PROFILE = AccountJson.Profile( email = "test@bitwarden.com", isEmailVerified = true, name = "Bitwarden Tester", - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, stamp = null, organizationId = null, avatarColorHex = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryTest.kt index f104f784f6..334934fbc8 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryTest.kt @@ -785,7 +785,8 @@ private val USER_STATE = UserStateJson( stamp = "stamp", organizationId = "organizationId", avatarColorHex = "avatarColorHex", - hasPremium = true, + hasPremiumPersonally = true, + hasPremiumFromOrganization = null, forcePasswordResetReason = ForcePasswordResetReason.ADMIN_FORCE_PASSWORD_RESET, kdfType = KdfTypeJson.ARGON2_ID, kdfIterations = 600000, 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 343d1182d7..15709f0bba 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 @@ -3069,7 +3069,8 @@ private val MOCK_PROFILE = AccountJson.Profile( stamp = "mockSecurityStamp-1", organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = null, kdfIterations = null, 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 69cf48358e..b404866638 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 @@ -620,7 +620,8 @@ private val MOCK_PROFILE = AccountJson.Profile( stamp = "mockSecurityStamp-1", organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = null, kdfIterations = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt index e2693a7821..4474b515c9 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt @@ -996,7 +996,8 @@ private val MOCK_PROFILE = AccountJson.Profile( stamp = "mockSecurityStamp-1", organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = null, kdfIterations = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt index 4ed7e05f68..b56a0971b8 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt @@ -2132,7 +2132,8 @@ private val MOCK_PROFILE = AccountJson.Profile( stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = null, kdfIterations = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultMigrationManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultMigrationManagerTest.kt index 335704bdb0..b64293aed4 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultMigrationManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultMigrationManagerTest.kt @@ -1213,7 +1213,8 @@ private val MOCK_USER_STATE = UserStateJson( stamp = null, organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = null, kdfIterations = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultSyncManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultSyncManagerTest.kt index bed8d6c546..0a754f4b7c 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultSyncManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultSyncManagerTest.kt @@ -731,6 +731,7 @@ class VaultSyncManagerTest { profile = MOCK_PROFILE.copy( avatarColorHex = "mockAvatarColor-1", stamp = "mockSecurityStamp-1", + hasPremiumFromOrganization = false, kdfType = KdfTypeJson.PBKDF2_SHA256, kdfIterations = 600000, kdfMemory = null, @@ -1321,7 +1322,8 @@ private val MOCK_PROFILE = AccountJson.Profile( stamp = "mockSecurityStamp-1", organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = null, kdfIterations = null, 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 5012943144..8803aa2efc 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 @@ -1743,7 +1743,8 @@ private val MOCK_BASE_PROFILE = AccountJson.Profile( stamp = "mockSecurityStamp-1", organizationId = null, avatarColorHex = null, - hasPremium = false, + hasPremiumPersonally = false, + hasPremiumFromOrganization = null, forcePasswordResetReason = null, kdfType = null, kdfIterations = null, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/model/AccountJsonUtil.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/model/AccountJsonUtil.kt index c5c103b916..71ec28181e 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/model/AccountJsonUtil.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/model/AccountJsonUtil.kt @@ -20,7 +20,7 @@ fun createMockAccountJsonProfile( stamp: String = "mockSecurityStamp-$number", organizationId: String? = "mockOrganizationId-$number", avatarColorHex: String? = "mockAvatarColorHex-$number", - hasPremium: Boolean = false, + hasPremiumPersonally: Boolean = false, forcePasswordResetReason: ForcePasswordResetReason? = null, kdfType: KdfTypeJson? = null, kdfIterations: Int? = null, @@ -37,7 +37,8 @@ fun createMockAccountJsonProfile( stamp = stamp, organizationId = organizationId, avatarColorHex = avatarColorHex, - hasPremium = hasPremium, + hasPremiumPersonally = hasPremiumPersonally, + hasPremiumFromOrganization = null, forcePasswordResetReason = forcePasswordResetReason, kdfType = kdfType, kdfIterations = kdfIterations, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt index 85bef24727..77da995d45 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt @@ -423,6 +423,7 @@ private val DEFAULT_USER_ACCOUNT = UserState.Account( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt index a9919c892a..40b8395c91 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt @@ -84,6 +84,7 @@ class LandingViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -238,6 +239,7 @@ class LandingViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -297,6 +299,7 @@ class LandingViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -360,6 +363,7 @@ class LandingViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = true, needsPasswordReset = false, @@ -524,6 +528,7 @@ class LandingViewModelTest : BaseViewModelTest() { avatarColorHex = "lorem", environment = Environment.Us, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -562,6 +567,7 @@ class LandingViewModelTest : BaseViewModelTest() { avatarColorHex = "lorem", environment = Environment.Us, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt index 0e68beb0ae..e31c4e22c0 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt @@ -121,6 +121,7 @@ class LoginViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt index 31b4aa92f3..ebf25588b2 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt @@ -284,6 +284,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( environment = Environment.Us, avatarColorHex = "#aa00aa", isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt index 8a99ec29db..c047aa69a1 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt @@ -282,6 +282,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( environment = Environment.Us, avatarColorHex = "#aa00aa", isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt index f7190b2e10..62025b9055 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt @@ -260,6 +260,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { avatarColorHex = "#00aaaa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -302,6 +303,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { avatarColorHex = "#00aaaa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = false, needsPasswordReset = false, @@ -1406,6 +1408,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( environment = Environment.Us, avatarColorHex = "#aa00aa", isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanViewModelTest.kt index c2f2ee73c3..8b84515325 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanViewModelTest.kt @@ -1314,6 +1314,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( avatarColorHex = "#000000", environment = mockk(), isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt index 9b3078b559..c82b542713 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt @@ -115,6 +115,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = true, needsPasswordReset = false, @@ -154,6 +155,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = true, @@ -190,6 +192,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = true, @@ -231,6 +234,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -274,6 +278,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = false, needsPasswordReset = false, @@ -317,6 +322,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = true, needsPasswordReset = false, @@ -362,6 +368,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -407,6 +414,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -453,6 +461,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -495,6 +504,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -537,6 +547,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -580,6 +591,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -625,6 +637,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -677,6 +690,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -734,6 +748,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarHexColor", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -801,6 +816,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarHexColor", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -849,6 +865,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarHexColor", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -896,6 +913,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarHexColor", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1033,6 +1051,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarHexColor", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1089,6 +1108,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = false, needsPasswordReset = false, @@ -1153,6 +1173,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarHexColor", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1197,6 +1218,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = false, needsPasswordReset = false, @@ -1236,6 +1258,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = false, needsPasswordReset = false, @@ -1273,6 +1296,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1313,6 +1337,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1353,6 +1378,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1393,6 +1419,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1433,6 +1460,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1473,6 +1501,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = false, needsPasswordReset = false, @@ -1513,6 +1542,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1553,6 +1583,7 @@ class RootNavViewModelTest : BaseViewModelTest() { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1759,6 +1790,7 @@ private val MOCK_VAULT_UNLOCKED_USER_STATE = UserState( avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1786,6 +1818,7 @@ private val MOCK_VAULT_UNLOCKED_USER_MULTIPLE_ACCOUNTS_STATE = UserState( avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -1808,6 +1841,7 @@ private val MOCK_VAULT_UNLOCKED_USER_MULTIPLE_ACCOUNTS_STATE = UserState( avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt index 443343954f..6539e2674e 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt @@ -2393,6 +2393,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreenTest.kt index 5f354ec2dc..f204787afd 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreenTest.kt @@ -305,4 +305,5 @@ private val DEFAULT_STATE = SettingsState( securityCount = 0, autoFillCount = 0, vaultCount = 0, + isPlanRowEligible = false, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModelTest.kt index 35a6245142..1a6ee6f36b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModelTest.kt @@ -2,10 +2,8 @@ package com.x8bit.bitwarden.ui.platform.feature.settings import androidx.lifecycle.SavedStateHandle import app.cash.turbine.test -import com.bitwarden.core.data.manager.model.FlagKey import com.bitwarden.ui.platform.base.BaseViewModelTest import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager -import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance @@ -31,16 +29,7 @@ class SettingsViewModelTest : BaseViewModelTest() { private val mutableAutofillBadgeCountFlow = MutableStateFlow(0) private val mutableVaultBadgeCountFlow = MutableStateFlow(0) private val mutableSecurityBadgeCountFlow = MutableStateFlow(0) - private val mutableMobilePremiumUpgradeFlagFlow = MutableStateFlow(false) - private val featureFlagManager = mockk { - every { - getFeatureFlag(FlagKey.MobilePremiumUpgrade) - } answers { mutableMobilePremiumUpgradeFlagFlow.value } - every { - getFeatureFlagFlow(FlagKey.MobilePremiumUpgrade) - } returns mutableMobilePremiumUpgradeFlagFlow - } private val firstTimeManager = mockk { every { allSecuritySettingsBadgeCountFlow } returns mutableSecurityBadgeCountFlow every { allAutofillSettingsBadgeCountFlow } returns mutableAutofillBadgeCountFlow @@ -50,8 +39,10 @@ class SettingsViewModelTest : BaseViewModelTest() { every { specialCircumstance } returns null } + private val mutablePlanRowEligibleFlow = MutableStateFlow(false) private val mutableUpgradedToPremiumCardEligibleFlow = MutableStateFlow(false) private val premiumStateManager: PremiumStateManager = mockk(relaxed = true) { + every { isPlanRowEligibleFlow } returns mutablePlanRowEligibleFlow every { isUpgradedToPremiumCardEligibleFlow } returns mutableUpgradedToPremiumCardEligibleFlow @@ -220,11 +211,8 @@ class SettingsViewModelTest : BaseViewModelTest() { } @Test - fun `Plan row should appear when feature flag is enabled and not preAuth`() { - every { - featureFlagManager.getFeatureFlag(FlagKey.MobilePremiumUpgrade) - } returns true - mutableMobilePremiumUpgradeFlagFlow.value = true + fun `Plan row should appear when plan row eligibility is true and not preAuth`() { + mutablePlanRowEligibleFlow.value = true val viewModel = createViewModel() assertTrue( viewModel.stateFlow.value.settingRows @@ -233,10 +221,8 @@ class SettingsViewModelTest : BaseViewModelTest() { } @Test - fun `Plan row should be hidden when feature flag is disabled`() { - every { - featureFlagManager.getFeatureFlag(FlagKey.MobilePremiumUpgrade) - } returns false + fun `Plan row should be hidden when plan row eligibility is false`() { + mutablePlanRowEligibleFlow.value = false val viewModel = createViewModel() assertFalse( viewModel.stateFlow.value.settingRows @@ -246,10 +232,7 @@ class SettingsViewModelTest : BaseViewModelTest() { @Test fun `Plan row should be hidden in preAuth mode`() { - every { - featureFlagManager.getFeatureFlag(FlagKey.MobilePremiumUpgrade) - } returns true - mutableMobilePremiumUpgradeFlagFlow.value = true + mutablePlanRowEligibleFlow.value = true val viewModel = createViewModel(isPreAuth = true) assertFalse( viewModel.stateFlow.value.settingRows @@ -259,10 +242,7 @@ class SettingsViewModelTest : BaseViewModelTest() { @Test fun `Plan row should appear between Appearance and Other in settings rows`() { - every { - featureFlagManager.getFeatureFlag(FlagKey.MobilePremiumUpgrade) - } returns true - mutableMobilePremiumUpgradeFlagFlow.value = true + mutablePlanRowEligibleFlow.value = true val viewModel = createViewModel() val rows = viewModel.stateFlow.value.settingRows val planIndex = rows.indexOf(Settings.PLAN) @@ -273,20 +253,16 @@ class SettingsViewModelTest : BaseViewModelTest() { } @Test - fun `Plan row should update when feature flag changes to enabled`() = + fun `Plan row should update when plan row eligibility changes to true`() = runTest { - every { - featureFlagManager.getFeatureFlag( - FlagKey.MobilePremiumUpgrade, - ) - } returns false + mutablePlanRowEligibleFlow.value = false val viewModel = createViewModel() assertFalse( viewModel.stateFlow.value.settingRows .contains(Settings.PLAN), ) - mutableMobilePremiumUpgradeFlagFlow.value = true + mutablePlanRowEligibleFlow.value = true viewModel.stateFlow.test { assertTrue( awaitItem().settingRows.contains(Settings.PLAN), @@ -349,7 +325,6 @@ class SettingsViewModelTest : BaseViewModelTest() { private fun createViewModel(isPreAuth: Boolean = false) = SettingsViewModel( firstTimeActionManager = firstTimeManager, - featureFlagManager = featureFlagManager, specialCircumstanceManager = specialCircumstanceManager, premiumStateManager = premiumStateManager, savedStateHandle = SavedStateHandle().apply { @@ -363,4 +338,5 @@ private val DEFAULT_STATE = SettingsState( autoFillCount = 0, securityCount = 0, vaultCount = 0, + isPlanRowEligible = false, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt index 2510e6b17e..ba87d83b02 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt @@ -909,6 +909,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt index 5bcd7523d0..44be900a6a 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt @@ -232,6 +232,7 @@ private val DEFAULT_USER_STATE: UserState = UserState( avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt index 119fcae574..74fb4911b3 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt @@ -485,6 +485,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#aa00aa", isBiometricsEnabled = false, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -506,6 +507,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#aa00aa", isBiometricsEnabled = false, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt index 5730442708..36f5354a27 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt @@ -844,6 +844,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt index 87de0dbcf7..b6c27f30ed 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt @@ -2697,6 +2697,7 @@ private val DEFAULT_USER_STATE = UserState( environment = Environment.Us, avatarColorHex = "#aa00aa", isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt index c4b52b03c7..2b3ef5fee4 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt @@ -782,6 +782,7 @@ private val DEFAULT_USER_ACCOUNT_STATE = UserState.Account( avatarColorHex = "#ff00ff", environment = Environment.Us, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/addedit/AddEditSendViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/addedit/AddEditSendViewModelTest.kt index b56c333db6..3bd31909cc 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/addedit/AddEditSendViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/tools/feature/send/addedit/AddEditSendViewModelTest.kt @@ -1568,6 +1568,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( environment = Environment.Us, avatarColorHex = "#aa00aa", isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, 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 8cceec9a4e..d4127908d2 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 @@ -6557,6 +6557,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt index 0a9659efff..0e924de077 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt @@ -751,6 +751,7 @@ class CipherViewExtensionsTest { avatarColorHex = "#ffecbc49", environment = Environment.Eu, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt index 55d9c66f1a..adf5ffb9d2 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt @@ -820,6 +820,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( environment = Environment.Us, avatarColorHex = "#aa00aa", isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/SelectAccountViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/SelectAccountViewModelTest.kt index dbc0ab5dac..808c035ed3 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/SelectAccountViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/SelectAccountViewModelTest.kt @@ -283,6 +283,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( environment = Environment.Us, avatarColorHex = "#FF00FF00", isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/reviewexport/ReviewExportViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/reviewexport/ReviewExportViewModelTest.kt index 94eeea8de2..6998459058 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/reviewexport/ReviewExportViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/reviewexport/ReviewExportViewModelTest.kt @@ -444,6 +444,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/verifypassword/VerifyPasswordScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/verifypassword/VerifyPasswordScreenTest.kt index a154bc0200..845904c3d2 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/verifypassword/VerifyPasswordScreenTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/verifypassword/VerifyPasswordScreenTest.kt @@ -241,6 +241,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/verifypassword/VerifyPasswordViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/verifypassword/VerifyPasswordViewModelTest.kt index dfde417c16..499e10300c 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/verifypassword/VerifyPasswordViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/exportitems/verifypassword/VerifyPasswordViewModelTest.kt @@ -726,6 +726,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -756,6 +757,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt index a5ea94d208..32857a3344 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt @@ -3839,6 +3839,7 @@ class VaultItemViewModelTest : BaseViewModelTest() { avatarColorHex = "#ff00ff", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt index a9bd72bc55..1e755ff477 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt @@ -6670,6 +6670,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( environment = Environment.Us, avatarColorHex = "#aa00aa", isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/leaveorganization/LeaveOrganizationViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/leaveorganization/LeaveOrganizationViewModelTest.kt index 9e19bae0de..0ccc47479e 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/leaveorganization/LeaveOrganizationViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/leaveorganization/LeaveOrganizationViewModelTest.kt @@ -268,6 +268,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#175DDC", environment = Environment.Us, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt index 91d32d0e1a..e2086bcbd0 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt @@ -588,6 +588,7 @@ private val DEFAULT_USER_STATE = UserState( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt index 700496ed89..4d7b443333 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt @@ -94,6 +94,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState = avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt index 846fb0566f..611f306bcc 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt @@ -615,6 +615,7 @@ class VaultViewModelTest : BaseViewModelTest() { avatarColorHex = "#00aaaa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -702,6 +703,7 @@ class VaultViewModelTest : BaseViewModelTest() { avatarColorHex = "#00aaaa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -4535,6 +4537,7 @@ private val DEFAULT_ACTIVE_ACCOUNT = UserState.Account( avatarColorHex = "#aa00aa", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -4557,6 +4560,7 @@ private val DEFAULT_INACTIVE_ACCOUNT = UserState.Account( avatarColorHex = "#00aaaa", environment = Environment.Us, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = false, needsPasswordReset = false, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt index 982dc368ca..8a71e68d65 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt @@ -69,6 +69,7 @@ class UserStateExtensionsTest { avatarColorHex = "activeAvatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -97,6 +98,7 @@ class UserStateExtensionsTest { avatarColorHex = "lockedAvatarColorHex", environment = Environment.Eu, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = false, needsPasswordReset = false, @@ -129,6 +131,7 @@ class UserStateExtensionsTest { ), ), isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -161,6 +164,7 @@ class UserStateExtensionsTest { ), ), isPremium = true, + isPremiumFromSelf = true, isLoggedIn = false, isVaultUnlocked = false, needsPasswordReset = false, @@ -208,6 +212,7 @@ class UserStateExtensionsTest { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -253,6 +258,7 @@ class UserStateExtensionsTest { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = false, + isPremiumFromSelf = false, isLoggedIn = true, isVaultUnlocked = false, needsPasswordReset = false, @@ -302,6 +308,7 @@ class UserStateExtensionsTest { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -339,6 +346,7 @@ class UserStateExtensionsTest { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -383,6 +391,7 @@ class UserStateExtensionsTest { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false, @@ -441,6 +450,7 @@ class UserStateExtensionsTest { avatarColorHex = "avatarColorHex", environment = Environment.Us, isPremium = true, + isPremiumFromSelf = true, isLoggedIn = true, isVaultUnlocked = true, needsPasswordReset = false,