Skip to content

Commit

Permalink
[ANCHOR-537] Re-enable SEP-6 e2e tests (#1193)
Browse files Browse the repository at this point in the history
  • Loading branch information
philipliu authored Nov 10, 2023
1 parent 8c45181 commit 98ff5ab
Show file tree
Hide file tree
Showing 14 changed files with 244 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ package org.stellar.anchor.platform

import io.ktor.client.plugins.*
import io.ktor.http.*
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.retryWhen
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.stellar.anchor.util.Sep1Helper.TomlContent
import org.stellar.anchor.util.Sep1Helper.parse
import org.stellar.walletsdk.ApplicationConfiguration
Expand All @@ -23,6 +29,23 @@ abstract class AbstractIntegrationTests(val config: TestConfig) {
var walletKeyPair = SigningKeyPair.fromSecret(CLIENT_WALLET_SECRET)
var anchor = wallet.anchor(config.env["anchor.domain"]!!)
var token: AuthToken
private val submissionLock = Mutex()

suspend fun transactionWithRetry(
maxAttempts: Int = 5,
delay: Int = 5,
transactionLogic: suspend () -> Unit
) =
flow<Unit> { submissionLock.withLock { transactionLogic() } }
.retryWhen { _, attempt ->
if (attempt < maxAttempts) {
delay((delay + (1..5).random()).seconds)
return@retryWhen true
} else {
return@retryWhen false
}
}
.collect {}

init {
runBlocking { token = anchor.auth().authenticate(walletKeyPair) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ import kotlin.test.fail
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.*
import org.junit.jupiter.api.TestInstance.Lifecycle.*
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT
import org.junit.jupiter.params.ParameterizedTest
Expand All @@ -37,7 +34,6 @@ import org.stellar.anchor.platform.TestConfig
import org.stellar.anchor.util.Log.info
import org.stellar.reference.client.AnchorReferenceServerClient
import org.stellar.reference.wallet.WalletServerClient
import org.stellar.sdk.*
import org.stellar.walletsdk.InteractiveFlowResponse
import org.stellar.walletsdk.anchor.*
import org.stellar.walletsdk.anchor.TransactionStatus.*
Expand All @@ -47,7 +43,6 @@ import org.stellar.walletsdk.asset.XLM
import org.stellar.walletsdk.auth.AuthToken
import org.stellar.walletsdk.horizon.SigningKeyPair
import org.stellar.walletsdk.horizon.sign
import org.stellar.walletsdk.horizon.transaction.transferWithdrawalTransaction

const val WITHDRAW_FUND_CLIENT_SECRET_1 = "SCGHF6KF6CBQ6Z4ZZUMU4DGRM6LR2PS7XOUN5VOETMPTPLD5BQE2FKL3"
const val WITHDRAW_FUND_CLIENT_SECRET_2 = "SBSO7FVRDHCETSGPYETIFNVK64LS4KH325GOAENGV5Z7L6FAP7YS2BPK"
Expand Down Expand Up @@ -211,19 +206,19 @@ class Sep24End2EndTests : AbstractIntegrationTests(TestConfig(testProfileName =
// Submit transfer transaction
val walletTxn =
(anchor.interactive().getTransaction(withdrawTxn.id, token) as WithdrawalTransaction)
val transfer =
wallet
.stellar()
.transaction(walletTxn.from!!)
.transferWithdrawalTransaction(walletTxn, asset)
.build()
transfer.sign(keypair)

submissionLock.withLock {
transactionWithRetry {
val transfer =
wallet
.stellar()
.transaction(walletTxn.from!!)
.transferWithdrawalTransaction(walletTxn, asset)
.build()
transfer.sign(keypair)

wallet.stellar().submitTransaction(transfer)
// Wait for the status to change to PENDING_USER_TRANSFER_END
waitForTxnStatus(withdrawTxn.id, COMPLETED, token)
}
// Wait for the status to change to PENDING_USER_TRANSFER_END
waitForTxnStatus(withdrawTxn.id, COMPLETED, token)

// Check if the transaction can be listed by stellar transaction id
val fetchTxn =
Expand Down Expand Up @@ -319,22 +314,22 @@ class Sep24End2EndTests : AbstractIntegrationTests(TestConfig(testProfileName =
val keypair = SigningKeyPair.fromSecret(walletSecretKey)
val newAcc = wallet.stellar().account().createKeyPair()

val tx =
wallet
.stellar()
.transaction(keypair)
.sponsoring(keypair, newAcc) {
createAccount(newAcc)
addAssetSupport(USDC)
}
.build()
.sign(keypair)
.sign(newAcc)
transactionWithRetry {
val tx =
wallet
.stellar()
.transaction(keypair)
.sponsoring(keypair, newAcc) {
createAccount(newAcc)
addAssetSupport(USDC)
}
.build()
.sign(keypair)
.sign(newAcc)

submissionLock.withLock {
wallet.stellar().submitTransaction(tx)
delay(5000)
}
delay(5000)
val token = anchor.auth().authenticate(newAcc)
val deposits =
(0..1).map {
Expand All @@ -348,8 +343,6 @@ class Sep24End2EndTests : AbstractIntegrationTests(TestConfig(testProfileName =
}

companion object {
// This is to make sure transaction submission is mutual exclusive to avoid failures
private val submissionLock = Mutex()
private val USDC =
IssuedAssetId("USDC", "GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP")
private val expectedDepositStatuses =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode
import org.stellar.anchor.api.sep.SepTransactionStatus
import org.stellar.anchor.api.sep.SepTransactionStatus.*
import org.stellar.anchor.api.sep.sep6.GetTransactionResponse
import org.stellar.anchor.api.shared.InstructionField
import org.stellar.anchor.client.Sep6Client
import org.stellar.anchor.platform.AbstractIntegrationTests
import org.stellar.anchor.platform.TestConfig
import org.stellar.anchor.util.GsonUtils
import org.stellar.anchor.util.Log
import org.stellar.reference.client.AnchorReferenceServerClient
import org.stellar.reference.wallet.WalletServerClient
Expand All @@ -26,7 +27,8 @@ import org.stellar.walletsdk.anchor.customer
import org.stellar.walletsdk.asset.IssuedAssetId
import org.stellar.walletsdk.horizon.sign

@Disabled
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Execution(ExecutionMode.CONCURRENT)
class Sep6End2EndTest : AbstractIntegrationTests(TestConfig(testProfileName = "default")) {
private val maxTries = 30
private val anchorReferenceServerClient =
Expand Down Expand Up @@ -57,12 +59,13 @@ class Sep6End2EndTest : AbstractIntegrationTests(TestConfig(testProfileName = "d

@Test
fun `test typical deposit end-to-end flow`() = runBlocking {
val token = anchor.auth().authenticate(walletKeyPair)
val memo = (10000..20000).random().toULong()
val token = anchor.auth().authenticate(walletKeyPair, memoId = memo)
// TODO: migrate this to wallet-sdk when it's available
val sep6Client = Sep6Client("${config.env["anchor.domain"]}/sep6", token.token)

// Create a customer before starting the transaction
anchor.customer(token).add(basicInfoFields.associateWith { customerInfo[it]!! })
anchor.customer(token).add(basicInfoFields.associateWith { customerInfo[it]!! }, memo)

val deposit =
sep6Client.deposit(
Expand All @@ -73,12 +76,15 @@ class Sep6End2EndTest : AbstractIntegrationTests(TestConfig(testProfileName = "d
"type" to "SWIFT"
)
)
Log.info("Deposit initiated: ${deposit.id}")
waitStatus(deposit.id, PENDING_CUSTOMER_INFO_UPDATE, sep6Client)

// Supply missing KYC info to continue with the transaction
val additionalRequiredFields =
sep6Client.getTransaction(mapOf("id" to deposit.id)).transaction.requiredCustomerInfoUpdates
anchor.customer(token).add(additionalRequiredFields.associateWith { customerInfo[it]!! })
anchor.customer(token).add(additionalRequiredFields.associateWith { customerInfo[it]!! }, memo)
Log.info("Submitted additional KYC info: $additionalRequiredFields")
Log.info("Bank transfer complete")
waitStatus(deposit.id, COMPLETED, sep6Client)

val completedDepositTxn = sep6Client.getTransaction(mapOf("id" to deposit.id))
Expand Down Expand Up @@ -119,36 +125,44 @@ class Sep6End2EndTest : AbstractIntegrationTests(TestConfig(testProfileName = "d

@Test
fun `test typical withdraw end-to-end flow`() = runBlocking {
val token = anchor.auth().authenticate(walletKeyPair)
val memo = (30000..40000).random().toULong()
val token = anchor.auth().authenticate(walletKeyPair, memoId = memo)
// TODO: migrate this to wallet-sdk when it's available
val sep6Client = Sep6Client("${config.env["anchor.domain"]}/sep6", token.token)

// Create a customer before starting the transaction
anchor.customer(token).add(basicInfoFields.associateWith { customerInfo[it]!! })
anchor.customer(token).add(basicInfoFields.associateWith { customerInfo[it]!! }, memo)

val withdraw =
sep6Client.withdraw(
mapOf("asset_code" to USDC.code, "amount" to "1", "type" to "bank_account")
)
Log.info("Withdrawal initiated: ${withdraw.id}")
waitStatus(withdraw.id, PENDING_CUSTOMER_INFO_UPDATE, sep6Client)

// Supply missing financial account info to continue with the transaction
val additionalRequiredFields =
sep6Client.getTransaction(mapOf("id" to withdraw.id)).transaction.requiredCustomerInfoUpdates
anchor.customer(token).add(additionalRequiredFields.associateWith { customerInfo[it]!! })
anchor.customer(token).add(additionalRequiredFields.associateWith { customerInfo[it]!! }, memo)
Log.info("Submitted additional KYC info: $additionalRequiredFields")

waitStatus(withdraw.id, PENDING_USR_TRANSFER_START, sep6Client)

val withdrawTxn = sep6Client.getTransaction(mapOf("id" to withdraw.id)).transaction

// Transfer the withdrawal amount to the Anchor
val transfer =
wallet
.stellar()
.transaction(walletKeyPair, memo = Pair(MemoType.HASH, withdrawTxn.withdrawMemo))
.transfer(withdrawTxn.withdrawAnchorAccount, USDC, "1")
.build()
transfer.sign(walletKeyPair)
wallet.stellar().submitTransaction(transfer)
Log.info("Transferring 1 USDC to Anchor account: ${withdrawTxn.withdrawAnchorAccount}")
transactionWithRetry {
val transfer =
wallet
.stellar()
.transaction(walletKeyPair, memo = Pair(MemoType.HASH, withdrawTxn.withdrawMemo))
.transfer(withdrawTxn.withdrawAnchorAccount, USDC, "1")
.build()
transfer.sign(walletKeyPair)
wallet.stellar().submitTransaction(transfer)
}
Log.info("Transfer complete")
waitStatus(withdraw.id, COMPLETED, sep6Client)

val expectedStatuses =
Expand Down Expand Up @@ -189,19 +203,20 @@ class Sep6End2EndTest : AbstractIntegrationTests(TestConfig(testProfileName = "d
expectedStatus: SepTransactionStatus,
sep6Client: Sep6Client
) {
var status: String? = null
for (i in 0..maxTries) {
val transaction = sep6Client.getTransaction(mapOf("id" to id))
if (expectedStatus.status != transaction.transaction.status) {
Log.info("Transaction status: ${transaction.transaction.status}")
} else {
Log.info("${GsonUtils.getInstance().toJson(transaction)}")
if (!status.equals(transaction.transaction.status)) {
status = transaction.transaction.status
Log.info(
"Transaction status ${transaction.transaction.status} matched expected status $expectedStatus"
"Transaction(${transaction.transaction.id}) status changed to ${status}. Message: ${transaction.transaction.message}"
)
}
if (transaction.transaction.status == expectedStatus.status) {
return
}
delay(1.seconds)
}
fail("Transaction status did not match expected status $expectedStatus")
fail("Transaction status $status did not match expected status $expectedStatus")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ open class Sep12Tests : AbstractIntegrationTests(TestConfig(testProfileName = "d

// Upload a customer
printRequest("Calling PUT /customer", customer)
var pr = anchor.sep12(token).add(customer, "sep24")
var pr = anchor.sep12(token).add(customer, type = "sep24")
printResponse(pr)

// make sure the customer was uploaded correctly.
printRequest("Calling GET /customer", customer)
var gr = anchor.sep12(token).getByIdAndType(pr.id, "sep24")
var gr = anchor.sep12(token).get(pr.id, type = "sep24")
printResponse(gr)

assertEquals(pr.id, gr.id)
Expand All @@ -47,12 +47,12 @@ open class Sep12Tests : AbstractIntegrationTests(TestConfig(testProfileName = "d

// Modify the customer
printRequest("Calling PUT /customer", customer)
pr = anchor.sep12(token).add(customer, "sep31-receiver")
pr = anchor.sep12(token).add(customer, type = "sep31-receiver")
printResponse(pr)

// Make sure the customer is modified correctly.
printRequest("Calling GET /customer", customer)
gr = anchor.sep12(token).getByIdAndType(pr.id, "sep31-receiver")
gr = anchor.sep12(token).get(pr.id, type = "sep31-receiver")
printResponse(gr)

assertEquals(pr.id, gr.id)
Expand All @@ -63,7 +63,7 @@ open class Sep12Tests : AbstractIntegrationTests(TestConfig(testProfileName = "d
anchor.sep12(token).delete(walletKeyPair.address)

val ex: ClientRequestException = assertThrows {
anchor.sep12(token).getByIdAndType(pr.id, "sep31-receiver")
anchor.sep12(token).get(pr.id, type = "sep31-receiver")
}
println(ex)
}
Expand Down
Loading

0 comments on commit 98ff5ab

Please sign in to comment.