libeufin

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

commit 7bbfbcb64e05315f4cbd70c361df63faf95a9275
parent cf3fbbcd2b029dac65d628c1ae6654faafaa32f4
Author: Antoine A <>
Date:   Thu, 12 Oct 2023 14:58:14 +0000

Simpler and stricter amount parser

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt | 31+++++++++++++------------------
Mbank/src/test/kotlin/AmountTest.kt | 16+++++++++-------
2 files changed, 22 insertions(+), 25 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt @@ -235,26 +235,20 @@ class TalerAmount { constructor(encoded: String) { fun badAmount(hint: String): Exception = badRequest(hint, TalerErrorCode.TALER_EC_BANK_BAD_FORMAT_AMOUNT) - - if (encoded.isEmpty()) throw badAmount("Empty amount") - val currencySplit: List<String> = encoded.split(':', limit = 2) - if (currencySplit.size != 2) throw badAmount("Missing value") - currency = currencySplit[0].trimStart() - if (currency.length > 12) throw badAmount("Currency too big") - val dotSplit: List<String> = currencySplit[1].split('.', limit = 2) - - value = dotSplit[0].toLongOrNull() ?: throw badAmount("Invalid value") - if (value > MAX_SAFE_INTEGER) throw badAmount("Value specified in amount is too large") - - if (dotSplit.size == 2) { - if (dotSplit[1].length > 8) throw badAmount("Fractional value too precise") - var tmp: Int = dotSplit[1].toIntOrNull() ?: throw badAmount("Invalid fractional value") - repeat(8 - dotSplit[1].length) { + + val match = PATTERN.matchEntire(encoded) ?: throw badAmount("Invalid amount format"); + val (currency, value, frac) = match.destructured + this.currency = currency + this.value = value.toLongOrNull() ?: throw badAmount("Invalid value") + if (this.value > MAX_SAFE_INTEGER) throw badAmount("Value specified in amount is too large") + this.frac = if (frac.isEmpty()) { + 0 + } else { + var tmp = frac.toIntOrNull() ?: throw badAmount("Invalid fractional value") + repeat(8 - frac.length) { tmp *= 10 } - frac = tmp - } else { - frac = 0 + tmp } } @@ -314,6 +308,7 @@ class TalerAmount { companion object { const val FRACTION_BASE = 100000000 + private val PATTERN = Regex("([A-Z]{1,11}):([0-9]+)(?:\\.([0-9]{0,8}))?"); } } diff --git a/bank/src/test/kotlin/AmountTest.kt b/bank/src/test/kotlin/AmountTest.kt @@ -75,18 +75,20 @@ class AmountTest { fun parseValid() { assertEquals(TalerAmount("EUR:4"), TalerAmount(4L, 0, "EUR")) assertEquals(TalerAmount("EUR:0.02"), TalerAmount(0L, 2000000, "EUR")) - assertEquals(TalerAmount(" EUR:4.12"), TalerAmount(4L, 12000000, "EUR")) - assertEquals(TalerAmount(" *LOCAL:4444.1000"), TalerAmount(4444L, 10000000, "*LOCAL")) + assertEquals(TalerAmount("EUR:4.12"), TalerAmount(4L, 12000000, "EUR")) + assertEquals(TalerAmount("LOCAL:4444.1000"), TalerAmount(4444L, 10000000, "LOCAL")) } @Test fun parseInvalid() { - assertException("Empty amount") {TalerAmount("")} - assertException("Missing value") {TalerAmount("EUR")} - assertException("Currency too big") {TalerAmount("AZERTYUIOPQSD:")} + assertException("Invalid amount format") {TalerAmount("")} + assertException("Invalid amount format") {TalerAmount("EUR")} + assertException("Invalid amount format") {TalerAmount("eur:12")} + assertException("Invalid amount format") {TalerAmount(" EUR:12")} + assertException("Invalid amount format") {TalerAmount("AZERTYUIOPQSD:12")} assertException("Value specified in amount is too large") {TalerAmount("EUR:${Long.MAX_VALUE}")} - assertException("Fractional value too precise") {TalerAmount("EUR:4.000000000")} - assertException("Invalid fractional value") {TalerAmount("EUR:4.4a")} + assertException("Invalid amount format") {TalerAmount("EUR:4.000000000")} + assertException("Invalid amount format") {TalerAmount("EUR:4.4a")} } @Test