Updated on 2026-08-14
This commit is contained in:
parent
4fc52e2052
commit
017eece8b7
18 changed files with 938 additions and 14 deletions
|
|
@ -130,6 +130,7 @@ internal interface TangemPayDataModule {
|
|||
repository: DefaultTariffPlanTransitionsRepository,
|
||||
): TangemPayTariffPlanTransitionsRepository
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
companion object {
|
||||
|
||||
@Provides
|
||||
|
|
@ -335,5 +336,34 @@ internal interface TangemPayDataModule {
|
|||
pollingUseCase = pollingUseCase,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideCancelTangemPayOrderUseCase(
|
||||
customerOrderRepository: CustomerOrderRepository,
|
||||
issueCardRepository: TangemPayIssueCardRepository,
|
||||
paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
|
||||
startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase,
|
||||
): CancelTangemPayOrderUseCase {
|
||||
return CancelTangemPayOrderUseCase(
|
||||
customerOrderRepository = customerOrderRepository,
|
||||
paymentAccountStatusFetcher = paymentAccountStatusFetcher,
|
||||
startTangemPayOrderPollingUseCase = startTangemPayOrderPollingUseCase,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideCreateTariffPlanTransitionOrderUseCase(
|
||||
customerOrderRepository: CustomerOrderRepository,
|
||||
issueCardRepository: TangemPayIssueCardRepository,
|
||||
startTangemPayOrderPollingUseCase: StartTangemPayOrderPollingUseCase,
|
||||
appCoroutineScope: AppCoroutineScope,
|
||||
): CreateTariffPlanTransitionOrderUseCase {
|
||||
return CreateTariffPlanTransitionOrderUseCase(
|
||||
customerOrderRepository = customerOrderRepository,
|
||||
issueCardRepository = issueCardRepository,
|
||||
startTangemPayOrderPollingUseCase = startTangemPayOrderPollingUseCase,
|
||||
appCoroutineScope = appCoroutineScope,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,4 +79,10 @@ internal class DefaultCustomerOrderRepository @Inject constructor(
|
|||
OrderConverter.convert(result)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun cancelOrder(userWalletId: UserWalletId, orderId: String): Either<VisaApiError, Unit> {
|
||||
return requestHelper.performRequest(userWalletId) { authHeader ->
|
||||
tangemPayApi.cancelOrder(authHeader = authHeader, orderId = orderId)
|
||||
}.map {}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
package com.tangem.data.pay.converter
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
|
||||
import com.tangem.domain.models.account.TangemPayTariffPlan
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TangemPayTariffPlanConverterTest {
|
||||
|
||||
@Test
|
||||
fun `GIVEN null value WHEN convert THEN returns null`() {
|
||||
assertThat(TangemPayTariffPlanConverter.convert(null)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN missing id WHEN convert THEN returns null`() {
|
||||
val value = tariffPlan(id = null)
|
||||
|
||||
assertThat(TangemPayTariffPlanConverter.convert(value)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN missing name WHEN convert THEN returns null`() {
|
||||
val value = tariffPlan(name = null)
|
||||
|
||||
assertThat(TangemPayTariffPlanConverter.convert(value)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN full valid plan WHEN convert THEN maps all fields`() {
|
||||
// GIVEN
|
||||
val value = tariffPlan(
|
||||
type = "PLUS",
|
||||
descriptionItems = listOf(
|
||||
CustomerMeResponse.DescriptionItem(
|
||||
type = "PLAN_RELATED",
|
||||
order = 2,
|
||||
title = "Title",
|
||||
body = "Body",
|
||||
),
|
||||
),
|
||||
images = listOf(CustomerMeResponse.Image(type = "MAIN", url = "https://img")),
|
||||
fees = listOf(
|
||||
CustomerMeResponse.Fee(
|
||||
type = "RECURRING",
|
||||
amount = BigDecimal("9.99"),
|
||||
currency = "USD",
|
||||
description = "Monthly",
|
||||
period = "MONTH",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = TangemPayTariffPlanConverter.convert(value)
|
||||
|
||||
// THEN
|
||||
val expected = TangemPayTariffPlan(
|
||||
id = PLAN_ID,
|
||||
type = TangemPayTariffPlan.Type.PLUS,
|
||||
name = PLAN_NAME,
|
||||
descriptionItems = listOf(
|
||||
TangemPayTariffPlan.DescriptionItem(
|
||||
section = TangemPayTariffPlan.Section.PLAN_RELATED,
|
||||
order = 2,
|
||||
title = "Title",
|
||||
body = "Body",
|
||||
),
|
||||
),
|
||||
images = listOf(
|
||||
TangemPayTariffPlan.Image(type = TangemPayTariffPlan.Image.Type.MAIN, url = "https://img"),
|
||||
),
|
||||
fees = listOf(
|
||||
TangemPayTariffPlan.Fee(
|
||||
type = TangemPayTariffPlan.Fee.Type.RECURRING,
|
||||
amount = BigDecimal("9.99"),
|
||||
currency = "USD",
|
||||
description = "Monthly",
|
||||
period = TangemPayTariffPlan.Fee.Period.MONTH,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertThat(result).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN unknown enum strings and null optional fields WHEN convert THEN falls back to defaults`() {
|
||||
// GIVEN
|
||||
val value = tariffPlan(
|
||||
type = "SOMETHING_NEW",
|
||||
descriptionItems = listOf(
|
||||
CustomerMeResponse.DescriptionItem(type = "wat", order = null, title = "Title", body = null),
|
||||
),
|
||||
images = listOf(CustomerMeResponse.Image(type = null, url = "https://img")),
|
||||
fees = listOf(
|
||||
CustomerMeResponse.Fee(
|
||||
type = null,
|
||||
amount = BigDecimal.ONE,
|
||||
currency = null,
|
||||
description = null,
|
||||
period = null,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = TangemPayTariffPlanConverter.convert(value)
|
||||
|
||||
// THEN
|
||||
val expected = TangemPayTariffPlan(
|
||||
id = PLAN_ID,
|
||||
type = TangemPayTariffPlan.Type.UNKNOWN,
|
||||
name = PLAN_NAME,
|
||||
descriptionItems = listOf(
|
||||
TangemPayTariffPlan.DescriptionItem(
|
||||
section = TangemPayTariffPlan.Section.UNKNOWN,
|
||||
order = 0,
|
||||
title = "Title",
|
||||
body = "",
|
||||
),
|
||||
),
|
||||
images = listOf(
|
||||
TangemPayTariffPlan.Image(type = TangemPayTariffPlan.Image.Type.UNKNOWN, url = "https://img"),
|
||||
),
|
||||
fees = listOf(
|
||||
TangemPayTariffPlan.Fee(
|
||||
type = TangemPayTariffPlan.Fee.Type.UNKNOWN,
|
||||
amount = BigDecimal.ONE,
|
||||
currency = "",
|
||||
description = "",
|
||||
period = null,
|
||||
),
|
||||
),
|
||||
)
|
||||
assertThat(result).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN nested items with missing required fields WHEN convert THEN filters them out`() {
|
||||
// GIVEN
|
||||
val value = tariffPlan(
|
||||
descriptionItems = listOf(
|
||||
CustomerMeResponse.DescriptionItem(type = "PLAN_RELATED", order = 1, title = null, body = "Body"),
|
||||
),
|
||||
images = listOf(CustomerMeResponse.Image(type = "MAIN", url = null)),
|
||||
fees = listOf(
|
||||
CustomerMeResponse.Fee(
|
||||
type = "FREE",
|
||||
amount = null,
|
||||
currency = "USD",
|
||||
description = "d",
|
||||
period = null,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = TangemPayTariffPlanConverter.convert(value)
|
||||
|
||||
// THEN
|
||||
assertThat(result?.descriptionItems).isEmpty()
|
||||
assertThat(result?.images).isEmpty()
|
||||
assertThat(result?.fees).isEmpty()
|
||||
}
|
||||
|
||||
private fun tariffPlan(
|
||||
id: String? = PLAN_ID,
|
||||
type: String? = "BASIC",
|
||||
name: String? = PLAN_NAME,
|
||||
descriptionItems: List<CustomerMeResponse.DescriptionItem>? = null,
|
||||
images: List<CustomerMeResponse.Image>? = null,
|
||||
fees: List<CustomerMeResponse.Fee>? = null,
|
||||
) = CustomerMeResponse.TariffPlan(
|
||||
id = id,
|
||||
type = type,
|
||||
name = name,
|
||||
descriptionItems = descriptionItems,
|
||||
images = images,
|
||||
fees = fees,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
const val PLAN_ID = "plan-1"
|
||||
const val PLAN_NAME = "Plus"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package com.tangem.data.pay.repository
|
||||
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.datasource.api.pay.TangemPayApi
|
||||
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
|
||||
import com.tangem.datasource.api.pay.models.response.TariffPlanTransitionResponse
|
||||
import com.tangem.datasource.api.pay.models.response.TariffPlanTransitionsResponse
|
||||
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.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class DefaultTariffPlanTransitionsRepositoryTest {
|
||||
|
||||
private val tangemPayApi: TangemPayApi = mockk()
|
||||
private val requestHelper: TangemPayRequestPerformer = mockk()
|
||||
|
||||
private val repository = DefaultTariffPlanTransitionsRepository(
|
||||
tangemPayApi = tangemPayApi,
|
||||
requestHelper = requestHelper,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
coEvery {
|
||||
requestHelper.performRequest<Any>(userWalletId = any(), requestBlock = any())
|
||||
} coAnswers {
|
||||
val block = secondArg<suspend (String) -> ApiResponse<Any>>()
|
||||
when (val response = block(AUTH_HEADER)) {
|
||||
is ApiResponse.Success -> response.data.right()
|
||||
is ApiResponse.Error -> VisaApiError.Unspecified.left()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN backend error WHEN getTransitions THEN returns error`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { tangemPayApi.getTariffPlanTransitions(any()) } returns
|
||||
ApiResponse.Error(ApiResponseError.NetworkException()) as ApiResponse<TariffPlanTransitionsResponse>
|
||||
|
||||
// WHEN
|
||||
val result = repository.getTransitions(USER_WALLET_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.leftOrNull()).isEqualTo(VisaApiError.Unspecified)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN null result WHEN getTransitions THEN returns empty list`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { tangemPayApi.getTariffPlanTransitions(any()) } returns
|
||||
ApiResponse.Success(TariffPlanTransitionsResponse(result = null))
|
||||
|
||||
// WHEN
|
||||
val result = repository.getTransitions(USER_WALLET_ID)
|
||||
|
||||
// THEN
|
||||
assertThat(result.getOrNull()).isEqualTo(emptyList<TangemPayTariffPlanTransition>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN valid transitions WHEN getTransitions THEN maps them to domain`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { tangemPayApi.getTariffPlanTransitions(any()) } returns ApiResponse.Success(
|
||||
TariffPlanTransitionsResponse(
|
||||
result = listOf(
|
||||
TariffPlanTransitionResponse(type = "UPGRADE", tariffPlan = tariffPlan()),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = repository.getTransitions(USER_WALLET_ID)
|
||||
|
||||
// THEN
|
||||
val expected = listOf(
|
||||
TangemPayTariffPlanTransition(
|
||||
type = TangemPayTariffPlanTransition.Type.UPGRADE,
|
||||
plan = TangemPayTariffPlan(
|
||||
id = PLAN_ID,
|
||||
type = TangemPayTariffPlan.Type.PLUS,
|
||||
name = PLAN_NAME,
|
||||
descriptionItems = emptyList(),
|
||||
images = emptyList(),
|
||||
fees = emptyList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
assertThat(result.getOrNull()).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a transition with unconvertible plan WHEN getTransitions THEN it is filtered out`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { tangemPayApi.getTariffPlanTransitions(any()) } returns ApiResponse.Success(
|
||||
TariffPlanTransitionsResponse(
|
||||
result = listOf(
|
||||
TariffPlanTransitionResponse(type = "UPGRADE", tariffPlan = null),
|
||||
TariffPlanTransitionResponse(type = "DOWNGRADE", tariffPlan = tariffPlan(id = null)),
|
||||
TariffPlanTransitionResponse(type = "ACTIVATION", tariffPlan = tariffPlan()),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result = repository.getTransitions(USER_WALLET_ID)
|
||||
|
||||
// THEN
|
||||
val transitions = result.getOrNull().orEmpty()
|
||||
assertThat(transitions.map { it.type })
|
||||
.containsExactly(TangemPayTariffPlanTransition.Type.ACTIVATION)
|
||||
}
|
||||
|
||||
private fun tariffPlan(id: String? = PLAN_ID) = CustomerMeResponse.TariffPlan(
|
||||
id = id,
|
||||
type = "PLUS",
|
||||
name = PLAN_NAME,
|
||||
descriptionItems = null,
|
||||
images = null,
|
||||
fees = null,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
val USER_WALLET_ID = UserWalletId("aabbcc112233")
|
||||
const val AUTH_HEADER = "auth-header"
|
||||
const val PLAN_ID = "plan-plus"
|
||||
const val PLAN_NAME = "Plus"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue