diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManagerFactory.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManagerFactory.kt index 7be658f54a60..1067b93d7847 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManagerFactory.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManagerFactory.kt @@ -18,11 +18,10 @@ import com.woocommerce.android.cardreader.internal.payments.PaymentManager import com.woocommerce.android.cardreader.internal.payments.PaymentUtils import com.woocommerce.android.cardreader.internal.payments.RefundErrorMapper import com.woocommerce.android.cardreader.internal.payments.actions.CancelPaymentAction -import com.woocommerce.android.cardreader.internal.payments.actions.CollectInteracRefundAction import com.woocommerce.android.cardreader.internal.payments.actions.CollectPaymentAction import com.woocommerce.android.cardreader.internal.payments.actions.CreatePaymentAction -import com.woocommerce.android.cardreader.internal.payments.actions.ProcessInteracRefundAction import com.woocommerce.android.cardreader.internal.payments.actions.ProcessPaymentAction +import com.woocommerce.android.cardreader.internal.payments.actions.ProcessRefundAction import com.woocommerce.android.cardreader.internal.wrappers.PaymentIntentParametersFactory import com.woocommerce.android.cardreader.internal.wrappers.PaymentMethodTypeMapper import com.woocommerce.android.cardreader.internal.wrappers.TerminalWrapper @@ -69,8 +68,7 @@ object CardReaderManagerFactory { cardReaderConfigFactory ), InteracRefundManager( - CollectInteracRefundAction(terminal), - ProcessInteracRefundAction(terminal), + ProcessRefundAction(terminal), RefundErrorMapper(), paymentUtils, ), diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/InteracRefundManager.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/InteracRefundManager.kt index 516657246476..cc299114a8ef 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/InteracRefundManager.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/InteracRefundManager.kt @@ -1,19 +1,16 @@ package com.woocommerce.android.cardreader.internal.payments -import com.woocommerce.android.cardreader.internal.payments.actions.CollectInteracRefundAction -import com.woocommerce.android.cardreader.internal.payments.actions.ProcessInteracRefundAction +import com.woocommerce.android.cardreader.internal.payments.actions.ProcessRefundAction import com.woocommerce.android.cardreader.payments.CardInteracRefundStatus import com.woocommerce.android.cardreader.payments.RefundConfig import com.woocommerce.android.cardreader.payments.RefundParams import com.woocommerce.android.cardreader.payments.toStripeRefundConfiguration import com.woocommerce.android.cardreader.payments.toStripeRefundParameters import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.flow internal class InteracRefundManager( - private val collectInteracRefundAction: CollectInteracRefundAction, - private val processInteracRefundAction: ProcessInteracRefundAction, + private val processRefundAction: ProcessRefundAction, private val refundErrorMapper: RefundErrorMapper, private val paymentsUtils: PaymentUtils, ) { @@ -21,37 +18,16 @@ internal class InteracRefundManager( refundParameters: RefundParams, refundConfig: RefundConfig, ): Flow = flow { - collectInteracRefund(refundParameters, refundConfig) - } - - private suspend fun FlowCollector.collectInteracRefund( - refundParameters: RefundParams, - refundConfig: RefundConfig, - ) { emit(CardInteracRefundStatus.CollectingInteracRefund) - collectInteracRefundAction.collectRefund( + processRefundAction.processRefund( refundParameters.toStripeRefundParameters(paymentsUtils), refundConfig.toStripeRefundConfiguration() - ).collect { refundStatus -> - when (refundStatus) { - CollectInteracRefundAction.CollectInteracRefundStatus.Success -> { - processInteracRefund(refundParameters) - } - is CollectInteracRefundAction.CollectInteracRefundStatus.Failure -> { - emit(refundErrorMapper.mapTerminalError(refundParameters, refundStatus.exception)) - } - } - } - } - - private suspend fun FlowCollector.processInteracRefund(refundParameters: RefundParams) { - emit(CardInteracRefundStatus.ProcessingInteracRefund) - processInteracRefundAction.processRefund().collect { status -> + ).collect { status -> when (status) { - is ProcessInteracRefundAction.ProcessRefundStatus.Success -> { + is ProcessRefundAction.ProcessRefundStatus.Success -> { emit(CardInteracRefundStatus.InteracRefundSuccess) } - is ProcessInteracRefundAction.ProcessRefundStatus.Failure -> { + is ProcessRefundAction.ProcessRefundStatus.Failure -> { emit(refundErrorMapper.mapTerminalError(refundParameters, status.exception)) } } diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessInteracRefundAction.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessInteracRefundAction.kt deleted file mode 100644 index e05f99b4ca0d..000000000000 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessInteracRefundAction.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.woocommerce.android.cardreader.internal.payments.actions - -import com.stripe.stripeterminal.external.callable.RefundCallback -import com.stripe.stripeterminal.external.models.Refund -import com.stripe.stripeterminal.external.models.TerminalException -import com.woocommerce.android.cardreader.internal.wrappers.TerminalWrapper -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow - -internal class ProcessInteracRefundAction(private val terminal: TerminalWrapper) { - sealed class ProcessRefundStatus { - data class Success(val refund: Refund) : ProcessRefundStatus() - data class Failure(val exception: TerminalException) : ProcessRefundStatus() - } - - fun processRefund(): Flow { - return callbackFlow { - terminal.processRefund(object : RefundCallback { - override fun onSuccess(refund: Refund) { - trySend(ProcessRefundStatus.Success(refund)) - close() - } - - override fun onFailure(e: TerminalException) { - trySend(ProcessRefundStatus.Failure(e)) - close() - } - }) - awaitClose() - } - } -} diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/actions/CollectInteracRefundAction.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessRefundAction.kt similarity index 62% rename from libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/actions/CollectInteracRefundAction.kt rename to libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessRefundAction.kt index 1b9a0bf2b198..534dd62c7696 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/actions/CollectInteracRefundAction.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessRefundAction.kt @@ -1,7 +1,9 @@ package com.woocommerce.android.cardreader.internal.payments.actions import com.stripe.stripeterminal.external.callable.Callback +import com.stripe.stripeterminal.external.callable.RefundCallback import com.stripe.stripeterminal.external.models.CollectRefundConfiguration +import com.stripe.stripeterminal.external.models.Refund import com.stripe.stripeterminal.external.models.RefundParameters import com.stripe.stripeterminal.external.models.TerminalException import com.woocommerce.android.cardreader.internal.wrappers.TerminalWrapper @@ -9,28 +11,28 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow -internal class CollectInteracRefundAction(private val terminal: TerminalWrapper) { - sealed class CollectInteracRefundStatus { - object Success : CollectInteracRefundStatus() - data class Failure(val exception: TerminalException) : CollectInteracRefundStatus() +internal class ProcessRefundAction(private val terminal: TerminalWrapper) { + sealed class ProcessRefundStatus { + data class Success(val refund: Refund) : ProcessRefundStatus() + data class Failure(val exception: TerminalException) : ProcessRefundStatus() } - fun collectRefund( + fun processRefund( refundParameters: RefundParameters, refundConfiguration: CollectRefundConfiguration - ): Flow { + ): Flow { return callbackFlow { - val cancelable = terminal.refundPayment( + val cancelable = terminal.processRefund( refundParameters, refundConfiguration, - object : Callback { - override fun onSuccess() { - trySend(CollectInteracRefundStatus.Success) + object : RefundCallback { + override fun onSuccess(refund: Refund) { + trySend(ProcessRefundStatus.Success(refund)) close() } override fun onFailure(e: TerminalException) { - trySend(CollectInteracRefundStatus.Failure(e)) + trySend(ProcessRefundStatus.Failure(e)) close() } } diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/wrappers/TerminalWrapper.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/wrappers/TerminalWrapper.kt index 44dcb11d9d8b..59e644e201ef 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/wrappers/TerminalWrapper.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/wrappers/TerminalWrapper.kt @@ -87,16 +87,11 @@ internal class TerminalWrapper { fun cancelPayment(paymentIntent: PaymentIntent, callback: PaymentIntentCallback) = Terminal.getInstance().cancelPaymentIntent(paymentIntent, callback) - @Suppress("DEPRECATION") - fun refundPayment( + fun processRefund( refundParameters: RefundParameters, refundConfiguration: CollectRefundConfiguration, - callback: Callback - ) = Terminal.getInstance().collectRefundPaymentMethod(refundParameters, refundConfiguration, callback) - - @Suppress("DEPRECATION") - fun processRefund(callback: RefundCallback) = - Terminal.getInstance().confirmRefund(callback) + callback: RefundCallback + ): Cancelable = Terminal.getInstance().processRefund(refundParameters, refundConfiguration, callback) fun installSoftwareUpdate() = Terminal.getInstance().installAvailableUpdate() diff --git a/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/InteracRefundManagerTest.kt b/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/InteracRefundManagerTest.kt index f7cdfa9dfbd3..7f1879c20e0c 100644 --- a/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/InteracRefundManagerTest.kt +++ b/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/InteracRefundManagerTest.kt @@ -1,8 +1,7 @@ package com.woocommerce.android.cardreader.internal.payments import com.woocommerce.android.cardreader.internal.CardReaderBaseUnitTest -import com.woocommerce.android.cardreader.internal.payments.actions.CollectInteracRefundAction -import com.woocommerce.android.cardreader.internal.payments.actions.ProcessInteracRefundAction +import com.woocommerce.android.cardreader.internal.payments.actions.ProcessRefundAction import com.woocommerce.android.cardreader.payments.CardInteracRefundStatus import com.woocommerce.android.cardreader.payments.CardInteracRefundStatus.RefundStatusErrorType.DeclinedByBackendError import com.woocommerce.android.cardreader.payments.CardInteracRefundStatus.RefundStatusErrorType.Generic @@ -24,10 +23,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.junit.MockitoJUnitRunner import org.mockito.kotlin.any -import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.mock -import org.mockito.kotlin.never -import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.math.BigDecimal import kotlin.reflect.KClass @@ -43,22 +39,19 @@ private const val TIMEOUT = 1000L class InteracRefundManagerTest : CardReaderBaseUnitTest() { private lateinit var manager: InteracRefundManager - private val collectInteracRefundAction: CollectInteracRefundAction = mock() - private val processInteracRefundAction: ProcessInteracRefundAction = mock() + private val processRefundAction: ProcessRefundAction = mock() private val refundErrorMapper: RefundErrorMapper = mock() private val paymentsUtils: PaymentUtils = mock() private val expectedInteracRefundSequence = listOf( CardInteracRefundStatus.CollectingInteracRefund::class, - CardInteracRefundStatus.ProcessingInteracRefund::class, CardInteracRefundStatus.InteracRefundSuccess::class ) @Before fun setUp() = testBlocking { manager = InteracRefundManager( - collectInteracRefundAction, - processInteracRefundAction, + processRefundAction, refundErrorMapper, paymentsUtils, ) @@ -73,34 +66,21 @@ class InteracRefundManagerTest : CardReaderBaseUnitTest() { } @Test - fun `given collect interac refund success, when refund starts, then ProcessingInteracRefund is emitted`() = + fun `given process refund success, when refund starts, then InteracRefundSuccess is emitted`() = testBlocking { - whenever(collectInteracRefundAction.collectRefund(anyOrNull(), any())) - .thenReturn(flow { emit(CollectInteracRefundAction.CollectInteracRefundStatus.Success) }) + whenever(processRefundAction.processRefund(any(), any())) + .thenReturn(flow { emit(ProcessRefundAction.ProcessRefundStatus.Success(mock())) }) val result = manager.refundInteracPayment(createRefundParams(), refundConfig) - .takeUntil(CardInteracRefundStatus.ProcessingInteracRefund::class).toList() + .takeUntil(CardInteracRefundStatus.InteracRefundSuccess::class).toList() - assertThat(result.last()).isInstanceOf(CardInteracRefundStatus.ProcessingInteracRefund::class.java) + assertThat(result.last()).isInstanceOf(CardInteracRefundStatus.InteracRefundSuccess::class.java) } @Test - fun `given collect interac refund failure, when refund starts, then ProcessingInteracRefund is NOT emitted`() = + fun `given process refund failure, when refund starts, then failure is emitted`() = testBlocking { - whenever(collectInteracRefundAction.collectRefund(anyOrNull(), any())) - .thenReturn(flow { emit(CollectInteracRefundAction.CollectInteracRefundStatus.Failure(mock())) }) - whenever(refundErrorMapper.mapTerminalError(any(), any())) - .thenReturn(CardInteracRefundStatus.InteracRefundFailure(Generic, "", mock())) - val result = manager.refundInteracPayment(createRefundParams(), refundConfig).toList() - - assertThat(result.last()).isNotInstanceOf(CardInteracRefundStatus.ProcessingInteracRefund::class.java) - verify(processInteracRefundAction, never()).processRefund() - } - - @Test - fun `given collect interac refund failure, when refund starts, then failure is emitted`() = - testBlocking { - whenever(collectInteracRefundAction.collectRefund(anyOrNull(), any())) - .thenReturn(flow { emit(CollectInteracRefundAction.CollectInteracRefundStatus.Failure(mock())) }) + whenever(processRefundAction.processRefund(any(), any())) + .thenReturn(flow { emit(ProcessRefundAction.ProcessRefundStatus.Failure(mock())) }) whenever(refundErrorMapper.mapTerminalError(any(), any())) .thenReturn( CardInteracRefundStatus.InteracRefundFailure(Generic, "", mock()) @@ -111,11 +91,11 @@ class InteracRefundManagerTest : CardReaderBaseUnitTest() { } @Test - fun `given collect interac refund failure, when refund starts, then failure message is captured`() = + fun `given process refund failure, when refund starts, then failure message is captured`() = testBlocking { val expectedErrorMessage = "Generic Error" - whenever(collectInteracRefundAction.collectRefund(anyOrNull(), any())) - .thenReturn(flow { emit(CollectInteracRefundAction.CollectInteracRefundStatus.Failure(mock())) }) + whenever(processRefundAction.processRefund(any(), any())) + .thenReturn(flow { emit(ProcessRefundAction.ProcessRefundStatus.Failure(mock())) }) whenever(refundErrorMapper.mapTerminalError(any(), any())) .thenReturn( CardInteracRefundStatus.InteracRefundFailure(Generic, expectedErrorMessage, mock()) @@ -129,11 +109,11 @@ class InteracRefundManagerTest : CardReaderBaseUnitTest() { } @Test - fun `given collect interac refund failure, when refund starts, then failure type is captured`() = + fun `given process refund failure, when refund starts, then failure type is captured`() = testBlocking { val expectedErrorType = DeclinedByBackendError.Unknown - whenever(collectInteracRefundAction.collectRefund(anyOrNull(), any())) - .thenReturn(flow { emit(CollectInteracRefundAction.CollectInteracRefundStatus.Failure(mock())) }) + whenever(processRefundAction.processRefund(any(), any())) + .thenReturn(flow { emit(ProcessRefundAction.ProcessRefundStatus.Failure(mock())) }) whenever(refundErrorMapper.mapTerminalError(any(), any())) .thenReturn( CardInteracRefundStatus.InteracRefundFailure(expectedErrorType, "Declined", mock()) @@ -147,11 +127,11 @@ class InteracRefundManagerTest : CardReaderBaseUnitTest() { } @Test - fun `given collect interac refund failure, when refund starts, then refund params is captured`() = + fun `given process refund failure, when refund starts, then refund params is captured`() = testBlocking { val expectedRefundParams = createRefundParams() - whenever(collectInteracRefundAction.collectRefund(anyOrNull(), any())) - .thenReturn(flow { emit(CollectInteracRefundAction.CollectInteracRefundStatus.Failure(mock())) }) + whenever(processRefundAction.processRefund(any(), any())) + .thenReturn(flow { emit(ProcessRefundAction.ProcessRefundStatus.Failure(mock())) }) whenever(refundErrorMapper.mapTerminalError(any(), any())) .thenReturn( CardInteracRefundStatus.InteracRefundFailure( @@ -169,15 +149,15 @@ class InteracRefundManagerTest : CardReaderBaseUnitTest() { } @Test - fun `given collect interac refund failure, when refund starts, then flow terminates`() = + fun `given process refund failure, when refund starts, then flow terminates`() = testBlocking { - whenever(collectInteracRefundAction.collectRefund(anyOrNull(), any())) - .thenReturn(flow { emit(CollectInteracRefundAction.CollectInteracRefundStatus.Failure(mock())) }) + whenever(processRefundAction.processRefund(any(), any())) + .thenReturn(flow { emit(ProcessRefundAction.ProcessRefundStatus.Failure(mock())) }) val result = withTimeoutOrNull(TIMEOUT) { manager.refundInteracPayment(createRefundParams(), refundConfig).toList() } - assertThat(result).isNotNull // verify the flow did not timeout + assertThat(result).isNotNull } private fun Flow.takeUntil(untilStatus: KClass<*>): Flow = diff --git a/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/actions/CollectInteracRefundActionTest.kt b/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/actions/CollectInteracRefundActionTest.kt deleted file mode 100644 index 3e6dd6b08ad3..000000000000 --- a/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/actions/CollectInteracRefundActionTest.kt +++ /dev/null @@ -1,98 +0,0 @@ -package com.woocommerce.android.cardreader.internal.payments.actions - -import com.stripe.stripeterminal.external.callable.Callback -import com.stripe.stripeterminal.external.callable.Cancelable -import com.woocommerce.android.cardreader.internal.CardReaderBaseUnitTest -import com.woocommerce.android.cardreader.internal.payments.actions.CollectInteracRefundAction.CollectInteracRefundStatus.Failure -import com.woocommerce.android.cardreader.internal.payments.actions.CollectInteracRefundAction.CollectInteracRefundStatus.Success -import com.woocommerce.android.cardreader.internal.wrappers.TerminalWrapper -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.launch -import org.assertj.core.api.Assertions.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner -import org.mockito.kotlin.any -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever - -@Suppress("DoNotMockDataClass") -@ExperimentalCoroutinesApi -@RunWith(MockitoJUnitRunner::class) -class CollectInteracRefundActionTest : CardReaderBaseUnitTest() { - private lateinit var action: CollectInteracRefundAction - private val terminal: TerminalWrapper = mock() - - @Before - fun setUp() { - action = CollectInteracRefundAction(terminal) - } - - @Test - fun `when collecting interac refund succeeds, then Success is emitted`() = testBlocking { - whenever(terminal.refundPayment(any(), any(), any())).thenAnswer { - (it.arguments[2] as Callback).onSuccess() - mock() - } - - val result = action.collectRefund(mock(), mock()).first() - - assertThat(result).isExactlyInstanceOf(Success::class.java) - } - - @Test - fun `when collecting interac refund fails, then Failure is emitted`() = testBlocking { - whenever(terminal.refundPayment(any(), any(), any())).thenAnswer { - (it.arguments[2] as Callback).onFailure(mock()) - mock() - } - - val result = action.collectRefund(mock(), mock()).first() - - assertThat(result).isExactlyInstanceOf(Failure::class.java) - } - - @Test - fun `when collecting interac refund succeeds, then flow is terminated`() = testBlocking { - whenever(terminal.refundPayment(any(), any(), any())).thenAnswer { - (it.arguments[2] as Callback).onSuccess() - mock() - } - - val result = action.collectRefund(mock(), mock()).toList() - - assertThat(result.size).isEqualTo(1) - } - - @Test - fun `when collecting interac refund fails, then flow is terminated`() = testBlocking { - whenever(terminal.refundPayment(any(), any(), any())).thenAnswer { - (it.arguments[2] as Callback).onFailure(mock()) - mock() - } - - val result = action.collectRefund(mock(), mock()).toList() - - assertThat(result.size).isEqualTo(1) - } - - @Test - fun `given flow not terminated, when job canceled, then interac refund gets canceled`() = testBlocking { - val cancelable = mock() - whenever(cancelable.isCompleted).thenReturn(false) - whenever(terminal.refundPayment(any(), any(), any())).thenAnswer { cancelable } - val job = launch { - action.collectRefund(mock(), mock()).collect { } - } - - job.cancel() - joinAll(job) - - verify(cancelable).cancel(any()) - } -} diff --git a/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessInteracRefundActionTest.kt b/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessInteracRefundActionTest.kt deleted file mode 100644 index 83ff9c4318b7..000000000000 --- a/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessInteracRefundActionTest.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.woocommerce.android.cardreader.internal.payments.actions - -import com.stripe.stripeterminal.external.callable.Cancelable -import com.stripe.stripeterminal.external.callable.RefundCallback -import com.woocommerce.android.cardreader.internal.CardReaderBaseUnitTest -import com.woocommerce.android.cardreader.internal.payments.actions.ProcessInteracRefundAction.ProcessRefundStatus -import com.woocommerce.android.cardreader.internal.wrappers.TerminalWrapper -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.toList -import org.assertj.core.api.Assertions.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner -import org.mockito.kotlin.any -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever - -@Suppress("DoNotMockDataClass") -@ExperimentalCoroutinesApi -@RunWith(MockitoJUnitRunner::class) -class ProcessInteracRefundActionTest : CardReaderBaseUnitTest() { - private lateinit var action: ProcessInteracRefundAction - private val terminal: TerminalWrapper = mock() - - @Before - fun setUp() { - action = ProcessInteracRefundAction(terminal) - } - - @Test - fun `when processing interac refund succeeds, then Success is emitted`() = testBlocking { - whenever(terminal.processRefund(any())).thenAnswer { - (it.arguments[0] as RefundCallback).onSuccess(mock()) - mock() - } - - val result = action.processRefund().first() - - assertThat(result).isExactlyInstanceOf(ProcessRefundStatus.Success::class.java) - } - - @Test - fun `when processing interac refund fails, then Failure is emitted`() = testBlocking { - whenever(terminal.processRefund(any())).thenAnswer { - (it.arguments[0] as RefundCallback).onFailure(mock()) - mock() - } - - val result = action.processRefund().first() - - assertThat(result).isExactlyInstanceOf(ProcessRefundStatus.Failure::class.java) - } - - @Test - fun `when processing interac refund succeeds, then flow is terminated`() = testBlocking { - whenever(terminal.processRefund(any())).thenAnswer { - (it.arguments[0] as RefundCallback).onSuccess(mock()) - mock() - } - - val result = action.processRefund().toList() - - assertThat(result.size).isEqualTo(1) - } - - @Test - fun `when processing interac refund fails, then flow is terminated`() = testBlocking { - whenever(terminal.processRefund(any())).thenAnswer { - (it.arguments[0] as RefundCallback).onFailure(mock()) - mock() - } - - val result = action.processRefund().toList() - - assertThat(result.size).isEqualTo(1) - } -} diff --git a/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessRefundActionTest.kt b/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessRefundActionTest.kt new file mode 100644 index 000000000000..d918a2962ec5 --- /dev/null +++ b/libs/cardreader/src/test/java/com/woocommerce/android/cardreader/internal/payments/actions/ProcessRefundActionTest.kt @@ -0,0 +1,110 @@ +package com.woocommerce.android.cardreader.internal.payments.actions + +import com.stripe.stripeterminal.external.callable.Cancelable +import com.stripe.stripeterminal.external.callable.RefundCallback +import com.stripe.stripeterminal.external.models.Refund +import com.woocommerce.android.cardreader.internal.CardReaderBaseUnitTest +import com.woocommerce.android.cardreader.internal.payments.actions.ProcessRefundAction.ProcessRefundStatus.Failure +import com.woocommerce.android.cardreader.internal.payments.actions.ProcessRefundAction.ProcessRefundStatus.Success +import com.woocommerce.android.cardreader.internal.wrappers.TerminalWrapper +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@Suppress("DoNotMockDataClass") +@ExperimentalCoroutinesApi +internal class ProcessRefundActionTest : CardReaderBaseUnitTest() { + private lateinit var action: ProcessRefundAction + private val terminal: TerminalWrapper = mock() + + @Before + fun setUp() { + action = ProcessRefundAction(terminal) + } + + @Test + fun `when process refund succeeds, then Success is emitted`() = testBlocking { + whenever(terminal.processRefund(any(), any(), any())).thenAnswer { + (it.arguments[2] as RefundCallback).onSuccess(mock()) + mock() + } + + val result = action.processRefund(mock(), mock()).first() + + assertThat(result).isExactlyInstanceOf(Success::class.java) + } + + @Test + fun `when process refund fails, then Failure is emitted`() = testBlocking { + whenever(terminal.processRefund(any(), any(), any())).thenAnswer { + (it.arguments[2] as RefundCallback).onFailure(mock()) + mock() + } + + val result = action.processRefund(mock(), mock()).first() + + assertThat(result).isExactlyInstanceOf(Failure::class.java) + } + + @Test + fun `when process refund succeeds, then refund is returned`() = testBlocking { + val refund = mock() + whenever(terminal.processRefund(any(), any(), any())).thenAnswer { + (it.arguments[2] as RefundCallback).onSuccess(refund) + mock() + } + + val result = action.processRefund(mock(), mock()).first() + + assertThat((result as Success).refund).isEqualTo(refund) + } + + @Test + fun `when process refund succeeds, then flow is terminated`() = testBlocking { + whenever(terminal.processRefund(any(), any(), any())).thenAnswer { + (it.arguments[2] as RefundCallback).onSuccess(mock()) + mock() + } + + val result = action.processRefund(mock(), mock()).toList() + + assertThat(result.size).isEqualTo(1) + } + + @Test + fun `when process refund fails, then flow is terminated`() = testBlocking { + whenever(terminal.processRefund(any(), any(), any())).thenAnswer { + (it.arguments[2] as RefundCallback).onFailure(mock()) + mock() + } + + val result = action.processRefund(mock(), mock()).toList() + + assertThat(result.size).isEqualTo(1) + } + + @Test + fun `when job canceled, then process refund gets canceled`() = + testBlocking { + val cancelable = mock() + whenever(cancelable.isCompleted).thenReturn(false) + whenever(terminal.processRefund(any(), any(), any())).thenAnswer { cancelable } + val job = launch { + action.processRefund(mock(), mock()).collect { } + } + + job.cancel() + joinAll(job) + + verify(cancelable).cancel(any()) + } +}