libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 7f5ad7e980ce63eb9652a620d73d991737a320e2
parent 5bca2e1eedeb85d6b206413e7166018dce748f44
Author: MS <ms@taler.net>
Date:   Fri, 21 Apr 2023 20:23:23 +0200

testing the last changes

Diffstat:
Mnexus/src/test/kotlin/ConversionServiceTest.kt | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mnexus/src/test/kotlin/Iso20022Test.kt | 2+-
Mnexus/src/test/kotlin/JsonTest.kt | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mnexus/src/test/kotlin/MakeEnv.kt | 7+++----
Mnexus/src/test/kotlin/SubjectNormalization.kt | 3+--
Mnexus/src/test/kotlin/XLibeufinBankTest.kt | 1-
6 files changed, 162 insertions(+), 43 deletions(-)

diff --git a/nexus/src/test/kotlin/ConversionServiceTest.kt b/nexus/src/test/kotlin/ConversionServiceTest.kt @@ -2,16 +2,120 @@ import io.ktor.client.* import io.ktor.client.engine.mock.* 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.server.nexusApp import tech.libeufin.sandbox.* class ConversionServiceTest { + // Without this, "launch {}" never returns. + val doNothingHandler = CoroutineExceptionHandler { _, _ -> } + + /** + * Testing the buy-in monitor in the normal case: Nexus + * communicates a new incoming fiat transaction and the + * monitor wires funds to the exchange. + */ + @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. + withTestDatabase { + prepSandboxDb(currency = "REGIO") + prepNexusDb() + // Credits 22 TESTKUDOS to "foo". This information comes + // normally from the fiat bank that Nexus is connected to. + val reservePub = "GX5H5RME193FDRCM1HZKERXXQ2K21KH7788CKQM8X6MYKYRBP8F0" + newNexusBankTransaction( + currency = "TESTKUDOS", + value = "22", + /** + * If the subject does NOT have the format of a public key, + * the conversion service does NOT wire any regio amount to the + * exchange, just ignores it. + */ + subject = reservePub + ) + // Start Nexus, to let it serve the fiat transaction. + testApplication { + application(nexusApp) + // Start the buy-in monitor to let it download the fiat transaction. + val job = CoroutineScope(Dispatchers.IO).launch(doNothingHandler) { + buyinMonitor( + demobankName = "default", + accountToCredit = "exchange-0", + client = client + ) + } + delay(1000L) + job.cancel() + } + // Checking that exchange got the converted amount. + transaction { + /** + * Asserting that the exchange has only one incoming transaction. + * + * The Sandbox DB has two entries where the exchange IBAN shows + * as the 'creditorIban': one DBIT related to the "admin" account, + * and one CRDT related to the "exchange-0" account. Thus filtering + * the direction is also required. + */ + assert(BankAccountTransactionEntity.find { + BankAccountTransactionsTable.creditorIban eq "AT561936082973364859" and ( + BankAccountTransactionsTable.direction eq "CRDT" + ) + }.count() == 1L) + + // Asserting that the one incoming transactino has the wired reserve public key. + assert(BankAccountTransactionEntity.find { + BankAccountTransactionsTable.creditorIban eq "AT561936082973364859" + }.first().subject == reservePub) + } + } + } + + /** + * Checks that the cash-out monitor reacts after + * a CRDT transaction arrives at the designated account. + */ + @Test + fun cashoutTest() { + withTestDatabase { + prepSandboxDb() + prepNexusDb() + wireTransfer( + debitAccount = "foo", + creditAccount = "admin", + subject = "fiat #0", + amount = "TESTKUDOS:3" + ) + testApplication { + application(nexusApp) + CoroutineScope(Dispatchers.IO).launch(doNothingHandler) { + cashoutMonitor(client) + } + delay(1000L) // Lets DB persist the information. + transaction { + assert(CashoutSubmissionEntity.all().count() == 1L) + assert(CashoutSubmissionEntity.all().first().isSubmitted) + } + } + } + } + /** * Tests whether the conversion service is able to skip * submissions that had problems and proceed to new ones. + ---------------------------------------------------------- + * Ignoring the test because the new version just fails the + * process on client side errors. Still however keeping the + * (ignored) test as a model to create faulty situations. */ + @Ignore @Test fun testWrongSubmissionSkip() { withTestDatabase { @@ -42,35 +146,4 @@ class ConversionServiceTest { } } } - - /** - * Checks that the cash-out monitor reacts after - * a CRDT transaction arrives at the designated account. - */ - @Test - fun cashoutTest() { - withTestDatabase { - prepSandboxDb(); prepNexusDb() - wireTransfer( - debitAccount = "foo", - creditAccount = "admin", - subject = "fiat", - amount = "TESTKUDOS:3" - ) - testApplication { - application(nexusApp) - runBlocking { - val monitorJob = launch(Dispatchers.IO) { cashoutMonitor(client) } - launch { - delay(4000L) - transaction { - assert(CashoutSubmissionEntity.all().count() == 1L) - assert(CashoutSubmissionEntity.all().first().isSubmitted) - } - monitorJob.cancel() - } - } - } - } - } } \ No newline at end of file diff --git a/nexus/src/test/kotlin/Iso20022Test.kt b/nexus/src/test/kotlin/Iso20022Test.kt @@ -1,4 +1,5 @@ package tech.libeufin.nexus +import CamtBankAccountEntry import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.junit.Ignore import org.junit.Test @@ -74,7 +75,6 @@ class Iso20022Test { ) // Third Entry - // Make sure that round-tripping of entry CamtBankAccountEntry JSON works for (entry in r.reports.flatMap { it.entries }) { val txStr = jacksonObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(entry) diff --git a/nexus/src/test/kotlin/JsonTest.kt b/nexus/src/test/kotlin/JsonTest.kt @@ -1,15 +1,15 @@ -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper import org.junit.Test import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue -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 io.ktor.utils.io.jvm.javaio.* import org.junit.Ignore import tech.libeufin.nexus.server.CreateBankConnectionFromBackupRequestJson import tech.libeufin.nexus.server.CreateBankConnectionFromNewRequestJson +import tech.libeufin.sandbox.NexusTransactions import tech.libeufin.sandbox.sandboxApp enum class EnumTest { TEST } @@ -37,7 +37,7 @@ class JsonTest { fun enumTest() { val m = jacksonObjectMapper() m.readValue<EnumWrapper>("{\"enum_test\":\"TEST\"}") - m.readValue<EnumTest>("\"TEST\"") + m.readValue<EnumTest>("\"TEST\"") } /** @@ -57,4 +57,53 @@ class JsonTest { } } } + + data class CamtEntryWrapper( + val unusedValue: String, + val camtData: CamtBankAccountEntry + ) + + // Testing whether generating and parsing a CaMt JSON mapping works. + @Test + fun testCamtRoundTrip() { + val obj = genNexusIncomingCamt( + CurrencyAmount(value = "2", currency = "EUR"), + subject = "round trip test" + ) + val str = jacksonObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(obj) + val map = jacksonObjectMapper().readValue(str, CamtBankAccountEntry::class.java) + assert(str == jacksonObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(map)) + } + + @Test + fun parseRawJson() { + val camtModel = """ + { + "amount" : "TESTKUDOS:22", + "creditDebitIndicator" : "CRDT", + "status" : "BOOK", + "bankTransactionCode" : "mock", + "batches" : [ { + "batchTransactions" : [ { + "amount" : "TESTKUDOS:22", + "creditDebitIndicator" : "CRDT", + "details" : { + "debtor" : { + "name" : "Mock Payer" + }, + "debtorAccount" : { + "iban" : "MOCK-IBAN" + }, + "debtorAgent" : { + "bic" : "MOCK-BIC" + }, + "unstructuredRemittanceInformation" : "raw" + } + } ] + } ] + } + """.trimIndent() + val obj = jacksonObjectMapper().readValue(camtModel, CamtBankAccountEntry::class.java) + assert(obj.getSingletonSubject() == "raw") + } } \ No newline at end of file diff --git a/nexus/src/test/kotlin/MakeEnv.kt b/nexus/src/test/kotlin/MakeEnv.kt @@ -8,7 +8,6 @@ import tech.libeufin.nexus.dbCreateTables import tech.libeufin.nexus.dbDropTables import tech.libeufin.nexus.iso20022.* import tech.libeufin.nexus.server.BankConnectionType -import tech.libeufin.nexus.server.CurrencyAmount import tech.libeufin.nexus.server.FetchLevel import tech.libeufin.nexus.server.FetchSpecAllJson import tech.libeufin.sandbox.* @@ -192,11 +191,11 @@ fun prepNexusDb() { } } -fun prepSandboxDb(usersDebtLimit: Int = 1000) { +fun prepSandboxDb(usersDebtLimit: Int = 1000, currency: String = "TESTKUDOS") { tech.libeufin.sandbox.dbCreateTables(TEST_DB_CONN) transaction { val config = DemobankConfig( - currency = "TESTKUDOS", + currency = currency, bankDebtLimit = 10000, usersDebtLimit = usersDebtLimit, allowRegistrations = true, @@ -415,7 +414,7 @@ private fun genNexusIncomingXLibeufinBank( * values are either resorted from other sources by Nexus, or actually * not useful so far. */ -private fun genNexusIncomingCamt( +fun genNexusIncomingCamt( amount: CurrencyAmount, subject: String, ): CamtBankAccountEntry = diff --git a/nexus/src/test/kotlin/SubjectNormalization.kt b/nexus/src/test/kotlin/SubjectNormalization.kt @@ -1,6 +1,5 @@ import org.junit.Test - -import tech.libeufin.nexus.extractReservePubFromSubject +import tech.libeufin.util.extractReservePubFromSubject class SubjectNormalization { diff --git a/nexus/src/test/kotlin/XLibeufinBankTest.kt b/nexus/src/test/kotlin/XLibeufinBankTest.kt @@ -9,7 +9,6 @@ import org.junit.Test import tech.libeufin.nexus.* import tech.libeufin.nexus.bankaccount.addPaymentInitiation import tech.libeufin.nexus.bankaccount.ingestBankMessagesIntoAccount -import tech.libeufin.nexus.iso20022.CamtBankAccountEntry import tech.libeufin.nexus.server.* import tech.libeufin.nexus.xlibeufinbank.XlibeufinBankConnectionProtocol import tech.libeufin.sandbox.BankAccountTransactionEntity