PM-24726: Update MDM functionality (#5694)

This commit is contained in:
David Perez 2025-08-14 13:21:24 -05:00 committed by GitHub
parent a68fd8b44f
commit 474ec4907f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 75 additions and 189 deletions

View File

@ -8,6 +8,7 @@ import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConfigManager import com.x8bit.bitwarden.data.platform.manager.network.NetworkConfigManager
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManager import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManager
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -38,6 +39,17 @@ class BitwardenApplication : Application() {
@Inject @Inject
lateinit var restrictionManager: RestrictionManager lateinit var restrictionManager: RestrictionManager
@Inject
lateinit var environmentRepository: EnvironmentRepository
override fun onCreate() {
super.onCreate()
// These must be initialized in order to ensure that the restrictionManager does not
// override the environmentRepository values.
restrictionManager.initialize()
environmentRepository.initialize()
}
override fun onLowMemory() { override fun onLowMemory() {
super.onLowMemory() super.onLowMemory()
Timber.w("onLowMemory") Timber.w("onLowMemory")

View File

@ -335,13 +335,8 @@ object PlatformManagerModule {
@Singleton @Singleton
fun provideRestrictionManager( fun provideRestrictionManager(
@ApplicationContext context: Context, @ApplicationContext context: Context,
appStateManager: AppStateManager,
dispatcherManager: DispatcherManager,
environmentRepository: EnvironmentRepository, environmentRepository: EnvironmentRepository,
): RestrictionManager = RestrictionManagerImpl( ): RestrictionManager = RestrictionManagerImpl(
appStateManager = appStateManager,
dispatcherManager = dispatcherManager,
context = context,
environmentRepository = environmentRepository, environmentRepository = environmentRepository,
restrictionsManager = requireNotNull(context.getSystemService()), restrictionsManager = requireNotNull(context.getSystemService()),
) )

View File

@ -3,4 +3,9 @@ package com.x8bit.bitwarden.data.platform.manager.restriction
/** /**
* A manager for handling restrictions. * A manager for handling restrictions.
*/ */
interface RestrictionManager interface RestrictionManager {
/**
* Initializes the [RestrictionManager].
*/
fun initialize()
}

View File

@ -1,57 +1,19 @@
package com.x8bit.bitwarden.data.platform.manager.restriction package com.x8bit.bitwarden.data.platform.manager.restriction
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.RestrictionsManager import android.content.RestrictionsManager
import android.os.Bundle import com.bitwarden.data.datasource.disk.model.EnvironmentUrlDataJson
import com.bitwarden.data.manager.DispatcherManager
import com.bitwarden.data.repository.model.Environment import com.bitwarden.data.repository.model.Environment
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
/** /**
* The default implementation of the [RestrictionManager]. * The default implementation of the [RestrictionManager].
*/ */
class RestrictionManagerImpl( class RestrictionManagerImpl(
appStateManager: AppStateManager,
dispatcherManager: DispatcherManager,
private val context: Context,
private val environmentRepository: EnvironmentRepository, private val environmentRepository: EnvironmentRepository,
private val restrictionsManager: RestrictionsManager, private val restrictionsManager: RestrictionsManager,
) : RestrictionManager { ) : RestrictionManager {
private val mainScope = CoroutineScope(dispatcherManager.main)
private val intentFilter = IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)
private val restrictionsChangedReceiver = RestrictionsChangedReceiver()
private var isReceiverRegistered = false
init { override fun initialize() {
appStateManager
.appForegroundStateFlow
.onEach {
when (it) {
AppForegroundState.BACKGROUNDED -> handleBackground()
AppForegroundState.FOREGROUNDED -> handleForeground()
}
}
.launchIn(mainScope)
}
private fun handleBackground() {
if (isReceiverRegistered) {
context.unregisterReceiver(restrictionsChangedReceiver)
}
isReceiverRegistered = false
}
private fun handleForeground() {
context.registerReceiver(restrictionsChangedReceiver, intentFilter)
isReceiverRegistered = true
updatePreconfiguredRestrictionSettings() updatePreconfiguredRestrictionSettings()
} }
@ -59,65 +21,22 @@ class RestrictionManagerImpl(
restrictionsManager restrictionsManager
.applicationRestrictions .applicationRestrictions
?.takeUnless { it.isEmpty } ?.takeUnless { it.isEmpty }
?.let { setPreconfiguredSettings(it) } ?.getString(BASE_ENVIRONMENT_URL_RESTRICTION_KEY)
}
private fun setPreconfiguredSettings(bundle: Bundle) {
bundle
.getString(BASE_ENVIRONMENT_URL_RESTRICTION_KEY)
?.let { url -> setPreconfiguredUrl(baseEnvironmentUrl = url) } ?.let { url -> setPreconfiguredUrl(baseEnvironmentUrl = url) }
} }
private fun setPreconfiguredUrl(baseEnvironmentUrl: String) { private fun setPreconfiguredUrl(baseEnvironmentUrl: String) {
environmentRepository.environment = when (val current = environmentRepository.environment) { environmentRepository.environment = when (baseEnvironmentUrl) {
Environment.Us -> { // If the baseEnvironmentUrl matches the predefined US environment, assume it is the
when (baseEnvironmentUrl) { // default US environment.
// If the base matches the predefined US environment, leave it alone Environment.Us.environmentUrlData.base -> Environment.Us
Environment.Us.environmentUrlData.base -> current // If the baseEnvironmentUrl matches the predefined EU environment, assume it is the
// If the base does not match the predefined US environment, create a // default EU environment.
// self-hosted environment with the new base Environment.Eu.environmentUrlData.base -> Environment.Eu
else -> current.toSelfHosted(base = baseEnvironmentUrl) // Otherwise make a custom self-host environment.
} else -> Environment.SelfHosted(EnvironmentUrlDataJson(baseEnvironmentUrl))
}
Environment.Eu -> {
when (baseEnvironmentUrl) {
// If the base matches the predefined EU environment, leave it alone
Environment.Eu.environmentUrlData.base -> current
// If the base does not match the predefined EU environment, create a
// self-hosted environment with the new base
else -> current.toSelfHosted(base = baseEnvironmentUrl)
}
}
is Environment.SelfHosted -> current.toSelfHosted(base = baseEnvironmentUrl)
}
}
/**
* A [BroadcastReceiver] used to listen for [Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED]
* updates.
*
* Note: The `Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED` will only be received if the
* `BroadcastReceiver` is dynamically registered, so this cannot be registered in the manifest.
*/
private inner class RestrictionsChangedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED) {
updatePreconfiguredRestrictionSettings()
}
} }
} }
} }
private const val BASE_ENVIRONMENT_URL_RESTRICTION_KEY: String = "baseEnvironmentUrl" private const val BASE_ENVIRONMENT_URL_RESTRICTION_KEY: String = "baseEnvironmentUrl"
/**
* Helper method for creating a new [Environment.SelfHosted] with a new base.
*/
private fun Environment.toSelfHosted(
base: String,
): Environment.SelfHosted =
Environment.SelfHosted(
environmentUrlData = environmentUrlData.copy(base = base),
)

View File

@ -17,6 +17,11 @@ interface EnvironmentRepository {
*/ */
val environmentStateFlow: StateFlow<Environment> val environmentStateFlow: StateFlow<Environment>
/**
* Initializes the [EnvironmentRepository].
*/
fun initialize()
/** /**
* Stores the current environment for the given [userEmail]. * Stores the current environment for the given [userEmail].
*/ */

View File

@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import timber.log.Timber import timber.log.Timber
@ -20,7 +21,7 @@ import timber.log.Timber
*/ */
class EnvironmentRepositoryImpl( class EnvironmentRepositoryImpl(
private val environmentDiskSource: EnvironmentDiskSource, private val environmentDiskSource: EnvironmentDiskSource,
authDiskSource: AuthDiskSource, private val authDiskSource: AuthDiskSource,
dispatcherManager: DispatcherManager, dispatcherManager: DispatcherManager,
) : EnvironmentRepository { ) : EnvironmentRepository {
@ -44,16 +45,13 @@ class EnvironmentRepositoryImpl(
initialValue = environment, initialValue = environment,
) )
init { override fun initialize() {
authDiskSource authDiskSource
.userStateFlow .userStateFlow
.onEach { userState -> .mapNotNull { userState -> userState?.activeAccount?.settings?.environmentUrlData }
.onEach { environmentUrlDataJson ->
// If the active account has environment data, set that as the current value. // If the active account has environment data, set that as the current value.
userState environmentDiskSource.preAuthEnvironmentUrlData = environmentUrlDataJson
?.activeAccount
?.settings
?.environmentUrlData
?.let { environmentDiskSource.preAuthEnvironmentUrlData = it }
} }
.launchIn(scope) .launchIn(scope)
} }

View File

@ -1,118 +1,91 @@
package com.x8bit.bitwarden.data.platform.manager.restriction package com.x8bit.bitwarden.data.platform.manager.restriction
import android.annotation.SuppressLint
import android.content.Context
import android.content.RestrictionsManager import android.content.RestrictionsManager
import android.os.Bundle import android.os.Bundle
import com.bitwarden.data.datasource.disk.base.FakeDispatcherManager
import com.bitwarden.data.datasource.disk.model.EnvironmentUrlDataJson import com.bitwarden.data.datasource.disk.model.EnvironmentUrlDataJson
import com.bitwarden.data.repository.model.Environment import com.bitwarden.data.repository.model.Environment
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
import com.x8bit.bitwarden.data.platform.manager.util.FakeAppStateManager
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
import io.mockk.clearMocks
import io.mockk.every import io.mockk.every
import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.runs
import io.mockk.verify import io.mockk.verify
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@SuppressLint("UnspecifiedRegisterReceiverFlag")
class RestrictionManagerTest { class RestrictionManagerTest {
private val context = mockk<Context> {
every { registerReceiver(any(), any()) } returns null
every { unregisterReceiver(any()) } just runs
}
private val fakeAppStateManager = FakeAppStateManager()
private val fakeDispatcherManager = FakeDispatcherManager().apply {
setMain(unconfined)
}
private val fakeEnvironmentRepository = FakeEnvironmentRepository() private val fakeEnvironmentRepository = FakeEnvironmentRepository()
private val restrictionsManager = mockk<RestrictionsManager>() private val restrictionsManager = mockk<RestrictionsManager>()
private val restrictionManager: RestrictionManager = RestrictionManagerImpl( private val restrictionManager: RestrictionManager = RestrictionManagerImpl(
appStateManager = fakeAppStateManager,
dispatcherManager = fakeDispatcherManager,
context = context,
environmentRepository = fakeEnvironmentRepository, environmentRepository = fakeEnvironmentRepository,
restrictionsManager = restrictionsManager, restrictionsManager = restrictionsManager,
) )
@AfterEach
fun tearDown() {
fakeDispatcherManager.resetMain()
}
@Test @Test
fun `on app foreground with a null bundle should register receiver and do nothing else`() { fun `initialize with a null bundle should do nothing`() {
every { restrictionsManager.applicationRestrictions } returns null every { restrictionsManager.applicationRestrictions } returns null
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED restrictionManager.initialize()
verify(exactly = 1) { verify(exactly = 1) {
context.registerReceiver(any(), any()) restrictionsManager.applicationRestrictions
} }
assertEquals(Environment.Us, fakeEnvironmentRepository.environment) assertEquals(Environment.Us, fakeEnvironmentRepository.environment)
} }
@Test @Test
fun `on app foreground with an empty bundle should register receiver and do nothing else`() { fun `initialize with an empty bundle should do nothing`() {
every { restrictionsManager.applicationRestrictions } returns mockBundle() every { restrictionsManager.applicationRestrictions } returns mockBundle()
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED restrictionManager.initialize()
verify(exactly = 1) { verify(exactly = 1) {
context.registerReceiver(any(), any()) restrictionsManager.applicationRestrictions
} }
assertEquals(Environment.Us, fakeEnvironmentRepository.environment) assertEquals(Environment.Us, fakeEnvironmentRepository.environment)
} }
@Suppress("MaxLineLength")
@Test @Test
fun `on app foreground with unknown bundle data should register receiver and do nothing else`() { fun `initialize with unknown bundle data should do nothing`() {
every { every {
restrictionsManager.applicationRestrictions restrictionsManager.applicationRestrictions
} returns mockBundle("key" to "unknown") } returns mockBundle("key" to "unknown")
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED restrictionManager.initialize()
verify(exactly = 1) { verify(exactly = 1) {
context.registerReceiver(any(), any()) restrictionsManager.applicationRestrictions
} }
assertEquals(Environment.Us, fakeEnvironmentRepository.environment) assertEquals(Environment.Us, fakeEnvironmentRepository.environment)
} }
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `on app foreground with baseEnvironmentUrl bundle data matching the current US environment should register receiver and set the environment to US`() { fun `initialize with baseEnvironmentUrl bundle data matching the current US environment should set the environment to US`() {
every { every {
restrictionsManager.applicationRestrictions restrictionsManager.applicationRestrictions
} returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.com") } returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.com")
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED restrictionManager.initialize()
verify(exactly = 1) { verify(exactly = 1) {
context.registerReceiver(any(), any()) restrictionsManager.applicationRestrictions
} }
assertEquals(Environment.Us, fakeEnvironmentRepository.environment) assertEquals(Environment.Us, fakeEnvironmentRepository.environment)
} }
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `on app foreground with baseEnvironmentUrl bundle data not matching the current US environment should register receiver and set the environment to self-hosted`() { fun `initialize with baseEnvironmentUrl bundle data not matching the current US environment should set the environment to self-hosted`() {
val baseUrl = "https://other.bitwarden.com" val baseUrl = "https://other.bitwarden.com"
every { every {
restrictionsManager.applicationRestrictions restrictionsManager.applicationRestrictions
} returns mockBundle("baseEnvironmentUrl" to baseUrl) } returns mockBundle("baseEnvironmentUrl" to baseUrl)
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED restrictionManager.initialize()
verify(exactly = 1) { verify(exactly = 1) {
context.registerReceiver(any(), any()) restrictionsManager.applicationRestrictions
} }
assertEquals( assertEquals(
Environment.SelfHosted( Environment.SelfHosted(
@ -124,33 +97,33 @@ class RestrictionManagerTest {
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `on app foreground with baseEnvironmentUrl bundle data matching the current EU environment should register receiver and set the environment to EU`() { fun `initialize with baseEnvironmentUrl bundle data matching the current EU environment should set the environment to EU`() {
fakeEnvironmentRepository.environment = Environment.Eu fakeEnvironmentRepository.environment = Environment.Eu
every { every {
restrictionsManager.applicationRestrictions restrictionsManager.applicationRestrictions
} returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.eu") } returns mockBundle("baseEnvironmentUrl" to "https://vault.bitwarden.eu")
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED restrictionManager.initialize()
verify(exactly = 1) { verify(exactly = 1) {
context.registerReceiver(any(), any()) restrictionsManager.applicationRestrictions
} }
assertEquals(Environment.Eu, fakeEnvironmentRepository.environment) assertEquals(Environment.Eu, fakeEnvironmentRepository.environment)
} }
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `on app foreground with baseEnvironmentUrl bundle data not matching the current EU environment should register receiver and set the environment to self-hosted`() { fun `initialize with baseEnvironmentUrl bundle data not matching the current EU environment should set the environment to self-hosted`() {
val baseUrl = "https://other.bitwarden.eu" val baseUrl = "https://other.bitwarden.eu"
fakeEnvironmentRepository.environment = Environment.Eu fakeEnvironmentRepository.environment = Environment.Eu
every { every {
restrictionsManager.applicationRestrictions restrictionsManager.applicationRestrictions
} returns mockBundle("baseEnvironmentUrl" to baseUrl) } returns mockBundle("baseEnvironmentUrl" to baseUrl)
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED restrictionManager.initialize()
verify(exactly = 1) { verify(exactly = 1) {
context.registerReceiver(any(), any()) restrictionsManager.applicationRestrictions
} }
assertEquals( assertEquals(
Environment.SelfHosted( Environment.SelfHosted(
@ -162,7 +135,7 @@ class RestrictionManagerTest {
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `on app foreground with baseEnvironmentUrl bundle data matching the current self-hosted environment should register receiver and set the environment to self-hosted`() { fun `initialize with baseEnvironmentUrl bundle data matching the current self-hosted environment should set the environment to self-hosted`() {
val baseUrl = "https://vault.qa.bitwarden.pw" val baseUrl = "https://vault.qa.bitwarden.pw"
val environment = Environment.SelfHosted( val environment = Environment.SelfHosted(
environmentUrlData = EnvironmentUrlDataJson(base = baseUrl), environmentUrlData = EnvironmentUrlDataJson(base = baseUrl),
@ -172,17 +145,17 @@ class RestrictionManagerTest {
restrictionsManager.applicationRestrictions restrictionsManager.applicationRestrictions
} returns mockBundle("baseEnvironmentUrl" to baseUrl) } returns mockBundle("baseEnvironmentUrl" to baseUrl)
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED restrictionManager.initialize()
verify(exactly = 1) { verify(exactly = 1) {
context.registerReceiver(any(), any()) restrictionsManager.applicationRestrictions
} }
assertEquals(environment, fakeEnvironmentRepository.environment) assertEquals(environment, fakeEnvironmentRepository.environment)
} }
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
@Test @Test
fun `on app foreground with baseEnvironmentUrl bundle data not matching the current self-hosted environment should register receiver and set the environment to self-hosted`() { fun `initialize with baseEnvironmentUrl bundle data not matching the current self-hosted environment should set the environment to self-hosted`() {
val baseUrl = "https://other.qa.bitwarden.pw" val baseUrl = "https://other.qa.bitwarden.pw"
val environment = Environment.SelfHosted( val environment = Environment.SelfHosted(
environmentUrlData = EnvironmentUrlDataJson(base = "https://vault.qa.bitwarden.pw"), environmentUrlData = EnvironmentUrlDataJson(base = "https://vault.qa.bitwarden.pw"),
@ -192,10 +165,10 @@ class RestrictionManagerTest {
restrictionsManager.applicationRestrictions restrictionsManager.applicationRestrictions
} returns mockBundle("baseEnvironmentUrl" to baseUrl) } returns mockBundle("baseEnvironmentUrl" to baseUrl)
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED restrictionManager.initialize()
verify(exactly = 1) { verify(exactly = 1) {
context.registerReceiver(any(), any()) restrictionsManager.applicationRestrictions
} }
assertEquals( assertEquals(
Environment.SelfHosted( Environment.SelfHosted(
@ -204,32 +177,6 @@ class RestrictionManagerTest {
fakeEnvironmentRepository.environment, fakeEnvironmentRepository.environment,
) )
} }
@Test
fun `on app background when not foregrounded should do nothing`() {
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
verify(exactly = 0) {
context.unregisterReceiver(any())
restrictionsManager.applicationRestrictions
}
}
@Test
fun `on app background after foreground should unregister receiver`() {
every { restrictionsManager.applicationRestrictions } returns null
fakeAppStateManager.appForegroundState = AppForegroundState.FOREGROUNDED
clearMocks(context, restrictionsManager, answers = false)
fakeAppStateManager.appForegroundState = AppForegroundState.BACKGROUNDED
verify(exactly = 1) {
context.unregisterReceiver(any())
}
verify(exactly = 0) {
restrictionsManager.applicationRestrictions
}
}
} }
/** /**

View File

@ -29,7 +29,7 @@ class EnvironmentRepositoryTest {
private val fakeEnvironmentDiskSource = FakeEnvironmentDiskSource() private val fakeEnvironmentDiskSource = FakeEnvironmentDiskSource()
private val fakeAuthDiskSource = FakeAuthDiskSource() private val fakeAuthDiskSource = FakeAuthDiskSource()
private val repository = EnvironmentRepositoryImpl( private val repository: EnvironmentRepository = EnvironmentRepositoryImpl(
environmentDiskSource = fakeEnvironmentDiskSource, environmentDiskSource = fakeEnvironmentDiskSource,
authDiskSource = fakeAuthDiskSource, authDiskSource = fakeAuthDiskSource,
dispatcherManager = dispatcherManager, dispatcherManager = dispatcherManager,
@ -46,7 +46,7 @@ class EnvironmentRepositoryTest {
} }
@Test @Test
fun `changes to the active user should update the environment if necessary`() { fun `after initialize changes to the active user should update the environment if necessary`() {
assertEquals( assertEquals(
Environment.Us, Environment.Us,
repository.environment, repository.environment,
@ -56,6 +56,8 @@ class EnvironmentRepositoryTest {
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData, fakeEnvironmentDiskSource.preAuthEnvironmentUrlData,
) )
repository.initialize()
// Updating the environment for the active user to a non-null value triggers an update // Updating the environment for the active user to a non-null value triggers an update
// in the saved environment. // in the saved environment.
fakeAuthDiskSource.userState = getMockUserState( fakeAuthDiskSource.userState = getMockUserState(

View File

@ -10,6 +10,9 @@ import kotlinx.coroutines.flow.asStateFlow
* A faked implementation of [EnvironmentRepository] based on in-memory caching. * A faked implementation of [EnvironmentRepository] based on in-memory caching.
*/ */
class FakeEnvironmentRepository : EnvironmentRepository { class FakeEnvironmentRepository : EnvironmentRepository {
override fun initialize() = Unit
override var environment: Environment override var environment: Environment
get() = mutableEnvironmentStateFlow.value get() = mutableEnvironmentStateFlow.value
set(value) { set(value) {