BIT-752: Add Environment/EnvironmentRepository/EnvironmentDiskSource (#151)

This commit is contained in:
Brian Yencho 2023-10-24 10:46:33 -05:00 committed by Álison Fernandes
parent f4dbe68527
commit e4ab70a106
15 changed files with 575 additions and 24 deletions

View file

@ -1,16 +1,15 @@
package com.x8bit.bitwarden.data.auth.datasource.disk package com.x8bit.bitwarden.data.auth.datasource.disk
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import androidx.core.content.edit
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseDiskSource.Companion.BASE_KEY
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.onSubscription import kotlinx.coroutines.flow.onSubscription
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
private const val BASE_KEY = "bwPreferencesStorage"
private const val REMEMBERED_EMAIL_ADDRESS_KEY = "$BASE_KEY:rememberedEmail" private const val REMEMBERED_EMAIL_ADDRESS_KEY = "$BASE_KEY:rememberedEmail"
private const val STATE_KEY = "$BASE_KEY:state" private const val STATE_KEY = "$BASE_KEY:state"
@ -18,9 +17,10 @@ private const val STATE_KEY = "$BASE_KEY:state"
* Primary implementation of [AuthDiskSource]. * Primary implementation of [AuthDiskSource].
*/ */
class AuthDiskSourceImpl( class AuthDiskSourceImpl(
private val sharedPreferences: SharedPreferences, sharedPreferences: SharedPreferences,
private val json: Json, private val json: Json,
) : AuthDiskSource { ) : BaseDiskSource(sharedPreferences = sharedPreferences),
AuthDiskSource {
override var rememberedEmailAddress: String? override var rememberedEmailAddress: String?
get() = getString(key = REMEMBERED_EMAIL_ADDRESS_KEY) get() = getString(key = REMEMBERED_EMAIL_ADDRESS_KEY)
set(value) { set(value) {
@ -48,25 +48,12 @@ class AuthDiskSourceImpl(
extraBufferCapacity = Int.MAX_VALUE, extraBufferCapacity = Int.MAX_VALUE,
) )
private val onSharedPreferenceChangeListener = override fun onSharedPreferenceChanged(
OnSharedPreferenceChangeListener { _, key -> sharedPreferences: SharedPreferences?,
key: String?,
) {
when (key) { when (key) {
STATE_KEY -> mutableUserStateFlow.tryEmit(userState) STATE_KEY -> mutableUserStateFlow.tryEmit(userState)
} }
} }
init {
sharedPreferences
.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener)
}
private fun getString(
key: String,
default: String? = null,
): String? = sharedPreferences.getString(key, default)
private fun putString(
key: String,
value: String?,
): Unit = sharedPreferences.edit { putString(key, value) }
} }

View file

@ -0,0 +1,33 @@
package com.x8bit.bitwarden.data.platform.datasource.disk
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import androidx.core.content.edit
/**
* Base class for simplifying interactions with [SharedPreferences].
*/
abstract class BaseDiskSource(
private val sharedPreferences: SharedPreferences,
) : OnSharedPreferenceChangeListener {
init {
@Suppress("LeakingThis")
sharedPreferences
.registerOnSharedPreferenceChangeListener(this)
}
protected fun getString(
key: String,
default: String? = null,
): String? = sharedPreferences.getString(key, default)
protected fun putString(
key: String,
value: String?,
): Unit = sharedPreferences.edit { putString(key, value) }
companion object {
const val BASE_KEY: String = "bwPreferencesStorage"
}
}

View file

@ -0,0 +1,20 @@
package com.x8bit.bitwarden.data.platform.datasource.disk
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import kotlinx.coroutines.flow.Flow
/**
* Primary access point for general environment-related disk information.
*/
interface EnvironmentDiskSource {
/**
* The currently persisted [EnvironmentUrlDataJson] (or `null` if not set).
*/
var preAuthEnvironmentUrlData: EnvironmentUrlDataJson?
/**
* Emits updates that track [preAuthEnvironmentUrlData]. This will replay the last known value,
* if any.
*/
val preAuthEnvironmentUrlDataFlow: Flow<EnvironmentUrlDataJson?>
}

View file

@ -0,0 +1,48 @@
package com.x8bit.bitwarden.data.platform.datasource.disk
import android.content.SharedPreferences
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseDiskSource.Companion.BASE_KEY
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.onSubscription
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
private const val PRE_AUTH_URLS_KEY = "$BASE_KEY:preAuthEnvironmentUrls"
/**
* Primary implementation of [EnvironmentDiskSource].
*/
class EnvironmentDiskSourceImpl(
sharedPreferences: SharedPreferences,
private val json: Json,
) : BaseDiskSource(sharedPreferences = sharedPreferences),
EnvironmentDiskSource {
override var preAuthEnvironmentUrlData: EnvironmentUrlDataJson?
get() = getString(key = PRE_AUTH_URLS_KEY)?.let { json.decodeFromString(it) }
set(value) {
putString(
key = PRE_AUTH_URLS_KEY,
value = value?.let { json.encodeToString(value) },
)
}
override val preAuthEnvironmentUrlDataFlow: Flow<EnvironmentUrlDataJson?>
get() = mutableEnvironmentUrlDataFlow
.onSubscription { emit(preAuthEnvironmentUrlData) }
private val mutableEnvironmentUrlDataFlow = MutableSharedFlow<EnvironmentUrlDataJson?>(
replay = 1,
extraBufferCapacity = Int.MAX_VALUE,
)
override fun onSharedPreferenceChanged(
sharedPreferences: SharedPreferences?,
key: String?,
) {
when (key) {
PRE_AUTH_URLS_KEY -> mutableEnvironmentUrlDataFlow.tryEmit(preAuthEnvironmentUrlData)
}
}
}

View file

@ -0,0 +1,30 @@
package com.x8bit.bitwarden.data.platform.datasource.disk.di
import android.content.SharedPreferences
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSourceImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import javax.inject.Singleton
/**
* Provides persistence-related dependencies in the platform package.
*/
@Module
@InstallIn(SingletonComponent::class)
object DiskModule {
@Provides
@Singleton
fun provideEnvironmentDiskSource(
sharedPreferences: SharedPreferences,
json: Json,
): EnvironmentDiskSource =
EnvironmentDiskSourceImpl(
sharedPreferences = sharedPreferences,
json = json,
)
}

View file

@ -0,0 +1,19 @@
package com.x8bit.bitwarden.data.platform.repository
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import kotlinx.coroutines.flow.StateFlow
/**
* Provides an API for observing and modifying environment state.
*/
interface EnvironmentRepository {
/**
* The currently set environment.
*/
var environment: Environment
/**
* Emits updates that track [environment].
*/
val environmentStateFlow: StateFlow<Environment>
}

View file

@ -0,0 +1,48 @@
package com.x8bit.bitwarden.data.platform.repository
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.data.platform.repository.util.toEnvironmentUrls
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/**
* Primary implementation of [EnvironmentRepository].
*/
class EnvironmentRepositoryImpl(
private val environmentDiskSource: EnvironmentDiskSource,
private val dispatcher: CoroutineDispatcher,
) : EnvironmentRepository {
private val scope = CoroutineScope(dispatcher)
override var environment: Environment
get() = environmentDiskSource
.preAuthEnvironmentUrlData
.toEnvironmentUrlsOrDefault()
set(value) {
environmentDiskSource.preAuthEnvironmentUrlData = value.environmentUrlData
}
override val environmentStateFlow: StateFlow<Environment>
get() = environmentDiskSource
.preAuthEnvironmentUrlDataFlow
.map { it.toEnvironmentUrlsOrDefault() }
.stateIn(
scope = scope,
started = SharingStarted.Lazily,
initialValue = Environment.Us,
)
}
/**
* Converts a nullable [EnvironmentUrlDataJson] to an [Environment], where `null` values default to
* the US environment.
*/
private fun EnvironmentUrlDataJson?.toEnvironmentUrlsOrDefault(): Environment =
this?.toEnvironmentUrls() ?: Environment.Us

View file

@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.onEach
class NetworkConfigRepositoryImpl( class NetworkConfigRepositoryImpl(
private val authRepository: AuthRepository, private val authRepository: AuthRepository,
private val authTokenInterceptor: AuthTokenInterceptor, private val authTokenInterceptor: AuthTokenInterceptor,
private val environmentRepository: EnvironmentRepository,
dispatcher: CoroutineDispatcher, dispatcher: CoroutineDispatcher,
) : NetworkConfigRepository { ) : NetworkConfigRepository {
@ -30,5 +31,12 @@ class NetworkConfigRepositoryImpl(
} }
} }
.launchIn(scope) .launchIn(scope)
environmentRepository
.environmentStateFlow
.onEach { environment ->
// TODO: Update base URL interceptors (BIT-725)
}
.launchIn(scope)
} }
} }

View file

@ -1,7 +1,10 @@
package com.x8bit.bitwarden.data.platform.repository.di package com.x8bit.bitwarden.data.platform.repository.di
import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepositoryImpl
import com.x8bit.bitwarden.data.platform.repository.NetworkConfigRepository import com.x8bit.bitwarden.data.platform.repository.NetworkConfigRepository
import com.x8bit.bitwarden.data.platform.repository.NetworkConfigRepositoryImpl import com.x8bit.bitwarden.data.platform.repository.NetworkConfigRepositoryImpl
import dagger.Module import dagger.Module
@ -18,15 +21,27 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
object RepositoryModule { object RepositoryModule {
@Provides
@Singleton
fun provideEnvironmentRepository(
environmentDiskSource: EnvironmentDiskSource,
): EnvironmentRepository =
EnvironmentRepositoryImpl(
environmentDiskSource = environmentDiskSource,
dispatcher = Dispatchers.IO,
)
@Provides @Provides
@Singleton @Singleton
fun provideNetworkConfigRepository( fun provideNetworkConfigRepository(
authRepository: AuthRepository, authRepository: AuthRepository,
authTokenInterceptor: AuthTokenInterceptor, authTokenInterceptor: AuthTokenInterceptor,
environmentRepository: EnvironmentRepository,
): NetworkConfigRepository = ): NetworkConfigRepository =
NetworkConfigRepositoryImpl( NetworkConfigRepositoryImpl(
authRepository = authRepository, authRepository = authRepository,
authTokenInterceptor = authTokenInterceptor, authTokenInterceptor = authTokenInterceptor,
environmentRepository = environmentRepository,
dispatcher = Dispatchers.IO, dispatcher = Dispatchers.IO,
) )
} }

View file

@ -0,0 +1,70 @@
package com.x8bit.bitwarden.data.platform.repository.model
import android.os.Parcelable
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import com.x8bit.bitwarden.ui.platform.base.util.Text
import com.x8bit.bitwarden.ui.platform.base.util.asText
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
/**
* A higher-level wrapper around [EnvironmentUrlDataJson] that provides type-safety, enumerability,
* and human-readable labels.
*/
sealed class Environment : Parcelable {
/**
* The [Type] of the environment.
*/
abstract val type: Type
/**
* The raw [environmentUrlData] that contains specific base URLs for each relevant domain.
*/
abstract val environmentUrlData: EnvironmentUrlDataJson
/**
* Helper for a returning a human-readable label from a [Type].
*/
val label: Text get() = type.label
/**
* The default US environment.
*/
@Parcelize
data object Us : Environment() {
override val type: Type get() = Type.US
override val environmentUrlData: EnvironmentUrlDataJson
get() = EnvironmentUrlDataJson.DEFAULT_US
}
/**
* The default EU environment.
*/
@Parcelize
data object Eu : Environment() {
override val type: Type get() = Type.EU
override val environmentUrlData: EnvironmentUrlDataJson
get() = EnvironmentUrlDataJson.DEFAULT_EU
}
/**
* A custom self-hosted environment with a fully configurable [environmentUrlData].
*/
@Parcelize
data class SelfHosted(
override val environmentUrlData: @RawValue EnvironmentUrlDataJson,
) : Environment() {
override val type: Type get() = Type.SELF_HOSTED
}
/**
* A summary of the various types that can be enumerated over and which contains a
* human-readable [label].
*/
enum class Type(val label: Text) {
US(label = "bitwarden.com".asText()),
EU(label = "bitwarden.eu".asText()),
SELF_HOSTED(label = R.string.self_hosted.asText()),
}
}

View file

@ -0,0 +1,14 @@
package com.x8bit.bitwarden.data.platform.repository.util
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import com.x8bit.bitwarden.data.platform.repository.model.Environment
/**
* Converts a raw [EnvironmentUrlDataJson] to an externally-consumable [Environment].
*/
fun EnvironmentUrlDataJson.toEnvironmentUrls(): Environment =
when (this) {
Environment.Us.environmentUrlData -> Environment.Us
Environment.Eu.environmentUrlData -> Environment.Eu
else -> Environment.SelfHosted(environmentUrlData = this)
}

View file

@ -0,0 +1,86 @@
package com.x8bit.bitwarden.data.platform.datasource.disk
import app.cash.turbine.test
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import com.x8bit.bitwarden.data.platform.base.FakeSharedPreferences
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
class EnvironmentDiskSourceTest {
private val fakeSharedPreferences = FakeSharedPreferences()
@OptIn(ExperimentalSerializationApi::class)
private val json = Json {
ignoreUnknownKeys = true
explicitNulls = false
}
private val environmentDiskSource = EnvironmentDiskSourceImpl(
sharedPreferences = fakeSharedPreferences,
json = json,
)
@Test
fun `preAuthEnvironmentUrlData should pull from and update SharedPreferences`() {
val environmentKey = "bwPreferencesStorage:preAuthEnvironmentUrls"
// Shared preferences and the repository start with the same value.
assertNull(environmentDiskSource.preAuthEnvironmentUrlData)
assertNull(fakeSharedPreferences.getString(environmentKey, null))
// Updating the repository updates shared preferences
environmentDiskSource.preAuthEnvironmentUrlData = ENVIRONMENT_URL_DATA
assertEquals(
json.parseToJsonElement(
ENVIRONMENT_URL_DATA_JSON,
),
json.parseToJsonElement(
fakeSharedPreferences.getString(environmentKey, null)!!,
),
)
// Update SharedPreferences updates the repository
fakeSharedPreferences.edit().putString(environmentKey, null).apply()
assertNull(environmentDiskSource.preAuthEnvironmentUrlData)
}
@Test
fun `preAuthEnvironmentUrlDataFlow should react to changes in preAuthEnvironmentUrlData`() =
runTest {
environmentDiskSource.preAuthEnvironmentUrlDataFlow.test {
// The initial values of the Flow and the property are in sync
assertNull(environmentDiskSource.preAuthEnvironmentUrlData)
assertNull(awaitItem())
// Updating the repository updates shared preferences
environmentDiskSource.preAuthEnvironmentUrlData = ENVIRONMENT_URL_DATA
assertEquals(ENVIRONMENT_URL_DATA, awaitItem())
}
}
}
private const val ENVIRONMENT_URL_DATA_JSON = """
{
"base": "base",
"api": "api",
"identity": "identity",
"icon": "icon",
"notifications": "notifications",
"webVault": "webVault",
"events": "events"
}
"""
private val ENVIRONMENT_URL_DATA = EnvironmentUrlDataJson(
base = "base",
api = "api",
identity = "identity",
icon = "icon",
notifications = "notifications",
webVault = "webVault",
events = "events",
)

View file

@ -0,0 +1,123 @@
package com.x8bit.bitwarden.data.platform.repository
import app.cash.turbine.test
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.data.platform.repository.util.toEnvironmentUrls
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.onSubscription
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class EnvironmentRepositoryTest {
private val fakeEnvironmentDiskSource = FakeEnvironmentDiskSource()
@OptIn(ExperimentalCoroutinesApi::class)
private val repository = EnvironmentRepositoryImpl(
environmentDiskSource = fakeEnvironmentDiskSource,
dispatcher = UnconfinedTestDispatcher(),
)
@BeforeEach
fun setUp() {
mockkStatic(ENVIRONMENT_EXTENSIONS_PATH)
}
@AfterEach
fun tearDown() {
unmockkStatic(ENVIRONMENT_EXTENSIONS_PATH)
}
@Test
fun `environment should pull from and update EnvironmentDiskSource`() {
val environmentUrlDataJson = mockk<EnvironmentUrlDataJson>()
val environment = mockk<Environment>() {
every { environmentUrlData } returns environmentUrlDataJson
}
every { environmentUrlDataJson.toEnvironmentUrls() } returns environment
// The repository exposes a non-null default value when the disk source is empty
assertNull(fakeEnvironmentDiskSource.preAuthEnvironmentUrlData)
assertEquals(
Environment.Us,
repository.environment,
)
// Updating the repository updates the disk source
repository.environment = environment
assertEquals(
environmentUrlDataJson,
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData,
)
// Updating the disk source updates the repository
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData = null
assertEquals(
Environment.Us,
repository.environment,
)
fakeEnvironmentDiskSource.preAuthEnvironmentUrlData = environmentUrlDataJson
assertEquals(
environment,
repository.environment,
)
}
@Test
fun `environmentStateFow should react to changes in environment`() = runTest {
val environmentUrlDataJson = mockk<EnvironmentUrlDataJson>()
val environment = mockk<Environment>() {
every { environmentUrlData } returns environmentUrlDataJson
}
every { environmentUrlDataJson.toEnvironmentUrls() } returns environment
repository.environmentStateFlow.test {
// The initial values of the Flow and the property are in sync
assertEquals(
Environment.Us,
repository.environment,
)
assertEquals(
Environment.Us,
awaitItem(),
)
// Updating the property causes a flow emissions
repository.environment = environment
assertEquals(environment, awaitItem())
}
}
}
private const val ENVIRONMENT_EXTENSIONS_PATH =
"com.x8bit.bitwarden.data.platform.repository.util.EnvironmentExtensionsKt"
private class FakeEnvironmentDiskSource : EnvironmentDiskSource {
override var preAuthEnvironmentUrlData: EnvironmentUrlDataJson? = null
set(value) {
field = value
mutablePreAuthEnvironmentUrlDataFlow.tryEmit(value)
}
override val preAuthEnvironmentUrlDataFlow: Flow<EnvironmentUrlDataJson?>
get() = mutablePreAuthEnvironmentUrlDataFlow
.onSubscription { emit(preAuthEnvironmentUrlData) }
private val mutablePreAuthEnvironmentUrlDataFlow =
MutableSharedFlow<EnvironmentUrlDataJson?>(
replay = 1,
extraBufferCapacity = Int.MAX_VALUE,
)
}

View file

@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.platform.repository
import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.AuthState import com.x8bit.bitwarden.data.auth.repository.model.AuthState
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -16,11 +17,16 @@ import org.junit.jupiter.api.Test
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
class NetworkConfigRepositoryTest { class NetworkConfigRepositoryTest {
private val mutableAuthStateFlow = MutableStateFlow<AuthState>(AuthState.Uninitialized) private val mutableAuthStateFlow = MutableStateFlow<AuthState>(AuthState.Uninitialized)
private val mutableEnvironmentStateFlow = MutableStateFlow<Environment>(Environment.Us)
private val authRepository: AuthRepository = mockk() { private val authRepository: AuthRepository = mockk() {
every { authStateFlow } returns mutableAuthStateFlow every { authStateFlow } returns mutableAuthStateFlow
} }
private val environmentRepository: EnvironmentRepository = mockk {
every { environmentStateFlow } returns mutableEnvironmentStateFlow
}
private val authTokenInterceptor = AuthTokenInterceptor() private val authTokenInterceptor = AuthTokenInterceptor()
private lateinit var networkConfigRepository: NetworkConfigRepository private lateinit var networkConfigRepository: NetworkConfigRepository
@ -30,6 +36,7 @@ class NetworkConfigRepositoryTest {
networkConfigRepository = NetworkConfigRepositoryImpl( networkConfigRepository = NetworkConfigRepositoryImpl(
authRepository = authRepository, authRepository = authRepository,
authTokenInterceptor = authTokenInterceptor, authTokenInterceptor = authTokenInterceptor,
environmentRepository = environmentRepository,
dispatcher = UnconfinedTestDispatcher(), dispatcher = UnconfinedTestDispatcher(),
) )
} }

View file

@ -0,0 +1,43 @@
package com.x8bit.bitwarden.data.platform.repository.util
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class EnvironmentExtensionsTest {
@Test
fun `toEnvironmentUrls should correctly convert US urls to the expected type`() {
assertEquals(
Environment.Us,
EnvironmentUrlDataJson.DEFAULT_US.toEnvironmentUrls(),
)
}
@Test
fun `toEnvironmentUrls should correctly convert EU urls to the expected type`() {
assertEquals(
Environment.Eu,
EnvironmentUrlDataJson.DEFAULT_EU.toEnvironmentUrls(),
)
}
@Test
fun `toEnvironmentUrls should correctly convert custom urls to the expected type`() {
val environmentUrlData = EnvironmentUrlDataJson(
base = "base",
api = "api",
identity = "identity",
icon = "icon",
notifications = "notifications",
webVault = "webVault",
events = "events",
)
assertEquals(
Environment.SelfHosted(
environmentUrlData = environmentUrlData,
),
environmentUrlData.toEnvironmentUrls(),
)
}
}