Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-08 19:28:17 +03:00
parent 8af068b74b
commit fc319f2b70
17 changed files with 518 additions and 68 deletions

View file

@ -36,6 +36,7 @@ dependencies {
// endregion
// region Tests
testImplementation(projects.test.core)
testImplementation(deps.test.coroutine)
testImplementation(deps.test.junit5)
testImplementation(deps.test.mockk)

View file

@ -6,6 +6,9 @@ import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.PushNotificationPreferencesBody
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getSyncOrNull
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pushnotificationpreferences.models.PushNotificationCategory
import com.tangem.domain.pushnotificationpreferences.models.WalletPushNotificationPreferences
@ -28,11 +31,23 @@ import kotlinx.coroutines.withContext
internal class DefaultWalletPushNotificationPreferencesRepository(
private val tangemTechApi: TangemTechApi,
private val cache: RuntimeSharedStore<Map<String, WalletPushNotificationPreferences>>,
private val appPreferencesStore: AppPreferencesStore,
private val dispatchers: CoroutineDispatcherProvider,
) : WalletPushNotificationPreferencesRepository {
private val walletMutexes = ConcurrentHashMap<String, Mutex>()
override suspend fun isFirstActivationDone(userWalletId: UserWalletId): Boolean =
appPreferencesStore.getSyncOrNull(PreferencesKeys.PUSH_NOTIFICATION_FIRST_ACTIVATION_DONE_WALLET_IDS_KEY)
?.contains(userWalletId.stringValue) == true
override suspend fun markFirstActivationDone(userWalletId: UserWalletId) {
appPreferencesStore.editData { preferences ->
val key = PreferencesKeys.PUSH_NOTIFICATION_FIRST_ACTIVATION_DONE_WALLET_IDS_KEY
preferences[key] = preferences.getOrDefault(key, emptySet()) + userWalletId.stringValue
}
}
override suspend fun preload(userWalletId: UserWalletId) {
if (isCached(userWalletId)) return
mutexFor(userWalletId).withLock {

View file

@ -3,6 +3,7 @@ package com.tangem.data.pushnotificationpreferences.di
import com.tangem.data.pushnotificationpreferences.DefaultWalletPushNotificationPreferencesRepository
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.pushnotificationpreferences.repository.WalletPushNotificationPreferencesRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
@ -19,10 +20,12 @@ internal object PushNotificationPreferencesModule {
@Provides
fun providesWalletPushNotificationPreferencesRepository(
tangemTechApi: TangemTechApi,
appPreferencesStore: AppPreferencesStore,
dispatchers: CoroutineDispatcherProvider,
): WalletPushNotificationPreferencesRepository = DefaultWalletPushNotificationPreferencesRepository(
tangemTechApi = tangemTechApi,
cache = RuntimeSharedStore(),
appPreferencesStore = appPreferencesStore,
dispatchers = dispatchers,
)
}

View file

@ -1,17 +1,21 @@
package com.tangem.data.pushnotificationpreferences
import androidx.datastore.preferences.core.emptyPreferences
import app.cash.turbine.test
import arrow.core.Either
import com.google.common.truth.Truth.assertThat
import com.squareup.moshi.Moshi
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.PushNotificationPreferencesBody
import com.tangem.datasource.api.tangemTech.models.PushNotificationPreferencesResponse
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pushnotificationpreferences.models.PushNotificationCategory
import com.tangem.domain.pushnotificationpreferences.models.PushNotificationPreference
import com.tangem.domain.pushnotificationpreferences.models.WalletPushNotificationPreferences
import com.tangem.test.core.datastore.MockStateDataStore
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.coEvery
import io.mockk.coVerify
@ -30,9 +34,17 @@ class DefaultWalletPushNotificationPreferencesRepositoryTest {
private val userWalletId = UserWalletId(stringValue = "0011223344556677")
private val otherWalletId = UserWalletId(stringValue = "ffeeddccbbaa9988")
// Real in-memory store so the persisted first-activation flag (a Set<String> merge) is genuinely exercised.
private val appPreferencesStore = AppPreferencesStore(
moshi = Moshi.Builder().build(),
dispatchers = TestingCoroutineDispatcherProvider(),
preferencesDataStore = MockStateDataStore(default = emptyPreferences()),
)
private val repository = DefaultWalletPushNotificationPreferencesRepository(
tangemTechApi = tangemTechApi,
cache = RuntimeSharedStore(),
appPreferencesStore = appPreferencesStore,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@ -202,6 +214,28 @@ class DefaultWalletPushNotificationPreferencesRepositoryTest {
}
}
@Test
fun `GIVEN wallet never activated WHEN isFirstActivationDone THEN false`() = runTest {
assertThat(repository.isFirstActivationDone(userWalletId)).isFalse()
}
@Test
fun `GIVEN wallet marked WHEN isFirstActivationDone THEN true and persisted`() = runTest {
repository.markFirstActivationDone(userWalletId)
assertThat(repository.isFirstActivationDone(userWalletId)).isTrue()
}
@Test
fun `GIVEN one wallet marked WHEN another wallet marked THEN both stay done`() = runTest {
// Guards the additive-merge backbone: a regression to a single-id overwrite would drop the first wallet.
repository.markFirstActivationDone(userWalletId)
repository.markFirstActivationDone(otherWalletId)
assertThat(repository.isFirstActivationDone(userWalletId)).isTrue()
assertThat(repository.isFirstActivationDone(otherWalletId)).isTrue()
}
private fun stubGet(id: UserWalletId, transaction: Boolean, offers: Boolean, price: Boolean) {
coEvery { tangemTechApi.getPushNotificationPreferences(id.stringValue) } returns
ApiResponse.Success(PushNotificationPreferencesResponse(transaction, offers, price))