Updated on 2026-08-14
This commit is contained in:
parent
92794cb2f6
commit
51ce41ee51
10 changed files with 1043 additions and 7 deletions
|
|
@ -0,0 +1,88 @@
|
|||
package com.tangem.common.test.data.staking
|
||||
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.AddressArgumentDTO
|
||||
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.YieldDTO
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object MockYieldDTOFactory {
|
||||
|
||||
val defaultStakingID = StakingID(
|
||||
integrationId = "ton-ton-chorus-one-pools-staking",
|
||||
address = "0x1",
|
||||
)
|
||||
|
||||
fun create(stakingID: StakingID = defaultStakingID): YieldDTO {
|
||||
return YieldDTO(
|
||||
id = stakingID.integrationId,
|
||||
token = TokenDTO(
|
||||
name = "Miguel Estes",
|
||||
network = NetworkTypeDTO.POLYGON,
|
||||
symbol = "splendide",
|
||||
decimals = 1323,
|
||||
address = null,
|
||||
coinGeckoId = null,
|
||||
logoURI = null,
|
||||
isPoints = null,
|
||||
),
|
||||
tokens = listOf(),
|
||||
args = YieldDTO.ArgsDTO(
|
||||
enter = YieldDTO.ArgsDTO.Enter(
|
||||
addresses = YieldDTO.ArgsDTO.Enter.Addresses(
|
||||
address = AddressArgumentDTO(required = false),
|
||||
),
|
||||
args = mapOf(),
|
||||
),
|
||||
exit = null,
|
||||
),
|
||||
status = YieldDTO.StatusDTO(enter = true, exit = true),
|
||||
apy = BigDecimal.ONE,
|
||||
rewardRate = 1.0,
|
||||
rewardType = YieldDTO.RewardTypeDTO.UNKNOWN,
|
||||
metadata = YieldDTO.MetadataDTO(
|
||||
name = "name",
|
||||
logoUri = "logoUri",
|
||||
description = "description",
|
||||
documentation = null,
|
||||
gasFeeTokenDTO = TokenDTO(
|
||||
name = "Johnnie Mullen",
|
||||
network = NetworkTypeDTO.POLYGON,
|
||||
symbol = "fuisset",
|
||||
decimals = 2957,
|
||||
address = null,
|
||||
coinGeckoId = null,
|
||||
logoURI = null,
|
||||
isPoints = null,
|
||||
),
|
||||
tokenDTO = TokenDTO(
|
||||
name = "Lazaro Wood",
|
||||
network = NetworkTypeDTO.POLYGON,
|
||||
symbol = "vocent",
|
||||
decimals = 1602,
|
||||
address = null,
|
||||
coinGeckoId = null,
|
||||
logoURI = null,
|
||||
isPoints = null,
|
||||
),
|
||||
tokensDTO = listOf(),
|
||||
type = "type",
|
||||
rewardSchedule = YieldDTO.MetadataDTO.RewardScheduleDTO.DAY,
|
||||
cooldownPeriod = null,
|
||||
warmupPeriod = YieldDTO.MetadataDTO.PeriodDTO(1),
|
||||
rewardClaiming = YieldDTO.MetadataDTO.RewardClaimingDTO.AUTO,
|
||||
defaultValidator = null,
|
||||
minimumStake = null,
|
||||
supportsMultipleValidators = null,
|
||||
revshare = YieldDTO.MetadataDTO.EnabledDTO(enabled = true),
|
||||
fee = YieldDTO.MetadataDTO.EnabledDTO(enabled = true),
|
||||
),
|
||||
validators = listOf(),
|
||||
isAvailable = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package com.tangem.data.staking.fetcher
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.left
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.ensure
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory
|
||||
import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.core.flow.FlowFetcher
|
||||
import com.tangem.domain.core.utils.catchOn
|
||||
import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import timber.log.Timber
|
||||
|
||||
internal fun <Params : YieldBalanceFetcherParams> commonFetcher(
|
||||
implementor: YieldBalanceFetcherImplementor<Params>,
|
||||
stakingYieldsStore: StakingYieldsStore,
|
||||
yieldsBalancesStore: YieldsBalancesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): FlowFetcher<Params> {
|
||||
return CommonYieldBalanceFetcher(
|
||||
implementor = implementor,
|
||||
stakingYieldsStore = stakingYieldsStore,
|
||||
yieldsBalancesStore = yieldsBalancesStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Common implementation of YieldBalanceFetcher
|
||||
*
|
||||
* @param implementor fetcher implementor
|
||||
* @property stakingYieldsStore staking yields store
|
||||
* @property yieldsBalancesStore yields balances store
|
||||
* @property dispatchers dispatchers
|
||||
*/
|
||||
private class CommonYieldBalanceFetcher<Params : YieldBalanceFetcherParams>(
|
||||
private val implementor: YieldBalanceFetcherImplementor<Params>,
|
||||
private val stakingYieldsStore: StakingYieldsStore,
|
||||
private val yieldsBalancesStore: YieldsBalancesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : FlowFetcher<Params> {
|
||||
|
||||
override suspend fun invoke(params: Params): Either<Throwable, Unit> {
|
||||
val stakingIds = getStakingIds(params).getOrElse {
|
||||
return it.left()
|
||||
}
|
||||
|
||||
return Either.catchOn(dispatchers.default) {
|
||||
val requests = prefetch(userWalletId = params.userWalletId, stakingIds = stakingIds)
|
||||
|
||||
implementor.fetch(params = params, stakingIds = stakingIds, requests)
|
||||
}
|
||||
.onLeft { yieldsBalancesStore.storeError(userWalletId = params.userWalletId, stakingIds = stakingIds) }
|
||||
}
|
||||
|
||||
private suspend fun getStakingIds(params: Params): Either<Throwable, Set<StakingID>> = either {
|
||||
val stakingIds = catch(
|
||||
block = { implementor.createStakingIds(params = params) },
|
||||
catch = { raise(it) },
|
||||
)
|
||||
|
||||
ensure(stakingIds.isNotEmpty()) {
|
||||
val exception = IllegalStateException("Unable to create staking ids for $params: list is empty")
|
||||
Timber.e(exception)
|
||||
|
||||
raise(exception)
|
||||
}
|
||||
|
||||
stakingIds
|
||||
}
|
||||
|
||||
private suspend fun prefetch(
|
||||
userWalletId: UserWalletId,
|
||||
stakingIds: Set<StakingID>,
|
||||
): List<YieldBalanceRequestBody> {
|
||||
yieldsBalancesStore.refresh(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||
|
||||
val yieldDTOs = stakingYieldsStore.getSyncWithTimeout()
|
||||
|
||||
if (yieldDTOs.isNullOrEmpty()) {
|
||||
val exception = IllegalStateException("No enabled yields for $userWalletId")
|
||||
Timber.e(exception)
|
||||
throw exception
|
||||
}
|
||||
|
||||
val yieldIds = yieldDTOs.mapNotNullTo(destination = hashSetOf(), transform = YieldDTO::id)
|
||||
|
||||
val requests = stakingIds
|
||||
.filter { stakingId -> yieldIds.any { it == stakingId.integrationId } }
|
||||
.map(YieldBalanceRequestBodyFactory::create)
|
||||
|
||||
if (requests.isEmpty()) {
|
||||
val exception = IllegalStateException(
|
||||
"""
|
||||
No available yields to fetch yield balances:
|
||||
– userWalletId: $userWalletId
|
||||
– stakingIds: ${stakingIds.joinToString()}
|
||||
""".trimIndent(),
|
||||
)
|
||||
Timber.d(exception)
|
||||
throw exception
|
||||
}
|
||||
|
||||
return requests
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,10 @@ import com.tangem.domain.staking.model.StakingID
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal interface YieldBalanceFetcherImplementor<out Params : YieldBalanceFetcherParams> {
|
||||
internal interface YieldBalanceFetcherImplementor<in Params : YieldBalanceFetcherParams> {
|
||||
|
||||
/** Create set of [StakingID] */
|
||||
suspend fun createStakingIds(params: @UnsafeVariance Params): Set<StakingID>
|
||||
suspend fun createStakingIds(params: Params): Set<StakingID>
|
||||
|
||||
/**
|
||||
* Fetch yield balances
|
||||
|
|
@ -21,9 +21,5 @@ internal interface YieldBalanceFetcherImplementor<out Params : YieldBalanceFetch
|
|||
* @param stakingIds set of [StakingID]
|
||||
* @param requests requests
|
||||
*/
|
||||
suspend fun fetch(
|
||||
params: @UnsafeVariance Params,
|
||||
stakingIds: Set<StakingID>,
|
||||
requests: List<YieldBalanceRequestBody>,
|
||||
)
|
||||
suspend fun fetch(params: Params, stakingIds: Set<StakingID>, requests: List<YieldBalanceRequestBody>)
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.data.staking.multi
|
||||
|
||||
import com.tangem.data.staking.fetcher.YieldBalanceFetcherImplementor
|
||||
import com.tangem.data.staking.fetcher.commonFetcher
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.data.staking.utils.StakingIdFactory
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.core.flow.FlowFetcher
|
||||
import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
||||
/**
|
||||
* Default implementation of [MultiYieldBalanceFetcher]
|
||||
*
|
||||
* @property stakingYieldsStore staking yields store
|
||||
* @property yieldsBalancesStore yields balances store
|
||||
* @property stakingIdFactory factory for creating StakingID
|
||||
* @property stakeKitApi stake kit API
|
||||
* @property dispatchers dispatchers
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultMultiYieldBalanceFetcher(
|
||||
private val stakingYieldsStore: StakingYieldsStore,
|
||||
private val yieldsBalancesStore: YieldsBalancesStore,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val stakeKitApi: StakeKitApi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : MultiYieldBalanceFetcher,
|
||||
FlowFetcher<YieldBalanceFetcherParams.Multi> by commonFetcher(
|
||||
implementor = createMultiFetcherImplementor(yieldsBalancesStore, stakingIdFactory, stakeKitApi, dispatchers),
|
||||
stakingYieldsStore = stakingYieldsStore,
|
||||
yieldsBalancesStore = yieldsBalancesStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
private fun createMultiFetcherImplementor(
|
||||
yieldsBalancesStore: YieldsBalancesStore,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
stakeKitApi: StakeKitApi,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): YieldBalanceFetcherImplementor<YieldBalanceFetcherParams.Multi> {
|
||||
return MultiYieldBalanceFetcherImplementor(
|
||||
yieldsBalancesStore = yieldsBalancesStore,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
stakeKitApi = stakeKitApi,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.data.staking.single
|
||||
|
||||
import com.tangem.data.staking.fetcher.YieldBalanceFetcherImplementor
|
||||
import com.tangem.data.staking.fetcher.commonFetcher
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.data.staking.utils.StakingIdFactory
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.core.flow.FlowFetcher
|
||||
import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
||||
/**
|
||||
* Default implementation of [MultiYieldBalanceFetcher]
|
||||
*
|
||||
* @property stakingYieldsStore staking yields store
|
||||
* @property yieldsBalancesStore yields balances store
|
||||
* @property stakingIdFactory factory for creating StakingID
|
||||
* @property stakeKitApi stake kit API
|
||||
* @property dispatchers dispatchers
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultSingleYieldBalanceFetcher(
|
||||
private val stakingYieldsStore: StakingYieldsStore,
|
||||
private val yieldsBalancesStore: YieldsBalancesStore,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val stakeKitApi: StakeKitApi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : SingleYieldBalanceFetcher,
|
||||
FlowFetcher<YieldBalanceFetcherParams.Single> by commonFetcher(
|
||||
implementor = createSingleFetcherImplementor(yieldsBalancesStore, stakingIdFactory, stakeKitApi, dispatchers),
|
||||
stakingYieldsStore = stakingYieldsStore,
|
||||
yieldsBalancesStore = yieldsBalancesStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
private fun createSingleFetcherImplementor(
|
||||
yieldsBalancesStore: YieldsBalancesStore,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
stakeKitApi: StakeKitApi,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): YieldBalanceFetcherImplementor<YieldBalanceFetcherParams.Single> {
|
||||
return SingleYieldBalanceFetcherImplementor(
|
||||
yieldsBalancesStore = yieldsBalancesStore,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
stakeKitApi = stakeKitApi,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.data.staking.utils
|
||||
|
||||
import com.tangem.datasource.api.stakekit.models.request.Address
|
||||
import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
|
||||
/**
|
||||
* Factory for creating [YieldBalanceRequestBody]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object YieldBalanceRequestBodyFactory {
|
||||
|
||||
fun create(stakingID: StakingID): YieldBalanceRequestBody {
|
||||
return YieldBalanceRequestBody(
|
||||
addresses = Address(
|
||||
address = stakingID.address,
|
||||
additionalAddresses = null, // todo fill additional addresses metadata if needed
|
||||
explorerUrl = "", // todo fill exporer url [REDACTED_JIRA]
|
||||
),
|
||||
args = YieldBalanceRequestBody.YieldBalanceRequestArgs(
|
||||
validatorAddresses = listOf(), // todo add validators [REDACTED_JIRA]
|
||||
),
|
||||
integrationId = stakingID.integrationId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,323 @@
|
|||
package com.tangem.data.staking.multi
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
|
||||
import com.tangem.common.test.data.staking.MockYieldDTOFactory
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.data.staking.utils.StakingIdFactory
|
||||
import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultMultiYieldBalanceFetcherTest {
|
||||
|
||||
private val stakingYieldsStore: StakingYieldsStore = mockk()
|
||||
private val yieldsBalancesStore: YieldsBalancesStore = mockk()
|
||||
private val stakingIdFactory: StakingIdFactory = mockk()
|
||||
private val stakeKitApi: StakeKitApi = mockk()
|
||||
|
||||
private val fetcher = DefaultMultiYieldBalanceFetcher(
|
||||
stakingYieldsStore = stakingYieldsStore,
|
||||
yieldsBalancesStore = yieldsBalancesStore,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
stakeKitApi = stakeKitApi,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances successfully`() = runTest {
|
||||
val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network)
|
||||
|
||||
val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap)
|
||||
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId)
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId)
|
||||
coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs
|
||||
|
||||
val yields = listOf(MockYieldDTOFactory.create(tonId), MockYieldDTOFactory.create(solanaId))
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields
|
||||
|
||||
val requests = tonAndSolanaIds.map(YieldBalanceRequestBodyFactory::create).sortedBy { it.integrationId }
|
||||
val result = setOf(
|
||||
MockYieldBalanceWrapperDTOFactory.createWithBalance(solanaId),
|
||||
MockYieldBalanceWrapperDTOFactory.createWithBalance(tonId),
|
||||
)
|
||||
coEvery { stakeKitApi.getMultipleYieldBalances(requests) } returns ApiResponse.Success(result)
|
||||
coEvery { yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = result) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.create(params.userWalletId, ton.id, ton.network)
|
||||
stakingIdFactory.create(params.userWalletId, solana.id, solana.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
stakeKitApi.getMultipleYieldBalances(requests)
|
||||
yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = result)
|
||||
}
|
||||
|
||||
coVerify(inverse = true) { yieldsBalancesStore.storeError(any(), any()) }
|
||||
|
||||
Truth.assertThat(actual.isRight()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if stakingIdFactory returns empty list`() = runTest {
|
||||
val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network)
|
||||
|
||||
val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap)
|
||||
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns emptySet()
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns emptySet()
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.create(params.userWalletId, ton.id, ton.network)
|
||||
stakingIdFactory.create(params.userWalletId, solana.id, solana.network)
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
yieldsBalancesStore.refresh(any(), any<Set<StakingID>>())
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
stakeKitApi.getMultipleYieldBalances(any())
|
||||
yieldsBalancesStore.storeActual(any(), any())
|
||||
yieldsBalancesStore.storeError(any(), any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException("Unable to create staking ids for $params: list is empty")
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if stakingYieldsStore getSyncWithTimeout returns null`() = runTest {
|
||||
val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network)
|
||||
|
||||
val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap)
|
||||
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId)
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId)
|
||||
coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns null
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.create(params.userWalletId, ton.id, ton.network)
|
||||
stakingIdFactory.create(params.userWalletId, solana.id, solana.network)
|
||||
yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds)
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds)
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
stakeKitApi.getMultipleYieldBalances(any())
|
||||
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException("No enabled yields for ${params.userWalletId}")
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if stakingYieldsStore getSyncWithTimeout returns empty list`() = runTest {
|
||||
val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network)
|
||||
|
||||
val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap)
|
||||
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId)
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId)
|
||||
coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns emptyList()
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.create(params.userWalletId, ton.id, ton.network)
|
||||
stakingIdFactory.create(params.userWalletId, solana.id, solana.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds)
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
stakeKitApi.getMultipleYieldBalances(any())
|
||||
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException("No enabled yields for ${params.userWalletId}")
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if yields converting is failed`() = runTest {
|
||||
val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network)
|
||||
|
||||
val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap)
|
||||
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId)
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId)
|
||||
coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs
|
||||
|
||||
val yields = listOf(
|
||||
MockYieldDTOFactory.create(tonId).copy(id = null),
|
||||
MockYieldDTOFactory.create(solanaId).copy(id = null),
|
||||
)
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.create(params.userWalletId, ton.id, ton.network)
|
||||
stakingIdFactory.create(params.userWalletId, solana.id, solana.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds)
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
stakeKitApi.getMultipleYieldBalances(any())
|
||||
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException(
|
||||
"""
|
||||
No available yields to fetch yield balances:
|
||||
– userWalletId: $userWalletId
|
||||
– stakingIds: ${setOf(solanaId, tonId).joinToString()}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if available yields does not contain ids from params`() = runTest {
|
||||
val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network)
|
||||
|
||||
val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap)
|
||||
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId)
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId)
|
||||
coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs
|
||||
|
||||
val yields = listOf(MockYieldDTOFactory.create(StakingID(integrationId = "polygon", address = "0x1")))
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.create(params.userWalletId, ton.id, ton.network)
|
||||
stakingIdFactory.create(params.userWalletId, solana.id, solana.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds)
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
stakeKitApi.getMultipleYieldBalances(any())
|
||||
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException(
|
||||
"""
|
||||
No available yields to fetch yield balances:
|
||||
– userWalletId: $userWalletId
|
||||
– stakingIds: ${setOf(solanaId, tonId).joinToString()}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if stakeKitApi getMultipleYieldBalances is failed`() = runTest {
|
||||
val currencyIdWithNetworkMap = mapOf(ton.id to ton.network, solana.id to solana.network)
|
||||
|
||||
val params = YieldBalanceFetcherParams.Multi(userWalletId, currencyIdWithNetworkMap)
|
||||
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, ton.id, ton.network) } returns setOf(tonId)
|
||||
coEvery { stakingIdFactory.create(params.userWalletId, solana.id, solana.network) } returns setOf(solanaId)
|
||||
coEvery { yieldsBalancesStore.refresh(params.userWalletId, tonAndSolanaIds) } just Runs
|
||||
|
||||
val yields = listOf(MockYieldDTOFactory.create(tonId), MockYieldDTOFactory.create(solanaId))
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields
|
||||
|
||||
val requests = setOf(solanaId, tonId).map(YieldBalanceRequestBodyFactory::create)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val errorResponse = ApiResponse.Error(ApiResponseError.NetworkException)
|
||||
as ApiResponse<Set<YieldBalanceWrapperDTO>>
|
||||
|
||||
coEvery { stakeKitApi.getMultipleYieldBalances(requests) } returns errorResponse
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, tonAndSolanaIds) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.create(params.userWalletId, ton.id, ton.network)
|
||||
stakingIdFactory.create(params.userWalletId, solana.id, solana.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = tonAndSolanaIds)
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
stakeKitApi.getMultipleYieldBalances(requests)
|
||||
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = tonAndSolanaIds)
|
||||
}
|
||||
|
||||
coVerify(inverse = true) { yieldsBalancesStore.storeActual(userWalletId = any(), values = any()) }
|
||||
|
||||
val expected = ApiResponseError.NetworkException
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val userWalletId = UserWalletId("011")
|
||||
|
||||
val mocks = MockCryptoCurrencyFactory()
|
||||
|
||||
val ton = mocks.createCoin(Blockchain.TON)
|
||||
val solana = mocks.createCoin(Blockchain.Solana)
|
||||
|
||||
val tonId = MockYieldBalanceWrapperDTOFactory.defaultStakingId
|
||||
val solanaId = StakingID(
|
||||
integrationId = "solana-sol-native-multivalidator-staking",
|
||||
address = "0x1",
|
||||
)
|
||||
|
||||
val tonAndSolanaIds = setOf(tonId, solanaId)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
package com.tangem.data.staking.single
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
|
||||
import com.tangem.common.test.data.staking.MockYieldDTOFactory
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.data.staking.store.YieldsBalancesStore
|
||||
import com.tangem.data.staking.utils.StakingIdFactory
|
||||
import com.tangem.data.staking.utils.YieldBalanceRequestBodyFactory
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody
|
||||
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 com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
|
||||
import com.tangem.domain.staking.model.StakingID
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultSingleYieldBalanceFetcherTest {
|
||||
|
||||
private val stakingYieldsStore: StakingYieldsStore = mockk()
|
||||
private val yieldsBalancesStore: YieldsBalancesStore = mockk()
|
||||
private val stakingIdFactory: StakingIdFactory = mockk()
|
||||
private val stakeKitApi: StakeKitApi = mockk()
|
||||
|
||||
private val fetcher = DefaultSingleYieldBalanceFetcher(
|
||||
stakingYieldsStore = stakingYieldsStore,
|
||||
yieldsBalancesStore = yieldsBalancesStore,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
stakeKitApi = stakeKitApi,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances successfully`() = runTest {
|
||||
val params = YieldBalanceFetcherParams.Single(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = ton.id,
|
||||
network = ton.network,
|
||||
)
|
||||
|
||||
coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId
|
||||
coEvery {
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
} just Runs
|
||||
|
||||
val yields = listOf(MockYieldDTOFactory.create(tonId))
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields
|
||||
|
||||
val request = YieldBalanceRequestBodyFactory.create(tonId)
|
||||
val result = listOf(createBalanceDTO())
|
||||
coEvery { stakeKitApi.getSingleYieldBalance(tonId.integrationId, request) } returns ApiResponse.Success(result)
|
||||
|
||||
val values = result.mapTo(hashSetOf()) { it.toWrapper(request) }
|
||||
coEvery { yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = values) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
stakeKitApi.getSingleYieldBalance(integrationId = tonId.integrationId, body = request)
|
||||
yieldsBalancesStore.storeActual(userWalletId = userWalletId, values = values)
|
||||
}
|
||||
|
||||
coVerify(inverse = true) { yieldsBalancesStore.storeError(any(), any()) }
|
||||
|
||||
Truth.assertThat(actual.isRight()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if stakingIdFactory createForDefault returns null`() = runTest {
|
||||
val params = YieldBalanceFetcherParams.Single(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = ton.id,
|
||||
network = ton.network,
|
||||
)
|
||||
|
||||
coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns null
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.createForDefault(userWalletId = userWalletId, currencyId = ton.id, network = ton.network)
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
yieldsBalancesStore.refresh(userWalletId = any(), stakingIds = any())
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
stakeKitApi.getSingleYieldBalance(integrationId = any(), body = any())
|
||||
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
|
||||
yieldsBalancesStore.storeError(userWalletId = any(), stakingIds = any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException("Unable to create staking ids for $params: list is empty")
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if stakingYieldsStore getSyncWithTimeout returns null`() = runTest {
|
||||
val params = YieldBalanceFetcherParams.Single(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = ton.id,
|
||||
network = ton.network,
|
||||
)
|
||||
|
||||
coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId
|
||||
coEvery {
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
} just Runs
|
||||
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns null
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, setOf(tonId)) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
yieldsBalancesStore.storeError(userWalletId, setOf(tonId))
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
stakeKitApi.getSingleYieldBalance(integrationId = any(), body = any())
|
||||
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException("No enabled yields for ${params.userWalletId}")
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if stakingYieldsStore getSyncWithTimeout returns empty list`() = runTest {
|
||||
val params = YieldBalanceFetcherParams.Single(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = ton.id,
|
||||
network = ton.network,
|
||||
)
|
||||
|
||||
coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId
|
||||
coEvery {
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
} just Runs
|
||||
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns emptyList()
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, setOf(tonId)) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
yieldsBalancesStore.storeError(userWalletId, setOf(tonId))
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
stakeKitApi.getSingleYieldBalance(integrationId = any(), body = any())
|
||||
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException("No enabled yields for ${params.userWalletId}")
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if yields converting is failed`() = runTest {
|
||||
val params = YieldBalanceFetcherParams.Single(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = ton.id,
|
||||
network = ton.network,
|
||||
)
|
||||
|
||||
coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId
|
||||
coEvery {
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
} just Runs
|
||||
|
||||
val yields = listOf(MockYieldDTOFactory.create(tonId).copy(id = null))
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, setOf(tonId)) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
yieldsBalancesStore.storeError(userWalletId, setOf(tonId))
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
stakeKitApi.getSingleYieldBalance(integrationId = any(), body = any())
|
||||
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException(
|
||||
"""
|
||||
No available yields to fetch yield balances:
|
||||
– userWalletId: $userWalletId
|
||||
– stakingIds: ${setOf(tonId).joinToString()}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if available yields does not contain ids from params`() = runTest {
|
||||
val params = YieldBalanceFetcherParams.Single(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = ton.id,
|
||||
network = ton.network,
|
||||
)
|
||||
|
||||
coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId
|
||||
coEvery {
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
} just Runs
|
||||
|
||||
val yields = listOf(MockYieldDTOFactory.create(StakingID(integrationId = "polygon", address = "0x1")))
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, setOf(tonId)) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
yieldsBalancesStore.storeError(userWalletId, setOf(tonId))
|
||||
}
|
||||
|
||||
coVerify(inverse = true) {
|
||||
stakeKitApi.getSingleYieldBalance(integrationId = any(), body = any())
|
||||
yieldsBalancesStore.storeActual(userWalletId = any(), values = any())
|
||||
}
|
||||
|
||||
val expected = IllegalStateException(
|
||||
"""
|
||||
No available yields to fetch yield balances:
|
||||
– userWalletId: $userWalletId
|
||||
– stakingIds: ${setOf(tonId).joinToString()}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch yields balances failure if stakeKitApi getMultipleYieldBalances is failed`() = runTest {
|
||||
val params = YieldBalanceFetcherParams.Single(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = ton.id,
|
||||
network = ton.network,
|
||||
)
|
||||
|
||||
coEvery { stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network) } returns tonId
|
||||
coEvery {
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
} just Runs
|
||||
|
||||
val yields = listOf(MockYieldDTOFactory.create(tonId))
|
||||
coEvery { stakingYieldsStore.getSyncWithTimeout() } returns yields
|
||||
|
||||
val request = YieldBalanceRequestBodyFactory.create(tonId)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val errorResponse = ApiResponse.Error(ApiResponseError.NetworkException) as ApiResponse<List<BalanceDTO>>
|
||||
|
||||
coEvery { stakeKitApi.getSingleYieldBalance(tonId.integrationId, request) } returns errorResponse
|
||||
coEvery { yieldsBalancesStore.storeError(userWalletId, setOf(tonId)) } just Runs
|
||||
|
||||
val actual = fetcher(params)
|
||||
|
||||
coVerify {
|
||||
stakingIdFactory.createForDefault(params.userWalletId, ton.id, ton.network)
|
||||
yieldsBalancesStore.refresh(userWalletId = params.userWalletId, stakingIds = setOf(tonId))
|
||||
stakingYieldsStore.getSyncWithTimeout()
|
||||
stakeKitApi.getSingleYieldBalance(tonId.integrationId, request)
|
||||
yieldsBalancesStore.storeError(userWalletId = userWalletId, stakingIds = setOf(tonId))
|
||||
}
|
||||
|
||||
coVerify(inverse = true) { yieldsBalancesStore.storeActual(userWalletId = any(), values = any()) }
|
||||
|
||||
val expected = ApiResponseError.NetworkException
|
||||
|
||||
Truth.assertThat(actual.isLeft()).isTrue()
|
||||
Truth.assertThat(actual.leftOrNull()).isInstanceOf(expected::class.java)
|
||||
Truth.assertThat(actual.leftOrNull()).hasMessageThat().isEqualTo(expected.message)
|
||||
}
|
||||
|
||||
private fun createBalanceDTO(): BalanceDTO {
|
||||
return BalanceDTO(
|
||||
groupId = "dictas",
|
||||
type = BalanceDTO.BalanceTypeDTO.REWARDS,
|
||||
amount = BigDecimal.ONE,
|
||||
date = null,
|
||||
pricePerShare = BigDecimal.ONE,
|
||||
pendingActions = listOf(),
|
||||
pendingActionConstraints = listOf(),
|
||||
tokenDTO = TokenDTO(
|
||||
name = "Casandra Paul",
|
||||
network = NetworkTypeDTO.POLYGON,
|
||||
symbol = "vim",
|
||||
decimals = 3994,
|
||||
address = null,
|
||||
coinGeckoId = null,
|
||||
logoURI = null,
|
||||
isPoints = null,
|
||||
),
|
||||
validatorAddress = null,
|
||||
validatorAddresses = listOf(),
|
||||
providerId = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun BalanceDTO.toWrapper(request: YieldBalanceRequestBody): YieldBalanceWrapperDTO {
|
||||
return YieldBalanceWrapperDTO(
|
||||
balances = listOf(this),
|
||||
integrationId = request.integrationId,
|
||||
addresses = request.addresses,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val userWalletId = UserWalletId("011")
|
||||
|
||||
val mocks = MockCryptoCurrencyFactory()
|
||||
|
||||
val ton = mocks.createCoin(Blockchain.TON)
|
||||
|
||||
val tonId = MockYieldBalanceWrapperDTOFactory.defaultStakingId
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.domain.staking.multi
|
||||
|
||||
import com.tangem.domain.core.flow.FlowFetcher
|
||||
import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
|
||||
|
||||
/**
|
||||
* Fetcher of yields balances
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface MultiYieldBalanceFetcher : FlowFetcher<YieldBalanceFetcherParams.Multi>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.domain.staking.single
|
||||
|
||||
import com.tangem.domain.core.flow.FlowFetcher
|
||||
import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
|
||||
|
||||
/**
|
||||
* Fetcher of yield balance
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface SingleYieldBalanceFetcher : FlowFetcher<YieldBalanceFetcherParams.Single>
|
||||
Loading…
Add table
Add a link
Reference in a new issue