Skip to content

Commit

Permalink
US707277: introducing coroutines and removing deprecated async tasks (#…
Browse files Browse the repository at this point in the history
…164)

* US707277: Replace async task with coroutines and fix tests

* US707277: Addressed all TODOs and fixed all tests

* US707277: Test fix

* US707277: add missing test coverage

* US707277: fix for broken test

* US730859: fix broken test

* US730859: fix "Job has not completed yed" issue

* US707277: fix failing tests

* US707277: remove unwanted comments

Co-authored-by: Olivier Chalet <olivier.chalet@worldpay.com>
  • Loading branch information
juned-miah and ochalet-wp authored Nov 16, 2021
1 parent a1fcbb5 commit 904b47e
Show file tree
Hide file tree
Showing 59 changed files with 1,478 additions and 2,356 deletions.
3 changes: 3 additions & 0 deletions access-checkout/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30"
implementation "org.jetbrains.kotlin:kotlin-reflect:1.5.30"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

androidTestImplementation 'androidx.test.ext:junit:1.1.3'
Expand All @@ -35,10 +36,12 @@ dependencies {
androidTestImplementation 'org.apache.httpcomponents:httpclient-android:4.3.5.1'
androidTestImplementation 'org.awaitility:awaitility-kotlin:3.1.6'
androidTestImplementation 'com.google.android.gms:play-services-vision:20.1.3'
androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9'

testImplementation 'org.awaitility:awaitility-kotlin:3.1.6'
testImplementation 'junit:junit:4.13.1'
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9'

// override the version of bcprov-jdk15on to avoid the ssl internal errors
testImplementation('org.robolectric:robolectric:4.4')
Expand Down
7 changes: 7 additions & 0 deletions access-checkout/gradle/android.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
android {
compileSdkVersion 30

packagingOptions {
exclude "**/attach_hotspot_windows.dll"
exclude "META-INF/licenses/**"
exclude "META-INF/AL2.0"
exclude "META-INF/LGPL2.1"
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class AccessCheckoutClientIntegrationTest {

getInstrumentation().runOnMainSync {
val accessCheckoutClient = AccessCheckoutClientBuilder()
.baseUrl(getBaseUrl())
.baseUrl(getBaseUrl().toString())
.merchantId(merchantId)
.sessionResponseListener(responseListener)
.context(applicationContext)
Expand Down Expand Up @@ -161,7 +161,7 @@ class AccessCheckoutClientIntegrationTest {

getInstrumentation().runOnMainSync {
val accessCheckoutClient = AccessCheckoutClientBuilder()
.baseUrl(getBaseUrl())
.baseUrl(getBaseUrl().toString())
.merchantId(merchantId)
.sessionResponseListener(responseListener)
.context(applicationContext)
Expand Down Expand Up @@ -204,7 +204,7 @@ class AccessCheckoutClientIntegrationTest {

getInstrumentation().runOnMainSync {
val accessCheckoutClient = AccessCheckoutClientBuilder()
.baseUrl(getBaseUrl())
.baseUrl(getBaseUrl().toString())
.merchantId(merchantId)
.sessionResponseListener(errorListener)
.context(applicationContext)
Expand Down Expand Up @@ -255,7 +255,7 @@ class AccessCheckoutClientIntegrationTest {

getInstrumentation().runOnMainSync {
val accessCheckoutClient = AccessCheckoutClientBuilder()
.baseUrl(getBaseUrl())
.baseUrl(getBaseUrl().toString())
.merchantId(merchantId)
.sessionResponseListener(errorListener)
.context(applicationContext)
Expand Down Expand Up @@ -294,7 +294,7 @@ class AccessCheckoutClientIntegrationTest {

getInstrumentation().runOnMainSync {
val accessCheckoutClient = AccessCheckoutClientBuilder()
.baseUrl(getBaseUrl())
.baseUrl(getBaseUrl().toString())
.merchantId(merchantId)
.sessionResponseListener(errorListener)
.context(applicationContext)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.worldpay.access.checkout.api

import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.github.tomakehurst.wiremock.client.WireMock.aResponse
Expand All @@ -11,23 +10,29 @@ import com.worldpay.access.checkout.api.ApiDiscoveryStubs.rootResponseMapping
import com.worldpay.access.checkout.api.ApiDiscoveryStubs.stubServiceDiscoveryResponses
import com.worldpay.access.checkout.api.ApiDiscoveryStubs.verifiedTokensMapping
import com.worldpay.access.checkout.api.MockServer.getBaseUrl
import com.worldpay.access.checkout.api.discovery.ApiDiscoveryAsyncTaskFactory
import com.worldpay.access.checkout.api.discovery.ApiDiscoveryClient
import com.worldpay.access.checkout.api.discovery.DiscoverLinks
import com.worldpay.access.checkout.api.discovery.DiscoveryCache
import com.worldpay.access.checkout.client.api.exception.AccessCheckoutException
import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.test.fail
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import org.awaitility.Awaitility.await
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class ApiDiscoveryIntegrationTest {

@get:Rule
var coroutinesTestRule = CoroutineTestRule()

private val applicationContext = InstrumentationRegistry.getInstrumentation().context.applicationContext

@Before
Expand All @@ -42,29 +47,19 @@ class ApiDiscoveryIntegrationTest {
}

@Test
fun shouldBeAbleToDiscoverVTSessionsFromRoot() {
fun shouldBeAbleToDiscoverVTSessionsFromRoot() = runBlocking {
stubServiceDiscoveryResponses()

var url: String? = null

val callback = object : Callback<String> {
override fun onResponse(error: Exception?, response: String?) {
url = response
}
}

val client = ApiDiscoveryClient(ApiDiscoveryAsyncTaskFactory())

client.discover(getBaseUrl(), callback, DiscoverLinks.verifiedTokens)
val client = ApiDiscoveryClient()
val endpoint = client.discoverEndpoint(getBaseUrl(), DiscoverLinks.verifiedTokens)

await().atMost(5, TimeUnit.SECONDS).until {
Log.d("AccessCheckoutDiscoveryIntegrationTest", "Discovered endpoint: $url")
url != null && url.equals("${getBaseUrl()}/verifiedTokens/sessions")
endpoint.toString() == "${getBaseUrl()}/verifiedTokens/sessions"
}
}

@Test
fun shouldReturnException_whenDiscoveryFails() {
fun shouldReturnExceptionWhenDiscoveryFails() = runBlocking {
stubFor(
get("/")
.willReturn(
Expand All @@ -73,26 +68,19 @@ class ApiDiscoveryIntegrationTest {
)
)

var assertionDone = false
val exceptedException = AccessCheckoutException("Error message was: Server Error")

val callback = object : Callback<String> {
override fun onResponse(error: Exception?, response: String?) {
assertEquals(exceptedException, error)
assertNull(response)
assertionDone = true
}
try {
val client = ApiDiscoveryClient()
client.discoverEndpoint(getBaseUrl(), DiscoverLinks.verifiedTokens)
fail("Expected exception but got none")
} catch (ace: AccessCheckoutException) {
assertEquals("Could not discover session endpoint", ace.message)
} catch (ex: Exception) {
fail("Expected AccessCheckoutException but got " + ex.javaClass.simpleName)
}

val client = ApiDiscoveryClient(ApiDiscoveryAsyncTaskFactory())

client.discover(getBaseUrl(), callback, DiscoverLinks.verifiedTokens)

await().atMost(5, TimeUnit.SECONDS).until { assertionDone }
}

@Test
fun shouldReturnUrl_whenFirstDiscoveryAttemptFailsThenRetrySucceeds() {
fun shouldReturnEndpointOnSecondRetry() = runBlocking {
val serviceAvailableState = "SERVICE_AVAILABLE_AGAIN"

stubFor(
Expand All @@ -111,20 +99,11 @@ class ApiDiscoveryIntegrationTest {

stubFor(verifiedTokensMapping())

var exception: Exception? = null
var url: String? = null
val callback = object : Callback<String> {
override fun onResponse(error: Exception?, response: String?) {
exception = error
url = response
}
}

val client = ApiDiscoveryClient(ApiDiscoveryAsyncTaskFactory())
client.discover(getBaseUrl(), callback, DiscoverLinks.verifiedTokens)
val client = ApiDiscoveryClient()
val endpoint = client.discoverEndpoint(getBaseUrl(), DiscoverLinks.verifiedTokens)

await().atMost(5, TimeUnit.SECONDS).until {
url != null && url.equals("${getBaseUrl()}/verifiedTokens/sessions") && exception == null
endpoint.toString() == "${getBaseUrl()}/verifiedTokens/sessions"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
package com.worldpay.access.checkout.api

import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.github.tomakehurst.wiremock.client.WireMock
import com.github.tomakehurst.wiremock.client.WireMock.get
import com.github.tomakehurst.wiremock.client.WireMock.stubFor
import com.worldpay.access.checkout.api.MockServer.getBaseUrl
import com.worldpay.access.checkout.api.configuration.CardConfiguration
import com.worldpay.access.checkout.api.configuration.CardConfigurationAsyncTask
import com.worldpay.access.checkout.api.configuration.CardConfigurationClient
import com.worldpay.access.checkout.client.api.exception.AccessCheckoutException
import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals
import kotlin.test.assertNull
import org.awaitility.Awaitility.await
import kotlin.test.assertNotNull
import kotlin.test.fail
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class CardConfigurationIntegrationTest {

@get:Rule
var coroutinesTestRule = CoroutineTestRule()

private val applicationContext = InstrumentationRegistry.getInstrumentation().context.applicationContext

private val cardConfigurationEndpoint = "/access-checkout/cardTypes.json"
Expand Down Expand Up @@ -98,7 +102,7 @@ class CardConfigurationIntegrationTest {
}

@Test
fun givenCardConfigurationAvailable_ThenCardConfigurationAsyncTaskCanFetchCardConfiguration() {
fun shouldBeAbleToRetrieveCardConfiguration() = runBlocking<Unit> {
stubFor(
get(cardConfigurationEndpoint)
.willReturn(
Expand All @@ -109,26 +113,14 @@ class CardConfigurationIntegrationTest {
)
)

var cardConfiguration: CardConfiguration? = null

val callback = object : Callback<CardConfiguration> {
override fun onResponse(error: Exception?, response: CardConfiguration?) {
cardConfiguration = response
}
}

val asyncTask = CardConfigurationAsyncTask(callback)

asyncTask.execute(getBaseUrl())
val cardConfigurationClient = CardConfigurationClient(getBaseUrl())
val cardConfig = cardConfigurationClient.getCardConfiguration()

await().atMost(5, TimeUnit.SECONDS).until {
Log.d("CardConfigurationIntegrationTest", "Got card configuration: $cardConfiguration")
cardConfiguration != null
}
assertNotNull(cardConfig)
}

@Test
fun givenAnErrorFetchingCardConfiguration_ThenExceptionIsPassedBackToCallback() {
fun shouldThrowExceptionWhenFailingToGetCardConfiguration() = runBlocking {
stubFor(
get(cardConfigurationEndpoint)
.willReturn(
Expand All @@ -137,21 +129,14 @@ class CardConfigurationIntegrationTest {
)
)

var assertionDone = false
val expectedException = AccessCheckoutException("Error message was: Server Error")

val callback = object : Callback<CardConfiguration> {
override fun onResponse(error: Exception?, response: CardConfiguration?) {
assertEquals(expectedException, error)
assertNull(response)
assertionDone = true
}
try {
val cardConfigurationClient = CardConfigurationClient(getBaseUrl())
cardConfigurationClient.getCardConfiguration()
fail("Expected exception but got none")
} catch (ace: AccessCheckoutException) {
assertEquals("There was an error when trying to fetch the card configuration", ace.message)
} catch (ex: Exception) {
fail("Expected AccessCheckoutException but got " + ex.javaClass.simpleName)
}

val asyncTask = CardConfigurationAsyncTask(callback)

asyncTask.execute(getBaseUrl())

await().atMost(5, TimeUnit.SECONDS).until { assertionDone }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.worldpay.access.checkout.api

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.rules.TestWatcher
import org.junit.runner.Description

@ExperimentalCoroutinesApi
class CoroutineTestRule(
private val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
) : TestWatcher() {

override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(testDispatcher)
}

override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.worldpay.access.checkout.api.ssl.client.TrustAllSSLSocketFactory
import com.worldpay.access.checkout.api.ssl.server.CustomHttpServerFactory
import java.io.File
import java.io.FileOutputStream
import java.net.URL
import javax.net.ssl.HttpsURLConnection

object MockServer {
Expand Down Expand Up @@ -62,8 +63,8 @@ object MockServer {
wireMockServer.stop()
}

fun getBaseUrl(): String {
return baseUrl
fun getBaseUrl(): URL {
return URL(baseUrl)
}

private fun waitForWiremock() {
Expand Down
Loading

0 comments on commit 904b47e

Please sign in to comment.