libeufin

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

commit 53392e3316ce21ea9ca8254caf9c5a0811331387
parent c4fa1ddacc51d9bb2a1aa6020576d4a57bf7d021
Author: Antoine A <>
Date:   Fri, 21 Jun 2024 14:45:02 +0200

common: clean more code

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt | 2+-
Mbank/src/main/kotlin/tech/libeufin/bank/db/ConversionDAO.kt | 2+-
Mbank/src/test/kotlin/AmountTest.kt | 45+++++++++++++++++++++++++--------------------
Mbank/src/test/kotlin/DatabaseTest.kt | 9+++++----
Mcommon/src/main/kotlin/TalerCommon.kt | 22+++++++++++++++-------
5 files changed, 47 insertions(+), 33 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt @@ -131,7 +131,7 @@ class AccountDAO(private val db: Database) { setString(5, phone) setString(6, cashoutPayto?.full(name)) setString(7, tanChannel?.name) - oneOrNull { it.getLong("customer_id") }!! + one { it.getLong("customer_id") } } conn.withStatement(""" diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/ConversionDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/ConversionDAO.kt @@ -75,7 +75,7 @@ class ConversionDAO(private val db: Database) { val roundingMode = conn.prepareStatement("SELECT config_get_rounding_mode(?)") fun getAmount(name: String, currency: String): TalerAmount { amount.setString(1, name) - return amount.oneOrNull { it.getAmount("amount", currency) }!! + return amount.one { it.getAmount("amount", currency) } } fun getRatio(name: String): DecimalNumber = getAmount(name, "").run { DecimalNumber(value, frac) } fun getMode(name: String): RoundingMode { diff --git a/bank/src/test/kotlin/AmountTest.kt b/bank/src/test/kotlin/AmountTest.kt @@ -21,7 +21,7 @@ import org.junit.Test import tech.libeufin.bank.db.TransactionDAO.BankTransactionResult import tech.libeufin.bank.db.WithdrawalDAO.WithdrawalCreationResult import tech.libeufin.common.* -import tech.libeufin.common.db.oneOrNull +import tech.libeufin.common.db.one import java.time.Instant import java.util.* import kotlin.test.assertEquals @@ -141,16 +141,16 @@ class AmountTest { fun normalize() = dbSetup { db -> db.conn { conn -> val stmt = conn.prepareStatement("SELECT normalized.val, normalized.frac FROM amount_normalize((?, ?)::taler_amount) as normalized") - fun TalerAmount.normalize(): TalerAmount? { + fun TalerAmount.normalize(): TalerAmount { stmt.setLong(1, value) stmt.setInt(2, frac) - return stmt.oneOrNull { + return stmt.one { TalerAmount( it.getLong(1), it.getInt(2), "EUR" ) - }!! + } } assertEquals(TalerAmount("EUR:6"), TalerAmount(4L, 2 * TalerAmount.FRACTION_BASE, "EUR").normalize()) @@ -158,6 +158,10 @@ class AmountTest { assertEquals(TalerAmount("EUR:${TalerAmount.MAX_VALUE}.99999999"), TalerAmount("EUR:${TalerAmount.MAX_VALUE}.99999999").normalize()) assertException("ERROR: bigint out of range") { TalerAmount(Long.MAX_VALUE, TalerAmount.FRACTION_BASE, "EUR").normalize() } assertException("ERROR: amount value overflowed") { TalerAmount(TalerAmount.MAX_VALUE, TalerAmount.FRACTION_BASE , "EUR").normalize() } + + for (amount in listOf(TalerAmount.max("EUR"), TalerAmount.zero("EUR"))) { + assertEquals(amount, amount.normalize()) + } } } @@ -165,20 +169,21 @@ class AmountTest { fun add() = dbSetup { db -> db.conn { conn -> val stmt = conn.prepareStatement("SELECT sum.val, sum.frac FROM amount_add((?, ?)::taler_amount, (?, ?)::taler_amount) as sum") - operator fun TalerAmount.plus(increment: TalerAmount): TalerAmount? { + operator fun TalerAmount.plus(increment: TalerAmount): TalerAmount { stmt.setLong(1, value) stmt.setInt(2, frac) stmt.setLong(3, increment.value) stmt.setInt(4, increment.frac) - return stmt.oneOrNull { + return stmt.one { TalerAmount( it.getLong(1), it.getInt(2), "EUR" ) - }!! + } } - + assertEquals(TalerAmount.max("EUR"), TalerAmount.max("EUR") + TalerAmount.zero("EUR")) + assertEquals(TalerAmount.zero("EUR"), TalerAmount.zero("EUR") + TalerAmount.zero("EUR")) assertEquals(TalerAmount("EUR:6.41") + TalerAmount("EUR:4.69"), TalerAmount("EUR:11.1")) assertEquals(TalerAmount("EUR:${TalerAmount.MAX_VALUE}") + TalerAmount("EUR:0.99999999"), TalerAmount("EUR:${TalerAmount.MAX_VALUE}.99999999")) assertException("ERROR: amount value overflowed") { TalerAmount(TalerAmount.MAX_VALUE - 5, 0, "EUR") + TalerAmount(6, 0, "EUR") } @@ -191,7 +196,7 @@ class AmountTest { @Test fun conversionApply() = dbSetup { db -> db.conn { conn -> - fun apply(nb: TalerAmount, times: DecimalNumber, tiny: DecimalNumber = DecimalNumber("0.00000001"), roundingMode: String = "zero"): TalerAmount? { + fun apply(nb: TalerAmount, times: DecimalNumber, tiny: DecimalNumber = DecimalNumber("0.00000001"), roundingMode: String = "zero"): TalerAmount { val stmt = conn.prepareStatement("SELECT amount.val, amount.frac FROM conversion_apply_ratio((?, ?)::taler_amount, (?, ?)::taler_amount, (?, ?)::taler_amount, ?::rounding_mode) as amount") stmt.setLong(1, nb.value) stmt.setInt(2, nb.frac) @@ -200,13 +205,13 @@ class AmountTest { stmt.setLong(5, tiny.value) stmt.setInt(6, tiny.frac) stmt.setString(7, roundingMode) - return stmt.oneOrNull { + return stmt.one { TalerAmount( it.getLong(1), it.getInt(2), nb.currency ) - }!! + } } assertEquals(TalerAmount("EUR:30.0629"), apply(TalerAmount("EUR:6.41"), DecimalNumber("4.69"))) @@ -255,7 +260,7 @@ class AmountTest { @Test fun conversionRevert() = dbSetup { db -> db.conn { conn -> - fun TalerAmount.apply(ratio: DecimalNumber, tiny: DecimalNumber = DecimalNumber("0.00000001"), roundingMode: String = "zero"): TalerAmount? { + fun TalerAmount.apply(ratio: DecimalNumber, tiny: DecimalNumber = DecimalNumber("0.00000001"), roundingMode: String = "zero"): TalerAmount { val stmt = conn.prepareStatement("SELECT amount.val, amount.frac FROM conversion_apply_ratio((?, ?)::taler_amount, (?, ?)::taler_amount, (?, ?)::taler_amount, ?::rounding_mode) as amount") stmt.setLong(1, this.value) stmt.setInt(2, this.frac) @@ -264,16 +269,16 @@ class AmountTest { stmt.setLong(5, tiny.value) stmt.setInt(6, tiny.frac) stmt.setString(7, roundingMode) - return stmt.oneOrNull { + return stmt.one { TalerAmount( it.getLong(1), it.getInt(2), currency ) - }!! + } } - fun TalerAmount.revert(ratio: DecimalNumber, tiny: DecimalNumber = DecimalNumber("0.00000001"), roundingMode: String = "zero"): TalerAmount? { + fun TalerAmount.revert(ratio: DecimalNumber, tiny: DecimalNumber = DecimalNumber("0.00000001"), roundingMode: String = "zero"): TalerAmount { val stmt = conn.prepareStatement("SELECT amount.val, amount.frac FROM conversion_revert_ratio((?, ?)::taler_amount, (?, ?)::taler_amount, (?, ?)::taler_amount, ?::rounding_mode) as amount") stmt.setLong(1, this.value) stmt.setInt(2, this.frac) @@ -282,13 +287,13 @@ class AmountTest { stmt.setLong(5, tiny.value) stmt.setInt(6, tiny.frac) stmt.setString(7, roundingMode) - return stmt.oneOrNull { + return stmt.one { TalerAmount( it.getLong(1), it.getInt(2), currency ) - }!! + } } assertEquals(TalerAmount("EUR:6.41"), TalerAmount("EUR:30.0629").revert(DecimalNumber("4.69"))) @@ -308,11 +313,11 @@ class AmountTest { val ratio = DecimalNumber(ratio) val base = TalerAmount("EUR:$amount") // Apply ratio - val rounded = base.apply(ratio, tiny, mode)!! + val rounded = base.apply(ratio, tiny, mode) // Revert ratio - val revert = rounded.revert(ratio, tiny, mode)!! + val revert = rounded.revert(ratio, tiny, mode) // Check applying ratio again give the same result - val check = revert.apply(ratio, tiny, mode)!! + val check = revert.apply(ratio, tiny, mode) assertEquals(rounded, check) } } diff --git a/bank/src/test/kotlin/DatabaseTest.kt b/bank/src/test/kotlin/DatabaseTest.kt @@ -23,6 +23,7 @@ import kotlinx.coroutines.launch import org.junit.Test import tech.libeufin.bank.createAdminAccount import tech.libeufin.bank.db.AccountDAO.AccountCreationResult +import tech.libeufin.common.db.one import tech.libeufin.common.db.oneOrNull import tech.libeufin.common.* import java.time.Duration @@ -121,23 +122,23 @@ class DatabaseTest { createStmt.setLong(2, ChronoUnit.MICROS.between(Instant.EPOCH, timestamp)) createStmt.setLong(3, TimeUnit.MICROSECONDS.convert(validityPeriod)) createStmt.setInt(4, retryCounter) - return createStmt.oneOrNull { it.getLong(1) }!! + return createStmt.one { it.getLong(1) } } fun markSent(id: Long, timestamp: Instant) { markSentStmt.setLong(1, id) markSentStmt.setLong(2, ChronoUnit.MICROS.between(Instant.EPOCH, timestamp)) markSentStmt.setLong(3, TimeUnit.MICROSECONDS.convert(retransmissionPeriod)) - return markSentStmt.oneOrNull { }!! + return markSentStmt.one { } } fun cTry(id: Long, code: String, timestamp: Instant): Triple<Boolean, Boolean, Boolean> { tryStmt.setLong(1, id) tryStmt.setString(2, code) tryStmt.setLong(3, ChronoUnit.MICROS.between(Instant.EPOCH, timestamp)) - return tryStmt.oneOrNull { + return tryStmt.one { Triple(it.getBoolean(1), it.getBoolean(2), it.getBoolean(3)) - }!! + } } fun send(id: Long, code: String, timestamp: Instant): String? { diff --git a/common/src/main/kotlin/TalerCommon.kt b/common/src/main/kotlin/TalerCommon.kt @@ -120,12 +120,17 @@ class DecimalNumber { constructor(encoded: String) { val match = PATTERN.matchEntire(encoded) ?: throw badRequest("Invalid decimal number format") val (value, frac) = match.destructured - this.value = value.toLongOrNull() ?: throw badRequest("Invalid value") - if (this.value > TalerAmount.MAX_VALUE) throw badRequest("Value specified in decimal number is too large") + this.value = value.toLongOrNull() ?: + throw badRequest("Invalid value") + if (this.value > TalerAmount.MAX_VALUE) + throw badRequest("Value specified in decimal number is too large") this.frac = if (frac.isEmpty()) { 0 } else { - var tmp = frac.toIntOrNull() ?: throw badRequest("Invalid fractional value") + var tmp = frac.toIntOrNull() ?: + throw badRequest("Invalid fractional value") + if (tmp > TalerAmount.FRACTION_BASE) + throw badRequest("Fractional value specified in decimal number is too large") repeat(8 - frac.length) { tmp *= 10 } @@ -237,6 +242,7 @@ class TalerAmount { private val PATTERN = Regex("([A-Z]{1,11}):([0-9]+)(?:\\.([0-9]{1,8}))?") fun zero(currency: String) = TalerAmount(0, 0, currency) + fun max(currency: String) = TalerAmount(MAX_VALUE, FRACTION_BASE-1, currency) } } @@ -479,8 +485,9 @@ class Base32Crockford32B { } fun encoded(): String { - encoded = encoded ?: Base32Crockford.encode(raw) - return encoded!! + val tmp = encoded ?: Base32Crockford.encode(raw) + encoded = tmp + return tmp } override fun toString(): String { @@ -533,8 +540,9 @@ class Base32Crockford64B { } fun encoded(): String { - encoded = encoded ?: Base32Crockford.encode(raw) - return encoded!! + val tmp = encoded ?: Base32Crockford.encode(raw) + encoded = tmp + return tmp } override fun toString(): String {