Updated on 2026-08-14
This commit is contained in:
parent
aef7c97866
commit
eda701fed9
21 changed files with 1006 additions and 40 deletions
|
|
@ -2,8 +2,6 @@ package com.tangem.domain.models.account
|
|||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue.Loaded
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue.Deactivated
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.kyc.KycStatus
|
||||
|
|
@ -326,10 +324,30 @@ private fun buildCryptoCurrencyStatusValue(
|
|||
}
|
||||
}
|
||||
|
||||
fun PaymentAccountStatusValue.hasAccountData(): Boolean = this is Loaded || this is Deactivated
|
||||
val PaymentAccountStatusValue.tariffPlan: TangemPayCustomerTariffPlan?
|
||||
get() = when (this) {
|
||||
is PaymentAccountStatusValue.Error,
|
||||
is PaymentAccountStatusValue.IssuingCard,
|
||||
is PaymentAccountStatusValue.Empty,
|
||||
is PaymentAccountStatusValue.NotCreated,
|
||||
is PaymentAccountStatusValue.UnderReview,
|
||||
is PaymentAccountStatusValue.Loading,
|
||||
is PaymentAccountStatusValue.Deactivated,
|
||||
-> null
|
||||
is PaymentAccountStatusValue.Inactive -> tariffPlan.tariff
|
||||
is PaymentAccountStatusValue.AwaitingPlanSelection -> tariffPlan
|
||||
is PaymentAccountStatusValue.Loaded -> tariffPlan?.tariff
|
||||
}
|
||||
|
||||
fun Loaded.hasCardWithId(cardId: String): Boolean = cards.any { it.id == cardId }
|
||||
fun PaymentAccountStatusValue.hasAccountData(): Boolean = this is PaymentAccountStatusValue.Loaded ||
|
||||
this is PaymentAccountStatusValue.Deactivated
|
||||
|
||||
fun Loaded.findCardWithId(cardId: String): TangemPayCard? = cards.firstOrNull { it.id == cardId }
|
||||
fun PaymentAccountStatusValue.Loaded.hasCardWithId(cardId: String): Boolean = cards.any { it.id == cardId }
|
||||
|
||||
fun Loaded.requireCardWithId(cardId: String): TangemPayCard = requireNotNull(findCardWithId(cardId))
|
||||
fun PaymentAccountStatusValue.Loaded.findCardWithId(cardId: String): TangemPayCard? {
|
||||
return cards.firstOrNull { it.id == cardId }
|
||||
}
|
||||
|
||||
fun PaymentAccountStatusValue.Loaded.requireCardWithId(cardId: String): TangemPayCard {
|
||||
return requireNotNull(findCardWithId(cardId))
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import java.util.Locale
|
|||
* Customer's current tariff plan.
|
||||
*
|
||||
* @property status Lifecycle status of the subscription.
|
||||
* @property source Source of tariff plan. Where [Source.DEFAULT] is basic value users with no tariff selection.
|
||||
* @property plan The currently active plan ([TangemPayTariffPlan]).
|
||||
* @property nextBillingAt When the next plan fee is charged; `null` for free plans.
|
||||
* @property pendingPlan Plan the customer will be moved to (scheduled downgrade), or `null`.
|
||||
|
|
@ -17,6 +18,7 @@ import java.util.Locale
|
|||
@Serializable
|
||||
data class TangemPayCustomerTariffPlan(
|
||||
@SerialName("status") val status: Status,
|
||||
@SerialName("source") val source: Source,
|
||||
@SerialName("plan") val plan: TangemPayTariffPlan,
|
||||
@SerialName("next_billing_at") val nextBillingAt: SerializedDateTime?,
|
||||
@SerialName("pending_plan") val pendingPlan: TangemPayTariffPlan?,
|
||||
|
|
@ -55,4 +57,28 @@ data class TangemPayCustomerTariffPlan(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class Source {
|
||||
@SerialName("DEFAULT")
|
||||
DEFAULT,
|
||||
|
||||
@SerialName("CUSTOMER")
|
||||
CUSTOMER,
|
||||
|
||||
@SerialName("UNKNOWN")
|
||||
UNKNOWN,
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun fromString(value: String?) = when (value?.uppercase(Locale.US)) {
|
||||
"DEFAULT" -> DEFAULT
|
||||
"CUSTOMER" -> CUSTOMER
|
||||
else -> UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val TangemPayCustomerTariffPlan.isDefaultTariff: Boolean
|
||||
get() = source == TangemPayCustomerTariffPlan.Source.DEFAULT && plan.isBasicTier
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.domain.pay.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.domain.models.account.isDefaultTariff
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
|
||||
class CancelTariffTransitionUseCase(
|
||||
private val cancelTangemPayOrderUseCase: CancelTangemPayOrderUseCase,
|
||||
private val getTariffTransitionUseCase: GetTangemPayTariffPlanTransitionsUseCase,
|
||||
private val submitTariffTransitionUseCase: SubmitTariffTransitionUseCase,
|
||||
private val getCurrentTariffUseCase: GetCurrentTariffUseCase,
|
||||
) {
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, orderId: String): Either<VisaApiError, Unit> = either {
|
||||
cancelTangemPayOrderUseCase.invoke(userWalletId, orderId).bind()
|
||||
|
||||
val (source, tariff) = getCurrentTariffUseCase(userWalletId) ?: return@either
|
||||
|
||||
if (source.isActual() && tariff.isDefaultTariff) {
|
||||
val transitions = getTariffTransitionUseCase.invoke(userWalletId).bind()
|
||||
val basicPlanTransition = transitions.find { it.plan.isBasicTier } ?: return@either
|
||||
submitTariffTransitionUseCase.invoke(userWalletId, basicPlanTransition).bind()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import arrow.core.Either
|
|||
import arrow.core.raise.either
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlanTransition
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.pay.model.OrderType
|
||||
import com.tangem.domain.pay.model.TangemPayOrderInfo
|
||||
|
|
@ -19,6 +20,7 @@ class CreateTariffPlanTransitionOrderUseCase(
|
|||
private val customerOrderRepository: CustomerOrderRepository,
|
||||
private val issueCardRepository: TangemPayIssueCardRepository,
|
||||
private val startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase,
|
||||
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
|
||||
private val appCoroutineScope: AppCoroutineScope,
|
||||
) {
|
||||
suspend operator fun invoke(
|
||||
|
|
@ -57,5 +59,7 @@ class CreateTariffPlanTransitionOrderUseCase(
|
|||
onTerminalReached = { issueCardRepository.removeIssueOrderId(userWalletId, order.id) },
|
||||
)
|
||||
}
|
||||
|
||||
paymentAccountStatusFetcher.invoke(userWalletId)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.domain.pay.usecase
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
|
||||
import com.tangem.domain.models.account.tariffPlan
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
|
||||
class GetCurrentTariffUseCase(
|
||||
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
|
||||
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
|
||||
) {
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Pair<StatusSource, TangemPayCustomerTariffPlan>? {
|
||||
paymentAccountStatusFetcher.invoke(userWalletId)
|
||||
val currentStatus = paymentAccountStatusSupplier.invoke(userWalletId).firstOrNull()
|
||||
val statusSource = currentStatus?.value?.source
|
||||
val tariffPlan = currentStatus?.value?.tariffPlan
|
||||
return if (statusSource != null && tariffPlan != null) {
|
||||
statusSource to tariffPlan
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.domain.pay.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.right
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlanTransition
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
|
||||
class SubmitTariffTransitionUseCase(
|
||||
private val createTransitionOrder: CreateTariffPlanTransitionOrderUseCase,
|
||||
private val setPendingTransition: SetTariffPlanPendingTransitionUseCase,
|
||||
) {
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
transition: TangemPayTariffPlanTransition,
|
||||
): Either<VisaApiError, Unit> {
|
||||
return when (transition.type) {
|
||||
TangemPayTariffPlanTransition.Type.ACTIVATION,
|
||||
TangemPayTariffPlanTransition.Type.UPGRADE,
|
||||
-> {
|
||||
createTransitionOrder(
|
||||
userWalletId = userWalletId,
|
||||
targetTariffPlanId = transition.plan.id,
|
||||
transitionType = transition.type,
|
||||
)
|
||||
}
|
||||
TangemPayTariffPlanTransition.Type.DOWNGRADE -> {
|
||||
setPendingTransition(
|
||||
userWalletId = userWalletId,
|
||||
pendingTariffPlanId = transition.plan.id,
|
||||
)
|
||||
}
|
||||
TangemPayTariffPlanTransition.Type.SYSTEM_DOWNGRADE,
|
||||
TangemPayTariffPlanTransition.Type.UNKNOWN,
|
||||
-> Unit.right()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
package com.tangem.domain.pay.usecase
|
||||
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlan
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlanTransition
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class CancelTariffTransitionUseCaseTest {
|
||||
|
||||
private val cancelTangemPayOrderUseCase: CancelTangemPayOrderUseCase = mockk()
|
||||
private val getTariffTransitionUseCase: GetTangemPayTariffPlanTransitionsUseCase = mockk()
|
||||
private val submitTariffTransitionUseCase: SubmitTariffTransitionUseCase = mockk()
|
||||
private val getCurrentTariffUseCase: GetCurrentTariffUseCase = mockk()
|
||||
|
||||
private val useCase = CancelTariffTransitionUseCase(
|
||||
cancelTangemPayOrderUseCase = cancelTangemPayOrderUseCase,
|
||||
getTariffTransitionUseCase = getTariffTransitionUseCase,
|
||||
submitTariffTransitionUseCase = submitTariffTransitionUseCase,
|
||||
getCurrentTariffUseCase = getCurrentTariffUseCase,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `GIVEN cancel order fails WHEN invoke THEN returns Left and skips further steps`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { cancelTangemPayOrderUseCase(USER_WALLET_ID, ORDER_ID) } returns VisaApiError.Unspecified.left()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, ORDER_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.leftOrNull()).isEqualTo(VisaApiError.Unspecified)
|
||||
coVerify(exactly = 0) { getCurrentTariffUseCase(any()) }
|
||||
coVerify(exactly = 0) { getTariffTransitionUseCase(any()) }
|
||||
coVerify(exactly = 0) { submitTariffTransitionUseCase(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN current tariff is null WHEN invoke THEN returns Right and skips transition`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { cancelTangemPayOrderUseCase(USER_WALLET_ID, ORDER_ID) } returns Unit.right()
|
||||
coEvery { getCurrentTariffUseCase(USER_WALLET_ID) } returns null
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, ORDER_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 0) { getTariffTransitionUseCase(any()) }
|
||||
coVerify(exactly = 0) { submitTariffTransitionUseCase(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN source is not actual WHEN invoke THEN returns Right and skips transition`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { cancelTangemPayOrderUseCase(USER_WALLET_ID, ORDER_ID) } returns Unit.right()
|
||||
coEvery { getCurrentTariffUseCase(USER_WALLET_ID) } returns (StatusSource.CACHE to DEFAULT_BASIC_TARIFF)
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, ORDER_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 0) { getTariffTransitionUseCase(any()) }
|
||||
coVerify(exactly = 0) { submitTariffTransitionUseCase(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN tariff is not default WHEN invoke THEN returns Right and skips transition`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { cancelTangemPayOrderUseCase(USER_WALLET_ID, ORDER_ID) } returns Unit.right()
|
||||
coEvery { getCurrentTariffUseCase(USER_WALLET_ID) } returns (StatusSource.ACTUAL to CUSTOMER_PLUS_TARIFF)
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, ORDER_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 0) { getTariffTransitionUseCase(any()) }
|
||||
coVerify(exactly = 0) { submitTariffTransitionUseCase(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN actual default tariff AND getTransitions fails WHEN invoke THEN returns Left`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { cancelTangemPayOrderUseCase(USER_WALLET_ID, ORDER_ID) } returns Unit.right()
|
||||
coEvery { getCurrentTariffUseCase(USER_WALLET_ID) } returns (StatusSource.ACTUAL to DEFAULT_BASIC_TARIFF)
|
||||
coEvery { getTariffTransitionUseCase(USER_WALLET_ID) } returns VisaApiError.Unspecified.left()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, ORDER_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.leftOrNull()).isEqualTo(VisaApiError.Unspecified)
|
||||
coVerify(exactly = 0) { submitTariffTransitionUseCase(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN actual default tariff AND basic transition exists WHEN invoke THEN submits basic transition`() =
|
||||
runTest {
|
||||
// GIVEN
|
||||
coEvery { cancelTangemPayOrderUseCase(USER_WALLET_ID, ORDER_ID) } returns Unit.right()
|
||||
coEvery { getCurrentTariffUseCase(USER_WALLET_ID) } returns (StatusSource.ACTUAL to DEFAULT_BASIC_TARIFF)
|
||||
coEvery { getTariffTransitionUseCase(USER_WALLET_ID) } returns
|
||||
listOf(PLUS_TRANSITION, BASIC_TRANSITION).right()
|
||||
coEvery { submitTariffTransitionUseCase(USER_WALLET_ID, BASIC_TRANSITION) } returns Unit.right()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, ORDER_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 1) { submitTariffTransitionUseCase(USER_WALLET_ID, BASIC_TRANSITION) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN actual default tariff AND no basic transition WHEN invoke THEN returns Right without submit`() =
|
||||
runTest {
|
||||
// GIVEN
|
||||
coEvery { cancelTangemPayOrderUseCase(USER_WALLET_ID, ORDER_ID) } returns Unit.right()
|
||||
coEvery { getCurrentTariffUseCase(USER_WALLET_ID) } returns (StatusSource.ACTUAL to DEFAULT_BASIC_TARIFF)
|
||||
coEvery { getTariffTransitionUseCase(USER_WALLET_ID) } returns listOf(PLUS_TRANSITION).right()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, ORDER_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 0) { submitTariffTransitionUseCase(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN actual default tariff AND submit fails WHEN invoke THEN returns Left`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { cancelTangemPayOrderUseCase(USER_WALLET_ID, ORDER_ID) } returns Unit.right()
|
||||
coEvery { getCurrentTariffUseCase(USER_WALLET_ID) } returns (StatusSource.ACTUAL to DEFAULT_BASIC_TARIFF)
|
||||
coEvery { getTariffTransitionUseCase(USER_WALLET_ID) } returns listOf(BASIC_TRANSITION).right()
|
||||
coEvery { submitTariffTransitionUseCase(USER_WALLET_ID, BASIC_TRANSITION) } returns
|
||||
VisaApiError.Unspecified.left()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, ORDER_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.leftOrNull()).isEqualTo(VisaApiError.Unspecified)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val USER_WALLET_ID = UserWalletId("aabbcc112233")
|
||||
const val ORDER_ID = "order-test-1"
|
||||
|
||||
val BASIC_PLAN = TangemPayTariffPlan(
|
||||
id = "plan-basic",
|
||||
tierId = "BASIC",
|
||||
isBasicTier = true,
|
||||
name = "Basic",
|
||||
programName = "program-basic",
|
||||
descriptionItems = emptyList(),
|
||||
)
|
||||
val PLUS_PLAN = TangemPayTariffPlan(
|
||||
id = "plan-plus",
|
||||
tierId = "PLUS",
|
||||
isBasicTier = false,
|
||||
name = "Plus",
|
||||
programName = "program-plus",
|
||||
descriptionItems = emptyList(),
|
||||
)
|
||||
val DEFAULT_BASIC_TARIFF = customerTariff(TangemPayCustomerTariffPlan.Source.DEFAULT, BASIC_PLAN)
|
||||
val CUSTOMER_PLUS_TARIFF = customerTariff(TangemPayCustomerTariffPlan.Source.CUSTOMER, PLUS_PLAN)
|
||||
|
||||
val BASIC_TRANSITION = TangemPayTariffPlanTransition(
|
||||
type = TangemPayTariffPlanTransition.Type.SYSTEM_DOWNGRADE,
|
||||
plan = BASIC_PLAN,
|
||||
)
|
||||
val PLUS_TRANSITION = TangemPayTariffPlanTransition(
|
||||
type = TangemPayTariffPlanTransition.Type.UPGRADE,
|
||||
plan = PLUS_PLAN,
|
||||
)
|
||||
|
||||
private fun customerTariff(source: TangemPayCustomerTariffPlan.Source, plan: TangemPayTariffPlan) =
|
||||
TangemPayCustomerTariffPlan(
|
||||
status = TangemPayCustomerTariffPlan.Status.ACTIVE,
|
||||
source = source,
|
||||
plan = plan,
|
||||
nextBillingAt = null,
|
||||
pendingPlan = null,
|
||||
pendingTransitionAt = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
import com.tangem.domain.pay.model.Order
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.pay.model.OrderStep
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||
import com.tangem.domain.pay.model.OrderType
|
||||
import com.tangem.domain.pay.repository.CustomerOrderRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayIssueCardRepository
|
||||
|
|
@ -24,11 +25,13 @@ internal class CreateTariffPlanTransitionOrderUseCaseTest {
|
|||
private val customerOrderRepository: CustomerOrderRepository = mockk()
|
||||
private val issueCardRepository: TangemPayIssueCardRepository = mockk(relaxed = true)
|
||||
private val startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase = mockk(relaxed = true)
|
||||
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher = mockk(relaxed = true)
|
||||
|
||||
private val useCase = CreateTariffPlanTransitionOrderUseCase(
|
||||
customerOrderRepository = customerOrderRepository,
|
||||
issueCardRepository = issueCardRepository,
|
||||
startTangemPayOrderPollingUseCase = startTangemPayOrderPollingUseCase,
|
||||
paymentAccountStatusFetcher = paymentAccountStatusFetcher,
|
||||
appCoroutineScope = TestAppCoroutineScope(),
|
||||
)
|
||||
|
||||
|
|
@ -48,6 +51,7 @@ internal class CreateTariffPlanTransitionOrderUseCaseTest {
|
|||
customerOrderRepository.createOrder(any(), any(), any(), any(), any(), any())
|
||||
}
|
||||
coVerify(exactly = 0) { issueCardRepository.storeIssueOrderId(any(), any()) }
|
||||
coVerify(exactly = 0) { paymentAccountStatusFetcher.invoke(any<UserWalletId>()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -118,6 +122,7 @@ internal class CreateTariffPlanTransitionOrderUseCaseTest {
|
|||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 1) { issueCardRepository.storeIssueOrderId(USER_WALLET_ID, newOrder.id) }
|
||||
coVerify(exactly = 1) { paymentAccountStatusFetcher.invoke(USER_WALLET_ID) }
|
||||
}
|
||||
|
||||
private fun order(id: String, status: OrderStatus): Order = Order(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
package com.tangem.domain.pay.usecase
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.domain.models.account.TangemPayCustomerTariffPlan
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlan
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class GetCurrentTariffUseCaseTest {
|
||||
|
||||
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher = mockk(relaxed = true)
|
||||
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier = mockk()
|
||||
|
||||
private val useCase = GetCurrentTariffUseCase(
|
||||
paymentAccountStatusFetcher = paymentAccountStatusFetcher,
|
||||
paymentAccountStatusSupplier = paymentAccountStatusSupplier,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `GIVEN status with tariff WHEN invoke THEN fetches and returns source with tariff`() = runTest {
|
||||
// GIVEN
|
||||
val status = paymentStatus(value = awaitingPlanSelection(StatusSource.ACTUAL, CUSTOMER_TARIFF))
|
||||
every { paymentAccountStatusSupplier.invoke(USER_WALLET_ID) } returns flowOf(status)
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isEqualTo(StatusSource.ACTUAL to CUSTOMER_TARIFF)
|
||||
coVerify(exactly = 1) { paymentAccountStatusFetcher.invoke(USER_WALLET_ID) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN empty supplier flow WHEN invoke THEN returns null`() = runTest {
|
||||
// GIVEN
|
||||
every { paymentAccountStatusSupplier.invoke(USER_WALLET_ID) } returns emptyFlow()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN status without tariff WHEN invoke THEN returns null`() = runTest {
|
||||
// GIVEN
|
||||
val status = paymentStatus(value = PaymentAccountStatusValue.NotCreated)
|
||||
every { paymentAccountStatusSupplier.invoke(USER_WALLET_ID) } returns flowOf(status)
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
private fun paymentStatus(value: PaymentAccountStatusValue) = AccountStatus.Payment(
|
||||
account = mockk(),
|
||||
value = value,
|
||||
)
|
||||
|
||||
private fun awaitingPlanSelection(source: StatusSource, tariff: TangemPayCustomerTariffPlan) =
|
||||
PaymentAccountStatusValue.AwaitingPlanSelection(
|
||||
source = source,
|
||||
cryptoCurrency = mockk(),
|
||||
tariffPlan = tariff,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
val USER_WALLET_ID = UserWalletId("aabbcc112233")
|
||||
|
||||
val PLAN = TangemPayTariffPlan(
|
||||
id = "plan-plus",
|
||||
tierId = "PLUS",
|
||||
isBasicTier = false,
|
||||
name = "Plus",
|
||||
programName = "program-plus",
|
||||
descriptionItems = emptyList(),
|
||||
)
|
||||
val CUSTOMER_TARIFF = TangemPayCustomerTariffPlan(
|
||||
status = TangemPayCustomerTariffPlan.Status.ACTIVE,
|
||||
source = TangemPayCustomerTariffPlan.Source.CUSTOMER,
|
||||
plan = PLAN,
|
||||
nextBillingAt = null,
|
||||
pendingPlan = null,
|
||||
pendingTransitionAt = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -169,6 +169,7 @@ internal class GetTangemPayTariffPlanStateUseCaseTest {
|
|||
)
|
||||
val CUSTOMER_TARIFF = TangemPayCustomerTariffPlan(
|
||||
status = TangemPayCustomerTariffPlan.Status.ACTIVE,
|
||||
source = TangemPayCustomerTariffPlan.Source.CUSTOMER,
|
||||
plan = CURRENT_PLAN,
|
||||
nextBillingAt = null,
|
||||
pendingPlan = null,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
package com.tangem.domain.pay.usecase
|
||||
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlan
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlanTransition
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class SubmitTariffTransitionUseCaseTest {
|
||||
|
||||
private val createTransitionOrder: CreateTariffPlanTransitionOrderUseCase = mockk()
|
||||
private val setPendingTransition: SetTariffPlanPendingTransitionUseCase = mockk()
|
||||
|
||||
private val useCase = SubmitTariffTransitionUseCase(
|
||||
createTransitionOrder = createTransitionOrder,
|
||||
setPendingTransition = setPendingTransition,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `GIVEN activation transition WHEN invoke THEN creates transition order`() = runTest {
|
||||
// GIVEN
|
||||
val transition = transition(TangemPayTariffPlanTransition.Type.ACTIVATION)
|
||||
coEvery {
|
||||
createTransitionOrder(USER_WALLET_ID, PLAN_ID, TangemPayTariffPlanTransition.Type.ACTIVATION)
|
||||
} returns Unit.right()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, transition)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 1) {
|
||||
createTransitionOrder(USER_WALLET_ID, PLAN_ID, TangemPayTariffPlanTransition.Type.ACTIVATION)
|
||||
}
|
||||
coVerify(exactly = 0) { setPendingTransition(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN upgrade transition WHEN invoke THEN creates transition order`() = runTest {
|
||||
// GIVEN
|
||||
val transition = transition(TangemPayTariffPlanTransition.Type.UPGRADE)
|
||||
coEvery {
|
||||
createTransitionOrder(USER_WALLET_ID, PLAN_ID, TangemPayTariffPlanTransition.Type.UPGRADE)
|
||||
} returns Unit.right()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, transition)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 1) {
|
||||
createTransitionOrder(USER_WALLET_ID, PLAN_ID, TangemPayTariffPlanTransition.Type.UPGRADE)
|
||||
}
|
||||
coVerify(exactly = 0) { setPendingTransition(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN createTransitionOrder fails WHEN invoke THEN returns Left`() = runTest {
|
||||
// GIVEN
|
||||
val transition = transition(TangemPayTariffPlanTransition.Type.UPGRADE)
|
||||
coEvery {
|
||||
createTransitionOrder(USER_WALLET_ID, PLAN_ID, TangemPayTariffPlanTransition.Type.UPGRADE)
|
||||
} returns VisaApiError.Unspecified.left()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, transition)
|
||||
|
||||
// THEN
|
||||
assertThat(result.leftOrNull()).isEqualTo(VisaApiError.Unspecified)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN downgrade transition WHEN invoke THEN sets pending transition`() = runTest {
|
||||
// GIVEN
|
||||
val transition = transition(TangemPayTariffPlanTransition.Type.DOWNGRADE)
|
||||
coEvery { setPendingTransition(USER_WALLET_ID, PLAN_ID) } returns Unit.right()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, transition)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 1) { setPendingTransition(USER_WALLET_ID, PLAN_ID) }
|
||||
coVerify(exactly = 0) { createTransitionOrder(any(), any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN setPendingTransition fails WHEN invoke THEN returns Left`() = runTest {
|
||||
// GIVEN
|
||||
val transition = transition(TangemPayTariffPlanTransition.Type.DOWNGRADE)
|
||||
coEvery { setPendingTransition(USER_WALLET_ID, PLAN_ID) } returns VisaApiError.Unspecified.left()
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, transition)
|
||||
|
||||
// THEN
|
||||
assertThat(result.leftOrNull()).isEqualTo(VisaApiError.Unspecified)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN system downgrade transition WHEN invoke THEN returns Right without side effects`() = runTest {
|
||||
// GIVEN
|
||||
val transition = transition(TangemPayTariffPlanTransition.Type.SYSTEM_DOWNGRADE)
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, transition)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 0) { createTransitionOrder(any(), any(), any()) }
|
||||
coVerify(exactly = 0) { setPendingTransition(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN unknown transition WHEN invoke THEN returns Right without side effects`() = runTest {
|
||||
// GIVEN
|
||||
val transition = transition(TangemPayTariffPlanTransition.Type.UNKNOWN)
|
||||
|
||||
// WHEN
|
||||
val result = useCase(USER_WALLET_ID, transition)
|
||||
|
||||
// THEN
|
||||
assertThat(result.isRight()).isTrue()
|
||||
coVerify(exactly = 0) { createTransitionOrder(any(), any(), any()) }
|
||||
coVerify(exactly = 0) { setPendingTransition(any(), any()) }
|
||||
}
|
||||
|
||||
private fun transition(type: TangemPayTariffPlanTransition.Type) = TangemPayTariffPlanTransition(
|
||||
type = type,
|
||||
plan = PLAN,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
val USER_WALLET_ID = UserWalletId("aabbcc112233")
|
||||
const val PLAN_ID = "plan-plus"
|
||||
|
||||
val PLAN = TangemPayTariffPlan(
|
||||
id = PLAN_ID,
|
||||
tierId = "PLUS",
|
||||
isBasicTier = false,
|
||||
name = "Plus",
|
||||
programName = "program-plus",
|
||||
descriptionItems = emptyList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue