gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] 06/06: Conversion service tests.


From: gnunet
Subject: [libeufin] 06/06: Conversion service tests.
Date: Mon, 22 May 2023 16:43:08 +0200

This is an automated email from the git hooks/post-receive script.

ms pushed a commit to branch master
in repository libeufin.

commit 26d9e2a804f50d27d3e610b40fa77b0729a442e0
Author: MS <ms@taler.net>
AuthorDate: Mon May 22 16:41:53 2023 +0200

    Conversion service tests.
    
    Covering all the HTTP response paths.
---
 nexus/src/test/kotlin/ConversionServiceTest.kt | 204 ++++++++++++++++---------
 1 file changed, 128 insertions(+), 76 deletions(-)

diff --git a/nexus/src/test/kotlin/ConversionServiceTest.kt 
b/nexus/src/test/kotlin/ConversionServiceTest.kt
index 9bf1e005..356fcda8 100644
--- a/nexus/src/test/kotlin/ConversionServiceTest.kt
+++ b/nexus/src/test/kotlin/ConversionServiceTest.kt
@@ -1,35 +1,47 @@
-import com.fasterxml.jackson.databind.ObjectMapper
 import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
 import io.ktor.client.*
+import io.ktor.client.engine.cio.*
 import io.ktor.client.engine.mock.*
-import io.ktor.client.plugins.*
 import io.ktor.client.request.*
-import io.ktor.client.statement.*
 import io.ktor.http.*
 import io.ktor.server.testing.*
 import kotlinx.coroutines.*
 import org.jetbrains.exposed.sql.and
 import org.jetbrains.exposed.sql.transactions.transaction
-import org.junit.Ignore
 import org.junit.Test
-import tech.libeufin.nexus.bankaccount.getBankAccount
 import tech.libeufin.nexus.server.nexusApp
 import tech.libeufin.sandbox.*
-import tech.libeufin.util.internalServerError
 import tech.libeufin.util.parseAmount
 
 class ConversionServiceTest {
+    private fun CoroutineScope.launchBuyinMonitor(httpClient: HttpClient): Job 
{
+        val job = launch {
+            /**
+             * The runInterruptible wrapper lets code without suspension
+             * points be cancel()'d.  Without it, such code would ignore
+             * any call to cancel() and the test never return.
+             */
+            runInterruptible {
+                buyinMonitor(
+                    demobankName = "default",
+                    accountToCredit = "exchange-0",
+                    client = httpClient
+                )
+            }
+        }
+        return job
+    }
     /**
-     * Testing the buy-in monitor in the normal case: Nexus
-     * communicates a new incoming fiat transaction and the
-     * monitor wires funds to the exchange.
+     * Testing the buy-in monitor in all the HTTP scenarios,
+     * successful case, client's and server's error cases.
      */
     @Test
     fun buyinTest() {
-        // First create an incoming fiat payment _at Nexus_.
-        // This payment is addressed to the Nexus user whose
-        // (Nexus) credentials will be used by Sandbox to fetch
-        // new incoming fiat payments.
+        // 1, testing the successful case.
+        /* First create an incoming fiat payment _at Nexus_.
+          This payment is addressed to the Nexus user whose
+          (Nexus) credentials will be used by Sandbox to fetch
+          new incoming fiat payments. */
         withTestDatabase {
             prepSandboxDb(currency = "REGIO")
             prepNexusDb()
@@ -48,23 +60,13 @@ class ConversionServiceTest {
             )
             // Start Nexus, to let it serve the fiat transaction.
             testApplication {
+                val client = this.createClient {
+                    followRedirects = false
+                }
                 application(nexusApp)
                 // Start the buy-in monitor to let it download the fiat 
transaction.
                 runBlocking {
-                    val job = launch {
-                        /**
-                         * The runInterruptible wrapper lets code without 
suspension
-                         * points be cancel()'d.  Without it, such code would 
ignore
-                         * any call to cancel() and the test never return.
-                         */
-                        runInterruptible {
-                            buyinMonitor(
-                                demobankName = "default",
-                                accountToCredit = "exchange-0",
-                                client = client
-                            )
-                        }
-                    }
+                    val job = launchBuyinMonitor(client)
                     delay(1000L) // Lets the DB persist.
                     job.cancelAndJoin()
                 }
@@ -93,6 +95,48 @@ class ConversionServiceTest {
                 // and the regional currency.
                 assert(boughtIn.subject == reservePub && boughtIn.currency == 
"REGIO")
             }
+            // 2, testing the client side error case.
+            assertException<BuyinClientError>(
+                {
+                    runBlocking {
+                        /**
+                         * As soon as the buy-in monitor requests again the 
history
+                         * to Nexus, it'll get 400 from the mock client.
+                         */
+                        launchBuyinMonitor(getMockedClient { 
respondBadRequest() })
+                    }
+                }
+            )
+            /**
+             * 3, testing the server side error case.  Here the monitor should
+             * NOT throw any error and instead keep operating normally.  This 
allows
+             * Sandbox to tolerate server errors and retry the requests.
+             */
+            runBlocking {
+                /**
+                 * As soon as the buy-in monitor requests again the history
+                 * to Nexus, it'll get 500 from the mock client.
+                 */
+                val job = launchBuyinMonitor(getMockedClient { 
respondError(HttpStatusCode.InternalServerError) })
+                delay(1000L)
+                // Getting here means no exceptions.  Can now cancel the 
service.
+                job.cancelAndJoin()
+            }
+            /**
+             * 4, testing the unhandled error case.  This case is treated
+             * as a client error, to signal the calling logic to intervene.
+             */
+            assertException<BuyinClientError>(
+                {
+                    runBlocking {
+                        /**
+                         * As soon as the buy-in monitor requests again the 
history
+                         * to Nexus, it'll get 307 from the mock client.
+                         */
+                        launchBuyinMonitor(getMockedClient { respondRedirect() 
})
+                    }
+                }
+            )
         }
     }
     private fun CoroutineScope.launchCashoutMonitor(httpClient: HttpClient): 
Job {
@@ -119,7 +163,7 @@ class ConversionServiceTest {
     }
 
     // This function mocks a 500 response to a cash-out request.
-    private fun MockRequestHandleScope.mock500Response(request: 
HttpRequestData): HttpResponseData {
+    private fun MockRequestHandleScope.mock500Response(): HttpResponseData {
         return respondError(HttpStatusCode.InternalServerError)
     }
     // This function implements a mock server that checks the currency in the 
cash-out request.
@@ -137,8 +181,10 @@ class ConversionServiceTest {
         }
     }
 
-    private fun getMockClient(handler: 
MockRequestHandleScope.(HttpRequestData) -> HttpResponseData): HttpClient {
+    // Abstracts the mock handler installation.
+    private fun getMockedClient(handler: 
MockRequestHandleScope.(HttpRequestData) -> HttpResponseData): HttpClient {
         return HttpClient(MockEngine) {
+            followRedirects = false
             engine {
                 addHandler {
                         request -> handler(request)
@@ -146,7 +192,6 @@ class ConversionServiceTest {
             }
         }
     }
-
     /**
      * Checks that the cash-out monitor reacts after
      * a CRDT transaction arrives at the designated account.
@@ -160,9 +205,13 @@ class ConversionServiceTest {
             )
             prepNexusDb()
             testApplication {
+                val client = this.createClient {
+                    followRedirects = false
+                }
                 application(nexusApp)
                 // Mock server to intercept and inspect the cash-out request.
                 val checkCurrencyClient = HttpClient(MockEngine) {
+                    followRedirects = false
                     engine {
                         addHandler { 
                             request -> inspectCashoutCurrency(request)
@@ -202,9 +251,10 @@ class ConversionServiceTest {
                      */
                     job.cancelAndJoin()
                     val error500Client = HttpClient(MockEngine) {
+                        followRedirects = false
                         engine {
                             addHandler {
-                                    request -> mock500Response(request)
+                                    request -> mock500Response()
                             }
                         }
                     }
@@ -224,7 +274,7 @@ class ConversionServiceTest {
                         assert(bankaccount.lastFiatSubmission?.id?.value == 1L)
                     }
                     /* Removing now the mocked 500 response and checking that
-                     * indeed the cash-out does get sent.  */
+                     * the problematic cash-out get then sent.  */
                     job = launchCashoutMonitor(client) // Should find the non 
cashed-out wire transfer and react.
                     delay(1000L) // Lets the reaction complete.
                     job.cancelAndJoin()
@@ -234,61 +284,63 @@ class ConversionServiceTest {
                         assert(bankaccount.lastFiatSubmission?.subject == 
"fiat #1")
                     }
                     /**
-                     * 3, the client error case, where the conversion service 
is
-                     * supposed to exit the whole process.
+                     * 3, testing the client error case, where
+                     * the conversion service is supposed to throw exception.
                      */
-                    job = launchCashoutMonitor(
-                        getMockClient {
-                            /**
-                             * This causes the cash-out request sent to Nexus 
to
-                             * respond with 400.
-                             */
-                            respondBadRequest()
-                        }
-                    ) // Should find the non cashed-out wire transfer and 
react.
-                    // Triggering now a cash-out operation via a new wire 
transfer to admin.
-                    wireTransfer(
-                        debitAccount = "foo",
-                        creditAccount = "admin",
-                        subject = "fiat #2",
-                        amount = "REGIO:22"
-                    )
-                    delay(1000L) // Lets the reaction complete.
-                    job.cancelAndJoin()
-                    // Checking that the cash-out counter did NOT update.
-                    transaction {
-                        val bankaccount = getBankAccountFromLabel("admin")
-                        // Checks that the once failing cash-out did go 
through.
-                        assert(bankaccount.lastFiatSubmission?.subject == 
"fiat #1")
-                    }
+                    assertException<CashoutClientError>({
+                        runBlocking {
+                            launchCashoutMonitor(
+                                httpClient = getMockedClient {
+                                    tech.libeufin.sandbox.logger.debug("MOCK 
400")
+                                    /**
+                                     * This causes the cash-out request sent 
to Nexus to
+                                     * respond with 400.
+                                     */
+                                    respondBadRequest()
+                                }
+                            )
+                            // Triggering now a cash-out operation via a new 
wire transfer to admin.
+                            wireTransfer(
+                                debitAccount = "foo",
+                                creditAccount = "admin",
+                                subject = "fiat #2",
+                                amount = "REGIO:22"
+                            )
+                        }})
                     /**
                      * 4, checking a redirect response.  Because this is an 
unhandled
                      * error case, it is treated as a client error.  No need 
to wire a
-                     * new cash-out to trigger a cash-out request, since the 
last failing
+                     * new cash-out to trigger a cash-out request, since the 
last failed
                      * one will be retried.
                      */
-                    job = launchCashoutMonitor(
-                        getMockClient {
-                            /**
-                             * This causes the cash-out request sent to Nexus 
to
-                             * respond with 307 Temporary Redirect.
-                             */
-                            respondRedirect()
+                    assertException<CashoutClientError>({
+                        runBlocking {
+                            launchCashoutMonitor(
+                                getMockedClient {
+                                    /**
+                                     * This causes the cash-out request sent 
to Nexus to
+                                     * respond with 307 Temporary Redirect.
+                                     */
+                                    respondRedirect()
+                                }
+                            )
                         }
-                    )
-                    assert(job.isActive)
-                    delay(1000L) // Lets the reaction complete.
-                    // Checking that the service stopped because of the 
client-side error.
-                    assert(!job.isActive)
-                    // 5, Mocking a network error.  The previous failed 
cash-out
-                    // will again trigger the service to POST at Nexus.
+                    })
+                    /* 5, Mocking a network error.  The previous failed 
cash-out
+                       will again trigger the service to POST to Nexus.  Here 
the
+                       monitor tolerates the failure, as it's not due to its 
state
+                       and should be temporary.
+                     */
+                    var requestMade = false
                     job = launchCashoutMonitor(
-                        getMockClient {
+                        getMockedClient {
+                            requestMade = true
                             throw Exception("Network Issue.")
                         }
                     )
-                    delay(1000L) // Lets the reaction complete.
-                    assert(job.isActive) // asserting that the service is 
still running.
+                    delay(2000L) // Lets the reaction complete.
+                    // asserting that the service is still running after the 
failed request.
+                    assert(requestMade && job.isActive)
                     job.cancelAndJoin()
                 }
             }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]