Updated on 2026-08-14
This commit is contained in:
parent
c509046a8b
commit
adb8c50ae2
13 changed files with 448 additions and 1 deletions
|
|
@ -0,0 +1,50 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import androidx.datastore.preferences.core.longPreferencesKey
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.utils.get
|
||||
import com.tangem.domain.settings.UsedeskTokenTtlManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
* Development implementation of [UsedeskTokenTtlManager].
|
||||
*
|
||||
* Reads and writes the Usedesk chat token TTL from [AppPreferencesStore],
|
||||
* allowing testers to override it via the Tester Menu.
|
||||
* The preference [kotlinx.coroutines.flow.Flow] is converted to a [StateFlow] on construction,
|
||||
* so [getTokenTtlMillisSync] can be called from non-suspending contexts.
|
||||
* Defaults to [UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS] when no value is stored.
|
||||
*/
|
||||
internal class DevUsedeskTokenTtlManager(
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
) : UsedeskTokenTtlManager {
|
||||
|
||||
private val ttlMillisState: StateFlow<Long> =
|
||||
appPreferencesStore
|
||||
.get(key = USEDESK_TOKEN_TTL_MILLIS_KEY, default = UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS)
|
||||
.stateIn(
|
||||
scope = CoroutineScope(dispatchers.io + SupervisorJob()),
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS,
|
||||
)
|
||||
|
||||
override fun getTokenTtlMillis(): StateFlow<Long> = ttlMillisState
|
||||
|
||||
override fun getTokenTtlMillisSync(): Long = ttlMillisState.value
|
||||
|
||||
override suspend fun setTokenTtlMillis(millis: Long) {
|
||||
appPreferencesStore.editData { preferences ->
|
||||
preferences[USEDESK_TOKEN_TTL_MILLIS_KEY] = millis
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val USEDESK_TOKEN_TTL_MILLIS_KEY = longPreferencesKey(name = "usedeskTokenTtlMillis")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import com.tangem.domain.settings.UsedeskTokenTtlManager
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Production implementation of [UsedeskTokenTtlManager].
|
||||
*
|
||||
* The Usedesk chat token TTL is fixed to [UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS] in production —
|
||||
* a tester override must never leak into release builds.
|
||||
* [setTokenTtlMillis] is a no-op since the TTL cannot be changed at runtime.
|
||||
*/
|
||||
internal class ProdUsedeskTokenTtlManager : UsedeskTokenTtlManager {
|
||||
|
||||
private val state: StateFlow<Long> = MutableStateFlow(UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS)
|
||||
|
||||
override fun getTokenTtlMillis(): StateFlow<Long> = state
|
||||
override fun getTokenTtlMillisSync(): Long = UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS
|
||||
override suspend fun setTokenTtlMillis(millis: Long) = Unit
|
||||
}
|
||||
|
|
@ -6,11 +6,14 @@ import com.tangem.data.settings.DefaultAppRatingRepository
|
|||
import com.tangem.data.settings.DefaultPermissionRepository
|
||||
import com.tangem.data.settings.DefaultSettingsRepository
|
||||
import com.tangem.data.settings.DevHotWalletRestrictionManager
|
||||
import com.tangem.data.settings.DevUsedeskTokenTtlManager
|
||||
import com.tangem.data.settings.ProdHotWalletRestrictionManager
|
||||
import com.tangem.data.settings.ProdUsedeskTokenTtlManager
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.logs.AppLogsStore
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.domain.settings.HotWalletRestrictionManager
|
||||
import com.tangem.domain.settings.UsedeskTokenTtlManager
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.settings.repositories.PermissionRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
|
|
@ -72,4 +75,17 @@ internal object SettingsDataModule {
|
|||
ProdHotWalletRestrictionManager()
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUsedeskTokenTtlManager(
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): UsedeskTokenTtlManager {
|
||||
return if (BuildConfig.TESTER_MENU_ENABLED) {
|
||||
DevUsedeskTokenTtlManager(appPreferencesStore, dispatchers)
|
||||
} else {
|
||||
ProdUsedeskTokenTtlManager()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.utils.get
|
||||
import com.tangem.domain.settings.UsedeskTokenTtlManager
|
||||
import com.tangem.test.core.getEmittedValues
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class DevUsedeskTokenTtlManagerTest {
|
||||
|
||||
private val appPreferencesStore = mockk<AppPreferencesStore>(relaxed = true)
|
||||
private val dispatchers: CoroutineDispatcherProvider = TestingCoroutineDispatcherProvider()
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockkStatic("com.tangem.datasource.local.preferences.utils.PreferencesDataStoreExtKt")
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
unmockkStatic("com.tangem.datasource.local.preferences.utils.PreferencesDataStoreExtKt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN stored ttl WHEN getTokenTtlMillis THEN emits stored value`() = runTest {
|
||||
// Arrange
|
||||
val storedTtl = 15L * 60 * 1000
|
||||
every {
|
||||
appPreferencesStore.get(key = any<Preferences.Key<Long>>(), default = DEFAULT)
|
||||
} returns flowOf(storedTtl)
|
||||
|
||||
val manager = DevUsedeskTokenTtlManager(appPreferencesStore, dispatchers)
|
||||
|
||||
// Act
|
||||
val emitted = getEmittedValues(manager.getTokenTtlMillis())
|
||||
|
||||
// Assert
|
||||
assertThat(emitted).containsExactly(storedTtl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN no stored ttl WHEN getTokenTtlMillisSync THEN returns default`() = runTest {
|
||||
// Arrange
|
||||
every {
|
||||
appPreferencesStore.get(key = any<Preferences.Key<Long>>(), default = DEFAULT)
|
||||
} returns flowOf(DEFAULT)
|
||||
|
||||
val manager = DevUsedeskTokenTtlManager(appPreferencesStore, dispatchers)
|
||||
|
||||
// Act & Assert
|
||||
assertThat(manager.getTokenTtlMillisSync()).isEqualTo(DEFAULT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN stored ttl WHEN getTokenTtlMillisSync THEN returns cached value`() = runTest {
|
||||
// Arrange
|
||||
val storedTtl = 60L * 60 * 1000
|
||||
every {
|
||||
appPreferencesStore.get(key = any<Preferences.Key<Long>>(), default = DEFAULT)
|
||||
} returns flowOf(storedTtl)
|
||||
|
||||
val manager = DevUsedeskTokenTtlManager(appPreferencesStore, dispatchers)
|
||||
|
||||
// Act & Assert
|
||||
assertThat(manager.getTokenTtlMillisSync()).isEqualTo(storedTtl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN setTokenTtlMillis THEN opens editData transaction`() = runTest {
|
||||
// Arrange
|
||||
every {
|
||||
appPreferencesStore.get(key = any<Preferences.Key<Long>>(), default = DEFAULT)
|
||||
} returns flowOf(DEFAULT)
|
||||
coEvery { appPreferencesStore.editData(any()) } returns mockk(relaxed = true)
|
||||
|
||||
val manager = DevUsedeskTokenTtlManager(appPreferencesStore, dispatchers)
|
||||
|
||||
// Act
|
||||
manager.setTokenTtlMillis(millis = 15L * 60 * 1000)
|
||||
|
||||
// Assert
|
||||
coVerify { appPreferencesStore.editData(any()) }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val DEFAULT = UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.settings.UsedeskTokenTtlManager
|
||||
import com.tangem.test.core.getEmittedValues
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class ProdUsedeskTokenTtlManagerTest {
|
||||
|
||||
private val manager = ProdUsedeskTokenTtlManager()
|
||||
|
||||
@Test
|
||||
fun `WHEN getTokenTtlMillis THEN always emits default`() = runTest {
|
||||
val emitted = getEmittedValues(manager.getTokenTtlMillis())
|
||||
|
||||
assertThat(emitted).containsExactly(UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN getTokenTtlMillisSync THEN returns default`() = runTest {
|
||||
assertThat(manager.getTokenTtlMillisSync()).isEqualTo(UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN override attempt WHEN setTokenTtlMillis THEN sync still returns default`() = runTest {
|
||||
manager.setTokenTtlMillis(millis = 15L * 60 * 1000)
|
||||
|
||||
assertThat(manager.getTokenTtlMillisSync()).isEqualTo(UsedeskTokenTtlManager.DEFAULT_TTL_MILLIS)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue