mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 07:05:35 +03:00
[PM-9875] Server configurations (#3645)
Some checks failed
Crowdin Push / Crowdin Push (push) Waiting to run
Scan / Check PR run (push) Failing after 0s
Scan / SAST scan (push) Has been skipped
Scan / Quality scan (push) Has been skipped
Test / Check PR run (push) Failing after 0s
Test / Test (push) Has been skipped
Some checks failed
Crowdin Push / Crowdin Push (push) Waiting to run
Scan / Check PR run (push) Failing after 0s
Scan / SAST scan (push) Has been skipped
Scan / Quality scan (push) Has been skipped
Test / Check PR run (push) Failing after 0s
Test / Test (push) Has been skipped
This commit is contained in:
parent
b26e1a082e
commit
646566edd8
12 changed files with 536 additions and 1 deletions
|
@ -7,6 +7,7 @@ import com.x8bit.bitwarden.data.platform.manager.CrashLogsManager
|
|||
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.ServerConfigRepository
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -32,4 +33,7 @@ class BitwardenApplication : Application() {
|
|||
|
||||
@Inject
|
||||
lateinit var restrictionManager: RestrictionManager
|
||||
|
||||
@Inject
|
||||
lateinit var serverConfigRepository: ServerConfigRepository
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.disk
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Primary access point for server configuration-related disk information.
|
||||
*/
|
||||
interface ConfigDiskSource {
|
||||
|
||||
/**
|
||||
* The currently persisted [ServerConfig] (or `null` if not set).
|
||||
*/
|
||||
var serverConfig: ServerConfig?
|
||||
|
||||
/**
|
||||
* Emits updates that track [ServerConfig]. This will replay the last known value,
|
||||
* if any.
|
||||
*/
|
||||
val serverConfigFlow: Flow<ServerConfig?>
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.disk
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import com.x8bit.bitwarden.data.platform.util.decodeFromStringOrNull
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.onSubscription
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
private const val SERVER_CONFIGURATIONS = "serverConfigurations"
|
||||
|
||||
/**
|
||||
* Primary implementation of [ConfigDiskSource].
|
||||
*/
|
||||
class ConfigDiskSourceImpl(
|
||||
sharedPreferences: SharedPreferences,
|
||||
private val json: Json,
|
||||
) : BaseDiskSource(sharedPreferences = sharedPreferences),
|
||||
ConfigDiskSource {
|
||||
|
||||
override var serverConfig: ServerConfig?
|
||||
get() = getString(key = SERVER_CONFIGURATIONS)?.let { json.decodeFromStringOrNull(it) }
|
||||
set(value) {
|
||||
putString(
|
||||
key = SERVER_CONFIGURATIONS,
|
||||
value = value?.let { json.encodeToString(it) },
|
||||
)
|
||||
mutableServerConfigFlow.tryEmit(value)
|
||||
}
|
||||
|
||||
override val serverConfigFlow: Flow<ServerConfig?>
|
||||
get() = mutableServerConfigFlow.onSubscription { emit(serverConfig) }
|
||||
|
||||
private val mutableServerConfigFlow = bufferedMutableSharedFlow<ServerConfig?>(replay = 1)
|
||||
}
|
|
@ -24,7 +24,7 @@ class EnvironmentDiskSourceImpl(
|
|||
set(value) {
|
||||
putString(
|
||||
key = PRE_AUTH_URLS_KEY,
|
||||
value = value?.let { json.encodeToString(value) },
|
||||
value = value?.let { json.encodeToString(it) },
|
||||
)
|
||||
mutableEnvironmentUrlDataFlow.tryEmit(value)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import android.content.SharedPreferences
|
|||
import androidx.room.Room
|
||||
import com.x8bit.bitwarden.data.platform.datasource.di.EncryptedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.di.UnencryptedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.ConfigDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.ConfigDiskSourceImpl
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSourceImpl
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.EventDiskSource
|
||||
|
@ -51,6 +53,17 @@ object PlatformDiskModule {
|
|||
json = json,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideConfigDiskSource(
|
||||
@UnencryptedPreferences sharedPreferences: SharedPreferences,
|
||||
json: Json,
|
||||
): ConfigDiskSource =
|
||||
ConfigDiskSourceImpl(
|
||||
sharedPreferences = sharedPreferences,
|
||||
json = json,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideEventDatabase(app: Application): PlatformDatabase =
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.disk.model
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* A higher-level wrapper around [ConfigResponseJson] that provides a timestamp
|
||||
* to check if a sync is necessary
|
||||
*
|
||||
* @property lastSync The [Long] of the last sync.
|
||||
* @property serverData The raw [ConfigResponseJson] that contains specific data of the
|
||||
* server configuration
|
||||
*/
|
||||
@Serializable
|
||||
data class ServerConfig(
|
||||
val lastSync: Long,
|
||||
val serverData: ConfigResponseJson,
|
||||
)
|
|
@ -0,0 +1,21 @@
|
|||
package com.x8bit.bitwarden.data.platform.repository
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Provides an API for observing the server config state.
|
||||
*/
|
||||
interface ServerConfigRepository {
|
||||
|
||||
/**
|
||||
* Gets the state [ServerConfig]. If needed or forced by [forceRefresh],
|
||||
* updates the values using server side data.
|
||||
*/
|
||||
suspend fun getServerConfig(forceRefresh: Boolean): ServerConfig?
|
||||
|
||||
/**
|
||||
* Emits updates that track [ServerConfig].
|
||||
*/
|
||||
val serverConfigStateFlow: StateFlow<ServerConfig?>
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.x8bit.bitwarden.data.platform.repository
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.ConfigDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.service.ConfigService
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Primary implementation of [ServerConfigRepositoryImpl].
|
||||
*/
|
||||
class ServerConfigRepositoryImpl(
|
||||
private val configDiskSource: ConfigDiskSource,
|
||||
private val configService: ConfigService,
|
||||
private val clock: Clock,
|
||||
environmentRepository: EnvironmentRepository,
|
||||
dispatcherManager: DispatcherManager,
|
||||
) : ServerConfigRepository {
|
||||
|
||||
private val unconfinedScope = CoroutineScope(dispatcherManager.unconfined)
|
||||
|
||||
init {
|
||||
environmentRepository
|
||||
.environmentStateFlow
|
||||
.onEach {
|
||||
getServerConfig(true)
|
||||
}
|
||||
.launchIn(unconfinedScope)
|
||||
}
|
||||
|
||||
override suspend fun getServerConfig(forceRefresh: Boolean): ServerConfig? {
|
||||
val localConfig = configDiskSource.serverConfig
|
||||
val needsRefresh = localConfig == null ||
|
||||
Instant
|
||||
.ofEpochMilli(localConfig.lastSync)
|
||||
.isAfter(
|
||||
clock.instant().plusSeconds(MINIMUM_CONFIG_SYNC_INTERVAL_SEC),
|
||||
)
|
||||
|
||||
if (needsRefresh || forceRefresh) {
|
||||
configService
|
||||
.getConfig()
|
||||
.onSuccess { configResponse ->
|
||||
val serverConfig = ServerConfig(
|
||||
lastSync = clock.instant().toEpochMilli(),
|
||||
serverData = configResponse,
|
||||
)
|
||||
configDiskSource.serverConfig = serverConfig
|
||||
return serverConfig
|
||||
}
|
||||
}
|
||||
|
||||
// If we are unable to retrieve a configuration from the server,
|
||||
// fall back to the local configuration.
|
||||
return localConfig
|
||||
}
|
||||
|
||||
override val serverConfigStateFlow: StateFlow<ServerConfig?>
|
||||
get() = configDiskSource
|
||||
.serverConfigFlow
|
||||
.stateIn(
|
||||
scope = unconfinedScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = configDiskSource.serverConfig,
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val MINIMUM_CONFIG_SYNC_INTERVAL_SEC: Long = 60 * 60
|
||||
}
|
||||
}
|
|
@ -3,13 +3,17 @@ package com.x8bit.bitwarden.data.platform.repository.di
|
|||
import android.view.autofill.AutofillManager
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManager
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.ConfigDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.service.ConfigService
|
||||
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepositoryImpl
|
||||
import com.x8bit.bitwarden.data.platform.repository.ServerConfigRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.ServerConfigRepositoryImpl
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepositoryImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
|
@ -17,6 +21,7 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import java.time.Clock
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
|
@ -26,6 +31,23 @@ import javax.inject.Singleton
|
|||
@InstallIn(SingletonComponent::class)
|
||||
object PlatformRepositoryModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideServerConfigRepository(
|
||||
configDiskSource: ConfigDiskSource,
|
||||
configService: ConfigService,
|
||||
clock: Clock,
|
||||
environmentRepository: EnvironmentRepository,
|
||||
dispatcherManager: DispatcherManager,
|
||||
): ServerConfigRepository =
|
||||
ServerConfigRepositoryImpl(
|
||||
configDiskSource = configDiskSource,
|
||||
configService = configService,
|
||||
clock = clock,
|
||||
environmentRepository = environmentRepository,
|
||||
dispatcherManager = dispatcherManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideEnvironmentRepository(
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.disk
|
||||
|
||||
import androidx.core.content.edit
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeSharedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.di.PlatformNetworkModule
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.EnvironmentJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.ServerJson
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.Instant
|
||||
|
||||
class ConfigDiskSourceTest {
|
||||
private val json = PlatformNetworkModule.providesJson()
|
||||
|
||||
private val fakeSharedPreferences = FakeSharedPreferences()
|
||||
|
||||
private val configDiskSource = ConfigDiskSourceImpl(
|
||||
sharedPreferences = fakeSharedPreferences,
|
||||
json = json,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `serverConfig should pull from and update SharedPreferences`() {
|
||||
val serverConfigKey = "bwPreferencesStorage:serverConfigurations"
|
||||
|
||||
// Shared preferences and the repository start with the same value.
|
||||
assertNull(configDiskSource.serverConfig)
|
||||
assertNull(fakeSharedPreferences.getString(serverConfigKey, null))
|
||||
|
||||
// Updating the repository updates shared preferences
|
||||
configDiskSource.serverConfig = SERVER_CONFIG
|
||||
assertEquals(
|
||||
json.parseToJsonElement(
|
||||
SERVER_CONFIG_JSON,
|
||||
),
|
||||
json.parseToJsonElement(
|
||||
fakeSharedPreferences.getString(serverConfigKey, null)!!,
|
||||
),
|
||||
)
|
||||
|
||||
// Update SharedPreferences updates the repository
|
||||
fakeSharedPreferences.edit { putString(serverConfigKey, null) }
|
||||
assertNull(configDiskSource.serverConfig)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `serverConfigFlow should react to changes in serverConfig`() =
|
||||
runTest {
|
||||
configDiskSource.serverConfigFlow.test {
|
||||
// The initial values of the Flow and the property are in sync
|
||||
assertNull(configDiskSource.serverConfig)
|
||||
assertNull(awaitItem())
|
||||
|
||||
// Updating the repository updates shared preferences
|
||||
configDiskSource.serverConfig = SERVER_CONFIG
|
||||
assertEquals(SERVER_CONFIG, awaitItem())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val SERVER_CONFIG_JSON = """
|
||||
{
|
||||
"lastSync": 1698408000000,
|
||||
"serverData": {
|
||||
"version": "2024.7.0",
|
||||
"gitHash": "25cf6119-dirty",
|
||||
"server": {
|
||||
"name": "example",
|
||||
"url": "https://localhost:8080"
|
||||
},
|
||||
"environment": {
|
||||
"vault": "https://localhost:8080",
|
||||
"api": "http://localhost:4000",
|
||||
"identity": "http://localhost:33656",
|
||||
"notifications": "http://localhost:61840",
|
||||
"sso": "http://localhost:51822"
|
||||
},
|
||||
"featureStates": {
|
||||
"duo-redirect": true,
|
||||
"flexible-collections-v-1": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
private val SERVER_CONFIG = ServerConfig(
|
||||
lastSync = Instant.parse("2023-10-27T12:00:00Z").toEpochMilli(),
|
||||
serverData = ConfigResponseJson(
|
||||
type = null,
|
||||
version = "2024.7.0",
|
||||
gitHash = "25cf6119-dirty",
|
||||
server = ServerJson(
|
||||
name = "example",
|
||||
url = "https://localhost:8080",
|
||||
),
|
||||
environment = EnvironmentJson(
|
||||
cloudRegion = null,
|
||||
vaultUrl = "https://localhost:8080",
|
||||
apiUrl = "http://localhost:4000",
|
||||
identityUrl = "http://localhost:33656",
|
||||
notificationsUrl = "http://localhost:61840",
|
||||
ssoUrl = "http://localhost:51822",
|
||||
),
|
||||
featureStates = mapOf("duo-redirect" to true, "flexible-collections-v-1" to false),
|
||||
),
|
||||
)
|
|
@ -0,0 +1,25 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.disk.util
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.ConfigDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.onSubscription
|
||||
|
||||
class FakeConfigDiskSource : ConfigDiskSource {
|
||||
private var serverConfigValue: ServerConfig? = null
|
||||
|
||||
override var serverConfig: ServerConfig?
|
||||
get() = serverConfigValue
|
||||
set(value) {
|
||||
serverConfigValue = value
|
||||
mutableServerConfigFlow.tryEmit(value)
|
||||
}
|
||||
|
||||
override val serverConfigFlow: Flow<ServerConfig?>
|
||||
get() = mutableServerConfigFlow
|
||||
.onSubscription { emit(serverConfig) }
|
||||
|
||||
private val mutableServerConfigFlow =
|
||||
bufferedMutableSharedFlow<ServerConfig?>(replay = 1)
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
package com.x8bit.bitwarden.data.platform.repository
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeConfigDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.EnvironmentJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson.ServerJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.service.ConfigService
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
|
||||
class ServerConfigRepositoryTest {
|
||||
private val fakeDispatcherManager: DispatcherManager = FakeDispatcherManager()
|
||||
private val fakeConfigDiskSource = FakeConfigDiskSource()
|
||||
private val configService: ConfigService = mockk {
|
||||
coEvery {
|
||||
getConfig()
|
||||
} returns CONFIG_RESPONSE_JSON.asSuccess()
|
||||
}
|
||||
|
||||
private val environmentRepository = FakeEnvironmentRepository().apply {
|
||||
environment = Environment.Us
|
||||
}
|
||||
|
||||
private val fixedClock: Clock = Clock.fixed(
|
||||
Instant.parse("2023-10-27T12:00:00Z"),
|
||||
ZoneOffset.UTC,
|
||||
)
|
||||
|
||||
private val repository = ServerConfigRepositoryImpl(
|
||||
configDiskSource = fakeConfigDiskSource,
|
||||
configService = configService,
|
||||
clock = fixedClock,
|
||||
environmentRepository = environmentRepository,
|
||||
dispatcherManager = fakeDispatcherManager,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
fakeConfigDiskSource.serverConfig = null
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `environmentRepository stateflow should trigger new server configuration`() = runTest {
|
||||
assertNull(
|
||||
fakeConfigDiskSource.serverConfig,
|
||||
)
|
||||
|
||||
// This should trigger a new server config to be fetched
|
||||
environmentRepository.environment = Environment.Eu
|
||||
|
||||
repository.serverConfigStateFlow.test {
|
||||
assertEquals(
|
||||
SERVER_CONFIG,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getServerConfig should fetch a new server configuration with force refresh as true`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
configService.getConfig()
|
||||
} returns CONFIG_RESPONSE_JSON.copy(version = "NEW VERSION").asSuccess()
|
||||
|
||||
fakeConfigDiskSource.serverConfig = SERVER_CONFIG.copy(
|
||||
lastSync = fixedClock.instant().toEpochMilli(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
fakeConfigDiskSource.serverConfig,
|
||||
SERVER_CONFIG,
|
||||
)
|
||||
|
||||
repository.getServerConfig(forceRefresh = true)
|
||||
|
||||
assertNotEquals(
|
||||
fakeConfigDiskSource.serverConfig,
|
||||
SERVER_CONFIG,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getServerConfig should fetch a new server configuration if there is none in state`() =
|
||||
runTest {
|
||||
assertNull(
|
||||
fakeConfigDiskSource.serverConfig,
|
||||
)
|
||||
|
||||
repository.getServerConfig(forceRefresh = false)
|
||||
|
||||
assertEquals(
|
||||
fakeConfigDiskSource.serverConfig,
|
||||
SERVER_CONFIG,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getServerConfig should return state server config if refresh is not necessary`() =
|
||||
runTest {
|
||||
val testConfig = SERVER_CONFIG.copy(
|
||||
lastSync = fixedClock.instant().plusSeconds(1000L).toEpochMilli(),
|
||||
serverData = CONFIG_RESPONSE_JSON.copy(
|
||||
version = "new version!!",
|
||||
),
|
||||
)
|
||||
fakeConfigDiskSource.serverConfig = testConfig
|
||||
|
||||
coEvery {
|
||||
configService.getConfig()
|
||||
} returns CONFIG_RESPONSE_JSON.asSuccess()
|
||||
|
||||
repository.getServerConfig(forceRefresh = false)
|
||||
|
||||
assertEquals(
|
||||
fakeConfigDiskSource.serverConfig,
|
||||
testConfig,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `serverConfigStateFlow should react to new server configurations`() = runTest {
|
||||
repository.getServerConfig(forceRefresh = true)
|
||||
|
||||
repository.serverConfigStateFlow.test {
|
||||
assertEquals(fakeConfigDiskSource.serverConfig, awaitItem())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val SERVER_CONFIG = ServerConfig(
|
||||
lastSync = Instant.parse("2023-10-27T12:00:00Z").toEpochMilli(),
|
||||
serverData = ConfigResponseJson(
|
||||
type = null,
|
||||
version = "2024.7.0",
|
||||
gitHash = "25cf6119-dirty",
|
||||
server = ServerJson(
|
||||
name = "example",
|
||||
url = "https://localhost:8080",
|
||||
),
|
||||
environment = EnvironmentJson(
|
||||
cloudRegion = null,
|
||||
vaultUrl = "https://localhost:8080",
|
||||
apiUrl = "http://localhost:4000",
|
||||
identityUrl = "http://localhost:33656",
|
||||
notificationsUrl = "http://localhost:61840",
|
||||
ssoUrl = "http://localhost:51822",
|
||||
),
|
||||
featureStates = mapOf("duo-redirect" to true, "flexible-collections-v-1" to false),
|
||||
),
|
||||
)
|
||||
|
||||
private val CONFIG_RESPONSE_JSON = ConfigResponseJson(
|
||||
type = null,
|
||||
version = "2024.7.0",
|
||||
gitHash = "25cf6119-dirty",
|
||||
server = ServerJson(
|
||||
name = "example",
|
||||
url = "https://localhost:8080",
|
||||
),
|
||||
environment = EnvironmentJson(
|
||||
cloudRegion = null,
|
||||
vaultUrl = "https://localhost:8080",
|
||||
apiUrl = "http://localhost:4000",
|
||||
identityUrl = "http://localhost:33656",
|
||||
notificationsUrl = "http://localhost:61840",
|
||||
ssoUrl = "http://localhost:51822",
|
||||
),
|
||||
featureStates = mapOf("duo-redirect" to true, "flexible-collections-v-1" to false),
|
||||
)
|
Loading…
Reference in a new issue