Updated on 2026-08-14
This commit is contained in:
parent
a2480ff021
commit
ba8797ec97
2 changed files with 183 additions and 20 deletions
|
|
@ -18,6 +18,7 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.utils.parseBigDecimalOrNull
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
|
|
@ -286,10 +287,9 @@ internal class GiveApprovalModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun getApprovalAmount(): BigDecimal? {
|
||||
return if (uiState.value.approveType == ApproveType.LIMITED) {
|
||||
params.amount.toBigDecimalOrNull()
|
||||
} else {
|
||||
null
|
||||
return when (uiState.value.approveType) {
|
||||
ApproveType.LIMITED -> params.amount.parseBigDecimalOrNull()
|
||||
ApproveType.UNLIMITED -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +302,7 @@ internal class GiveApprovalModel @Inject constructor(
|
|||
val tokenCurrency = cryptoCurrencyStatus.currency as? CryptoCurrency.Token
|
||||
?: return GetFeeError.DataError(IllegalStateException("Currency is not a token")).left()
|
||||
|
||||
val amount = params.amount.toBigDecimalOrNull()
|
||||
val amount = params.amount.parseBigDecimalOrNull()
|
||||
?: return GetFeeError.DataError(IllegalArgumentException("Invalid amount format")).left()
|
||||
|
||||
val allowance = getAllowanceInfoUseCase(
|
||||
|
|
|
|||
|
|
@ -2,16 +2,21 @@ package com.tangem.features.approval.impl.model
|
|||
|
||||
import arrow.core.right
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.model.MutableParamsContainer
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
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.transaction.error.GetFeeError
|
||||
import com.tangem.domain.transaction.models.AllowanceInfo
|
||||
import com.tangem.domain.transaction.models.TransactionFeeExtended
|
||||
import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.GetAllowanceInfoUseCase
|
||||
import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
||||
|
|
@ -23,6 +28,7 @@ import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
|||
import com.tangem.features.approval.api.GiveApprovalComponent
|
||||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
|
@ -30,6 +36,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class GiveApprovalModelTest {
|
||||
|
|
@ -48,31 +55,73 @@ class GiveApprovalModelTest {
|
|||
private val userWalletId = UserWalletId(stringValue = "0123456789ABCDEF")
|
||||
private val userWallet: UserWallet.Hot = mockk(relaxed = true)
|
||||
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus = mockk {
|
||||
every { currency } returns mockk<CryptoCurrency.Token>(relaxed = true)
|
||||
private val tokenCurrency: CryptoCurrency.Token = mockk(relaxed = true) {
|
||||
every { contractAddress } returns "0xContract"
|
||||
every { network } returns mockk<Network>(relaxed = true)
|
||||
}
|
||||
|
||||
private val params = GiveApprovalComponent.Params(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
amount = "10",
|
||||
spenderAddress = "0xSpender",
|
||||
amountFooter = TextReference.EMPTY,
|
||||
feeFooter = TextReference.EMPTY,
|
||||
callback = mockk(relaxed = true),
|
||||
)
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus = mockk {
|
||||
every { currency } returns tokenCurrency
|
||||
}
|
||||
|
||||
private val approvalTx: TransactionData.Uncompiled = mockk(relaxed = true)
|
||||
private val transactionFee: TransactionFee = mockk(relaxed = true)
|
||||
private val transactionFeeExtended: TransactionFeeExtended = mockk(relaxed = true) {
|
||||
every { transactionFee } returns this@GiveApprovalModelTest.transactionFee
|
||||
}
|
||||
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase = mockk(relaxed = true)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
every { getUserWalletUseCase.invoke(userWalletId) } returns userWallet.right()
|
||||
|
||||
coEvery {
|
||||
createApprovalTransactionUseCase(
|
||||
cryptoCurrencyStatus = any(),
|
||||
userWalletId = any(),
|
||||
amount = any(),
|
||||
contractAddress = any(),
|
||||
spenderAddress = any(),
|
||||
)
|
||||
} returns approvalTx.right()
|
||||
|
||||
coEvery {
|
||||
getAllowanceInfoUseCase(
|
||||
userWalletId = any(),
|
||||
cryptoCurrency = any(),
|
||||
spenderAddress = any(),
|
||||
requiredAmount = any(),
|
||||
)
|
||||
} returns AllowanceInfo.Enough(allowance = BigDecimal.ZERO).right()
|
||||
|
||||
coEvery {
|
||||
getFeeUseCase(transactionData = any(), userWallet = any(), network = any())
|
||||
} returns transactionFee.right()
|
||||
|
||||
coEvery {
|
||||
getFeeForGaslessUseCase(transactionData = any(), userWallet = any(), network = any())
|
||||
} returns transactionFeeExtended.right()
|
||||
|
||||
coEvery {
|
||||
getFeeForTokenUseCase(transactionData = any(), userWallet = any(), token = any())
|
||||
} returns transactionFeeExtended.right()
|
||||
}
|
||||
|
||||
private fun createModel(): GiveApprovalModel = GiveApprovalModel(
|
||||
private fun createParams(amount: String): GiveApprovalComponent.Params = GiveApprovalComponent.Params(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
amount = amount,
|
||||
spenderAddress = "0xSpender",
|
||||
amountFooter = TextReference.EMPTY,
|
||||
feeFooter = TextReference.EMPTY,
|
||||
callback = mockk(relaxed = true),
|
||||
)
|
||||
|
||||
private fun createModel(amount: String = "10"): GiveApprovalModel = GiveApprovalModel(
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
paramsContainer = MutableParamsContainer(params),
|
||||
paramsContainer = MutableParamsContainer(createParams(amount)),
|
||||
createApprovalTransactionUseCase = createApprovalTransactionUseCase,
|
||||
getAllowanceInfoUseCase = getAllowanceInfoUseCase,
|
||||
sendTransactionUseCase = sendTransactionUseCase,
|
||||
|
|
@ -109,4 +158,118 @@ class GiveApprovalModelTest {
|
|||
coVerify(exactly = 0) { feeSelectorReloadTrigger.triggerLoadingState() }
|
||||
coVerify(exactly = 0) { feeSelectorReloadTrigger.triggerUpdate() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN comma decimal amount and LIMITED approveType WHEN loadFeeExtended THEN creates approval tx with parsed amount`() =
|
||||
runTest {
|
||||
val model = createModel(amount = "1,1")
|
||||
|
||||
val result = model.loadFeeExtended(maybeToken = null)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify {
|
||||
createApprovalTransactionUseCase(
|
||||
cryptoCurrencyStatus = any(),
|
||||
userWalletId = any(),
|
||||
amount = match { it != null && it.compareTo(BigDecimal("1.1")) == 0 },
|
||||
contractAddress = any(),
|
||||
spenderAddress = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN point decimal amount and LIMITED approveType WHEN loadFeeExtended THEN creates approval tx with parsed amount`() =
|
||||
runTest {
|
||||
val model = createModel(amount = "1.1")
|
||||
|
||||
val result = model.loadFeeExtended(maybeToken = null)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify {
|
||||
createApprovalTransactionUseCase(
|
||||
cryptoCurrencyStatus = any(),
|
||||
userWalletId = any(),
|
||||
amount = match { it != null && it.compareTo(BigDecimal("1.1")) == 0 },
|
||||
contractAddress = any(),
|
||||
spenderAddress = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN comma decimal amount and UNLIMITED approveType WHEN loadFeeExtended THEN creates approval tx with null amount`() =
|
||||
runTest {
|
||||
val model = createModel(amount = "1,1")
|
||||
model.onChangeApproveType(ApproveType.UNLIMITED)
|
||||
|
||||
val result = model.loadFeeExtended(maybeToken = null)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify {
|
||||
createApprovalTransactionUseCase(
|
||||
cryptoCurrencyStatus = any(),
|
||||
userWalletId = any(),
|
||||
amount = isNull(),
|
||||
contractAddress = any(),
|
||||
spenderAddress = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN unparseable amount and LIMITED approveType WHEN loadFeeExtended THEN creates approval tx with null amount`() =
|
||||
runTest {
|
||||
val model = createModel(amount = "abc")
|
||||
|
||||
val result = model.loadFeeExtended(maybeToken = null)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify {
|
||||
createApprovalTransactionUseCase(
|
||||
cryptoCurrencyStatus = any(),
|
||||
userWalletId = any(),
|
||||
amount = isNull(),
|
||||
contractAddress = any(),
|
||||
spenderAddress = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN comma decimal amount and LIMITED approveType WHEN loadFee THEN creates approval tx with parsed amount`() =
|
||||
runTest {
|
||||
val model = createModel(amount = "2,5")
|
||||
|
||||
val result = model.loadFee()
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify {
|
||||
createApprovalTransactionUseCase(
|
||||
cryptoCurrencyStatus = any(),
|
||||
userWalletId = any(),
|
||||
amount = match { it != null && it.compareTo(BigDecimal("2.5")) == 0 },
|
||||
contractAddress = any(),
|
||||
spenderAddress = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN unparseable amount WHEN loadFee THEN returns DataError and does not check allowance`() = runTest {
|
||||
val model = createModel(amount = "abc")
|
||||
|
||||
val result = model.loadFee()
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
assertThat(result.leftOrNull()).isInstanceOf(GetFeeError.DataError::class.java)
|
||||
coVerify(exactly = 0) {
|
||||
getAllowanceInfoUseCase(
|
||||
userWalletId = any(),
|
||||
cryptoCurrency = any(),
|
||||
spenderAddress = any(),
|
||||
requiredAmount = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue