diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/AbstractIntegrationTest.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/AbstractIntegrationTest.kt index 9856b1cb43..3c90019dd2 100644 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/AbstractIntegrationTest.kt +++ b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/AbstractIntegrationTest.kt @@ -3,7 +3,10 @@ package org.stellar.anchor.platform import io.ktor.client.plugins.* import io.ktor.http.* import kotlinx.coroutines.runBlocking -import org.stellar.anchor.platform.test.* +import org.stellar.anchor.platform.test.CustodyApiTests +import org.stellar.anchor.platform.test.PlatformApiCustodyTests +import org.stellar.anchor.platform.test.Sep24BaseEnd2EndTest +import org.stellar.anchor.platform.test.Sep31End2EndTests import org.stellar.anchor.util.Sep1Helper import org.stellar.walletsdk.ApplicationConfiguration import org.stellar.walletsdk.StellarConfiguration @@ -30,11 +33,11 @@ open class AbstractIntegrationTest(private val config: TestConfig) { val testProfileRunner = TestProfileExecutor(config) lateinit var platformApiCustodyTests: PlatformApiCustodyTests lateinit var custodyApiTests: CustodyApiTests - lateinit var sep24RpcE2eTests: Sep24RpcEnd2EndTests - lateinit var sep24CustodyE2eTests: Sep24CustodyEnd2EndTests - lateinit var sep24CustodyRpcE2eTests: Sep24CustodyRpcEnd2EndTests - lateinit var sep31RpcE2eTests: Sep31RpcEnd2EndTests - lateinit var sep31CustodyRpcE2eTests: Sep31CustodyRpcEnd2EndTests + lateinit var sep24RpcE2eTests: Sep24BaseEnd2EndTest + lateinit var sep24CustodyE2eTests: Sep24BaseEnd2EndTest + lateinit var sep24CustodyRpcE2eTests: Sep24BaseEnd2EndTest + lateinit var sep31RpcE2eTests: Sep31End2EndTests + lateinit var sep31CustodyRpcE2eTests: Sep31End2EndTests fun setUp(envMap: Map) { envMap.forEach { (key, value) -> config.env[key] = value } @@ -56,11 +59,11 @@ open class AbstractIntegrationTest(private val config: TestConfig) { platformApiCustodyTests = PlatformApiCustodyTests(config, toml, jwt) custodyApiTests = CustodyApiTests(config, toml, jwt) - sep24CustodyE2eTests = Sep24CustodyEnd2EndTests(config, jwt) - sep24RpcE2eTests = Sep24RpcEnd2EndTests(config, jwt) - sep24CustodyRpcE2eTests = Sep24CustodyRpcEnd2EndTests(config, jwt) - sep31RpcE2eTests = Sep31RpcEnd2EndTests(config, toml, jwt) - sep31CustodyRpcE2eTests = Sep31CustodyRpcEnd2EndTests(config, toml, jwt) + sep24CustodyE2eTests = Sep24BaseEnd2EndTest(config, jwt) + sep24RpcE2eTests = Sep24BaseEnd2EndTest(config, jwt) + sep24CustodyRpcE2eTests = Sep24BaseEnd2EndTest(config, jwt) + sep31RpcE2eTests = Sep31End2EndTests(config, toml, jwt) + sep31CustodyRpcE2eTests = Sep31End2EndTests(config, toml, jwt) } private suspend fun auth(): String { diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24RpcEnd2EndTests.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24BaseEnd2EndTest.kt similarity index 99% rename from extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24RpcEnd2EndTests.kt rename to extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24BaseEnd2EndTest.kt index c251687961..ce6e6b0704 100644 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24RpcEnd2EndTests.kt +++ b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24BaseEnd2EndTest.kt @@ -40,7 +40,8 @@ import org.stellar.walletsdk.horizon.SigningKeyPair import org.stellar.walletsdk.horizon.sign import org.stellar.walletsdk.horizon.transaction.transferWithdrawalTransaction -class Sep24RpcEnd2EndTests(config: TestConfig, val jwt: String) { +/** TODO: This should be replaced by Sep24End2EndTest */ +class Sep24BaseEnd2EndTest(config: TestConfig, val jwt: String) { private val walletSecretKey = System.getenv("WALLET_SECRET_KEY") ?: CLIENT_WALLET_SECRET private val keypair = SigningKeyPair.fromSecret(walletSecretKey) private val wallet = diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24CustodyEnd2EndTests.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24CustodyEnd2EndTests.kt deleted file mode 100644 index 43c5d6853f..0000000000 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24CustodyEnd2EndTests.kt +++ /dev/null @@ -1,347 +0,0 @@ -package org.stellar.anchor.platform.test - -import io.ktor.client.* -import io.ktor.client.plugins.* -import io.ktor.client.request.* -import io.ktor.http.* -import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.fail -import kotlin.time.Duration.Companion.seconds -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Assertions.assertNotNull -import org.springframework.web.util.UriComponentsBuilder -import org.stellar.anchor.api.callback.SendEventRequest -import org.stellar.anchor.api.callback.SendEventRequestPayload -import org.stellar.anchor.api.event.AnchorEvent -import org.stellar.anchor.api.event.AnchorEvent.Type.TRANSACTION_CREATED -import org.stellar.anchor.api.event.AnchorEvent.Type.TRANSACTION_STATUS_CHANGED -import org.stellar.anchor.api.sep.SepTransactionStatus -import org.stellar.anchor.api.sep.sep24.Sep24GetTransactionResponse -import org.stellar.anchor.auth.JwtService -import org.stellar.anchor.auth.Sep24InteractiveUrlJwt -import org.stellar.anchor.platform.CLIENT_WALLET_SECRET -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.walletsdk.ApplicationConfiguration -import org.stellar.walletsdk.InteractiveFlowResponse -import org.stellar.walletsdk.StellarConfiguration -import org.stellar.walletsdk.Wallet -import org.stellar.walletsdk.anchor.* -import org.stellar.walletsdk.anchor.TransactionStatus.* -import org.stellar.walletsdk.asset.IssuedAssetId -import org.stellar.walletsdk.asset.StellarAssetId -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 - -class Sep24CustodyEnd2EndTests(config: TestConfig, val jwt: String) { - private val walletSecretKey = System.getenv("WALLET_SECRET_KEY") ?: CLIENT_WALLET_SECRET - private val keypair = SigningKeyPair.fromSecret(walletSecretKey) - private val wallet = - Wallet( - StellarConfiguration.Testnet, - ApplicationConfiguration { defaultRequest { url { protocol = URLProtocol.HTTP } } } - ) - private val client = HttpClient { - install(HttpTimeout) { - requestTimeoutMillis = 300000 - connectTimeoutMillis = 300000 - socketTimeoutMillis = 300000 - } - } - private val anchor = - wallet.anchor(config.env["anchor.domain"]!!) { - install(HttpTimeout) { - requestTimeoutMillis = 300000 - connectTimeoutMillis = 300000 - socketTimeoutMillis = 300000 - } - } - private val maxTries = 90 - private val anchorReferenceServerClient = - AnchorReferenceServerClient(Url(config.env["reference.server.url"]!!)) - private val walletServerClient = WalletServerClient(Url(config.env["wallet.server.url"]!!)) - private val jwtService: JwtService = - JwtService( - config.env["secret.sep10.jwt_secret"]!!, - config.env["secret.sep24.interactive_url.jwt_secret"]!!, - config.env["secret.sep24.more_info_url.jwt_secret"]!!, - config.env["secret.callback_api.auth_secret"]!!, - config.env["secret.platform_api.auth_secret"]!!, - config.env["secret.custody_server.auth_secret"]!! - ) - - private fun `test typical deposit end-to-end flow`(asset: StellarAssetId, amount: String) = - runBlocking { - walletServerClient.clearCallbacks() - anchorReferenceServerClient.clearEvents() - - val token = anchor.auth().authenticate(keypair) - val response = makeDeposit(asset, amount, token) - - // Assert the interactive URL JWT is valid - val params = UriComponentsBuilder.fromUriString(response.url).build().queryParams - val cipher = params["token"]!![0] - val interactiveJwt = jwtService.decode(cipher, Sep24InteractiveUrlJwt::class.java) - assertEquals("referenceCustodial", interactiveJwt.claims[JwtService.CLIENT_NAME]) - - // Wait for the status to change to COMPLETED - waitForTxnStatus(response.id, COMPLETED, token) - - // Check if the transaction can be listed by stellar transaction id - val fetchedTxn = anchor.interactive().getTransaction(response.id, token) as DepositTransaction - val transactionByStellarId = - anchor - .interactive() - .getTransactionBy(token, stellarTransactionId = fetchedTxn.stellarTransactionId) - assertEquals(fetchedTxn.id, transactionByStellarId.id) - - // Check the events sent to the reference server are recorded correctly - val actualEvents = waitForBusinessServerEvents(response.id, 5) - assertEvents(actualEvents, expectedStatuses) - - // Check the callbacks sent to the wallet reference server are recorded correctly - val actualCallbacks = waitForWalletServerCallbacks(response.id, 5) - assertCallbacks(actualCallbacks, expectedStatuses) - } - - private suspend fun makeDeposit( - asset: StellarAssetId, - amount: String, - token: AuthToken - ): InteractiveFlowResponse { - // Start interactive deposit - val deposit = anchor.interactive().deposit(asset, token, mapOf("amount" to amount)) - - // Get transaction status and make sure it is INCOMPLETE - val transaction = anchor.interactive().getTransaction(deposit.id, token) - assertEquals(INCOMPLETE, transaction.status) - // Make sure the interactive url is valid. This will also start the reference server's - // withdrawal process. - val resp = client.get(deposit.url) - info("accessing ${deposit.url}...") - assertEquals(200, resp.status.value) - - return deposit - } - - private fun assertEvents( - actualEvents: List?, - expectedStatuses: List> - ) { - assertNotNull(actualEvents) - actualEvents?.let { - assertEquals(expectedStatuses.size, actualEvents.size) - - expectedStatuses.forEachIndexed { index, expectedStatus -> - actualEvents[index].let { actualEvent -> - assertNotNull(actualEvent.id) - assertNotNull(actualEvent.timestamp) - assertEquals(expectedStatus.first.type, actualEvent.type) - org.junit.jupiter.api.Assertions.assertTrue( - actualEvent.payload is SendEventRequestPayload - ) - assertEquals(expectedStatus.second, actualEvent.payload.transaction.status) - } - } - } - } - - private fun assertCallbacks( - actualCallbacks: List?, - expectedStatuses: List> - ) { - assertNotNull(actualCallbacks) - actualCallbacks?.let { - assertEquals(expectedStatuses.size, actualCallbacks.size) - - expectedStatuses.forEachIndexed { index, expectedStatus -> - actualCallbacks[index].let { actualCallback -> - assertNotNull(actualCallback.transaction.id) - assertEquals(expectedStatus.second.status, actualCallback.transaction.status) - } - } - } - } - - private fun `test typical withdraw end-to-end flow`(asset: StellarAssetId, amount: String) { - `test typical withdraw end-to-end flow`(asset, mapOf("amount" to amount)) - } - - private fun `test typical withdraw end-to-end flow`( - asset: StellarAssetId, - extraFields: Map - ) = runBlocking { - walletServerClient.clearCallbacks() - anchorReferenceServerClient.clearEvents() - - val token = anchor.auth().authenticate(keypair) - val withdrawTxn = anchor.interactive().withdraw(asset, token, extraFields) - - // Get transaction status and make sure it is INCOMPLETE - val transaction = anchor.interactive().getTransaction(withdrawTxn.id, token) - assertEquals(INCOMPLETE, transaction.status) - // Make sure the interactive url is valid. This will also start the reference server's - // withdrawal process. - val resp = client.get(withdrawTxn.url) - info("accessing ${withdrawTxn.url}...") - assertEquals(200, resp.status.value) - // Wait for the status to change to PENDING_USER_TRANSFER_START - waitForTxnStatus(withdrawTxn.id, PENDING_USER_TRANSFER_START, token) - // 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) - wallet.stellar().submitTransaction(transfer) - // 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 = - anchor.interactive().getTransaction(withdrawTxn.id, token) as WithdrawalTransaction - val transactionByStellarId = - anchor - .interactive() - .getTransactionBy(token, stellarTransactionId = fetchTxn.stellarTransactionId) - assertEquals(fetchTxn.id, transactionByStellarId.id) - - // Check the events sent to the reference server are recorded correctly - val actualEvents = waitForBusinessServerEvents(withdrawTxn.id, 5) - assertEvents(actualEvents, expectedStatuses) - - // Check the callbacks sent to the wallet reference server are recorded correctly - val actualCallbacks = waitForWalletServerCallbacks(withdrawTxn.id, 5) - assertCallbacks(actualCallbacks, expectedStatuses) - } - - private suspend fun waitForWalletServerCallbacks( - txnId: String, - count: Int - ): List? { - var retries = 5 - var callbacks: List? = null - while (retries > 0) { - callbacks = walletServerClient.getCallbacks(txnId, Sep24GetTransactionResponse::class.java) - if (callbacks.size == count) { - return callbacks - } - delay(1.seconds) - retries-- - } - return callbacks - } - - private suspend fun waitForBusinessServerEvents( - txnId: String, - count: Int - ): List? { - var retries = 5 - var events: List? = null - while (retries > 0) { - events = anchorReferenceServerClient.getEvents(txnId) - if (events.size == count) { - return events - } - delay(1.seconds) - retries-- - } - return events - } - - private suspend fun waitForTxnStatus( - id: String, - expectedStatus: TransactionStatus, - token: AuthToken - ) { - var status: TransactionStatus? = null - - for (i in 0..maxTries) { - // Get transaction info - val transaction = anchor.interactive().getTransaction(id, token) - - if (status != transaction.status) { - status = transaction.status - - info( - "Transaction(id=${transaction.id}) status changed to $status. Message: ${transaction.message}" - ) - } - - delay(1.seconds) - - if (transaction.status == expectedStatus) { - return - } - } - - fail("Transaction wasn't $expectedStatus in $maxTries tries, last status: $status") - } - - private fun `test created transactions show up in the get history call`( - asset: StellarAssetId, - amount: String - ) = runBlocking { - val newAcc = wallet.stellar().account().createKeyPair() - - val tx = - wallet - .stellar() - .transaction(keypair) - .sponsoring(keypair, newAcc) { - createAccount(newAcc) - addAssetSupport(USDC) - } - .build() - .sign(keypair) - .sign(newAcc) - - wallet.stellar().submitTransaction(tx) - - val token = anchor.auth().authenticate(newAcc) - val deposits = - (0..1).map { - val txnId = makeDeposit(asset, amount, token).id - waitForTxnStatus(txnId, COMPLETED, token) - txnId - } - val history = anchor.interactive().getTransactionsForAsset(asset, token) - - Assertions.assertThat(history).allMatch { deposits.contains(it.id) } - } - - fun testAll() { - info("Running SEP-24 USDC end-to-end tests...") - `test typical deposit end-to-end flow`(USDC, "1.1") - `test typical withdraw end-to-end flow`(USDC, "1.1") - `test created transactions show up in the get history call`(USDC, "1.1") - info("Running SEP-24 XLM end-to-end tests...") - `test typical deposit end-to-end flow`(XLM, "0.00001") - `test typical withdraw end-to-end flow`(XLM, "0.00001") - `test created transactions show up in the get history call`(XLM, "0.00001") - } - - companion object { - private val USDC = - IssuedAssetId("USDC", "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5") - private val expectedStatuses = - listOf( - TRANSACTION_CREATED to SepTransactionStatus.INCOMPLETE, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.PENDING_USR_TRANSFER_START, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.PENDING_ANCHOR, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.PENDING_STELLAR, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.COMPLETED - ) - } -} diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24CustodyRpcEnd2EndTests.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24CustodyRpcEnd2EndTests.kt deleted file mode 100644 index 6f8b5b233c..0000000000 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep24CustodyRpcEnd2EndTests.kt +++ /dev/null @@ -1,347 +0,0 @@ -package org.stellar.anchor.platform.test - -import io.ktor.client.* -import io.ktor.client.plugins.* -import io.ktor.client.request.* -import io.ktor.http.* -import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.fail -import kotlin.time.Duration.Companion.seconds -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Assertions.assertNotNull -import org.springframework.web.util.UriComponentsBuilder -import org.stellar.anchor.api.callback.SendEventRequest -import org.stellar.anchor.api.callback.SendEventRequestPayload -import org.stellar.anchor.api.event.AnchorEvent -import org.stellar.anchor.api.event.AnchorEvent.Type.TRANSACTION_CREATED -import org.stellar.anchor.api.event.AnchorEvent.Type.TRANSACTION_STATUS_CHANGED -import org.stellar.anchor.api.sep.SepTransactionStatus -import org.stellar.anchor.api.sep.sep24.Sep24GetTransactionResponse -import org.stellar.anchor.auth.JwtService -import org.stellar.anchor.auth.Sep24InteractiveUrlJwt -import org.stellar.anchor.platform.CLIENT_WALLET_SECRET -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.walletsdk.ApplicationConfiguration -import org.stellar.walletsdk.InteractiveFlowResponse -import org.stellar.walletsdk.StellarConfiguration -import org.stellar.walletsdk.Wallet -import org.stellar.walletsdk.anchor.* -import org.stellar.walletsdk.anchor.TransactionStatus.* -import org.stellar.walletsdk.asset.IssuedAssetId -import org.stellar.walletsdk.asset.StellarAssetId -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 - -class Sep24CustodyRpcEnd2EndTests(config: TestConfig, val jwt: String) { - private val walletSecretKey = System.getenv("WALLET_SECRET_KEY") ?: CLIENT_WALLET_SECRET - private val keypair = SigningKeyPair.fromSecret(walletSecretKey) - private val wallet = - Wallet( - StellarConfiguration.Testnet, - ApplicationConfiguration { defaultRequest { url { protocol = URLProtocol.HTTP } } } - ) - private val client = HttpClient { - install(HttpTimeout) { - requestTimeoutMillis = 300000 - connectTimeoutMillis = 300000 - socketTimeoutMillis = 300000 - } - } - private val anchor = - wallet.anchor(config.env["anchor.domain"]!!) { - install(HttpTimeout) { - requestTimeoutMillis = 300000 - connectTimeoutMillis = 300000 - socketTimeoutMillis = 300000 - } - } - private val maxTries = 90 - private val anchorReferenceServerClient = - AnchorReferenceServerClient(Url(config.env["reference.server.url"]!!)) - private val walletServerClient = WalletServerClient(Url(config.env["wallet.server.url"]!!)) - private val jwtService: JwtService = - JwtService( - config.env["secret.sep10.jwt_secret"]!!, - config.env["secret.sep24.interactive_url.jwt_secret"]!!, - config.env["secret.sep24.more_info_url.jwt_secret"]!!, - config.env["secret.callback_api.auth_secret"]!!, - config.env["secret.platform_api.auth_secret"]!!, - config.env["secret.custody_server.auth_secret"]!! - ) - - private fun `test typical deposit end-to-end flow`(asset: StellarAssetId, amount: String) = - runBlocking { - walletServerClient.clearCallbacks() - anchorReferenceServerClient.clearEvents() - - val token = anchor.auth().authenticate(keypair) - val response = makeDeposit(asset, amount, token) - - // Assert the interactive URL JWT is valid - val params = UriComponentsBuilder.fromUriString(response.url).build().queryParams - val cipher = params["token"]!![0] - val interactiveJwt = jwtService.decode(cipher, Sep24InteractiveUrlJwt::class.java) - assertEquals("referenceCustodial", interactiveJwt.claims[JwtService.CLIENT_NAME]) - - // Wait for the status to change to COMPLETED - waitForTxnStatus(response.id, COMPLETED, token) - - // Check if the transaction can be listed by stellar transaction id - val fetchedTxn = anchor.interactive().getTransaction(response.id, token) as DepositTransaction - val transactionByStellarId = - anchor - .interactive() - .getTransactionBy(token, stellarTransactionId = fetchedTxn.stellarTransactionId) - assertEquals(fetchedTxn.id, transactionByStellarId.id) - - // Check the events sent to the reference server are recorded correctly - val actualEvents = waitForBusinessServerEvents(response.id, 5) - assertEvents(actualEvents, expectedStatuses) - - // Check the callbacks sent to the wallet reference server are recorded correctly - val actualCallbacks = waitForWalletServerCallbacks(response.id, 5) - assertCallbacks(actualCallbacks, expectedStatuses) - } - - private suspend fun makeDeposit( - asset: StellarAssetId, - amount: String, - token: AuthToken - ): InteractiveFlowResponse { - // Start interactive deposit - val deposit = anchor.interactive().deposit(asset, token, mapOf("amount" to amount)) - - // Get transaction status and make sure it is INCOMPLETE - val transaction = anchor.interactive().getTransaction(deposit.id, token) - assertEquals(INCOMPLETE, transaction.status) - // Make sure the interactive url is valid. This will also start the reference server's - // withdrawal process. - val resp = client.get(deposit.url) - info("accessing ${deposit.url}...") - assertEquals(200, resp.status.value) - - return deposit - } - - private fun assertEvents( - actualEvents: List?, - expectedStatuses: List> - ) { - assertNotNull(actualEvents) - actualEvents?.let { - assertEquals(expectedStatuses.size, actualEvents.size) - - expectedStatuses.forEachIndexed { index, expectedStatus -> - actualEvents[index].let { actualEvent -> - assertNotNull(actualEvent.id) - assertNotNull(actualEvent.timestamp) - assertEquals(expectedStatus.first.type, actualEvent.type) - org.junit.jupiter.api.Assertions.assertTrue( - actualEvent.payload is SendEventRequestPayload - ) - assertEquals(expectedStatus.second, actualEvent.payload.transaction.status) - } - } - } - } - - private fun assertCallbacks( - actualCallbacks: List?, - expectedStatuses: List> - ) { - assertNotNull(actualCallbacks) - actualCallbacks?.let { - assertEquals(expectedStatuses.size, actualCallbacks.size) - - expectedStatuses.forEachIndexed { index, expectedStatus -> - actualCallbacks[index].let { actualCallback -> - assertNotNull(actualCallback.transaction.id) - assertEquals(expectedStatus.second.status, actualCallback.transaction.status) - } - } - } - } - - private fun `test typical withdraw end-to-end flow`(asset: StellarAssetId, amount: String) { - `test typical withdraw end-to-end flow`(asset, mapOf("amount" to amount)) - } - - private fun `test typical withdraw end-to-end flow`( - asset: StellarAssetId, - extraFields: Map - ) = runBlocking { - walletServerClient.clearCallbacks() - anchorReferenceServerClient.clearEvents() - - val token = anchor.auth().authenticate(keypair) - val withdrawTxn = anchor.interactive().withdraw(asset, token, extraFields) - - // Get transaction status and make sure it is INCOMPLETE - val transaction = anchor.interactive().getTransaction(withdrawTxn.id, token) - assertEquals(INCOMPLETE, transaction.status) - // Make sure the interactive url is valid. This will also start the reference server's - // withdrawal process. - val resp = client.get(withdrawTxn.url) - info("accessing ${withdrawTxn.url}...") - assertEquals(200, resp.status.value) - // Wait for the status to change to PENDING_USER_TRANSFER_START - waitForTxnStatus(withdrawTxn.id, PENDING_USER_TRANSFER_START, token) - // 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) - wallet.stellar().submitTransaction(transfer) - // 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 = - anchor.interactive().getTransaction(withdrawTxn.id, token) as WithdrawalTransaction - val transactionByStellarId = - anchor - .interactive() - .getTransactionBy(token, stellarTransactionId = fetchTxn.stellarTransactionId) - assertEquals(fetchTxn.id, transactionByStellarId.id) - - // Check the events sent to the reference server are recorded correctly - val actualEvents = waitForBusinessServerEvents(withdrawTxn.id, 4) - assertEvents(actualEvents, expectedStatuses) - - // Check the callbacks sent to the wallet reference server are recorded correctly - val actualCallbacks = waitForWalletServerCallbacks(withdrawTxn.id, 4) - assertCallbacks(actualCallbacks, expectedStatuses) - } - - private suspend fun waitForWalletServerCallbacks( - txnId: String, - count: Int - ): List? { - var retries = 5 - var callbacks: List? = null - while (retries > 0) { - callbacks = walletServerClient.getCallbacks(txnId, Sep24GetTransactionResponse::class.java) - if (callbacks.size == count) { - return callbacks - } - delay(1.seconds) - retries-- - } - return callbacks - } - - private suspend fun waitForBusinessServerEvents( - txnId: String, - count: Int - ): List? { - var retries = 5 - var events: List? = null - while (retries > 0) { - events = anchorReferenceServerClient.getEvents(txnId) - if (events.size == count) { - return events - } - delay(1.seconds) - retries-- - } - return events - } - - private suspend fun waitForTxnStatus( - id: String, - expectedStatus: TransactionStatus, - token: AuthToken - ) { - var status: TransactionStatus? = null - - for (i in 0..maxTries) { - // Get transaction info - val transaction = anchor.interactive().getTransaction(id, token) - - if (status != transaction.status) { - status = transaction.status - - info( - "Transaction(id=${transaction.id}) status changed to $status. Message: ${transaction.message}" - ) - } - - delay(1.seconds) - - if (transaction.status == expectedStatus) { - return - } - } - - fail("Transaction wasn't $expectedStatus in $maxTries tries, last status: $status") - } - - private fun `test created transactions show up in the get history call`( - asset: StellarAssetId, - amount: String - ) = runBlocking { - val newAcc = wallet.stellar().account().createKeyPair() - - val tx = - wallet - .stellar() - .transaction(keypair) - .sponsoring(keypair, newAcc) { - createAccount(newAcc) - addAssetSupport(USDC) - } - .build() - .sign(keypair) - .sign(newAcc) - - wallet.stellar().submitTransaction(tx) - - val token = anchor.auth().authenticate(newAcc) - val deposits = - (0..1).map { - val txnId = makeDeposit(asset, amount, token).id - waitForTxnStatus(txnId, COMPLETED, token) - txnId - } - val history = anchor.interactive().getTransactionsForAsset(asset, token) - - Assertions.assertThat(history).allMatch { deposits.contains(it.id) } - } - - fun testAll() { - info("Running SEP-24 USDC end-to-end tests...") - `test typical deposit end-to-end flow`(USDC, "1.1") - `test typical withdraw end-to-end flow`(USDC, "1.1") - `test created transactions show up in the get history call`(USDC, "1.1") - info("Running SEP-24 XLM end-to-end tests...") - `test typical deposit end-to-end flow`(XLM, "0.00001") - `test typical withdraw end-to-end flow`(XLM, "0.00001") - `test created transactions show up in the get history call`(XLM, "0.00001") - } - - companion object { - private val USDC = - IssuedAssetId("USDC", "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5") - private val expectedStatuses = - listOf( - TRANSACTION_CREATED to SepTransactionStatus.INCOMPLETE, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.PENDING_USR_TRANSFER_START, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.PENDING_ANCHOR, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.PENDING_STELLAR, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.COMPLETED - ) - } -} diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep31CustodyRpcEnd2EndTests.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep31CustodyRpcEnd2EndTests.kt deleted file mode 100644 index 774d9c044b..0000000000 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep31CustodyRpcEnd2EndTests.kt +++ /dev/null @@ -1,260 +0,0 @@ -package org.stellar.anchor.platform.test - -import io.ktor.client.plugins.* -import io.ktor.http.* -import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.fail -import kotlin.time.Duration.Companion.seconds -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Assertions.assertNotNull -import org.stellar.anchor.api.callback.SendEventRequest -import org.stellar.anchor.api.callback.SendEventRequestPayload -import org.stellar.anchor.api.event.AnchorEvent -import org.stellar.anchor.api.event.AnchorEvent.Type.TRANSACTION_CREATED -import org.stellar.anchor.api.event.AnchorEvent.Type.TRANSACTION_STATUS_CHANGED -import org.stellar.anchor.api.sep.SepTransactionStatus -import org.stellar.anchor.api.sep.sep12.Sep12PutCustomerRequest -import org.stellar.anchor.api.sep.sep31.Sep31GetTransactionResponse -import org.stellar.anchor.api.sep.sep31.Sep31PostTransactionRequest -import org.stellar.anchor.apiclient.PlatformApiClient -import org.stellar.anchor.auth.AuthHelper -import org.stellar.anchor.client.Sep12Client -import org.stellar.anchor.client.Sep31Client -import org.stellar.anchor.client.Sep38Client -import org.stellar.anchor.platform.CLIENT_WALLET_SECRET -import org.stellar.anchor.platform.TestConfig -import org.stellar.anchor.platform.integrationtest.Sep12Tests.Companion.testCustomer1Json -import org.stellar.anchor.platform.integrationtest.Sep12Tests.Companion.testCustomer2Json -import org.stellar.anchor.util.GsonUtils -import org.stellar.anchor.util.Log.info -import org.stellar.anchor.util.MemoHelper -import org.stellar.anchor.util.Sep1Helper -import org.stellar.reference.client.AnchorReferenceServerClient -import org.stellar.reference.wallet.WalletServerClient -import org.stellar.walletsdk.ApplicationConfiguration -import org.stellar.walletsdk.StellarConfiguration -import org.stellar.walletsdk.Wallet -import org.stellar.walletsdk.anchor.MemoType -import org.stellar.walletsdk.asset.IssuedAssetId -import org.stellar.walletsdk.asset.StellarAssetId -import org.stellar.walletsdk.horizon.SigningKeyPair -import org.stellar.walletsdk.horizon.sign - -class Sep31CustodyRpcEnd2EndTests( - config: TestConfig, - val toml: Sep1Helper.TomlContent, - val jwt: String -) { - private val gson = GsonUtils.getInstance() - private val walletSecretKey = System.getenv("WALLET_SECRET_KEY") ?: CLIENT_WALLET_SECRET - private val keypair = SigningKeyPair.fromSecret(walletSecretKey) - private val wallet = - Wallet( - StellarConfiguration.Testnet, - ApplicationConfiguration { defaultRequest { url { protocol = URLProtocol.HTTP } } } - ) - private val maxTries = 90 - private val anchorReferenceServerClient = - AnchorReferenceServerClient(Url(config.env["reference.server.url"]!!)) - private val walletServerClient = WalletServerClient(Url(config.env["wallet.server.url"]!!)) - private val sep12Client = Sep12Client(toml.getString("KYC_SERVER"), jwt) - private val sep31Client = Sep31Client(toml.getString("DIRECT_PAYMENT_SERVER"), jwt) - private val sep38Client = Sep38Client(toml.getString("ANCHOR_QUOTE_SERVER"), jwt) - private val platformApiClient = - PlatformApiClient(AuthHelper.forNone(), config.env["platform.server.url"]!!) - - private fun assertEvents( - actualEvents: List?, - expectedStatuses: List> - ) { - assertNotNull(actualEvents) - actualEvents?.let { - assertEquals(expectedStatuses.size, actualEvents.size) - - expectedStatuses.forEachIndexed { index, expectedStatus -> - actualEvents[index].let { actualEvent -> - assertNotNull(actualEvent.id) - assertNotNull(actualEvent.timestamp) - assertEquals(expectedStatus.first.type, actualEvent.type) - Assertions.assertTrue(actualEvent.payload is SendEventRequestPayload) - assertEquals(expectedStatus.second, actualEvent.payload.transaction.status) - } - } - } - } - - private fun assertCallbacks( - actualCallbacks: List?, - expectedStatuses: List> - ) { - assertNotNull(actualCallbacks) - actualCallbacks?.let { - assertEquals(expectedStatuses.size, actualCallbacks.size) - - expectedStatuses.forEachIndexed { index, expectedStatus -> - actualCallbacks[index].let { actualCallback -> - assertNotNull(actualCallback.transaction.id) - assertEquals(expectedStatus.second.status, actualCallback.transaction.status) - } - } - } - } - - private fun `test typical receive end-to-end flow`(asset: StellarAssetId, amount: String) = - runBlocking { - walletServerClient.clearCallbacks() - anchorReferenceServerClient.clearEvents() - - val senderCustomerRequest = - gson.fromJson(testCustomer1Json, Sep12PutCustomerRequest::class.java) - val senderCustomer = sep12Client.putCustomer(senderCustomerRequest) - - // Create receiver customer - val receiverCustomerRequest = - gson.fromJson(testCustomer2Json, Sep12PutCustomerRequest::class.java) - val receiverCustomer = sep12Client.putCustomer(receiverCustomerRequest) - - val quote = sep38Client.postQuote(asset.sep38, amount, FIAT_USD) - - // POST Sep31 transaction - val txnRequest = gson.fromJson(postSep31TxnRequest, Sep31PostTransactionRequest::class.java) - txnRequest.senderId = senderCustomer!!.id - txnRequest.receiverId = receiverCustomer!!.id - txnRequest.quoteId = quote.id - val postTxResponse = sep31Client.postTransaction(txnRequest) - - anchorReferenceServerClient.processSep31Receive(postTxResponse.id) - - // Get transaction status and make sure it is PENDING_SENDER - val transaction = platformApiClient.getTransaction(postTxResponse.id) - assertEquals(SepTransactionStatus.PENDING_SENDER, transaction.status) - - val memoType: MemoType = - when (postTxResponse.stellarMemoType) { - MemoHelper.memoTypeAsString(org.stellar.sdk.xdr.MemoType.MEMO_ID) -> { - MemoType.ID - } - MemoHelper.memoTypeAsString(org.stellar.sdk.xdr.MemoType.MEMO_HASH) -> { - MemoType.HASH - } - else -> { - MemoType.TEXT - } - } - - // Submit transfer transaction - val transfer = - wallet - .stellar() - .transaction(keypair) - .transfer(postTxResponse.stellarAccountId, asset, amount) - .setMemo(Pair(memoType, postTxResponse.stellarMemo)) - .build() - transfer.sign(keypair) - wallet.stellar().submitTransaction(transfer) - - // Wait for the status to change to COMPLETED - waitStatus(postTxResponse.id, SepTransactionStatus.COMPLETED) - - // Check the events sent to the reference server are recorded correctly - val actualEvents = waitForBusinessServerEvents(postTxResponse.id, 3) - assertEvents(actualEvents, expectedStatuses) - - // Check the callbacks sent to the wallet reference server are recorded correctly - val actualCallbacks = waitForWalletServerCallbacks(postTxResponse.id, 3) - assertCallbacks(actualCallbacks, expectedStatuses) - } - - private suspend fun waitForWalletServerCallbacks( - txnId: String, - count: Int - ): List? { - var retries = 5 - var callbacks: List? = null - while (retries > 0) { - callbacks = walletServerClient.getCallbacks(txnId, Sep31GetTransactionResponse::class.java) - if (callbacks.size == count) { - return callbacks - } - delay(1.seconds) - retries-- - } - return callbacks - } - - private suspend fun waitForBusinessServerEvents( - txnId: String, - count: Int - ): List? { - var retries = 5 - var events: List? = null - while (retries > 0) { - events = anchorReferenceServerClient.getEvents(txnId) - if (events.size == count) { - return events - } - delay(1.seconds) - retries-- - } - return events - } - - private suspend fun waitStatus(id: String, expectedStatus: SepTransactionStatus) { - var status: SepTransactionStatus? = null - - for (i in 0..maxTries) { - // Get transaction info - val transaction = platformApiClient.getTransaction(id) - - val current = transaction.status - info("Expected: $expectedStatus. Current: $current") - if (status != transaction.status) { - status = transaction.status - info("Deposit transaction status changed to $status. Message: ${transaction.message}") - } - - delay(1.seconds) - - if (transaction.status == expectedStatus) { - return - } - } - - fail("Transaction wasn't $expectedStatus in $maxTries tries, last status: $status") - } - - fun testAll() { - info("Running SEP-31 USDC end-to-end tests...") - `test typical receive end-to-end flow`(USDC, "5") - } - - companion object { - private val USDC = - IssuedAssetId("USDC", "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5") - private const val FIAT_USD = "iso4217:USD" - private val expectedStatuses = - listOf( - TRANSACTION_CREATED to SepTransactionStatus.PENDING_SENDER, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.PENDING_RECEIVER, - TRANSACTION_STATUS_CHANGED to SepTransactionStatus.COMPLETED - ) - } - - private val postSep31TxnRequest = - """{ - "amount": "5", - "asset_code": "USDC", - "asset_issuer": "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", - "receiver_id": "MOCK_RECEIVER_ID", - "sender_id": "MOCK_SENDER_ID", - "fields": { - "transaction": { - "receiver_routing_number": "r0123", - "receiver_account_number": "a0456", - "type": "SWIFT" - } - } -}""" -} diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep31RpcEnd2EndTests.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep31End2EndTests.kt similarity index 98% rename from extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep31RpcEnd2EndTests.kt rename to extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep31End2EndTests.kt index e0eed1144d..411a818180 100644 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep31RpcEnd2EndTests.kt +++ b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep31End2EndTests.kt @@ -42,7 +42,8 @@ import org.stellar.walletsdk.asset.StellarAssetId import org.stellar.walletsdk.horizon.SigningKeyPair import org.stellar.walletsdk.horizon.sign -class Sep31RpcEnd2EndTests(config: TestConfig, val toml: Sep1Helper.TomlContent, val jwt: String) { +/** TODO: This should be moved into essential tests */ +class Sep31End2EndTests(config: TestConfig, val toml: Sep1Helper.TomlContent, val jwt: String) { private val gson = GsonUtils.getInstance() private val walletSecretKey = System.getenv("WALLET_SECRET_KEY") ?: CLIENT_WALLET_SECRET private val keypair = SigningKeyPair.fromSecret(walletSecretKey)