Skip to content

Commit

Permalink
Replace FraudDetectionDataRequestExecutor with StripeNetworkClient (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ccen-stripe authored Nov 8, 2021
1 parent 04ccb63 commit fbd6965
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 193 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.stripe.android

import android.content.Context
import com.stripe.android.networking.DefaultFraudDetectionDataRequestExecutor
import com.stripe.android.core.networking.DefaultStripeNetworkClient
import com.stripe.android.core.networking.StripeNetworkClient
import com.stripe.android.core.networking.StripeResponse
import com.stripe.android.core.networking.responseJson
import com.stripe.android.model.parsers.FraudDetectionDataJsonParser
import com.stripe.android.networking.DefaultFraudDetectionDataRequestFactory
import com.stripe.android.networking.FraudDetectionData
import com.stripe.android.networking.FraudDetectionDataRequestExecutor
import com.stripe.android.networking.FraudDetectionDataRequestFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -32,28 +35,26 @@ internal interface FraudDetectionDataRepository {
fun save(fraudDetectionData: FraudDetectionData)
}

private val timestampSupplier: () -> Long = {
Calendar.getInstance().timeInMillis
}

internal class DefaultFraudDetectionDataRepository(
private val localStore: FraudDetectionDataStore,
private val fraudDetectionDataRequestFactory: FraudDetectionDataRequestFactory,
private val fraudDetectionDataRequestExecutor: FraudDetectionDataRequestExecutor,
private val stripeNetworkClient: StripeNetworkClient,
private val workContext: CoroutineContext
) : FraudDetectionDataRepository {
private var cachedFraudDetectionData: FraudDetectionData? = null

private val timestampSupplier: () -> Long = {
Calendar.getInstance().timeInMillis
}

@JvmOverloads
constructor(
context: Context,
workContext: CoroutineContext = Dispatchers.IO
) : this(
localStore = DefaultFraudDetectionDataStore(context, workContext),
fraudDetectionDataRequestFactory = DefaultFraudDetectionDataRequestFactory(context),
fraudDetectionDataRequestExecutor = DefaultFraudDetectionDataRequestExecutor(
workContext = workContext
),
stripeNetworkClient = DefaultStripeNetworkClient(workContext = workContext),
workContext = workContext
)

Expand All @@ -70,11 +71,14 @@ internal class DefaultFraudDetectionDataRepository(
if (localFraudDetectionData == null ||
localFraudDetectionData.isExpired(timestampSupplier())
) {
fraudDetectionDataRequestExecutor.execute(
request = fraudDetectionDataRequestFactory.create(
localFraudDetectionData
)
)
// fraud detection data request failures should be non-fatal
runCatching {
stripeNetworkClient.executeRequest(
fraudDetectionDataRequestFactory.create(
localFraudDetectionData
)
).fraudDetectionData()
}.getOrNull()
} else {
localFraudDetectionData
}
Expand All @@ -98,3 +102,11 @@ internal class DefaultFraudDetectionDataRepository(
localStore.save(fraudDetectionData)
}
}

private val fraudDetectionJsonParser = FraudDetectionDataJsonParser(timestampSupplier)

/**
* Internal extension to convert the [String] body of [StripeResponse] to a [FraudDetectionData].
*/
private fun StripeResponse<String>.fraudDetectionData(): FraudDetectionData? =
takeIf { isOk }?.let { fraudDetectionJsonParser.parse(it.responseJson()) }

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package com.stripe.android
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import com.stripe.android.core.networking.StripeNetworkClient
import com.stripe.android.core.networking.StripeResponse
import com.stripe.android.networking.DefaultFraudDetectionDataRequestFactory
import com.stripe.android.networking.FraudDetectionData
import com.stripe.android.networking.FraudDetectionDataRequest
import com.stripe.android.networking.FraudDetectionDataRequestExecutor
import com.stripe.android.networking.FraudDetectionDataRequestFactory
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
Expand All @@ -16,8 +16,11 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.robolectric.RobolectricTestRunner
import java.net.HttpURLConnection.HTTP_OK
import java.util.Calendar
import java.util.UUID
import java.util.concurrent.TimeUnit
import kotlin.test.AfterTest
import kotlin.test.Test
Expand All @@ -27,7 +30,6 @@ import kotlin.test.Test
class FraudDetectionDataRepositoryTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
private val testDispatcher = TestCoroutineDispatcher()
private val fraudDetectionDataRequestExecutor: FraudDetectionDataRequestExecutor = mock()

@AfterTest
fun after() {
Expand All @@ -46,46 +48,71 @@ class FraudDetectionDataRepositoryTest {
}

@Test
fun `get() when FraudDetectionData is expired should request new remote FraudDetectionData`() {
val expectedFraudDetectionData = createFraudDetectionData()
val repository = DefaultFraudDetectionDataRepository(
localStore = DefaultFraudDetectionDataStore(
context,
testDispatcher
),
fraudDetectionDataRequestFactory = DefaultFraudDetectionDataRequestFactory(context),
fraudDetectionDataRequestExecutor = object : FraudDetectionDataRequestExecutor {
override suspend fun execute(request: FraudDetectionDataRequest) = expectedFraudDetectionData
},
workContext = testDispatcher
)
repository.save(createFraudDetectionData(elapsedTime = -60L))
repository.refresh()
val actualFraudDetectionData = repository.getCached()
fun `get() when FraudDetectionData is expired should request new remote FraudDetectionData`() =
testDispatcher.runBlockingTest {
val mockStripeNetworkClient = mock<StripeNetworkClient>()
val expectedGUID = UUID.randomUUID().toString()
val expectedMUID = UUID.randomUUID().toString()
val expectedSID = UUID.randomUUID().toString()

assertThat(actualFraudDetectionData)
.isEqualTo(expectedFraudDetectionData)
}
whenever(mockStripeNetworkClient.executeRequest(any())).thenReturn(
StripeResponse(
code = HTTP_OK,
body =
"""
{
"guid": "$expectedGUID",
"muid": "$expectedMUID",
"sid": "$expectedSID"
}
""".trimIndent()
)
)

val repository = DefaultFraudDetectionDataRepository(
localStore = DefaultFraudDetectionDataStore(
context,
testDispatcher
),
fraudDetectionDataRequestFactory = DefaultFraudDetectionDataRequestFactory(context),
stripeNetworkClient = mockStripeNetworkClient,
workContext = testDispatcher
)
val expiredFraudDetectionData = createFraudDetectionData(elapsedTime = -60L)
repository.save(expiredFraudDetectionData)
repository.refresh()
val actualFraudDetectionData = repository.getCached()

assertThat(expiredFraudDetectionData.guid).isNotEqualTo(expectedGUID)
assertThat(expiredFraudDetectionData.muid).isNotEqualTo(expectedMUID)
assertThat(expiredFraudDetectionData.sid).isNotEqualTo(expectedSID)

assertThat(requireNotNull(actualFraudDetectionData).guid).isEqualTo(expectedGUID)
assertThat(actualFraudDetectionData.muid).isEqualTo(expectedMUID)
assertThat(actualFraudDetectionData.sid).isEqualTo(expectedSID)
}

@Test
fun `refresh() when advancedFraudSignals is disabled should not fetch FraudDetectionData`() = testDispatcher.runBlockingTest {
Stripe.advancedFraudSignalsEnabled = false
fun `refresh() when advancedFraudSignals is disabled should not fetch FraudDetectionData`() =
testDispatcher.runBlockingTest {
Stripe.advancedFraudSignalsEnabled = false
val mockStripeNetworkClient = mock<StripeNetworkClient>()

val store: FraudDetectionDataStore = mock()
val fraudDetectionDataRequestFactory: FraudDetectionDataRequestFactory = mock()
val repository = DefaultFraudDetectionDataRepository(
localStore = store,
fraudDetectionDataRequestFactory = fraudDetectionDataRequestFactory,
fraudDetectionDataRequestExecutor = fraudDetectionDataRequestExecutor,
workContext = testDispatcher
)
repository.refresh()
val store: FraudDetectionDataStore = mock()
val fraudDetectionDataRequestFactory: FraudDetectionDataRequestFactory = mock()
val repository = DefaultFraudDetectionDataRepository(
localStore = store,
fraudDetectionDataRequestFactory = fraudDetectionDataRequestFactory,
stripeNetworkClient = mockStripeNetworkClient,
workContext = testDispatcher
)
repository.refresh()

verify(store, never()).get()
verify(store, never()).save(any())
verify(fraudDetectionDataRequestFactory, never()).create(any())
verify(fraudDetectionDataRequestExecutor, never()).execute(any())
}
verify(store, never()).get()
verify(store, never()).save(any())
verify(fraudDetectionDataRequestFactory, never()).create(any())
verify(mockStripeNetworkClient, never()).executeRequest(any())
}

private companion object {
fun createFraudDetectionData(elapsedTime: Long = 0L): FraudDetectionData {
Expand Down

This file was deleted.

0 comments on commit fbd6965

Please sign in to comment.