Skip to content

Commit

Permalink
Make StripeError serializable (#2094)
Browse files Browse the repository at this point in the history
`StripeError` was previously not `Serializable`, which
was causing a `NotSerializableException` to be thrown
when a `StripeException` object was being serialized.

Mark `StripeError` as `Serializable` and update
`PaymentRelayStarterTest` to verify.

Fixes #2092
  • Loading branch information
mshafrir-stripe authored Jan 23, 2020
1 parent 612cf1c commit 8975175
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.stripe.android

import android.os.Bundle
import android.os.Parcelable
import com.stripe.android.model.Source
import com.stripe.android.model.StripeIntent
import com.stripe.android.view.AuthActivityStarter
import com.stripe.android.view.PaymentRelayActivity
import com.stripe.android.view.StripeIntentResultExtras
import kotlinx.android.parcel.Parcelize

/**
* Starts an instance of [PaymentRelayStarter].
Expand Down Expand Up @@ -38,11 +40,12 @@ internal interface PaymentRelayStarter : AuthActivityStarter<PaymentRelayStarter
}
}

@Parcelize
data class Args internal constructor(
val stripeIntent: StripeIntent? = null,
val source: Source? = null,
val exception: Exception? = null
) {
) : Parcelable {
internal companion object {
@JvmSynthetic
internal fun create(stripeIntent: StripeIntent): Args {
Expand Down
3 changes: 2 additions & 1 deletion stripe/src/main/java/com/stripe/android/StripeError.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.stripe.android

import com.stripe.android.model.StripeModel
import java.io.Serializable
import kotlinx.android.parcel.Parcelize

/**
Expand Down Expand Up @@ -75,4 +76,4 @@ data class StripeError internal constructor(
* [doc_url](https://stripe.com/docs/api/errors#errors-doc_url)
*/
val docUrl: String? = null
) : StripeModel
) : StripeModel, Serializable
86 changes: 56 additions & 30 deletions stripe/src/test/java/com/stripe/android/PaymentRelayStarterTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,88 @@ package com.stripe.android

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.nhaarman.mockitokotlin2.KArgumentCaptor
import com.nhaarman.mockitokotlin2.argumentCaptor
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.stripe.android.exception.PermissionException
import com.stripe.android.exception.StripeException
import com.stripe.android.model.PaymentIntentFixtures
import com.stripe.android.utils.ParcelUtils
import com.stripe.android.view.AuthActivityStarter
import com.stripe.android.view.StripeIntentResultExtras
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class PaymentRelayStarterTest {
@Mock
private lateinit var activity: Activity

private lateinit var intentArgumentCaptor: KArgumentCaptor<Intent>

private lateinit var starter: PaymentRelayStarter

@BeforeTest
fun setup() {
MockitoAnnotations.initMocks(this)
intentArgumentCaptor = argumentCaptor()
starter = PaymentRelayStarter.create(
AuthActivityStarter.Host.create(activity),
500
)
}
private val activity: Activity = mock()
private val intentArgumentCaptor: KArgumentCaptor<Intent> = argumentCaptor()
private val starter: PaymentRelayStarter = PaymentRelayStarter.create(
AuthActivityStarter.Host.create(activity),
500
)

@Test
fun start_withPaymentIntent_shouldSetCorrectIntentExtras() {
starter.start(PaymentRelayStarter.Args.create(PaymentIntentFixtures.PI_REQUIRES_MASTERCARD_3DS2))
starter.start(
PaymentRelayStarter.Args.create(PaymentIntentFixtures.PI_REQUIRES_MASTERCARD_3DS2)
)
verify(activity).startActivityForResult(intentArgumentCaptor.capture(), eq(500))
val intent = intentArgumentCaptor.firstValue
assertEquals(PaymentIntentFixtures.PI_REQUIRES_MASTERCARD_3DS2.clientSecret,
intent.getStringExtra(StripeIntentResultExtras.CLIENT_SECRET))
assertFalse(intent.hasExtra(StripeIntentResultExtras.FLOW_OUTCOME))
assertNull(intent.getSerializableExtra(StripeIntentResultExtras.AUTH_EXCEPTION))
val bundle = ParcelUtils.copy(
intentArgumentCaptor.firstValue.extras ?: Bundle(),
Bundle.CREATOR
)
assertEquals(
PaymentIntentFixtures.PI_REQUIRES_MASTERCARD_3DS2.clientSecret,
bundle.getString(StripeIntentResultExtras.CLIENT_SECRET)
)
assertFalse(bundle.containsKey(StripeIntentResultExtras.FLOW_OUTCOME))
assertNull(bundle.getSerializable(StripeIntentResultExtras.AUTH_EXCEPTION))
}

@Test
fun start_withException_shouldSetCorrectIntentExtras() {
val exception = RuntimeException()
starter.start(PaymentRelayStarter.Args.create(exception))
verify(activity).startActivityForResult(intentArgumentCaptor.capture(), eq(500))
val intent = intentArgumentCaptor.firstValue
assertNull(intent.getStringExtra(StripeIntentResultExtras.CLIENT_SECRET))
assertFalse(intent.hasExtra(StripeIntentResultExtras.FLOW_OUTCOME))
assertEquals(exception,
intent.getSerializableExtra(StripeIntentResultExtras.AUTH_EXCEPTION))
val bundle = ParcelUtils.copy(
intentArgumentCaptor.firstValue.extras ?: Bundle(),
Bundle.CREATOR
)
assertNull(bundle.getString(StripeIntentResultExtras.CLIENT_SECRET))
assertFalse(bundle.containsKey(StripeIntentResultExtras.FLOW_OUTCOME))
assertTrue(
bundle.getSerializable(StripeIntentResultExtras.AUTH_EXCEPTION) is RuntimeException
)
}

@Test
fun start_withStripeException_shouldSetCorrectIntentExtras() {
val exception = PermissionException(
stripeError = StripeErrorFixtures.INVALID_REQUEST_ERROR
)
starter.start(PaymentRelayStarter.Args.create(exception))
verify(activity).startActivityForResult(intentArgumentCaptor.capture(), eq(500))
val bundle = ParcelUtils.copy(
intentArgumentCaptor.firstValue.extras ?: Bundle(),
Bundle.CREATOR
)
assertNull(bundle.getString(StripeIntentResultExtras.CLIENT_SECRET))
assertFalse(bundle.containsKey(StripeIntentResultExtras.FLOW_OUTCOME))

val expectedException =
bundle.getSerializable(StripeIntentResultExtras.AUTH_EXCEPTION) as StripeException
assertEquals(
exception.stripeError,
expectedException.stripeError
)
}
}
18 changes: 18 additions & 0 deletions stripe/src/test/java/com/stripe/android/utils/ParcelUtils.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.stripe.android.utils

import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable

internal object ParcelUtils {
Expand All @@ -17,5 +18,22 @@ internal object ParcelUtils {
return requireNotNull(bundle.getParcelable(KEY))
}

/**
* @param source the source from which to parcel and unparcel a new object
* @param creator the [Parcelable.Creator]
*
* @return a new [SOURCE] instance based on the original source
*/
@JvmStatic
fun <SOURCE : Parcelable> copy(
source: SOURCE,
creator: Parcelable.Creator<SOURCE>
): SOURCE {
val parcel = Parcel.obtain()
source.writeToParcel(parcel, source.describeContents())
parcel.setDataPosition(0)
return creator.createFromParcel(parcel)
}

private const val KEY = "parcelable"
}

0 comments on commit 8975175

Please sign in to comment.