From a74a069dea4e622c4f828026935ecf5d8fa5a8ae Mon Sep 17 00:00:00 2001 From: Devin Morgan Date: Thu, 13 Jun 2024 16:49:56 -0400 Subject: [PATCH] Reuse pin element's context (#252) A small win that reduces the number of args vault submitters must request. Since UI elements have a `context` associated with them, we can use their context instead of needing to drill down a distinct context param Signed-off-by: Devin Morgan --- .../services/vault/AbstractVaultSubmitter.kt | 2 - .../vault/bt/BasisTheoryPinSubmitter.kt | 3 - .../services/vault/vgs/VgsPinSubmitter.kt | 6 +- .../ecom/ui/vault/bt/BTVaultWrapper.kt | 1 - .../ecom/ui/vault/vgs/VGSVaultWrapper.kt | 1 - .../vault/AbstractVaultSubmitterTest.kt | 10 -- .../vault/BasisTheoryPinSubmitterTest.kt | 1 - .../android/vault/VgsPinSubmitterTest.kt | 158 ------------------ 8 files changed, 2 insertions(+), 180 deletions(-) delete mode 100644 forage-android/src/test/java/com/joinforage/forage/android/vault/VgsPinSubmitterTest.kt diff --git a/forage-android/src/main/java/com/joinforage/forage/android/core/services/vault/AbstractVaultSubmitter.kt b/forage-android/src/main/java/com/joinforage/forage/android/core/services/vault/AbstractVaultSubmitter.kt index e055a9e0..4d80e7d4 100644 --- a/forage-android/src/main/java/com/joinforage/forage/android/core/services/vault/AbstractVaultSubmitter.kt +++ b/forage-android/src/main/java/com/joinforage/forage/android/core/services/vault/AbstractVaultSubmitter.kt @@ -1,6 +1,5 @@ package com.joinforage.forage.android.core.services.vault -import android.content.Context import com.joinforage.forage.android.core.services.ForageConstants import com.joinforage.forage.android.core.services.VaultType import com.joinforage.forage.android.core.services.forageapi.encryptkey.EncryptionKeys @@ -33,7 +32,6 @@ internal interface VaultSubmitter { } internal abstract class AbstractVaultSubmitter( - protected val context: Context, protected val foragePinEditText: ForagePinElement, protected val logger: Log ) : VaultSubmitter { diff --git a/forage-android/src/main/java/com/joinforage/forage/android/ecom/services/vault/bt/BasisTheoryPinSubmitter.kt b/forage-android/src/main/java/com/joinforage/forage/android/ecom/services/vault/bt/BasisTheoryPinSubmitter.kt index 4681c8c2..d78a05ab 100644 --- a/forage-android/src/main/java/com/joinforage/forage/android/ecom/services/vault/bt/BasisTheoryPinSubmitter.kt +++ b/forage-android/src/main/java/com/joinforage/forage/android/ecom/services/vault/bt/BasisTheoryPinSubmitter.kt @@ -1,6 +1,5 @@ package com.joinforage.forage.android.ecom.services.vault.bt -import android.content.Context import com.basistheory.android.service.BasisTheoryElements import com.basistheory.android.service.ProxyRequest import com.basistheory.android.view.TextElement @@ -19,12 +18,10 @@ import com.joinforage.forage.android.core.ui.element.ForagePinElement internal typealias BasisTheoryResponse = Result internal class BasisTheoryPinSubmitter( - context: Context, foragePinEditText: ForagePinElement, logger: Log, private val buildVaultProvider: () -> BasisTheoryElements = { buildBt() } ) : AbstractVaultSubmitter( - context = context, foragePinEditText = foragePinEditText, logger = logger ) { diff --git a/forage-android/src/main/java/com/joinforage/forage/android/ecom/services/vault/vgs/VgsPinSubmitter.kt b/forage-android/src/main/java/com/joinforage/forage/android/ecom/services/vault/vgs/VgsPinSubmitter.kt index 1589e7d7..f345c979 100644 --- a/forage-android/src/main/java/com/joinforage/forage/android/ecom/services/vault/vgs/VgsPinSubmitter.kt +++ b/forage-android/src/main/java/com/joinforage/forage/android/ecom/services/vault/vgs/VgsPinSubmitter.kt @@ -24,12 +24,10 @@ import org.json.JSONException import kotlin.coroutines.suspendCoroutine internal class VgsPinSubmitter( - context: Context, foragePinEditText: ForagePinElement, logger: Log, - private val buildVaultProvider: (context: Context) -> VGSCollect = { buildVGSCollect(context) } + private val buildVaultProvider: (context: Context) -> VGSCollect = { buildVGSCollect(foragePinEditText.context) } ) : AbstractVaultSubmitter( - context = context, foragePinEditText = foragePinEditText, logger = logger ) { @@ -37,7 +35,7 @@ internal class VgsPinSubmitter( override suspend fun submitProxyRequest( vaultProxyRequest: VaultProxyRequest ): ForageApiResponse = suspendCoroutine { continuation -> - val vgsCollect = buildVaultProvider(context) + val vgsCollect = buildVaultProvider(foragePinEditText.context) vgsCollect.bindView(foragePinEditText.getTextElement() as VGSEditText) vgsCollect.addOnResponseListeners(object : VgsCollectResponseListener { diff --git a/forage-android/src/main/java/com/joinforage/forage/android/ecom/ui/vault/bt/BTVaultWrapper.kt b/forage-android/src/main/java/com/joinforage/forage/android/ecom/ui/vault/bt/BTVaultWrapper.kt index f161936a..2b5aef09 100644 --- a/forage-android/src/main/java/com/joinforage/forage/android/ecom/ui/vault/bt/BTVaultWrapper.kt +++ b/forage-android/src/main/java/com/joinforage/forage/android/ecom/ui/vault/bt/BTVaultWrapper.kt @@ -115,7 +115,6 @@ internal class BTVaultWrapper @JvmOverloads constructor( foragePinElement: ForagePinElement, logger: Log ): AbstractVaultSubmitter = BasisTheoryPinSubmitter( - foragePinElement.context, foragePinElement, logger ) diff --git a/forage-android/src/main/java/com/joinforage/forage/android/ecom/ui/vault/vgs/VGSVaultWrapper.kt b/forage-android/src/main/java/com/joinforage/forage/android/ecom/ui/vault/vgs/VGSVaultWrapper.kt index 66cfddd8..f0d19da7 100644 --- a/forage-android/src/main/java/com/joinforage/forage/android/ecom/ui/vault/vgs/VGSVaultWrapper.kt +++ b/forage-android/src/main/java/com/joinforage/forage/android/ecom/ui/vault/vgs/VGSVaultWrapper.kt @@ -135,7 +135,6 @@ internal class VGSVaultWrapper @JvmOverloads constructor( foragePinElement: ForagePinElement, logger: Log ): AbstractVaultSubmitter = VgsPinSubmitter( - foragePinElement.context, foragePinElement, logger ) diff --git a/forage-android/src/test/java/com/joinforage/forage/android/vault/AbstractVaultSubmitterTest.kt b/forage-android/src/test/java/com/joinforage/forage/android/vault/AbstractVaultSubmitterTest.kt index a4ab51e0..aebddd10 100644 --- a/forage-android/src/test/java/com/joinforage/forage/android/vault/AbstractVaultSubmitterTest.kt +++ b/forage-android/src/test/java/com/joinforage/forage/android/vault/AbstractVaultSubmitterTest.kt @@ -70,7 +70,6 @@ class AbstractVaultSubmitterTest : MockServerSuite() { `when`(mockForagePinEditText.getElementState()).thenReturn(state) abstractVaultSubmitter = ConcreteVaultSubmitter( - context = mockContext, foragePinEditText = mockForagePinEditText, logger = mockLogger ) @@ -91,7 +90,6 @@ class AbstractVaultSubmitterTest : MockServerSuite() { @Test fun `submit with successful vault proxy response returns Success`() = runTest { val concreteSubmitter = object : ConcreteVaultSubmitter( - context = mockContext, foragePinEditText = mockForagePinEditText, logger = mockLogger ) { @@ -110,7 +108,6 @@ class AbstractVaultSubmitterTest : MockServerSuite() { @Test fun `submit with failed proxy response returns Failure`() = runTest { val concreteVaultSubmitter = object : ConcreteVaultSubmitter( - context = mockContext, foragePinEditText = mockForagePinEditText, logger = mockLogger ) { @@ -129,7 +126,6 @@ class AbstractVaultSubmitterTest : MockServerSuite() { @Test fun `submit with missing vault token returns UnknownErrorApiResponse`() = runTest { val concreteSubmitter = object : ConcreteVaultSubmitter( - context = mockContext, foragePinEditText = mockForagePinEditText, logger = mockLogger ) { @@ -157,7 +153,6 @@ class AbstractVaultSubmitterTest : MockServerSuite() { @Test fun `grabs the correct vault token`() = runTest { val basisTheorySubmitter = object : ConcreteVaultSubmitter( - context = mockContext, foragePinEditText = mockForagePinEditText, logger = mockLogger ) { @@ -167,7 +162,6 @@ class AbstractVaultSubmitterTest : MockServerSuite() { } val vgsSubmitter = object : ConcreteVaultSubmitter( - context = mockContext, foragePinEditText = mockForagePinEditText, logger = mockLogger ) { @@ -186,7 +180,6 @@ class AbstractVaultSubmitterTest : MockServerSuite() { @Test fun `success metrics event is reported`() = runTest { val concreteVaultSubmitter = object : ConcreteVaultSubmitter( - context = mockContext, foragePinEditText = mockForagePinEditText, logger = mockLogger ) { @@ -215,7 +208,6 @@ class AbstractVaultSubmitterTest : MockServerSuite() { @Test fun `failure metrics event is reported`() = runTest { val concreteVaultSubmitter = object : ConcreteVaultSubmitter( - context = mockContext, foragePinEditText = mockForagePinEditText, logger = mockLogger ) { @@ -256,11 +248,9 @@ class AbstractVaultSubmitterTest : MockServerSuite() { } internal open class ConcreteVaultSubmitter( - context: Context, foragePinEditText: ForagePINEditText, logger: Log ) : AbstractVaultSubmitter( - context = context, foragePinEditText = foragePinEditText, logger = logger ) { diff --git a/forage-android/src/test/java/com/joinforage/forage/android/vault/BasisTheoryPinSubmitterTest.kt b/forage-android/src/test/java/com/joinforage/forage/android/vault/BasisTheoryPinSubmitterTest.kt index 5397c9d0..67defec0 100644 --- a/forage-android/src/test/java/com/joinforage/forage/android/vault/BasisTheoryPinSubmitterTest.kt +++ b/forage-android/src/test/java/com/joinforage/forage/android/vault/BasisTheoryPinSubmitterTest.kt @@ -55,7 +55,6 @@ class BasisTheoryPinSubmitterTest() : MockServerSuite() { mockBasisTheoryResponse(Result.success("success")) submitter = BasisTheoryPinSubmitter( - context = mockContext, foragePinEditText = mockForagePinEditText, logger = mockLogger, buildVaultProvider = { mockBasisTheory } diff --git a/forage-android/src/test/java/com/joinforage/forage/android/vault/VgsPinSubmitterTest.kt b/forage-android/src/test/java/com/joinforage/forage/android/vault/VgsPinSubmitterTest.kt deleted file mode 100644 index 9d880bbe..00000000 --- a/forage-android/src/test/java/com/joinforage/forage/android/vault/VgsPinSubmitterTest.kt +++ /dev/null @@ -1,158 +0,0 @@ -package com.joinforage.forage.android.vault - -import android.content.Context -import com.joinforage.forage.android.core.services.ForageConstants -import com.joinforage.forage.android.core.services.forageapi.network.ForageApiResponse -import com.joinforage.forage.android.core.services.vault.VaultProxyRequest -import com.joinforage.forage.android.ecom.services.vault.vgs.VgsPinSubmitter -import com.joinforage.forage.android.ecom.ui.ForagePINEditText -import com.joinforage.forage.android.mock.MockLogger -import com.verygoodsecurity.vgscollect.core.HTTPMethod -import com.verygoodsecurity.vgscollect.core.VGSCollect -import com.verygoodsecurity.vgscollect.core.VgsCollectResponseListener -import com.verygoodsecurity.vgscollect.core.model.network.VGSRequest -import com.verygoodsecurity.vgscollect.core.model.network.VGSResponse -import com.verygoodsecurity.vgscollect.widget.VGSEditText -import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.assertTrue -import kotlinx.coroutines.test.runTest -import me.jorgecastillo.hiroaki.internal.MockServerSuite -import org.junit.Before -import org.junit.Test -import org.mockito.ArgumentMatchers.any -import org.mockito.Mockito.mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.mockito.kotlin.anyOrNull - -class VgsPinSubmitterTest() : MockServerSuite() { - private lateinit var mockLogger: MockLogger - private lateinit var mockForagePinEditText: ForagePINEditText - private lateinit var vgsPinSubmitter: VgsPinSubmitter - private lateinit var mockVgsCollect: VGSCollect - - @Before - fun setUp() { - super.setup() - - mockLogger = MockLogger() - - // Use Mockito judiciously (mainly for mocking views)! - // Opt for dependency injection and inheritance over Mockito - mockForagePinEditText = mock(ForagePINEditText::class.java) - val mockContext = mock(Context::class.java) - mockVgsCollect = mock(VGSCollect::class.java) - `when`(mockForagePinEditText.getTextElement()).thenReturn(mock(VGSEditText::class.java)) - `when`(mockVgsCollect.asyncSubmit(anyOrNull())).thenAnswer {} - - vgsPinSubmitter = VgsPinSubmitter( - context = mockContext, - foragePinEditText = mockForagePinEditText, - logger = mockLogger, - buildVaultProvider = { _ -> mockVgsCollect } - ) - } - - private fun mockVgsCollectResponse(response: VGSResponse) { - `when`(mockVgsCollect.addOnResponseListeners(any())).thenAnswer { - val listener = it.arguments[0] as VgsCollectResponseListener - listener.onResponse(response) - null - } - } - - @Test - fun `VGS request receives expected params`() = runTest { - mockVgsCollectResponse(VGSResponse.SuccessResponse(successCode = 200, rawResponse = "")) - - val vaultProxyRequest = VaultProxyRequest.emptyRequest() - .setHeader(ForageConstants.Headers.X_KEY, "tok_sandbox_sYiPe9Q249qQ5wQyUPP5f7") - .setHeader(ForageConstants.Headers.MERCHANT_ACCOUNT, "1234567") - .setHeader(ForageConstants.Headers.IDEMPOTENCY_KEY, "abcdef123") - .setHeader(ForageConstants.Headers.TRACE_ID, "65639248-03f2-498d-8aa8-9ebd1c60ee65") - .setToken("tok_sandbox_sYiPe9Q249qQ5wQyUPP5f7") - .setPath("/api/payments/abcdefg123/capture/") - - vgsPinSubmitter.submitProxyRequest(vaultProxyRequest) - - verify(mockVgsCollect).asyncSubmit( - VGSRequest.VGSRequestBuilder() - .setMethod(HTTPMethod.POST) - .setPath("/api/payments/abcdefg123/capture/") - .setCustomHeader( - mapOf( - ForageConstants.Headers.X_KEY to "tok_sandbox_sYiPe9Q249qQ5wQyUPP5f7", - ForageConstants.Headers.MERCHANT_ACCOUNT to "1234567", - ForageConstants.Headers.IDEMPOTENCY_KEY to "abcdef123", - ForageConstants.Headers.TRACE_ID to "65639248-03f2-498d-8aa8-9ebd1c60ee65" - ) - ) - .setCustomData( - hashMapOf( - "card_number_token" to "tok_sandbox_sYiPe9Q249qQ5wQyUPP5f7" - ) - ) - .build() - ) - } - - @Test - fun `submitProxyRequest with valid input should return success`() = runTest { - val responseStr = """{"content_id":"32489e7e-13d9-499c-b017-f68a0122da95","message_type":"0200","status":"sent_to_proxy","failed":false,"errors":[]}""" - mockVgsCollectResponse(VGSResponse.SuccessResponse(successCode = 200, rawResponse = responseStr)) - - val result = vgsPinSubmitter.submitProxyRequest(VaultProxyRequest.emptyRequest()) - - assertTrue(result is ForageApiResponse.Success) - assertEquals("[vgs] Received successful response from vgs", mockLogger.infoLogs.last().getMessage()) - assertEquals(responseStr, (result as ForageApiResponse.Success).data) - } - - @Test - fun `VGS returns a vault error`() = runTest { - mockVgsCollectResponse(VGSResponse.ErrorResponse(errorCode = 403, rawResponse = "VGS connection error")) - - val result = vgsPinSubmitter.submitProxyRequest(VaultProxyRequest.emptyRequest()) - - assertTrue(result is ForageApiResponse.Failure) - val firstError = (result as ForageApiResponse.Failure).errors[0] - assertEquals("[vgs] Received error from vgs: VGS connection error", mockLogger.errorLogs.last().getMessage()) - assertEquals("Unknown Server Error", firstError.message) - assertEquals(500, firstError.httpStatusCode) - assertEquals("unknown_server_error", firstError.code) - } - - @Test - fun `VGS returns a ForageError`() = runTest { - val responseStr = """ - { - "path": "/api/payments/abcdefg123/capture/", - "errors": [ - { - "code": "cannot_capture_payment", - "message": "Payment with ref abcdefg123 is either processing, succeeded, or canceled and therefore cannot be captured." - } - ] - } - """.trimIndent() - mockVgsCollectResponse(VGSResponse.ErrorResponse(errorCode = 400, rawResponse = responseStr)) - - val result = vgsPinSubmitter.submitProxyRequest(VaultProxyRequest.emptyRequest()) - - assertTrue(result is ForageApiResponse.Failure) - val firstError = (result as ForageApiResponse.Failure).errors[0] - assertEquals( - """ - [vgs] Received ForageError from vgs: Code: cannot_capture_payment - Message: Payment with ref abcdefg123 is either processing, succeeded, or canceled and therefore cannot be captured. - Status Code: 400 - Error Details (below): - null - """.trimIndent(), - mockLogger.errorLogs.last().getMessage() - ) - assertEquals("Payment with ref abcdefg123 is either processing, succeeded, or canceled and therefore cannot be captured.", firstError.message) - assertEquals(400, firstError.httpStatusCode) - assertEquals("cannot_capture_payment", firstError.code) - } -}