diff --git a/common/test/build.gradle.kts b/common/test/build.gradle.kts index 4ab99487fb..f2de491c8c 100644 --- a/common/test/build.gradle.kts +++ b/common/test/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(projects.core.utils) implementation(projects.data.common) + implementation(projects.data.staking) implementation(projects.domain.legacy) implementation(projects.domain.models) @@ -23,6 +24,7 @@ dependencies { implementation(projects.libs.blockchainSdk) implementation(deps.androidx.datastore) + implementation(deps.jodatime) implementation(deps.test.coroutine) implementation(tangemDeps.blockchain) diff --git a/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldBalanceWrapperDTOFactory.kt b/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldBalanceWrapperDTOFactory.kt new file mode 100644 index 0000000000..521604e487 --- /dev/null +++ b/common/test/src/main/java/com/tangem/common/test/data/staking/MockYieldBalanceWrapperDTOFactory.kt @@ -0,0 +1,51 @@ +package com.tangem.common.test.data.staking + +import com.tangem.data.staking.store.YieldsBalancesStore.StakingID +import com.tangem.datasource.api.stakekit.models.request.Address +import com.tangem.datasource.api.stakekit.models.response.model.BalanceDTO +import com.tangem.datasource.api.stakekit.models.response.model.NetworkTypeDTO +import com.tangem.datasource.api.stakekit.models.response.model.TokenDTO +import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO +import java.math.BigDecimal + +/** +[REDACTED_AUTHOR] + */ +object MockYieldBalanceWrapperDTOFactory { + + val defaultStakingId = StakingID( + integrationId = "ton-ton-chorus-one-pools-staking", + address = "0x1", + ) + + fun createWithBalance(stakingId: StakingID = defaultStakingId): YieldBalanceWrapperDTO { + return YieldBalanceWrapperDTO( + addresses = Address(address = stakingId.address), + balances = listOf( + BalanceDTO( + groupId = "groupId", + type = BalanceDTO.BalanceTypeDTO.UNKNOWN, + amount = BigDecimal.ONE, + date = null, + pricePerShare = BigDecimal.ZERO, + pendingActions = listOf(), + pendingActionConstraints = null, + tokenDTO = TokenDTO( + name = "The-Open-Network", + network = NetworkTypeDTO.TON, + symbol = "TON", + decimals = 8, + address = null, + coinGeckoId = null, + logoURI = null, + isPoints = null, + ), + validatorAddress = null, + validatorAddresses = null, + providerId = null, + ), + ), + integrationId = stakingId.integrationId, + ) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt index 051e63780e..de55373f39 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/converter/YieldBalanceConverter.kt @@ -7,7 +7,7 @@ import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.model.stakekit.YieldBalanceItem import com.tangem.utils.converter.Converter -internal class YieldBalanceConverter( +class YieldBalanceConverter( private val source: StatusSource, ) : Converter { diff --git a/data/staking/build.gradle.kts b/data/staking/build.gradle.kts index 9af26ac76e..b4d8282fac 100644 --- a/data/staking/build.gradle.kts +++ b/data/staking/build.gradle.kts @@ -62,4 +62,11 @@ dependencies { } // endregion + + testImplementation(deps.test.coroutine) + testImplementation(deps.test.junit) + testImplementation(deps.test.mockk) + testImplementation(deps.test.truth) + testImplementation(tangemDeps.card.core) + testImplementation(projects.common.test) } diff --git a/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt b/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt new file mode 100644 index 0000000000..32beb14ec3 --- /dev/null +++ b/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt @@ -0,0 +1,158 @@ +package com.tangem.data.staking.store + +import androidx.datastore.core.DataStore +import com.tangem.data.staking.store.YieldsBalancesStore.StakingID +import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.datasource.local.token.converter.YieldBalanceConverter +import com.tangem.domain.models.StatusSource +import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.extensions.addOrReplace +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.launch + +internal typealias WalletIdWithWrappers = Map> +internal typealias WalletIdWithBalances = Map> + +/** + * Default implementation of [YieldsBalancesStore] + * + * @property runtimeStore runtime store + * @property persistenceStore persistence store + * @param dispatchers + * +[REDACTED_AUTHOR] + */ +internal class DefaultYieldsBalancesStore( + private val runtimeStore: RuntimeSharedStore, + private val persistenceStore: DataStore, + dispatchers: CoroutineDispatcherProvider, +) : YieldsBalancesStore { + + private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io) + + init { + scope.launch { + val cachedStatuses = persistenceStore.data.firstOrNull() ?: return@launch + + runtimeStore.store( + value = cachedStatuses.map { (stringWalletId, wrappers) -> + val key = UserWalletId(stringWalletId) + val value = YieldBalanceConverter(isCached = true).convertSet(input = wrappers) + + key to value + } + .toMap(), + ) + } + } + + override fun get(userWalletId: UserWalletId): Flow> { + return runtimeStore.get().mapNotNull { it[userWalletId] } + } + + override suspend fun refresh(userWalletId: UserWalletId, stakingId: StakingID) { + updateBalanceInRuntime(userWalletId, stakingId) { + it.copySealed(source = StatusSource.CACHE) + } + } + + override suspend fun refresh(userWalletId: UserWalletId, stakingIds: Set) { + runtimeStore.update(default = emptyMap()) { stored -> + stored.toMutableMap().apply { + val storedBalances = this[userWalletId].orEmpty() + + val balances = stakingIds.mapTo(hashSetOf()) { stakingId -> + val balance = storedBalances.firstOrNull { + it.integrationId == stakingId.integrationId && + it.address == stakingId.address + } + ?: createDefaultBalance(id = stakingId) + + balance.copySealed(source = StatusSource.CACHE) + } + + val updatedBalances = storedBalances.addOrReplace(balances) { old, new -> + old.integrationId == new.integrationId && old.address == new.address + } + + put(key = userWalletId, value = updatedBalances) + } + } + } + + override suspend fun storeActual(userWalletId: UserWalletId, values: Set) { + coroutineScope { + launch { storeInRuntime(userWalletId = userWalletId, values = values) } + launch { storeInPersistence(userWalletId = userWalletId, values = values) } + } + } + + override suspend fun storeError(userWalletId: UserWalletId, stakingId: StakingID) { + updateBalanceInRuntime(userWalletId, stakingId) { + it.copySealed(source = StatusSource.ONLY_CACHE) + } + } + + private suspend fun storeInRuntime(userWalletId: UserWalletId, values: Set) { + val newBalances = YieldBalanceConverter(isCached = false).convertSet(input = values) + + runtimeStore.update(default = emptyMap()) { saved -> + saved.toMutableMap().apply { + this[userWalletId] = saved[userWalletId] + ?.addOrReplace(newBalances) { old, new -> + old.integrationId == new.integrationId && old.address == new.address + } + ?: newBalances + } + } + } + + private suspend fun storeInPersistence(userWalletId: UserWalletId, values: Set) { + persistenceStore.updateData { current -> + current.toMutableMap().apply { + this[userWalletId.stringValue] = current[userWalletId.stringValue] + ?.addOrReplace(items = values) { old, new -> + old.integrationId == new.integrationId && old.addresses.address == new.addresses.address + } + ?: values + } + } + } + + private suspend fun updateBalanceInRuntime( + userWalletId: UserWalletId, + stakingID: StakingID, + update: (YieldBalance) -> YieldBalance, + ) { + runtimeStore.update(default = emptyMap()) { stored -> + stored.toMutableMap().apply { + val balance = this[userWalletId].orEmpty() + .firstOrNull { + it.integrationId == stakingID.integrationId && + it.address == stakingID.address + } + ?: createDefaultBalance(id = stakingID) + + val updatedBalances = this[userWalletId].orEmpty() + .addOrReplace(item = update(balance)) { + it.integrationId == balance.integrationId && + it.address == balance.address + } + + put(key = userWalletId, value = updatedBalances) + } + } + } + + private fun createDefaultBalance(id: StakingID): YieldBalance { + return YieldBalance.Error(integrationId = id.integrationId, address = id.address) + } +} \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/store/YieldsBalancesStore.kt b/data/staking/src/main/java/com/tangem/data/staking/store/YieldsBalancesStore.kt new file mode 100644 index 0000000000..2e5c062f3d --- /dev/null +++ b/data/staking/src/main/java/com/tangem/data/staking/store/YieldsBalancesStore.kt @@ -0,0 +1,31 @@ +package com.tangem.data.staking.store + +import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO +import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.wallets.models.UserWalletId +import kotlinx.coroutines.flow.Flow + +/** + * Store of [YieldBalance]'s set + * +[REDACTED_AUTHOR] + */ +interface YieldsBalancesStore { + + /** Get flow of [YieldBalance]'s set by [userWalletId] */ + fun get(userWalletId: UserWalletId): Flow> + + /** Refresh balance of [stakingId] by [userWalletId] */ + suspend fun refresh(userWalletId: UserWalletId, stakingId: StakingID) + + /** Refresh balances of [stakingIds] by [userWalletId] */ + suspend fun refresh(userWalletId: UserWalletId, stakingIds: Set) + + /** Store actual [values] by [userWalletId] */ + suspend fun storeActual(userWalletId: UserWalletId, values: Set) + + /** Store error by [userWalletId] and [stakingId] */ + suspend fun storeError(userWalletId: UserWalletId, stakingId: StakingID) + + data class StakingID(val integrationId: String, val address: String) +} \ No newline at end of file diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/YieldBalanceExt.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/YieldBalanceExt.kt new file mode 100644 index 0000000000..c04560fbeb --- /dev/null +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/YieldBalanceExt.kt @@ -0,0 +1,10 @@ +package com.tangem.data.staking + +import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO +import com.tangem.datasource.local.token.converter.YieldBalanceConverter +import com.tangem.domain.models.StatusSource +import com.tangem.domain.staking.model.stakekit.YieldBalance + +internal fun YieldBalanceWrapperDTO.toDomain(source: StatusSource = StatusSource.CACHE): YieldBalance { + return YieldBalanceConverter(source = source).convert(this) +} \ No newline at end of file diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreGetMethodTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreGetMethodTest.kt new file mode 100644 index 0000000000..7900b655e1 --- /dev/null +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreGetMethodTest.kt @@ -0,0 +1,83 @@ +package com.tangem.data.staking.store + +import com.google.common.truth.Truth +import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.common.test.utils.getEmittedValues +import com.tangem.data.staking.toDomain +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class YieldsBalancesStoreGetMethodTest { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultYieldsBalancesStore( + runtimeStore = runtimeStore, + persistenceStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `test get if runtime store is empty`() = runTest { + val actual = store.get(userWalletId = userWalletId) + + val values = getEmittedValues(flow = actual) + + Truth.assertThat(values).isEqualTo(emptyList>()) + } + + @Test + fun `test get if runtime store contains empty map`() = runTest { + runtimeStore.store(value = emptyMap()) + + val actual = store.get(userWalletId = userWalletId) + + val values = getEmittedValues(flow = actual) + + Truth.assertThat(values).isEqualTo(emptyList>()) + } + + @Test + fun `test get if runtime store contains portfolio with empty balances`() = runTest { + runtimeStore.store( + value = mapOf(userWalletId to emptySet()), + ) + + val actual = store.get(userWalletId = userWalletId) + + val values = getEmittedValues(flow = actual) + + Truth.assertThat(values.size).isEqualTo(1) + Truth.assertThat(values).isEqualTo(listOf(emptySet())) + } + + @Test + fun `test get if runtime store is not empty`() = runTest { + val wrapper = MockYieldBalanceWrapperDTOFactory.createWithBalance() + + runtimeStore.store( + value = mapOf(userWalletId to setOf(wrapper.toDomain())), + ) + + val actual = store.get(userWalletId = userWalletId) + + val values = getEmittedValues(flow = actual) + + Truth.assertThat(values.size).isEqualTo(1) + Truth.assertThat(values).isEqualTo(listOf(setOf(wrapper.toDomain()))) + } + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + } +} \ No newline at end of file diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreInitializationTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreInitializationTest.kt new file mode 100644 index 0000000000..13bb51d6e3 --- /dev/null +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreInitializationTest.kt @@ -0,0 +1,80 @@ +package com.tangem.data.staking.store + +import androidx.datastore.core.DataStore +import com.google.common.truth.Truth +import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.data.staking.toDomain +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class YieldsBalancesStoreInitializationTest { + + @Test + fun `test initialization if cache store is empty`() = runTest { + val runtimeStore = RuntimeSharedStore() + val persistenceStore: DataStore = mockk() + + every { persistenceStore.data } returns emptyFlow() + + DefaultYieldsBalancesStore( + runtimeStore = runtimeStore, + persistenceStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(null) + } + + @Test + fun `test initialization if cache store contains empty map`() = runTest { + val runtimeStore = RuntimeSharedStore() + val persistenceStore = MockStateDataStore(default = emptyMap()) + + DefaultYieldsBalancesStore( + runtimeStore = runtimeStore, + persistenceStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `test initialization if cache store is not empty`() = runTest { + val runtimeStore = RuntimeSharedStore() + val persistenceStore = MockStateDataStore(default = emptyMap()) + + val wrapper = MockYieldBalanceWrapperDTOFactory.createWithBalance() + + persistenceStore.updateData { + it.toMutableMap().apply { + put(userWalletId.stringValue, setOf(wrapper)) + } + } + + DefaultYieldsBalancesStore( + runtimeStore = runtimeStore, + persistenceStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + val expected = mapOf(userWalletId to setOf(wrapper.toDomain())) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(expected) + } + + private companion object { + val userWalletId = UserWalletId(stringValue = "011") + } +} \ No newline at end of file diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt new file mode 100644 index 0000000000..f64a6da8e8 --- /dev/null +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt @@ -0,0 +1,188 @@ +package com.tangem.data.staking.store + +import com.google.common.truth.Truth +import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory +import com.tangem.common.test.datastore.MockStateDataStore +import com.tangem.data.staking.toDomain +import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO +import com.tangem.datasource.local.datastore.RuntimeSharedStore +import com.tangem.domain.models.StatusSource +import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.junit.Test + +/** +[REDACTED_AUTHOR] + */ +internal class YieldsBalancesStoreUpdateMethodsTest { + + private val runtimeStore = RuntimeSharedStore() + private val persistenceStore = MockStateDataStore(default = emptyMap()) + + private val store = DefaultYieldsBalancesStore( + runtimeStore = runtimeStore, + persistenceStore = persistenceStore, + dispatchers = TestingCoroutineDispatcherProvider(), + ) + + @Test + fun `refresh the single id if runtime store is empty`() = runTest { + store.refresh(userWalletId = userWalletId, stakingId = stakingId) + + val runtimeExpected = mapOf( + userWalletId to setOf( + YieldBalance.Error( + integrationId = stakingId.integrationId, + address = stakingId.address, + ), + ), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `refresh the single id if runtime store contains balance with this id`() = runTest { + val balance = MockYieldBalanceWrapperDTOFactory.createWithBalance().toDomain(source = StatusSource.ACTUAL) + + runtimeStore.store( + value = mapOf(userWalletId to setOf(balance)), + ) + + store.refresh(userWalletId = userWalletId, stakingId = stakingId) + + val runtimeExpected = mapOf( + userWalletId to setOf( + balance.copySealed(source = StatusSource.CACHE), + ), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `refresh the multi ids if runtime store is empty`() = runTest { + store.refresh(userWalletId = userWalletId, stakingIds = stakingIds) + + val runtimeExpected = mapOf( + userWalletId to stakingIds.mapTo(hashSetOf()) { + YieldBalance.Error(integrationId = it.integrationId, address = it.address) + }, + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `refresh the multi ids if runtime store contains balance with this id`() = runTest { + val firstBalance = MockYieldBalanceWrapperDTOFactory.createWithBalance(stakingId = stakingIds.first()) + .toDomain(source = StatusSource.ACTUAL) + + val secondBalance = MockYieldBalanceWrapperDTOFactory.createWithBalance(stakingId = stakingIds.last()) + .toDomain(source = StatusSource.ACTUAL) + + runtimeStore.store( + value = mapOf(userWalletId to setOf(firstBalance, secondBalance)), + ) + + store.refresh(userWalletId = userWalletId, stakingIds = stakingIds) + + val runtimeExpected = mapOf( + userWalletId to setOf( + firstBalance.copySealed(source = StatusSource.CACHE), + secondBalance.copySealed(source = StatusSource.CACHE), + ), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `store actual if runtime and cache stores contain balance with this id`() = runTest { + val prevWrapper = MockYieldBalanceWrapperDTOFactory.createWithBalance() + + runtimeStore.store( + value = mapOf(userWalletId to setOf(prevWrapper.toDomain(source = StatusSource.CACHE))), + ) + + persistenceStore.updateData { + it.toMutableMap().apply { + put(userWalletId.stringValue, setOf(prevWrapper)) + } + } + + val newWrapper = MockYieldBalanceWrapperDTOFactory.createWithBalance() + + store.storeActual(userWalletId, setOf(newWrapper)) + + val runtimeExpected = mapOf( + userWalletId to setOf(newWrapper.toDomain(source = StatusSource.ACTUAL)), + ) + + val persistenceExpected = mapOf( + userWalletId.stringValue to setOf(newWrapper), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(persistenceExpected) + } + + @Test + fun `store error if runtime store is empty`() = runTest { + store.storeError(userWalletId = userWalletId, stakingId = stakingId) + + val runtimeExpected = mapOf( + userWalletId to setOf( + YieldBalance.Error( + integrationId = stakingId.integrationId, + address = stakingId.address, + ), + ), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + @Test + fun `store error if runtime store contains balance with this id`() = runTest { + val wrapper = MockYieldBalanceWrapperDTOFactory.createWithBalance(stakingId) + + runtimeStore.store( + value = mapOf( + userWalletId to setOf(wrapper.toDomain(source = StatusSource.CACHE)), + ), + ) + + store.storeError(userWalletId = userWalletId, stakingId = stakingId) + + val runtimeExpected = mapOf( + userWalletId to setOf(wrapper.toDomain(source = StatusSource.ONLY_CACHE)), + ) + + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + } + + private companion object { + + val userWalletId = UserWalletId(stringValue = "011") + + val stakingId = MockYieldBalanceWrapperDTOFactory.defaultStakingId + + val stakingIds = setOf( + stakingId, + YieldsBalancesStore.StakingID( + integrationId = "solana-sol-native-multivalidator-staking", + address = "0x1", + ), + ) + } +} \ No newline at end of file diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt index ce016aee04..74d271b455 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt @@ -9,21 +9,32 @@ sealed class YieldBalance { abstract val integrationId: String? abstract val address: String? + abstract val source: StatusSource + + fun copySealed(source: StatusSource): YieldBalance { + return when (this) { + is Data -> copy(source = source) + is Empty -> copy(source = source) + is Error -> this + } + } data class Data( override val integrationId: String?, override val address: String, + override val source: StatusSource, val balance: YieldBalanceItem, - val source: StatusSource, ) : YieldBalance() data class Empty( override val integrationId: String?, override val address: String, - val source: StatusSource, + override val source: StatusSource, ) : YieldBalance() - data class Error(override val integrationId: String?, override val address: String?) : YieldBalance() + data class Error(override val integrationId: String?, override val address: String?) : YieldBalance() { + override val source: StatusSource = StatusSource.ACTUAL + } } data class YieldBalanceItem(