Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-17 21:58:50 +04:00
commit 592bc82e98
3 changed files with 175 additions and 1 deletions

View file

@ -122,6 +122,7 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
// [REDACTED_JIRA]
Blockchain.Dischain,
Blockchain.Polygon,
Blockchain.TON,
-> this.currency
else -> responseToken.symbol
}

View file

@ -241,8 +241,13 @@ internal class YieldSupplyActiveModel @Inject constructor(
if (!yieldSupplyFeatureToggles.isYieldPromoEnabled) return
if (designFeatureToggles.isRedesignEnabled) return
modelScope.launch(dispatchers.io) {
val status = getYieldBoostStatusUseCase(userWalletId).getOrNull() ?: return@launch
val token = cryptoCurrency as? CryptoCurrency.Token ?: return@launch
val cached = getYieldBoostStatusUseCase(userWalletId).getOrNull()
val status = if (cached is YieldBoostStatus.Enrolled && cached.matches(token)) {
cached
} else {
getYieldBoostStatusUseCase(userWalletId, forceRefresh = true).getOrNull()
}
if (status !is YieldBoostStatus.Enrolled || !status.matches(token)) return@launch
val state = resolveBoostBlockState(

View file

@ -0,0 +1,168 @@
package com.tangem.features.yield.supply.impl.active.model
import arrow.core.right
import com.google.common.truth.Truth.assertThat
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.model.MutableParamsContainer
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.common.routing.AppRouter
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.yield.supply.models.YieldBoostStatus
import com.tangem.domain.yield.supply.promo.usecase.GetYieldBoostStatusUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetCurrentFeeUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetDustMinAmountUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetMaxFeeUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetProtocolBalanceUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyMinAmountUseCase
import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
import com.tangem.features.yield.supply.impl.YieldBoostStoryPreloader
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.Clock
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.math.BigDecimal
import kotlin.time.Duration.Companion.days
@OptIn(ExperimentalCoroutinesApi::class)
class YieldSupplyActiveModelBoostBlockTest {
private val yieldSupplyGetProtocolBalanceUseCase: YieldSupplyGetProtocolBalanceUseCase = mockk(relaxed = true)
private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase = mockk(relaxed = true)
private val yieldSupplyMinAmountUseCase: YieldSupplyMinAmountUseCase = mockk(relaxed = true)
private val yieldSupplyGetCurrentFeeUseCase: YieldSupplyGetCurrentFeeUseCase = mockk(relaxed = true)
private val yieldSupplyGetMaxFeeUseCase: YieldSupplyGetMaxFeeUseCase = mockk(relaxed = true)
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase = mockk(relaxed = true)
private val getUserWalletUseCase: GetUserWalletUseCase = mockk(relaxed = true)
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier = mockk(relaxed = true)
private val urlOpener: UrlOpener = mockk(relaxed = true)
private val appRouter: AppRouter = mockk(relaxed = true)
private val yieldSupplyGetDustMinAmountUseCase: YieldSupplyGetDustMinAmountUseCase = mockk(relaxed = true)
private val getYieldBoostStatusUseCase: GetYieldBoostStatusUseCase = mockk(relaxed = true)
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles = mockk(relaxed = true)
private val designFeatureToggles: DesignFeatureToggles = mockk(relaxed = true)
private val boostStoryPreloader: YieldBoostStoryPreloader = mockk(relaxed = true)
private val analyticsHandler: AnalyticsEventHandler = mockk(relaxed = true)
private val userWalletId = UserWalletId(stringValue = "0123456789ABCDEF")
private val userWallet: UserWallet.Hot = mockk(relaxed = true)
private val network: Network = mockk(relaxed = true) {
every { rawId } returns NETWORK_ID
every { name } returns "Ethereum"
}
private val token: CryptoCurrency.Token = mockk(relaxed = true) {
every { contractAddress } returns CONTRACT_ADDRESS
every { network } returns this@YieldSupplyActiveModelBoostBlockTest.network
every { symbol } returns "USDT"
every { decimals } returns 6
}
private val enrolledStatus = YieldBoostStatus.Enrolled(
tokenName = "USDT",
networkId = NETWORK_ID,
moduleAddress = "0xModule",
userAddress = "0xUser",
contractAddress = CONTRACT_ADDRESS,
qualificationEndDate = Clock.System.now() + 100.days,
)
@BeforeEach
fun setUp() {
every { yieldSupplyFeatureToggles.isYieldPromoEnabled } returns true
every { designFeatureToggles.isRedesignEnabled } returns false
every { getUserWalletUseCase.invoke(userWalletId) } returns userWallet.right()
every { singleAccountStatusListSupplier.invoke(userWalletId) } returns emptyFlow()
coEvery { getSelectedAppCurrencyUseCase.invokeSync() } returns AppCurrency.Default.right()
coEvery { yieldSupplyGetProtocolBalanceUseCase(any(), any()) } returns BigDecimal.ONE.right()
}
private fun createModel(): YieldSupplyActiveModel = YieldSupplyActiveModel(
paramsContainer = MutableParamsContainer(
YieldSupplyActiveComponent.Params(userWalletId = userWalletId, cryptoCurrency = token),
),
dispatchers = TestingCoroutineDispatcherProvider(),
analyticsHandler = analyticsHandler,
yieldSupplyGetProtocolBalanceUseCase = yieldSupplyGetProtocolBalanceUseCase,
yieldSupplyGetTokenStatusUseCase = yieldSupplyGetTokenStatusUseCase,
yieldSupplyMinAmountUseCase = yieldSupplyMinAmountUseCase,
yieldSupplyGetCurrentFeeUseCase = yieldSupplyGetCurrentFeeUseCase,
yieldSupplyGetMaxFeeUseCase = yieldSupplyGetMaxFeeUseCase,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
getUserWalletUseCase = getUserWalletUseCase,
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
urlOpener = urlOpener,
appRouter = appRouter,
yieldSupplyGetDustMinAmountUseCase = yieldSupplyGetDustMinAmountUseCase,
getYieldBoostStatusUseCase = getYieldBoostStatusUseCase,
yieldSupplyFeatureToggles = yieldSupplyFeatureToggles,
designFeatureToggles = designFeatureToggles,
boostStoryPreloader = boostStoryPreloader,
)
@Test
fun `GIVEN cached status NotStarted WHEN model created THEN force refreshes and shows boost text`() = runTest {
coEvery { getYieldBoostStatusUseCase(userWalletId, false) } returns YieldBoostStatus.NotStarted.right()
coEvery { getYieldBoostStatusUseCase(userWalletId, true) } returns enrolledStatus.right()
val model = createModel()
assertThat(model.uiState.value.boostText).isNotNull()
coVerify(exactly = 1) { getYieldBoostStatusUseCase(userWalletId, false) }
coVerify(exactly = 1) { getYieldBoostStatusUseCase(userWalletId, true) }
}
@Test
fun `GIVEN cached status already Enrolled WHEN model created THEN does not force refresh`() = runTest {
coEvery { getYieldBoostStatusUseCase(userWalletId, false) } returns enrolledStatus.right()
val model = createModel()
assertThat(model.uiState.value.boostText).isNotNull()
coVerify(exactly = 1) { getYieldBoostStatusUseCase(userWalletId, false) }
coVerify(exactly = 0) { getYieldBoostStatusUseCase(userWalletId, true) }
}
@Test
fun `GIVEN status NotStarted even after refresh WHEN model created THEN shows no boost text`() = runTest {
coEvery { getYieldBoostStatusUseCase(userWalletId, false) } returns YieldBoostStatus.NotStarted.right()
coEvery { getYieldBoostStatusUseCase(userWalletId, true) } returns YieldBoostStatus.NotStarted.right()
val model = createModel()
assertThat(model.uiState.value.boostText).isNull()
coVerify(exactly = 1) { getYieldBoostStatusUseCase(userWalletId, false) }
coVerify(exactly = 1) { getYieldBoostStatusUseCase(userWalletId, true) }
}
@Test
fun `GIVEN promo toggle disabled WHEN model created THEN does not query boost status`() = runTest {
every { yieldSupplyFeatureToggles.isYieldPromoEnabled } returns false
val model = createModel()
assertThat(model.uiState.value.boostText).isNull()
coVerify(exactly = 0) { getYieldBoostStatusUseCase(any(), any()) }
}
private companion object {
const val CONTRACT_ADDRESS = "0xCONTRACT"
const val NETWORK_ID = "ethereum"
}
}