Skip to content

Commit

Permalink
Fix tests from merging against Devin's changes
Browse files Browse the repository at this point in the history
  • Loading branch information
djoksimo committed Jun 17, 2024
1 parent ff74df6 commit 4299337
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ internal abstract class AbstractVaultSubmitter(
}

val successfulResponse = parser.successfulResponse
if (successfulResponse != null) {
if (successfulResponse != null) {
logger.i("[$vaultType] Received successful response from $vaultType")
return successfulResponse
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.joinforage.forage.android.ecom.services.vault.forage

import android.content.Context
import android.widget.EditText
import com.joinforage.forage.android.core.services.EnvConfig
import com.joinforage.forage.android.core.services.ForageConstants
import com.joinforage.forage.android.core.services.VaultType
import com.joinforage.forage.android.core.services.addPathSegmentsSafe
Expand All @@ -14,10 +14,9 @@ import com.joinforage.forage.android.core.services.forageapi.network.UnknownErro
import com.joinforage.forage.android.core.services.forageapi.paymentmethod.PaymentMethod
import com.joinforage.forage.android.core.services.telemetry.Log
import com.joinforage.forage.android.core.services.vault.AbstractVaultSubmitter
import com.joinforage.forage.android.core.services.vault.StopgapGlobalState
import com.joinforage.forage.android.core.services.vault.SecurePinCollector
import com.joinforage.forage.android.core.services.vault.VaultProxyRequest
import com.joinforage.forage.android.core.services.vault.VaultSubmitterParams
import com.joinforage.forage.android.core.ui.element.ForagePinElement
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
Expand All @@ -27,13 +26,13 @@ import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject

internal class RosettaPinSubmitter(
context: Context,
foragePinEditText: ForagePinElement,
private val editText: EditText,
collector: SecurePinCollector,
private val envConfig: EnvConfig,
logger: Log,
private val vaultUrlBuilder: ((String) -> HttpUrl) = { path -> buildVaultUrl(path) }
private val vaultUrlBuilder: ((String) -> HttpUrl) = { path -> buildVaultUrl(envConfig, path) }
) : AbstractVaultSubmitter(
context = context,
foragePinEditText = foragePinEditText,
collector = collector,
logger = logger
) {
override val vaultType: VaultType = VaultType.FORAGE_VAULT_TYPE
Expand All @@ -47,7 +46,7 @@ internal class RosettaPinSubmitter(
return try {
val apiUrl = vaultUrlBuilder(vaultProxyRequest.path)
val baseRequestBody = buildBaseRequestBody(vaultProxyRequest)
val requestBody = buildForageVaultRequestBody(foragePinEditText, baseRequestBody)
val requestBody = buildForageVaultRequestBody(editText, baseRequestBody)

val request = Request.Builder()
.url(apiUrl)
Expand Down Expand Up @@ -91,17 +90,17 @@ internal class RosettaPinSubmitter(
companion object {
// this code assumes that .setForageConfig() has been called
// on a Forage***EditText before VAULT_BASE_URL gets referenced
private fun buildVaultUrl(path: String): HttpUrl =
StopgapGlobalState.envConfig.vaultBaseUrl.toHttpUrlOrNull()!!
private fun buildVaultUrl(envConfig: EnvConfig, path: String): HttpUrl =
envConfig.vaultBaseUrl.toHttpUrlOrNull()!!
.newBuilder()
.addPathSegment("proxy")
.addPathSegmentsSafe(path)
.addTrailingSlash()
.build()

private fun buildForageVaultRequestBody(foragePinEditText: ForagePinElement, baseRequestBody: Map<String, Any>): RequestBody {
private fun buildForageVaultRequestBody(editText: EditText, baseRequestBody: Map<String, Any>): RequestBody {
val jsonBody = JSONObject(baseRequestBody)
jsonBody.put("pin", (foragePinEditText.getTextElement() as EditText).text)
jsonBody.put("pin", editText.text)

val mediaType = "application/json".toMediaTypeOrNull()
return jsonBody.toString().toRequestBody(mediaType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import android.widget.EditText
import android.widget.LinearLayout
import androidx.core.content.getSystemService
import com.joinforage.forage.android.R
import com.joinforage.forage.android.core.services.EnvConfig
import com.joinforage.forage.android.core.services.VaultType
import com.joinforage.forage.android.core.services.telemetry.Log
import com.joinforage.forage.android.core.services.vault.AbstractVaultSubmitter
import com.joinforage.forage.android.core.services.vault.SecurePinCollector
import com.joinforage.forage.android.core.ui.VaultWrapper
import com.joinforage.forage.android.core.ui.element.ForagePinElement
import com.joinforage.forage.android.core.ui.element.state.PinElementStateManager
import com.joinforage.forage.android.core.ui.getBoxCornerRadiusBottomEnd
import com.joinforage.forage.android.core.ui.getBoxCornerRadiusBottomStart
import com.joinforage.forage.android.core.ui.getBoxCornerRadiusTopEnd
Expand All @@ -37,8 +37,6 @@ internal class RosettaPinElement @JvmOverloads constructor(
override val vaultType: VaultType = VaultType.FORAGE_VAULT_TYPE
private val _editText: EditText

override val manager: PinElementStateManager = PinElementStateManager.forEmptyInput()

init {
context.obtainStyledAttributes(attrs, R.styleable.ForagePINEditText, defStyleAttr, 0).apply {
try {
Expand All @@ -51,11 +49,17 @@ internal class RosettaPinElement @JvmOverloads constructor(
}

override fun getVaultSubmitter(
foragePinElement: ForagePinElement,
envConfig: EnvConfig,
logger: Log
): AbstractVaultSubmitter = RosettaPinSubmitter(
foragePinElement.context,
foragePinElement,
editText = _editText,
object : SecurePinCollector {
override fun clearText() {
this@RosettaPinElement.clearText()
}
override fun isComplete(): Boolean = inputState.isComplete
},
envConfig,
logger
)

Expand Down Expand Up @@ -194,14 +198,22 @@ internal class RosettaPinElement @JvmOverloads constructor(

private fun registerFocusChangeListener() {
_editText.setOnFocusChangeListener { _, hasFocus ->
manager.changeFocus(hasFocus)
focusState = focusState.changeFocus(hasFocus)
focusState.fireEvent(
onFocusEventListener = onFocusEventListener,
onBlurEventListener = onBlurEventListener
)
}
}

private fun registerTextWatcher() {
val pinTextWatcher = PinTextWatcher(_editText)
pinTextWatcher.onInputChangeEvent { isComplete, isEmpty ->
manager.handleChangeEvent(isComplete, isEmpty)
inputState = inputState.handleChangeEvent(
isComplete = isComplete,
isEmpty = isEmpty
)
onChangeEventListener?.invoke(pinEditTextState)
}
_editText.addTextChangedListener(pinTextWatcher)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,13 @@ class RosettaPinElementTests {
val focusChangeListener = editText.onFocusChangeListener

focusChangeListener.onFocusChange(editText, true)
assertTrue(rosettaPinElement.manager.isFocused)

assertTrue(rosettaPinElement.pinEditTextState.isFocused)
assertFalse(rosettaPinElement.pinEditTextState.isBlurred)

focusChangeListener.onFocusChange(editText, false)
assertFalse(rosettaPinElement.manager.isFocused)
assertFalse(rosettaPinElement.pinEditTextState.isFocused)
assertTrue(rosettaPinElement.pinEditTextState.isBlurred)
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.joinforage.forage.android.vault

import android.content.Context
import com.joinforage.forage.android.core.services.VaultType
import com.joinforage.forage.android.core.services.forageapi.encryptkey.EncryptionKeys
import com.joinforage.forage.android.core.services.forageapi.network.ForageApiResponse
Expand All @@ -9,11 +8,9 @@ import com.joinforage.forage.android.core.services.forageapi.paymentmethod.Payme
import com.joinforage.forage.android.core.services.telemetry.Log
import com.joinforage.forage.android.core.services.telemetry.UserAction
import com.joinforage.forage.android.core.services.vault.AbstractVaultSubmitter
import com.joinforage.forage.android.core.services.vault.SecurePinCollector
import com.joinforage.forage.android.core.services.vault.VaultProxyRequest
import com.joinforage.forage.android.core.services.vault.VaultSubmitterParams
import com.joinforage.forage.android.core.ui.element.state.INITIAL_PIN_ELEMENT_STATE
import com.joinforage.forage.android.core.ui.element.state.pin.PinEditTextState
import com.joinforage.forage.android.ecom.ui.element.ForagePINEditText
import com.joinforage.forage.android.mock.MockLogger
import com.joinforage.forage.android.mock.MockServiceFactory
import kotlinx.coroutines.test.runTest
Expand All @@ -23,16 +20,14 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`

class AbstractVaultSubmitterTest : MockServerSuite() {
private lateinit var mockLogger: MockLogger
private lateinit var mockForagePinEditText: ForagePINEditText
private lateinit var mockContext: Context
private lateinit var abstractVaultSubmitter: AbstractVaultSubmitter
private val mockCollector = object : SecurePinCollector {
override fun clearText() {}
override fun isComplete(): Boolean = true
}

companion object {
private val mockEncryptionKeys = EncryptionKeys("vgs-alias", "bt-alias")
Expand All @@ -53,25 +48,24 @@ class AbstractVaultSubmitterTest : MockServerSuite() {
super.setup()

mockLogger = MockLogger()
mockForagePinEditText = mock(ForagePINEditText::class.java)
mockContext = mock(Context::class.java)

val state = PinEditTextState.forEmptyInput(FocusState)
state.isComplete = true
`when`(mockForagePinEditText.getElementState()).thenReturn(state)

abstractVaultSubmitter = ConcreteVaultSubmitter(
context = mockContext,
foragePinEditText = mockForagePinEditText,
collector = mockCollector,
logger = mockLogger
)
}

@Test
fun `submit with invalid PIN returns IncompletePinError`() = runTest {
val state = INITIAL_PIN_ELEMENT_STATE.copy(isComplete = false)
val incompleteCollector = object : SecurePinCollector {
override fun clearText() {}
override fun isComplete(): Boolean = false
}

val abstractVaultSubmitter = ConcreteVaultSubmitter(
collector = incompleteCollector,
logger = mockLogger
)

`when`(mockForagePinEditText.getElementState()).thenReturn(state)
val response = abstractVaultSubmitter.submit(mockVaultParams)

val forageError = (response as ForageApiResponse.Failure).errors.first()
Expand All @@ -82,8 +76,7 @@ class AbstractVaultSubmitterTest : MockServerSuite() {
@Test
fun `submit with successful vault proxy response returns Success`() = runTest {
val concreteSubmitter = object : ConcreteVaultSubmitter(
context = mockContext,
foragePinEditText = mockForagePinEditText,
collector = mockCollector,
logger = mockLogger
) {
override suspend fun submitProxyRequest(
Expand All @@ -101,8 +94,7 @@ class AbstractVaultSubmitterTest : MockServerSuite() {
@Test
fun `submit with failed proxy response returns Failure`() = runTest {
val concreteVaultSubmitter = object : ConcreteVaultSubmitter(
context = mockContext,
foragePinEditText = mockForagePinEditText,
collector = mockCollector,
logger = mockLogger
) {
override suspend fun submitProxyRequest(
Expand All @@ -120,8 +112,7 @@ class AbstractVaultSubmitterTest : MockServerSuite() {
@Test
fun `submit with missing vault token returns UnknownErrorApiResponse`() = runTest {
val concreteSubmitter = object : ConcreteVaultSubmitter(
context = mockContext,
foragePinEditText = mockForagePinEditText,
collector = mockCollector,
logger = mockLogger
) {
// Mock missing token
Expand All @@ -139,17 +130,57 @@ class AbstractVaultSubmitterTest : MockServerSuite() {
}

@Test
fun `calls clearText after submitting`() = runTest {
abstractVaultSubmitter.submit(mockVaultParams)
fun `calls clearText after submitting on success`() = runTest {
var numTimesClearTextCalled = 0
val clearTextSpyCollector = object : SecurePinCollector {
override fun clearText() {
numTimesClearTextCalled += 1
}
override fun isComplete(): Boolean = true
}

verify(mockForagePinEditText, times(1)).clearText()
val successVaultSubmitter = object : ConcreteVaultSubmitter(
collector = clearTextSpyCollector,
logger = mockLogger
) {
override suspend fun submitProxyRequest(
vaultProxyRequest: VaultProxyRequest
): ForageApiResponse<String> {
return ForageApiResponse.Success("success")
}
}
successVaultSubmitter.submit(mockVaultParams)
assertEquals(1, numTimesClearTextCalled)
}

@Test
fun `calls clearText after submitting on failure`() = runTest {
var numTimesClearTextCalled = 0
val clearTextSpyCollector = object : SecurePinCollector {
override fun clearText() {
numTimesClearTextCalled += 1
}
override fun isComplete(): Boolean = true
}

val failedVaultSubmitter = object : ConcreteVaultSubmitter(
collector = clearTextSpyCollector,
logger = mockLogger
) {
override suspend fun submitProxyRequest(
vaultProxyRequest: VaultProxyRequest
): ForageApiResponse<String> {
return UnknownErrorApiResponse
}
}
failedVaultSubmitter.submit(mockVaultParams)
assertEquals(1, numTimesClearTextCalled)
}

@Test
fun `grabs the correct vault token`() = runTest {
val basisTheorySubmitter = object : ConcreteVaultSubmitter(
context = mockContext,
foragePinEditText = mockForagePinEditText,
collector = mockCollector,
logger = mockLogger
) {
override fun getVaultToken(paymentMethod: PaymentMethod): String? {
Expand All @@ -158,8 +189,7 @@ class AbstractVaultSubmitterTest : MockServerSuite() {
}

val vgsSubmitter = object : ConcreteVaultSubmitter(
context = mockContext,
foragePinEditText = mockForagePinEditText,
collector = mockCollector,
logger = mockLogger
) {
override fun getVaultToken(paymentMethod: PaymentMethod): String? {
Expand All @@ -177,8 +207,7 @@ class AbstractVaultSubmitterTest : MockServerSuite() {
@Test
fun `success metrics event is reported`() = runTest {
val concreteVaultSubmitter = object : ConcreteVaultSubmitter(
context = mockContext,
foragePinEditText = mockForagePinEditText,
collector = mockCollector,
logger = mockLogger
) {
override suspend fun submitProxyRequest(
Expand Down Expand Up @@ -206,8 +235,7 @@ class AbstractVaultSubmitterTest : MockServerSuite() {
@Test
fun `failure metrics event is reported`() = runTest {
val concreteVaultSubmitter = object : ConcreteVaultSubmitter(
context = mockContext,
foragePinEditText = mockForagePinEditText,
collector = mockCollector,
logger = mockLogger
) {
override suspend fun submitProxyRequest(
Expand Down Expand Up @@ -247,12 +275,10 @@ class AbstractVaultSubmitterTest : MockServerSuite() {
}

internal open class ConcreteVaultSubmitter(
context: Context,
foragePinEditText: ForagePINEditText,
collector: SecurePinCollector,
logger: Log
) : AbstractVaultSubmitter(
context = context,
foragePinEditText = foragePinEditText,
collector = collector,
logger = logger
) {
override val vaultType: VaultType = VaultType.VGS_VAULT_TYPE
Expand Down
Loading

0 comments on commit 4299337

Please sign in to comment.