mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-22 18:55:12 +00:00
Migrate paypal and stripe interactions to durable background jobs.
This commit is contained in:
committed by
Cody Henthorne
parent
ad00e7c5ab
commit
7cc4677120
@@ -4,26 +4,21 @@ import android.app.Application
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.signal.donations.InAppPaymentType
|
||||
import org.signal.donations.PaymentSourceType
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource
|
||||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListId
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
|
||||
import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule
|
||||
import org.thoughtcrime.securesms.testutil.RxPluginsRule
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE, application = Application::class)
|
||||
@@ -80,7 +75,7 @@ class OneTimeInAppPaymentRepositoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given a registered non-self individual, when I verifyRecipientIsAllowedToReceiveAGift, then I expect completion`() {
|
||||
fun `Given a registered non-self individual, when I verifyRecipientIsAllowedToReceiveAGift, then I expect no error`() {
|
||||
val recipientId = RecipientId.from(1L)
|
||||
val recipient = Recipient(
|
||||
id = recipientId,
|
||||
@@ -91,13 +86,10 @@ class OneTimeInAppPaymentRepositoryTest {
|
||||
mockkStatic(Recipient::class)
|
||||
every { Recipient.resolved(recipientId) } returns recipient
|
||||
|
||||
val testObserver = OneTimeInAppPaymentRepository.verifyRecipientIsAllowedToReceiveAGift(recipientId).test()
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
testObserver.assertComplete()
|
||||
OneTimeInAppPaymentRepository.verifyRecipientIsAllowedToReceiveAGiftSync(recipientId)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = DonationError.GiftRecipientVerificationError.SelectedRecipientIsInvalid::class)
|
||||
fun `Given self, when I verifyRecipientIsAllowedToReceiveAGift, then I expect SelectedRecipientIsInvalid`() {
|
||||
val recipientId = RecipientId.from(1L)
|
||||
val recipient = Recipient(
|
||||
@@ -108,7 +100,7 @@ class OneTimeInAppPaymentRepositoryTest {
|
||||
verifyRecipientIsNotAllowedToBeGiftedBadges(recipient)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = DonationError.GiftRecipientVerificationError.SelectedRecipientIsInvalid::class)
|
||||
fun `Given an unregistered individual, when I verifyRecipientIsAllowedToReceiveAGift, then I expect SelectedRecipientIsInvalid`() {
|
||||
val recipientId = RecipientId.from(1L)
|
||||
val recipient = Recipient(
|
||||
@@ -120,7 +112,7 @@ class OneTimeInAppPaymentRepositoryTest {
|
||||
verifyRecipientIsNotAllowedToBeGiftedBadges(recipient)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = DonationError.GiftRecipientVerificationError.SelectedRecipientIsInvalid::class)
|
||||
fun `Given a group, when I verifyRecipientIsAllowedToReceiveAGift, then I expect SelectedRecipientIsInvalid`() {
|
||||
val recipientId = RecipientId.from(1L)
|
||||
val recipient = Recipient(
|
||||
@@ -133,7 +125,7 @@ class OneTimeInAppPaymentRepositoryTest {
|
||||
verifyRecipientIsNotAllowedToBeGiftedBadges(recipient)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = DonationError.GiftRecipientVerificationError.SelectedRecipientIsInvalid::class)
|
||||
fun `Given a call link, when I verifyRecipientIsAllowedToReceiveAGift, then I expect SelectedRecipientIsInvalid`() {
|
||||
val recipientId = RecipientId.from(1L)
|
||||
val recipient = Recipient(
|
||||
@@ -146,7 +138,7 @@ class OneTimeInAppPaymentRepositoryTest {
|
||||
verifyRecipientIsNotAllowedToBeGiftedBadges(recipient)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = DonationError.GiftRecipientVerificationError.SelectedRecipientIsInvalid::class)
|
||||
fun `Given a distribution list, when I verifyRecipientIsAllowedToReceiveAGift, then I expect SelectedRecipientIsInvalid`() {
|
||||
val recipientId = RecipientId.from(1L)
|
||||
val recipient = Recipient(
|
||||
@@ -159,7 +151,7 @@ class OneTimeInAppPaymentRepositoryTest {
|
||||
verifyRecipientIsNotAllowedToBeGiftedBadges(recipient)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = DonationError.GiftRecipientVerificationError.SelectedRecipientIsInvalid::class)
|
||||
fun `Given release notes, when I verifyRecipientIsAllowedToReceiveAGift, then I expect SelectedRecipientIsInvalid`() {
|
||||
val recipientId = RecipientId.from(1L)
|
||||
val recipient = Recipient(
|
||||
@@ -210,68 +202,10 @@ class OneTimeInAppPaymentRepositoryTest {
|
||||
.assertComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given a long running transaction, when I waitForOneTimeRedemption, then I expect DonationPending`() {
|
||||
val inAppPayment = inAppPaymentsTestRule.createInAppPayment(InAppPaymentType.ONE_TIME_DONATION, PaymentSourceType.Stripe.SEPADebit)
|
||||
|
||||
every { SignalDatabase.Companion.inAppPayments.getById(inAppPayment.id) } returns inAppPayment
|
||||
|
||||
val testObserver = OneTimeInAppPaymentRepository
|
||||
.waitForOneTimeRedemption(inAppPayment, "test-intent-id")
|
||||
.test()
|
||||
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
rxRule.defaultScheduler.advanceTimeBy(10, TimeUnit.SECONDS)
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
testObserver
|
||||
.assertError { it is DonationError.BadgeRedemptionError.DonationPending }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given a non long running transaction, when I waitForOneTimeRedemption, then I expect TimeoutWaitingForTokenError`() {
|
||||
val inAppPayment = inAppPaymentsTestRule.createInAppPayment(InAppPaymentType.ONE_TIME_DONATION, PaymentSourceType.Stripe.CreditCard)
|
||||
|
||||
every { SignalDatabase.Companion.inAppPayments.getById(inAppPayment.id) } returns inAppPayment
|
||||
|
||||
val testObserver = OneTimeInAppPaymentRepository
|
||||
.waitForOneTimeRedemption(inAppPayment, "test-intent-id")
|
||||
.test()
|
||||
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
rxRule.defaultScheduler.advanceTimeBy(10, TimeUnit.SECONDS)
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
testObserver
|
||||
.assertError { it is DonationError.BadgeRedemptionError.TimeoutWaitingForTokenError }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given no delays, when I waitForOneTimeRedemption, then I expect happy path`() {
|
||||
val inAppPayment = inAppPaymentsTestRule.createInAppPayment(InAppPaymentType.ONE_TIME_DONATION, PaymentSourceType.Stripe.CreditCard)
|
||||
|
||||
every { InAppPaymentsRepository.observeUpdates(inAppPayment.id) } returns Flowable.just(inAppPayment.copy(state = InAppPaymentTable.State.END))
|
||||
every { SignalDatabase.Companion.inAppPayments.getById(inAppPayment.id) } returns inAppPayment
|
||||
|
||||
val testObserver = OneTimeInAppPaymentRepository
|
||||
.waitForOneTimeRedemption(inAppPayment, "test-intent-id")
|
||||
.test()
|
||||
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
testObserver
|
||||
.assertComplete()
|
||||
}
|
||||
|
||||
private fun verifyRecipientIsNotAllowedToBeGiftedBadges(recipient: Recipient) {
|
||||
mockkStatic(Recipient::class)
|
||||
every { Recipient.resolved(recipient.id) } returns recipient
|
||||
|
||||
val testObserver = OneTimeInAppPaymentRepository.verifyRecipientIsAllowedToReceiveAGift(recipient.id).test()
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
testObserver.assertError {
|
||||
it is DonationError.GiftRecipientVerificationError.SelectedRecipientIsInvalid
|
||||
}
|
||||
OneTimeInAppPaymentRepository.verifyRecipientIsAllowedToReceiveAGiftSync(recipient.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import io.mockk.mockkObject
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkAll
|
||||
import io.mockk.verify
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
@@ -17,10 +16,6 @@ import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.signal.donations.InAppPaymentType
|
||||
import org.signal.donations.PaymentSourceType
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError
|
||||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
|
||||
@@ -28,17 +23,12 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdate
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
||||
import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule
|
||||
import org.thoughtcrime.securesms.testutil.RxPluginsRule
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException
|
||||
import org.whispersystems.signalservice.api.subscriptions.IdempotencyKey
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import org.whispersystems.signalservice.internal.EmptyResponse
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import java.util.Currency
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE, application = Application::class)
|
||||
@@ -98,13 +88,10 @@ class RecurringInAppPaymentRepositoryTest {
|
||||
val initialSubscriber = createSubscriber()
|
||||
val ref = InAppPaymentsTestRule.mockLocalSubscriberAccess(initialSubscriber)
|
||||
|
||||
val testObserver = RecurringInAppPaymentRepository.ensureSubscriberId(
|
||||
RecurringInAppPaymentRepository.ensureSubscriberIdSync(
|
||||
subscriberType = InAppPaymentSubscriberRecord.Type.DONATION,
|
||||
isRotation = false
|
||||
).test()
|
||||
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
testObserver.assertComplete()
|
||||
)
|
||||
|
||||
val newSubscriber = ref.get()
|
||||
|
||||
@@ -116,13 +103,10 @@ class RecurringInAppPaymentRepositoryTest {
|
||||
val initialSubscriber = createSubscriber()
|
||||
val ref = InAppPaymentsTestRule.mockLocalSubscriberAccess(initialSubscriber)
|
||||
|
||||
val testObserver = RecurringInAppPaymentRepository.ensureSubscriberId(
|
||||
RecurringInAppPaymentRepository.ensureSubscriberIdSync(
|
||||
subscriberType = InAppPaymentSubscriberRecord.Type.DONATION,
|
||||
isRotation = true
|
||||
).test()
|
||||
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
testObserver.assertComplete()
|
||||
)
|
||||
|
||||
val newSubscriber = ref.get()
|
||||
|
||||
@@ -160,98 +144,6 @@ class RecurringInAppPaymentRepositoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given no delays, when I setSubscriptionLevel, then I expect happy path`() {
|
||||
val paymentSourceType = PaymentSourceType.Stripe.CreditCard
|
||||
val inAppPayment = inAppPaymentsTestRule.createInAppPayment(InAppPaymentType.RECURRING_DONATION, paymentSourceType)
|
||||
InAppPaymentsTestRule.mockLocalSubscriberAccess(createSubscriber())
|
||||
|
||||
every { SignalStore.inAppPayments.getLevelOperation("500") } returns LevelUpdateOperation(IdempotencyKey.generate(), "500")
|
||||
every { SignalDatabase.inAppPayments.getById(any()) } returns inAppPayment
|
||||
every { InAppPaymentsRepository.observeUpdates(inAppPayment.id) } returns Flowable.just(inAppPayment.copy(state = InAppPaymentTable.State.END))
|
||||
|
||||
val testObserver = RecurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, paymentSourceType).test()
|
||||
val processingUpdate = LevelUpdate.isProcessing.test()
|
||||
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
processingUpdate.assertValues(false, true, false)
|
||||
|
||||
testObserver.assertComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given 10s delay, when I setSubscriptionLevel, then I expect timeout`() {
|
||||
val paymentSourceType = PaymentSourceType.Stripe.CreditCard
|
||||
val inAppPayment = inAppPaymentsTestRule.createInAppPayment(InAppPaymentType.RECURRING_DONATION, paymentSourceType)
|
||||
InAppPaymentsTestRule.mockLocalSubscriberAccess(createSubscriber())
|
||||
|
||||
every { SignalStore.inAppPayments.getLevelOperation("500") } returns LevelUpdateOperation(IdempotencyKey.generate(), "500")
|
||||
every { SignalDatabase.inAppPayments.getById(any()) } returns inAppPayment
|
||||
|
||||
val testObserver = RecurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, paymentSourceType).test()
|
||||
val processingUpdate = LevelUpdate.isProcessing.test()
|
||||
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
processingUpdate.assertValues(false, true, false)
|
||||
|
||||
rxRule.defaultScheduler.advanceTimeBy(10, TimeUnit.SECONDS)
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
testObserver.assertError {
|
||||
it is DonationError.BadgeRedemptionError.TimeoutWaitingForTokenError
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given long running payment type with 10s delay, when I setSubscriptionLevel, then I expect pending`() {
|
||||
val paymentSourceType = PaymentSourceType.Stripe.SEPADebit
|
||||
val inAppPayment = inAppPaymentsTestRule.createInAppPayment(InAppPaymentType.RECURRING_DONATION, paymentSourceType)
|
||||
InAppPaymentsTestRule.mockLocalSubscriberAccess(createSubscriber())
|
||||
|
||||
every { SignalStore.inAppPayments.getLevelOperation("500") } returns LevelUpdateOperation(IdempotencyKey.generate(), "500")
|
||||
every { SignalDatabase.inAppPayments.getById(any()) } returns inAppPayment
|
||||
|
||||
val testObserver = RecurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, paymentSourceType).test()
|
||||
val processingUpdate = LevelUpdate.isProcessing.test()
|
||||
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
processingUpdate.assertValues(false, true, false)
|
||||
|
||||
rxRule.defaultScheduler.advanceTimeBy(10, TimeUnit.SECONDS)
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
testObserver.assertError {
|
||||
it is DonationError.BadgeRedemptionError.DonationPending
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given an execution error, when I setSubscriptionLevel, then I expect the same error`() {
|
||||
val expected = NonSuccessfulResponseCodeException(404)
|
||||
val paymentSourceType = PaymentSourceType.Stripe.SEPADebit
|
||||
val inAppPayment = inAppPaymentsTestRule.createInAppPayment(InAppPaymentType.RECURRING_DONATION, paymentSourceType)
|
||||
InAppPaymentsTestRule.mockLocalSubscriberAccess(createSubscriber())
|
||||
|
||||
every { SignalStore.inAppPayments.getLevelOperation("500") } returns LevelUpdateOperation(IdempotencyKey.generate(), "500")
|
||||
every { SignalDatabase.inAppPayments.getById(any()) } returns inAppPayment
|
||||
every { AppDependencies.donationsService.updateSubscriptionLevel(any(), any(), any(), any(), any()) } returns ServiceResponse.forExecutionError(expected)
|
||||
|
||||
val testObserver = RecurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, paymentSourceType).test()
|
||||
val processingUpdate = LevelUpdate.isProcessing.distinctUntilChanged().test()
|
||||
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
processingUpdate.assertValues(false, true, false)
|
||||
|
||||
rxRule.defaultScheduler.advanceTimeBy(10, TimeUnit.SECONDS)
|
||||
rxRule.defaultScheduler.triggerActions()
|
||||
|
||||
testObserver.assertError(expected)
|
||||
}
|
||||
|
||||
private fun createSubscriber(): InAppPaymentSubscriberRecord {
|
||||
return InAppPaymentSubscriberRecord(
|
||||
subscriberId = SubscriberId.generate(),
|
||||
|
||||
@@ -534,7 +534,7 @@ class InAppPaymentRecurringContextJobTest {
|
||||
|
||||
private fun insertInAppPayment(
|
||||
type: InAppPaymentType = InAppPaymentType.RECURRING_DONATION,
|
||||
state: InAppPaymentTable.State = InAppPaymentTable.State.CREATED,
|
||||
state: InAppPaymentTable.State = InAppPaymentTable.State.TRANSACTING,
|
||||
subscriberId: SubscriberId? = SubscriberId.generate(),
|
||||
paymentSourceType: PaymentSourceType = PaymentSourceType.Stripe.CreditCard,
|
||||
badge: BadgeList.Badge? = null,
|
||||
@@ -551,6 +551,11 @@ class InAppPaymentRecurringContextJobTest {
|
||||
endOfPeriod = null,
|
||||
inAppPaymentData = iap.data.copy(
|
||||
badge = badge,
|
||||
waitForAuth = null,
|
||||
stripeActionComplete = null,
|
||||
payPalActionComplete = null,
|
||||
payPalRequiresAction = null,
|
||||
stripeRequiresAction = null,
|
||||
redemption = redemptionState
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user