From 3f56f32dd6e2905a40c60268310ee4a6e1f444bf Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Jul 2026 18:39:43 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../tokens/actions/BaseActionsFactory.kt | 17 +++ .../tokens/actions/CommonActionsFactory.kt | 7 +- .../actions/OutdatedDataActionsFactory.kt | 7 +- .../actions/UnreachableActionsFactory.kt | 7 +- .../actions/CommonActionsFactoryTest.kt | 108 ++++++++++++++++++ 5 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 domain/tokens/src/test/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactoryTest.kt diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt index 4cc9fc0ed5..0e1e27ace2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt @@ -133,6 +133,23 @@ internal open class BaseActionsFactory( } } + /** + * Determines the unavailability reason for the BUY action + * + * @param userWallet the user's wallet + * @param currency the cryptocurrency to check + */ + protected fun getBuyUnavailabilityReason( + userWallet: UserWallet, + currency: CryptoCurrency, + ): ScenarioUnavailabilityReason { + return if (userWallet is UserWallet.Cold && userWallet.cardTypesResolver.isStart2Coin()) { + ScenarioUnavailabilityReason.BuyUnavailable(currency.name) + } else { + ScenarioUnavailabilityReason.None + } + } + /** Adds a "Buy" action to the builder based on the unavailability [reason] */ protected fun ActionAvailabilityBuilder.addBuyAction(reason: ScenarioUnavailabilityReason) { val action = ActionState.Buy(unavailabilityReason = reason) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt index f244724fd4..7d10d0735c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt @@ -91,7 +91,12 @@ internal class CommonActionsFactory( // endregion // region Buy - addBuyAction(reason = ScenarioUnavailabilityReason.None) + addBuyAction( + reason = getBuyUnavailabilityReason( + userWallet = userWallet, + currency = cryptoCurrencyStatus.currency, + ), + ) // endregion // region Sell diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt index 111e4685a5..7973e82ecf 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt @@ -79,7 +79,12 @@ internal class OutdatedDataActionsFactory( // endregion // region Buy - addBuyAction(reason = ScenarioUnavailabilityReason.None) + addBuyAction( + reason = getBuyUnavailabilityReason( + userWallet = userWallet, + currency = cryptoCurrencyStatus.currency, + ), + ) // endregion // region Stake diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/UnreachableActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/UnreachableActionsFactory.kt index 23e188e3dd..6ecb09d774 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/UnreachableActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/UnreachableActionsFactory.kt @@ -44,7 +44,12 @@ internal class UnreachableActionsFactory( // endregion // region Buy - addBuyAction(reason = ScenarioUnavailabilityReason.None) + addBuyAction( + reason = getBuyUnavailabilityReason( + userWallet = userWallet, + currency = cryptoCurrencyStatus.currency, + ), + ) // endregion // region Receive diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactoryTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactoryTest.kt new file mode 100644 index 0000000000..ba34737ad5 --- /dev/null +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactoryTest.kt @@ -0,0 +1,108 @@ +package com.tangem.domain.tokens.actions + +import arrow.core.right +import com.google.common.truth.Truth.assertThat +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.domain.card.CardTypesResolver +import com.tangem.domain.card.common.util.cardTypesResolver +import com.tangem.domain.exchange.RampStateManager +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.staking.model.StakingAvailability +import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason +import com.tangem.domain.tokens.model.TokenActionsState.ActionState +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.yield.supply.models.YieldSupplyAvailability +import io.mockk.clearMocks +import io.mockk.coEvery +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkStatic +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +internal class CommonActionsFactoryTest { + + private val walletManagersFacade: WalletManagersFacade = mockk(relaxed = true) + private val rampStateManager: RampStateManager = mockk(relaxed = true) + + private val factory = CommonActionsFactory( + walletManagersFacade = walletManagersFacade, + rampStateManager = rampStateManager, + ) + + private val currency: CryptoCurrency = MockCryptoCurrencyFactory().ethereum + + private val cardTypesResolver: CardTypesResolver = mockk() + private val userWallet: UserWallet.Cold = mockk(relaxed = true) + private val cryptoCurrencyStatus: CryptoCurrencyStatus = mockk() + + @BeforeAll + fun setupStatic() { + // cardTypesResolver is a UserWallet.Cold extension property, so it is stubbed via its file class. + mockkStatic("com.tangem.domain.card.common.util.ScanResponseExtKt") + } + + @AfterAll + fun tearDownStatic() { + unmockkStatic("com.tangem.domain.card.common.util.ScanResponseExtKt") + } + + @BeforeEach + fun setup() { + clearMocks(walletManagersFacade, rampStateManager, cardTypesResolver, userWallet, cryptoCurrencyStatus) + + val value = mockk(relaxed = true) + every { cryptoCurrencyStatus.value } returns value + every { cryptoCurrencyStatus.currency } returns currency + + every { userWallet.cardTypesResolver } returns cardTypesResolver + every { userWallet.isMultiCurrency } returns false + + coEvery { rampStateManager.getSendUnavailabilityReason(any(), any()) } returns ScenarioUnavailabilityReason.None + coEvery { rampStateManager.availableForSell(any(), any(), any()) } returns Unit.right() + } + + @Test + fun `GIVEN Start2Coin cold wallet WHEN create THEN buy action is unavailable`() = runTest { + // Arrange + every { cardTypesResolver.isStart2Coin() } returns true + + // Act + val buyAction = createBuyAction() + + // Assert + assertThat(buyAction.unavailabilityReason) + .isEqualTo(ScenarioUnavailabilityReason.BuyUnavailable(currency.name)) + } + + @Test + fun `GIVEN non-Start2Coin cold wallet WHEN create THEN buy action is available`() = runTest { + // Arrange + every { cardTypesResolver.isStart2Coin() } returns false + + // Act + val buyAction = createBuyAction() + + // Assert + assertThat(buyAction.unavailabilityReason).isEqualTo(ScenarioUnavailabilityReason.None) + } + + private suspend fun createBuyAction(): ActionState.Buy { + val actions = factory.create( + userWallet = userWallet, + cryptoCurrencyStatus = cryptoCurrencyStatus, + stakingAvailability = StakingAvailability.Unavailable, + yieldSupplyAvailability = YieldSupplyAvailability.Unavailable, + shouldShowSwapStories = false, + ) + return actions.filterIsInstance().single() + } +} \ No newline at end of file