libeufin

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

commit 4353d731b02685d00cf1f3275101ff33f840f8f1
parent 767e006ac586ca437c1ded8897f3be38a92441f2
Author: Antoine A <>
Date:   Thu, 13 Mar 2025 17:07:09 +0100

common: improve IBAN logic

Diffstat:
Mcommon/src/main/kotlin/TalerCommon.kt | 2+-
Mcommon/src/main/kotlin/iban.kt | 63++++++++++++++++++++++++++++++++-------------------------------
Acommon/src/main/kotlin/registry.kt | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acommon/src/test/kotlin/IbanTest.kt | 43+++++++++++++++++++++++++++++++++++++++++++
4 files changed, 292 insertions(+), 32 deletions(-)

diff --git a/common/src/main/kotlin/TalerCommon.kt b/common/src/main/kotlin/TalerCommon.kt @@ -443,7 +443,7 @@ class IbanPayto internal constructor( } fun rand(): IbanPayto { - return parse("payto://iban/SANDBOXX/${IBAN.rand()}").expectIban() + return parse("payto://iban/SANDBOXX/${IBAN.rand(Country.DE)}").expectIban() } } } diff --git a/common/src/main/kotlin/iban.kt b/common/src/main/kotlin/iban.kt @@ -19,21 +19,6 @@ package tech.libeufin.common -// Taken from the ISO20022 XSD schema -private val bicRegex = Regex("^[A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?$") - -fun validateBic(bic: String): Boolean { - return bicRegex.matches(bic) -} - -// Taken from the ISO20022 XSD schema -private val ibanRegex = Regex("^[A-Z]{2}[0-9]{2}[a-zA-Z0-9]{1,30}$") - -fun validateIban(iban: String): Boolean { - return ibanRegex.matches(iban) -} - - @JvmInline value class IBAN private constructor(val value: String) { override fun toString(): String = value @@ -44,16 +29,12 @@ value class IBAN private constructor(val value: String) { fun checksum(iban: String): Int = (iban.substring(4 until iban.length).asSequence() + iban.substring(0 until 4).asSequence()) .fold(0) { acc, char -> - var checksum = if (char.isDigit()) { - acc * 10 + char.code - '0'.code + if (char.isDigit()) { + (acc * 10 + char.code - '0'.code) % 97 } else { - acc * 100 + char.code - 'A'.code + 10 - } - if (checksum > 9_999_999) { - checksum %= 97 + (acc * 100 + char.code - 'A'.code + 10) % 97 } - checksum - } % 97 + } fun parse(raw: String): IBAN { @@ -61,26 +42,46 @@ value class IBAN private constructor(val value: String) { if (iban.length < 5) { throw CommonError.Payto("malformed IBAN, string is too small only ${iban.length} char") } - for (c in iban.substring(0 until 2)) { + val countryCode = iban.substring(0 until 2) + for (c in countryCode) { if (!c.isLetter()) throw CommonError.Payto("malformed IBAN, malformed country code") } for (c in iban.substring(2 until 4)) { if (!c.isDigit()) throw CommonError.Payto("malformed IBAN, malformed check digit") } + val country = try { + Country.valueOf(countryCode) + } catch (e: IllegalArgumentException) { + throw CommonError.Payto("malformed IBAN, unknown country $countryCode") + } + if (country == Country.DE && iban.length == 15) { + // This is allowed for retrocompatibility with libeufin-bank malformed DE IBAN + } else if (!country.bbanRegex.matches(iban.substring(4))) { + throw CommonError.Payto("malformed IBAN, invalid char") + } val checksum = checksum(iban) if (checksum != 1) throw CommonError.Payto("malformed IBAN, modulo is $checksum expected 1") return IBAN(iban) } - fun rand(): IBAN { - val bban = (0..10).map { - (0..9).random() - }.joinToString("") // 10 digits account number - val checkDigits = 98 - checksum("DE00$bban"); + fun rand(country: Country): IBAN { + val bban = buildString { + for ((repetition, c) in country.rules) { + val alphabet = when (c) { + IbanC.n -> "0123456789" + IbanC.a -> "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + IbanC.c -> "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + } + repeat(repetition) { + append(alphabet.random()) + } + } + } + val checkDigits = 98 - checksum("${country.name}00$bban"); return if (checkDigits < 10) { - IBAN("DE0$checkDigits$bban") + IBAN("${country.name}0$checkDigits$bban") } else { - IBAN("DE$checkDigits$bban") + IBAN("${country.name}$checkDigits$bban") } } } diff --git a/common/src/main/kotlin/registry.kt b/common/src/main/kotlin/registry.kt @@ -0,0 +1,215 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2025 Taler Systems S.A. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +package tech.libeufin.common + +/** IBAN ASCII characters rules */ +enum class IbanC { + /** Digits (0-9) */ + n, + /** Uppercase (A-Z) */ + a, + /** Digits or uppercase (0-9 & A-Z) */ + c, +} + +enum class Country(val ibanLen: Int, val rules: List<Pair<Int, IbanC>>, val bbanRegex: Regex) { + AD(24, listOf(Pair(8, IbanC.n),Pair(12, IbanC.c),), Regex("^[0-9]{8}[0-9A-Z]{12}$")), + AE(23, listOf(Pair(19, IbanC.n),), Regex("^[0-9]{19}$")), + AL(28, listOf(Pair(8, IbanC.n),Pair(16, IbanC.c),), Regex("^[0-9]{8}[0-9A-Z]{16}$")), + AT(20, listOf(Pair(16, IbanC.n),), Regex("^[0-9]{16}$")), + AZ(28, listOf(Pair(4, IbanC.a),Pair(20, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{20}$")), + BA(20, listOf(Pair(16, IbanC.n),), Regex("^[0-9]{16}$")), + BE(16, listOf(Pair(12, IbanC.n),), Regex("^[0-9]{12}$")), + BG(22, listOf(Pair(4, IbanC.a),Pair(6, IbanC.n),Pair(8, IbanC.c),), Regex("^[A-Z]{4}[0-9]{6}[0-9A-Z]{8}$")), + BH(22, listOf(Pair(4, IbanC.a),Pair(14, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{14}$")), + BI(27, listOf(Pair(23, IbanC.n),), Regex("^[0-9]{23}$")), + BR(29, listOf(Pair(23, IbanC.n),Pair(1, IbanC.a),Pair(1, IbanC.c),), Regex("^[0-9]{23}[A-Z]{1}[0-9A-Z]{1}$")), + BY(28, listOf(Pair(4, IbanC.c),Pair(4, IbanC.n),Pair(16, IbanC.c),), Regex("^[0-9A-Z]{4}[0-9]{4}[0-9A-Z]{16}$")), + CH(21, listOf(Pair(5, IbanC.n),Pair(12, IbanC.c),), Regex("^[0-9]{5}[0-9A-Z]{12}$")), + CR(22, listOf(Pair(18, IbanC.n),), Regex("^[0-9]{18}$")), + CY(28, listOf(Pair(8, IbanC.n),Pair(16, IbanC.c),), Regex("^[0-9]{8}[0-9A-Z]{16}$")), + CZ(24, listOf(Pair(20, IbanC.n),), Regex("^[0-9]{20}$")), + DE(22, listOf(Pair(18, IbanC.n),), Regex("^[0-9]{18}$")), + DJ(27, listOf(Pair(23, IbanC.n),), Regex("^[0-9]{23}$")), + DK(18, listOf(Pair(14, IbanC.n),), Regex("^[0-9]{14}$")), + DO(28, listOf(Pair(4, IbanC.c),Pair(20, IbanC.n),), Regex("^[0-9A-Z]{4}[0-9]{20}$")), + EE(20, listOf(Pair(16, IbanC.n),), Regex("^[0-9]{16}$")), + EG(29, listOf(Pair(25, IbanC.n),), Regex("^[0-9]{25}$")), + ES(24, listOf(Pair(20, IbanC.n),), Regex("^[0-9]{20}$")), + FI(18, listOf(Pair(14, IbanC.n),), Regex("^[0-9]{14}$")), + FK(18, listOf(Pair(2, IbanC.a),Pair(12, IbanC.n),), Regex("^[A-Z]{2}[0-9]{12}$")), + FO(18, listOf(Pair(14, IbanC.n),), Regex("^[0-9]{14}$")), + FR(27, listOf(Pair(10, IbanC.n),Pair(11, IbanC.c),Pair(2, IbanC.n),), Regex("^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$")), + GB(22, listOf(Pair(4, IbanC.a),Pair(14, IbanC.n),), Regex("^[A-Z]{4}[0-9]{14}$")), + GE(22, listOf(Pair(2, IbanC.a),Pair(16, IbanC.n),), Regex("^[A-Z]{2}[0-9]{16}$")), + GI(23, listOf(Pair(4, IbanC.a),Pair(15, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{15}$")), + GL(18, listOf(Pair(14, IbanC.n),), Regex("^[0-9]{14}$")), + GR(27, listOf(Pair(7, IbanC.n),Pair(16, IbanC.c),), Regex("^[0-9]{7}[0-9A-Z]{16}$")), + GT(28, listOf(Pair(24, IbanC.c),), Regex("^[0-9A-Z]{24}$")), + HN(28, listOf(Pair(4, IbanC.a),Pair(20, IbanC.n),), Regex("^[A-Z]{4}[0-9]{20}$")), + HR(21, listOf(Pair(17, IbanC.n),), Regex("^[0-9]{17}$")), + HU(28, listOf(Pair(24, IbanC.n),), Regex("^[0-9]{24}$")), + IE(22, listOf(Pair(4, IbanC.a),Pair(14, IbanC.n),), Regex("^[A-Z]{4}[0-9]{14}$")), + IL(23, listOf(Pair(19, IbanC.n),), Regex("^[0-9]{19}$")), + IQ(23, listOf(Pair(4, IbanC.a),Pair(15, IbanC.n),), Regex("^[A-Z]{4}[0-9]{15}$")), + IS(26, listOf(Pair(22, IbanC.n),), Regex("^[0-9]{22}$")), + IT(27, listOf(Pair(1, IbanC.a),Pair(10, IbanC.n),Pair(12, IbanC.c),), Regex("^[A-Z]{1}[0-9]{10}[0-9A-Z]{12}$")), + JO(30, listOf(Pair(4, IbanC.a),Pair(4, IbanC.n),Pair(18, IbanC.c),), Regex("^[A-Z]{4}[0-9]{4}[0-9A-Z]{18}$")), + KW(30, listOf(Pair(4, IbanC.a),Pair(22, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{22}$")), + KZ(20, listOf(Pair(3, IbanC.n),Pair(13, IbanC.c),), Regex("^[0-9]{3}[0-9A-Z]{13}$")), + LB(28, listOf(Pair(4, IbanC.n),Pair(20, IbanC.c),), Regex("^[0-9]{4}[0-9A-Z]{20}$")), + LC(32, listOf(Pair(4, IbanC.a),Pair(24, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{24}$")), + LI(21, listOf(Pair(5, IbanC.n),Pair(12, IbanC.c),), Regex("^[0-9]{5}[0-9A-Z]{12}$")), + LT(20, listOf(Pair(16, IbanC.n),), Regex("^[0-9]{16}$")), + LU(20, listOf(Pair(3, IbanC.n),Pair(13, IbanC.c),), Regex("^[0-9]{3}[0-9A-Z]{13}$")), + LV(21, listOf(Pair(4, IbanC.a),Pair(13, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{13}$")), + LY(25, listOf(Pair(21, IbanC.n),), Regex("^[0-9]{21}$")), + MC(27, listOf(Pair(10, IbanC.n),Pair(11, IbanC.c),Pair(2, IbanC.n),), Regex("^[0-9]{10}[0-9A-Z]{11}[0-9]{2}$")), + MD(24, listOf(Pair(20, IbanC.c),), Regex("^[0-9A-Z]{20}$")), + ME(22, listOf(Pair(18, IbanC.n),), Regex("^[0-9]{18}$")), + MK(19, listOf(Pair(3, IbanC.n),Pair(10, IbanC.c),Pair(2, IbanC.n),), Regex("^[0-9]{3}[0-9A-Z]{10}[0-9]{2}$")), + MN(20, listOf(Pair(16, IbanC.n),), Regex("^[0-9]{16}$")), + MR(27, listOf(Pair(23, IbanC.n),), Regex("^[0-9]{23}$")), + MT(31, listOf(Pair(4, IbanC.a),Pair(5, IbanC.n),Pair(18, IbanC.c),), Regex("^[A-Z]{4}[0-9]{5}[0-9A-Z]{18}$")), + MU(30, listOf(Pair(4, IbanC.a),Pair(19, IbanC.n),Pair(3, IbanC.a),), Regex("^[A-Z]{4}[0-9]{19}[A-Z]{3}$")), + NI(28, listOf(Pair(4, IbanC.a),Pair(20, IbanC.n),), Regex("^[A-Z]{4}[0-9]{20}$")), + NL(18, listOf(Pair(4, IbanC.a),Pair(10, IbanC.n),), Regex("^[A-Z]{4}[0-9]{10}$")), + NO(15, listOf(Pair(11, IbanC.n),), Regex("^[0-9]{11}$")), + OM(23, listOf(Pair(3, IbanC.n),Pair(16, IbanC.c),), Regex("^[0-9]{3}[0-9A-Z]{16}$")), + PK(24, listOf(Pair(4, IbanC.a),Pair(16, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{16}$")), + PL(28, listOf(Pair(24, IbanC.n),), Regex("^[0-9]{24}$")), + PS(29, listOf(Pair(4, IbanC.a),Pair(21, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{21}$")), + PT(25, listOf(Pair(21, IbanC.n),), Regex("^[0-9]{21}$")), + QA(29, listOf(Pair(4, IbanC.a),Pair(21, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{21}$")), + RO(24, listOf(Pair(4, IbanC.a),Pair(16, IbanC.c),), Regex("^[A-Z]{4}[0-9A-Z]{16}$")), + RS(22, listOf(Pair(18, IbanC.n),), Regex("^[0-9]{18}$")), + RU(33, listOf(Pair(14, IbanC.n),Pair(15, IbanC.c),), Regex("^[0-9]{14}[0-9A-Z]{15}$")), + SA(24, listOf(Pair(2, IbanC.n),Pair(18, IbanC.c),), Regex("^[0-9]{2}[0-9A-Z]{18}$")), + SC(31, listOf(Pair(4, IbanC.a),Pair(20, IbanC.n),Pair(3, IbanC.a),), Regex("^[A-Z]{4}[0-9]{20}[A-Z]{3}$")), + SD(18, listOf(Pair(14, IbanC.n),), Regex("^[0-9]{14}$")), + SE(24, listOf(Pair(20, IbanC.n),), Regex("^[0-9]{20}$")), + SI(19, listOf(Pair(15, IbanC.n),), Regex("^[0-9]{15}$")), + SK(24, listOf(Pair(20, IbanC.n),), Regex("^[0-9]{20}$")), + SM(27, listOf(Pair(1, IbanC.a),Pair(10, IbanC.n),Pair(12, IbanC.c),), Regex("^[A-Z]{1}[0-9]{10}[0-9A-Z]{12}$")), + SO(23, listOf(Pair(19, IbanC.n),), Regex("^[0-9]{19}$")), + ST(25, listOf(Pair(21, IbanC.n),), Regex("^[0-9]{21}$")), + SV(28, listOf(Pair(4, IbanC.a),Pair(20, IbanC.n),), Regex("^[A-Z]{4}[0-9]{20}$")), + TL(23, listOf(Pair(19, IbanC.n),), Regex("^[0-9]{19}$")), + TN(24, listOf(Pair(20, IbanC.n),), Regex("^[0-9]{20}$")), + TR(26, listOf(Pair(6, IbanC.n),Pair(16, IbanC.c),), Regex("^[0-9]{6}[0-9A-Z]{16}$")), + UA(29, listOf(Pair(6, IbanC.n),Pair(19, IbanC.c),), Regex("^[0-9]{6}[0-9A-Z]{19}$")), + VA(22, listOf(Pair(18, IbanC.n),), Regex("^[0-9]{18}$")), + VG(24, listOf(Pair(4, IbanC.a),Pair(16, IbanC.n),), Regex("^[A-Z]{4}[0-9]{16}$")), + XK(20, listOf(Pair(16, IbanC.n),), Regex("^[0-9]{16}$")), + YE(30, listOf(Pair(4, IbanC.a),Pair(4, IbanC.n),Pair(18, IbanC.c),), Regex("^[A-Z]{4}[0-9]{4}[0-9A-Z]{18}$")), +; + val bbanLen get() = ibanLen - 4 +} +val VALID_IBAN = listOf( + Pair("AD1200012030200359100100", "00012030200359100100"), + Pair("AE070331234567890123456", "0331234567890123456"), + Pair("AL47212110090000000235698741", "212110090000000235698741"), + Pair("AT611904300234573201", "1904300234573201"), + Pair("AZ21NABZ00000000137010001944", "NABZ00000000137010001944"), + Pair("BA391290079401028494", "1290079401028494"), + Pair("BE68539007547034", "539007547034"), + Pair("BG80BNBG96611020345678", "BNBG96611020345678"), + Pair("BH67BMAG00001299123456", "BMAG00001299123456"), + Pair("BI4210000100010000332045181", "10000100010000332045181"), + Pair("BR1800360305000010009795493C1", "00360305000010009795493C1"), + Pair("BY13NBRB3600900000002Z00AB00", "NBRB 3600900000002Z00AB00"), + Pair("CH9300762011623852957", "00762011623852957"), + Pair("CR05015202001026284066", "015202001026284066"), + Pair("CY17002001280000001200527600", "002001280000001200527600"), + Pair("CZ6508000000192000145399", "08000000192000145399"), + Pair("DE89370400440532013000", "370400440532013000"), + Pair("DJ2100010000000154000100186", "00010000000154000100186"), + Pair("DK5000400440116243", "00400440116243"), + Pair("DO28BAGR00000001212453611324", "BAGR00000001212453611324"), + Pair("EE382200221020145685", "2200221020145685"), + Pair("EG380019000500000000263180002", "0019000500000000263180002"), + Pair("ES9121000418450200051332", "21000418450200051332"), + Pair("FI2112345600000785", null), + Pair("FK88SC123456789012", "SC123456789012"), + Pair("FO6264600001631634", "64600001631634"), + Pair("FR1420041010050500013M02606", "20041010050500013M02606"), + Pair("GB29NWBK60161331926819", "NWBK60161331926819"), + Pair("GE29NB0000000101904917", "NB0000000101904917"), + Pair("GI75NWBK000000007099453", "NWBK000000007099453"), + Pair("GL8964710001000206", "64710001000206"), + Pair("GR1601101250000000012300695", "01101250000000012300695"), + Pair("GT82TRAJ01020000001210029690", "TRAJ01020000001210029690"), + Pair("HN88CABF00000000000250005469", "CABF00000000000250005469"), + Pair("HR1210010051863000160", "10010051863000160"), + Pair("HU42117730161111101800000000", "117730161111101800000000"), + Pair("IE29AIBK93115212345678", "AIBK93115212345678"), + Pair("IL620108000000099999999", "0108000000099999999"), + Pair("IQ98NBIQ850123456789012", "NBIQ850123456789012"), + Pair("IS140159260076545510730339", "0159260076545510730339"), + Pair("IT60X0542811101000000123456", "X0542811101000000123456"), + Pair("JO94CBJO0010000000000131000302", "CBJO0010000000000131000302"), + Pair("KW81CBKU0000000000001234560101", "CBKU0000000000001234560101"), + Pair("KZ86125KZT5004100100", "125KZT5004100100"), + Pair("LB62099900000001001901229114", "0999 0000 0001 0019 0122 9114"), + Pair("LC55HEMM000100010012001200023015", "HEMM000100010012001200023015"), + Pair("LI21088100002324013AA", "088100002324013AA"), + Pair("LT121000011101001000", "1000011101001000"), + Pair("LU280019400644750000", "0019400644750000"), + Pair("LV80BANK0000435195001", "BANK0000435195001"), + Pair("LY83002048000020100120361", "002048000020100120361"), + Pair("MC5811222000010123456789030", "11222 00001 01234567890 30"), + Pair("MD24AG000225100013104168", "AG000225100013104168"), + Pair("ME25505000012345678951", "505000012345678951"), + Pair("MK07250120000058984", "250120000058984"), + Pair("MN121234123456789123", "1234123456789123"), + Pair("MR1300020001010000123456753", "00020001010000123456753"), + Pair("MT84MALT011000012345MTLCAST001S", "MALT011000012345MTLCAST001S"), + Pair("MU17BOMM0101101030300200000MUR", "BOMM0101101030300200000MUR"), + Pair("NI45BAPR00000013000003558124", "BAPR00000013000003558124"), + Pair("NL91ABNA0417164300", "ABNA0417164300"), + Pair("NO9386011117947", "86011117947"), + Pair("OM810180000001299123456", "0180000001299123456"), + Pair("PK36SCBL0000001123456702", "SCBL0000001123456702"), + Pair("PL61109010140000071219812874", "109010140000071219812874"), + Pair("PS92PALS000000000400123456702", "PALS000000000400123456702"), + Pair("PT50000201231234567890154", "000201231234567890154"), + Pair("QA58DOHB00001234567890ABCDEFG", "DOHB00001234567890ABCDEFG"), + Pair("RO49AAAA1B31007593840000", "AAAA1B31007593840000"), + Pair("RS35260005601001611379", "260005601001611379"), + Pair("RU0304452522540817810538091310419", "044525225 40817 810 5 3809 1310419"), + Pair("SA0380000000608010167519", "80000000608010167519"), + Pair("SC18SSCB11010000000000001497USD", "SSCB11010000000000001497USD"), + Pair("SD2129010501234001", "29010501234001"), + Pair("SE4550000000058398257466", "50000000058398257466"), + Pair("SI56263300012039086", "263300012039086"), + Pair("SK3112000000198742637541", "12000000198742637541"), + Pair("SM86U0322509800000000270100", "U0322509800000000270100"), + Pair("SO211000001001000100141", "1000001001000100141"), + Pair("ST23000100010051845310146", "000100010051845310146"), + Pair("SV62CENR00000000000000700025", "CENR00000000000000700025"), + Pair("TL380080012345678910157", "0080012345678910157"), + Pair("TN5910006035183598478831", "10006035183598478831"), + Pair("TR330006100519786457841326", "0006100519786457841326"), + Pair("UA213223130000026007233566001", "3223130000026007233566001"), + Pair("VA59001123000012345678", "001123000012345678"), + Pair("VG96VPVG0000012345678901", "VPVG0000012345678901"), + Pair("XK051212012345678906", "1212012345678906"), + Pair("YE15CBYE0001018861234567891234", "CBYE0001018861234567891234"), +); +\ No newline at end of file diff --git a/common/src/test/kotlin/IbanTest.kt b/common/src/test/kotlin/IbanTest.kt @@ -0,0 +1,42 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2025 Taler Systems S.A. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +import org.junit.Test +import tech.libeufin.common.* +import kotlin.test.assertEquals + +class IbanTest { + @Test + fun valid() { + for ((iban, bban) in VALID_IBAN) { + val parsed = IBAN.parse(iban) + } + } + + @Test + fun roundtrip() { + for (country in Country.values()) { + val gen = IBAN.rand(country) + println("$gen") + val parsed = IBAN.parse("$gen") + println("$gen $parsed") + assertEquals(gen, parsed) + } + } +} +\ No newline at end of file