libeufin

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

commit f6a9023da8868f0f00bfa8c383fbc2b231e387f8
parent df346488f2d6a1a4fb22fe1428f3354771dc47bb
Author: MS <ms@taler.net>
Date:   Tue, 24 Oct 2023 12:47:54 +0200

nexus db: calling the bounce_payment function.

Diffstat:
Mdatabase-versioning/libeufin-nexus-procedures.sql | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/Database.kt | 19+++++++++++--------
Mnexus/src/test/kotlin/DatabaseTest.kt | 31++++++++++++++++++++++++++++---
3 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/database-versioning/libeufin-nexus-procedures.sql b/database-versioning/libeufin-nexus-procedures.sql @@ -13,7 +13,7 @@ INSERT INTO initiated_outgoing_transactions ( amount ,wire_transfer_subject ,credit_payto_uri - ,in_initiation_time + ,initiation_time ) SELECT amount diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt @@ -116,23 +116,26 @@ class Database(dbConfig: String): java.io.Closeable { /** * Flags an incoming payment as bounced. NOTE: the flag merely means * that the payment had an invalid subject for a Taler withdrawal _and_ - * it got sent to the initiated outgoing payments. In NO way this flag + * it got initiated as an outgoing payments. In NO way this flag * means that the actual value was returned to the initial debtor. * - * FIXME: this needs to run within the same transaction where the payment gets initiated. - * * @param rowId row ID of the payment to flag as bounced. - * @return true on success, false otherwise. + * @return true if the payment could be set as bounced, false otherwise. */ suspend fun incomingPaymentSetAsBounced(rowId: Long): Boolean = runConn { conn -> + val timestamp = Instant.now().toDbMicros() + ?: throw Exception("Could not convert Instant.now() to microseconds, won't bounce this payment.") val stmt = conn.prepareStatement(""" - UPDATE incoming_transactions - SET bounced = true - WHERE incoming_transaction_id=? + SELECT out_nx_incoming_payment + FROM bounce_payment(?,?) """ ) stmt.setLong(1, rowId) - return@runConn stmt.maybeUpdate() + stmt.setLong(2, timestamp) + stmt.executeQuery().use { maybeResult -> + if (!maybeResult.next()) throw Exception("Expected outcome from the SQL bounce_payment function") + return@runConn !maybeResult.getBoolean("out_nx_incoming_payment") + } } /** diff --git a/nexus/src/test/kotlin/DatabaseTest.kt b/nexus/src/test/kotlin/DatabaseTest.kt @@ -1,18 +1,43 @@ -import io.ktor.client.* -import io.ktor.client.request.* import kotlinx.coroutines.runBlocking import org.junit.Test import tech.libeufin.nexus.InitiatedPayment import tech.libeufin.nexus.NEXUS_CONFIG_SOURCE import tech.libeufin.nexus.PaymentInitiationOutcome import tech.libeufin.nexus.TalerAmount -import tech.libeufin.util.transaction import java.time.Instant import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue class IncomingPaymentsTest { + // Tests the function that flags incoming payments as bounced. + @Test + fun incomingPaymentBounce() { + val db = prepDb(TalerConfig(NEXUS_CONFIG_SOURCE)) + runBlocking { + // creating one incoming payment. + assertTrue(db.incomingPaymentCreate(genIncPay("to be bounced"))) // row ID == 1. + db.runConn { + val bouncedSql = """ + SELECT bounced + FROM incoming_transactions + WHERE incoming_transaction_id = 1""" + // asserting is NOT bounced. + val expectNotBounced = it.execSQLQuery(bouncedSql) + assertTrue(expectNotBounced.next()) + assertFalse(expectNotBounced.getBoolean("bounced")) + // now bouncing it. + assertTrue(db.incomingPaymentSetAsBounced(1)) + // asserting it got flagged as bounced. + val expectBounced = it.execSQLQuery(bouncedSql) + assertTrue(expectBounced.next()) + assertTrue(expectBounced.getBoolean("bounced")) + // Trying to bounce a non-existing payment. + assertFalse(db.incomingPaymentSetAsBounced(5)) + } + } + } + // Tests the creation of an incoming payment. @Test fun incomingPaymentCreation() {