Updated on 2026-08-14
This commit is contained in:
parent
242483fd4f
commit
887ea85b3b
11 changed files with 625 additions and 4 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<YieldBalanceWrapperDTO, YieldBalance> {
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, Set<YieldBalanceWrapperDTO>>
|
||||
internal typealias WalletIdWithBalances = Map<UserWalletId, Set<YieldBalance>>
|
||||
|
||||
/**
|
||||
* Default implementation of [YieldsBalancesStore]
|
||||
*
|
||||
* @property runtimeStore runtime store
|
||||
* @property persistenceStore persistence store
|
||||
* @param dispatchers
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultYieldsBalancesStore(
|
||||
private val runtimeStore: RuntimeSharedStore<WalletIdWithBalances>,
|
||||
private val persistenceStore: DataStore<WalletIdWithWrappers>,
|
||||
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<Set<YieldBalance>> {
|
||||
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<StakingID>) {
|
||||
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<YieldBalanceWrapperDTO>) {
|
||||
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<YieldBalanceWrapperDTO>) {
|
||||
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<YieldBalanceWrapperDTO>) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Set<YieldBalance>>
|
||||
|
||||
/** 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<StakingID>)
|
||||
|
||||
/** Store actual [values] by [userWalletId] */
|
||||
suspend fun storeActual(userWalletId: UserWalletId, values: Set<YieldBalanceWrapperDTO>)
|
||||
|
||||
/** Store error by [userWalletId] and [stakingId] */
|
||||
suspend fun storeError(userWalletId: UserWalletId, stakingId: StakingID)
|
||||
|
||||
data class StakingID(val integrationId: String, val address: String)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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<WalletIdWithBalances>()
|
||||
private val persistenceStore = MockStateDataStore<WalletIdWithWrappers>(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<Set<YieldBalance>>())
|
||||
}
|
||||
|
||||
@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<Set<YieldBalance>>())
|
||||
}
|
||||
|
||||
@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<YieldBalance>()))
|
||||
}
|
||||
|
||||
@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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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<WalletIdWithBalances>()
|
||||
val persistenceStore: DataStore<WalletIdWithWrappers> = 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<WalletIdWithBalances>()
|
||||
val persistenceStore = MockStateDataStore<WalletIdWithWrappers>(default = emptyMap())
|
||||
|
||||
DefaultYieldsBalancesStore(
|
||||
runtimeStore = runtimeStore,
|
||||
persistenceStore = persistenceStore,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
|
||||
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptyMap<String, Set<YieldBalance>>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test initialization if cache store is not empty`() = runTest {
|
||||
val runtimeStore = RuntimeSharedStore<WalletIdWithBalances>()
|
||||
val persistenceStore = MockStateDataStore<WalletIdWithWrappers>(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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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<WalletIdWithBalances>()
|
||||
private val persistenceStore = MockStateDataStore<WalletIdWithWrappers>(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<String, Set<YieldBalanceWrapperDTO>>())
|
||||
}
|
||||
|
||||
@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<String, Set<YieldBalanceWrapperDTO>>())
|
||||
}
|
||||
|
||||
@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<String, Set<YieldBalanceWrapperDTO>>())
|
||||
}
|
||||
|
||||
@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<String, Set<YieldBalanceWrapperDTO>>())
|
||||
}
|
||||
|
||||
@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<String, Set<YieldBalanceWrapperDTO>>())
|
||||
}
|
||||
|
||||
@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<String, Set<YieldBalanceWrapperDTO>>())
|
||||
}
|
||||
|
||||
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",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue