Skip to content

Commit eec8977

Browse files
committed
DRY tests
1 parent 26b3b35 commit eec8977

File tree

1 file changed

+58
-74
lines changed

1 file changed

+58
-74
lines changed

payments-core/src/test/java/com/stripe/android/payments/paymentlauncher/PaymentLauncherViewModelTest.kt

Lines changed: 58 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -661,117 +661,101 @@ class PaymentLauncherViewModelTest {
661661

662662
@Test
663663
fun `verify only one finished event is sent when result is already set`() = runTest {
664-
// Setup: Confirm succeeds without requiring action
665664
whenever(paymentIntent.requiresAction()).thenReturn(false)
666665
val viewModel = createViewModel()
667666

668-
// First confirm call - should send finished event
669667
viewModel.confirmStripeIntent(confirmPaymentIntentParams, authHost)
670-
671-
// Verify the result was set
672668
assertThat(viewModel.internalPaymentResult.value).isInstanceOf(InternalPaymentResult.Completed::class.java)
673669

674-
// Verify finished event was sent once
675-
verify(analyticsRequestFactory, times(1)).createRequest(
676-
eq(PaymentAnalyticsEvent.PaymentLauncherConfirmFinished),
677-
additionalParams = argThat { params ->
678-
params["status"] == "succeeded"
679-
}
680-
)
681-
682-
// Try to process a payment flow result after the result is already set
683-
// This should NOT send another finished event due to the guard
684-
val paymentFlowResult = mock<PaymentFlowResult.Unvalidated>()
685-
whenever(paymentIntentFlowResultProcessor.processResult(eq(paymentFlowResult)))
686-
.thenReturn(Result.success(succeededPaymentResult))
687-
viewModel.onPaymentFlowResult(paymentFlowResult)
688-
689-
// Verify finished event was still only sent once (guard prevented duplicate)
690-
verify(analyticsRequestFactory, times(1)).createRequest(
691-
eq(PaymentAnalyticsEvent.PaymentLauncherConfirmFinished),
692-
additionalParams = argThat { params ->
693-
params["status"] == "succeeded"
694-
}
670+
verifyGuardPreventsDuplicateFinishedEvents(
671+
viewModel = viewModel,
672+
expectedEvent = PaymentAnalyticsEvent.PaymentLauncherConfirmFinished,
673+
expectedStatus = "succeeded",
674+
expectedResultType = InternalPaymentResult.Completed::class.java,
675+
secondResult = succeededPaymentResult
695676
)
696-
697-
// Verify the result value didn't change
698-
assertThat(viewModel.internalPaymentResult.value).isInstanceOf(InternalPaymentResult.Completed::class.java)
699677
}
700678

701679
@Test
702-
fun `verify guard prevents duplicate events for failed results`() = runTest {
703-
// Setup: Confirm fails
704-
whenever(stripeApiRepository.confirmPaymentIntent(any(), any(), any()))
705-
.thenReturn(Result.failure(APIConnectionException()))
680+
fun `verify guard blocks different result types`() = runTest {
681+
whenever(paymentIntent.requiresAction()).thenReturn(false)
706682
val viewModel = createViewModel()
707683

708-
// Confirm call - should send finished event with failed status
709684
viewModel.confirmStripeIntent(confirmPaymentIntentParams, authHost)
685+
assertThat(viewModel.internalPaymentResult.value).isInstanceOf(InternalPaymentResult.Completed::class.java)
710686

711-
// Verify failed result was set
712-
assertThat(viewModel.internalPaymentResult.value).isInstanceOf(InternalPaymentResult.Failed::class.java)
713-
714-
// Verify finished event was sent once with failed status
715-
verify(analyticsRequestFactory, times(1)).createRequest(
716-
eq(PaymentAnalyticsEvent.PaymentLauncherConfirmFinished),
717-
additionalParams = argThat { params ->
718-
params["status"] == "failed"
719-
}
720-
)
721-
722-
// Try to send another result - should be blocked by guard
723-
val paymentFlowResult = mock<PaymentFlowResult.Unvalidated>()
724-
whenever(paymentIntentFlowResultProcessor.processResult(eq(paymentFlowResult)))
725-
.thenReturn(Result.success(canceledPaymentResult))
726-
viewModel.onPaymentFlowResult(paymentFlowResult)
727-
728-
// Verify no additional finished events were sent
729-
verify(analyticsRequestFactory, times(1)).createRequest(
730-
eq(PaymentAnalyticsEvent.PaymentLauncherConfirmFinished),
731-
additionalParams = any()
687+
verifyGuardPreventsDuplicateFinishedEvents(
688+
viewModel = viewModel,
689+
expectedEvent = PaymentAnalyticsEvent.PaymentLauncherConfirmFinished,
690+
expectedStatus = "succeeded",
691+
expectedResultType = InternalPaymentResult.Completed::class.java,
692+
secondResult = failedPaymentResult
732693
)
733-
734-
// Verify the result value remained as the first failure
735-
assertThat(viewModel.internalPaymentResult.value).isInstanceOf(InternalPaymentResult.Failed::class.java)
736694
}
737695

738696
@Test
739697
fun `verify guard works for next action flows`() = runTest {
740698
val savedStateHandle = SavedStateHandle()
741699
val viewModel = createViewModel(savedStateHandle = savedStateHandle)
742700

743-
// Start next action handling
744701
viewModel.handleNextActionForStripeIntent(CLIENT_SECRET, authHost)
745702

746-
// First result callback - should send finished event
747-
val paymentFlowResult1 = mock<PaymentFlowResult.Unvalidated>()
748-
whenever(paymentIntentFlowResultProcessor.processResult(eq(paymentFlowResult1)))
703+
val paymentFlowResult = mock<PaymentFlowResult.Unvalidated>()
704+
whenever(paymentIntentFlowResultProcessor.processResult(eq(paymentFlowResult)))
749705
.thenReturn(Result.success(succeededPaymentResult))
750-
viewModel.onPaymentFlowResult(paymentFlowResult1)
751-
752-
// Verify result was set
706+
viewModel.onPaymentFlowResult(paymentFlowResult)
753707
assertThat(viewModel.internalPaymentResult.value).isInstanceOf(InternalPaymentResult.Completed::class.java)
754708

755-
// Verify finished event was sent once
709+
verifyGuardPreventsDuplicateFinishedEvents(
710+
viewModel = viewModel,
711+
expectedEvent = PaymentAnalyticsEvent.PaymentLauncherNextActionFinished,
712+
expectedStatus = "succeeded",
713+
expectedResultType = InternalPaymentResult.Completed::class.java,
714+
secondResult = succeededPaymentResult
715+
)
716+
}
717+
718+
private fun verifyGuardPreventsDuplicateFinishedEvents(
719+
viewModel: PaymentLauncherViewModel,
720+
expectedEvent: PaymentAnalyticsEvent,
721+
expectedStatus: String,
722+
expectedResultType: Class<out InternalPaymentResult>,
723+
secondResult: StripeIntentResult<*>
724+
) {
725+
// Verify initial finished event was sent once
756726
verify(analyticsRequestFactory, times(1)).createRequest(
757-
eq(PaymentAnalyticsEvent.PaymentLauncherNextActionFinished),
727+
eq(expectedEvent),
758728
additionalParams = argThat { params ->
759-
params["status"] == "succeeded"
729+
params["status"] == expectedStatus
760730
}
761731
)
762732

763-
// Second result callback (simulating multiple callbacks from a buggy handler)
764-
// Should be blocked by guard
765-
val paymentFlowResult2 = mock<PaymentFlowResult.Unvalidated>()
766-
whenever(paymentIntentFlowResultProcessor.processResult(eq(paymentFlowResult2)))
767-
.thenReturn(Result.success(succeededPaymentResult))
768-
viewModel.onPaymentFlowResult(paymentFlowResult2)
733+
// Try to send another result - should be blocked by guard
734+
val paymentFlowResult = mock<PaymentFlowResult.Unvalidated>()
735+
whenever(paymentIntentFlowResultProcessor.processResult(eq(paymentFlowResult)))
736+
.thenReturn(Result.success(secondResult))
737+
viewModel.onPaymentFlowResult(paymentFlowResult)
769738

770739
// Verify still only one finished event was sent
771740
verify(analyticsRequestFactory, times(1)).createRequest(
772-
eq(PaymentAnalyticsEvent.PaymentLauncherNextActionFinished),
741+
eq(expectedEvent),
773742
additionalParams = any()
774743
)
744+
745+
// Verify the result value didn't change
746+
assertThat(viewModel.internalPaymentResult.value).isInstanceOf(expectedResultType)
747+
}
748+
749+
private fun finishedAnalyticsEventSent(
750+
status: String = "succeeded",
751+
times: Int = 1
752+
) {
753+
verify(analyticsRequestFactory, times(times)).createRequest(
754+
eq(PaymentAnalyticsEvent.PaymentLauncherConfirmFinished),
755+
additionalParams = argThat { params ->
756+
params["status"] == status
757+
}
758+
)
775759
}
776760

777761
companion object {

0 commit comments

Comments
 (0)