commit 1d10fa6fded425cc2d82c30c49d5bf15bac54ed9 parent de07e380ac52dc162f9844a730b3a8aff61267ae Author: Florian Dold <florian@dold.me> Date: Fri, 22 Sep 2023 10:29:56 +0200 refactoring: pass DB explicitly, pass currency explicitly Diffstat:
16 files changed, 132 insertions(+), 86 deletions(-)
diff --git a/.idea/misc.xml b/.idea/misc.xml @@ -1,5 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> + <component name="FrameworkDetectionExcludesConfiguration"> + <file type="web" url="file://$PROJECT_DIR$" /> + </component> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="16" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/bank/build.gradle b/bank/build.gradle @@ -23,7 +23,7 @@ compileTestKotlin { } } -task installToPrefix(type: Copy) { +tasks.register('installToPrefix', Copy) { dependsOn(installShadowDist) from("build/install/bank-shadow") { include("**/libeufin-bank") @@ -46,23 +46,17 @@ sourceSets { dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1-native-mt' - implementation "com.hubspot.jinjava:jinjava:2.5.9" implementation 'ch.qos.logback:logback-classic:1.4.5' implementation project(":util") + // XML: implementation "javax.xml.bind:jaxb-api:2.3.0" implementation "org.glassfish.jaxb:jaxb-runtime:2.3.1" - implementation 'org.apache.santuario:xmlsec:2.2.2' - implementation group: 'org.bouncycastle', name: 'bcprov-jdk16', version: '1.46' - implementation group: 'org.xerial', name: 'sqlite-jdbc', version: '3.36.0.1' - implementation 'org.postgresql:postgresql:42.2.23.jre7' + implementation 'org.postgresql:postgresql:42.2.27' implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.21' implementation('com.github.ajalt:clikt:2.8.0') - implementation "org.jetbrains.exposed:exposed-core:$exposed_version" - implementation "org.jetbrains.exposed:exposed-dao:$exposed_version" - implementation "org.jetbrains.exposed:exposed-jdbc:$exposed_version" implementation "io.ktor:ktor-server-core:$ktor_version" implementation "io.ktor:ktor-server-call-logging:$ktor_version" diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt @@ -21,6 +21,8 @@ package tech.libeufin.bank import org.postgresql.jdbc.PgConnection +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.sql.DriverManager import java.sql.PreparedStatement import java.sql.SQLException @@ -35,16 +37,19 @@ fun BankAccount.expectBalance(): TalerAmount = this.balance ?: throw internalSer fun BankAccount.expectRowId(): Long = this.bankAccountId ?: throw internalServerError("Bank account '${this.internalPaytoUri}' lacks database row ID.") fun BankAccountTransaction.expectRowId(): Long = this.dbRowId ?: throw internalServerError("Bank account transaction (${this.subject}) lacks database row ID.") +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank.Database") class Database(private val dbConfig: String) { private var dbConn: PgConnection? = null private var dbCtr: Int = 0 private val preparedStatements: MutableMap<String, PreparedStatement> = mutableMapOf() + private var cachedCurrency: String? = null; init { Class.forName("org.postgresql.Driver") } + private fun reconnect() { dbCtr++ val myDbConn = dbConn @@ -85,6 +90,23 @@ class Database(private val dbConfig: String) { return true } + /** + * Get the currency applicable to the bank. + */ + private fun getCurrency(): String { + var myCurrency = cachedCurrency + if (myCurrency != null) { + return myCurrency + } + // FIXME: Should be retrieved from the config file instead of the DB. + myCurrency = configGet("internal_currency") + if (myCurrency == null) { + throw Error("configuration does not specify currency") + } + cachedCurrency = myCurrency + return myCurrency + } + // CONFIG fun configGet(configKey: String): String? { reconnect() @@ -340,7 +362,8 @@ class Database(private val dbConfig: String) { internalPaytoUri = it.getString("internal_payto_uri"), balance = TalerAmount( it.getLong("balance_val"), - it.getInt("balance_frac") + it.getInt("balance_frac"), + getCurrency() ), lastNexusFetchRowId = it.getLong("last_nexus_fetch_row_id"), owningCustomerId = it.getLong("owning_customer_id"), @@ -348,7 +371,8 @@ class Database(private val dbConfig: String) { isTalerExchange = it.getBoolean("is_taler_exchange"), maxDebt = TalerAmount( value = it.getLong("max_debt_val"), - frac = it.getInt("max_debt_frac") + frac = it.getInt("max_debt_frac"), + getCurrency() ), bankAccountId = it.getLong("bank_account_id") ) @@ -380,7 +404,8 @@ class Database(private val dbConfig: String) { internalPaytoUri = internalPayto, balance = TalerAmount( it.getLong("balance_val"), - it.getInt("balance_frac") + it.getInt("balance_frac"), + getCurrency() ), lastNexusFetchRowId = it.getLong("last_nexus_fetch_row_id"), owningCustomerId = it.getLong("owning_customer_id"), @@ -388,7 +413,8 @@ class Database(private val dbConfig: String) { isTalerExchange = it.getBoolean("is_taler_exchange"), maxDebt = TalerAmount( value = it.getLong("max_debt_val"), - frac = it.getInt("max_debt_frac") + frac = it.getInt("max_debt_frac"), + getCurrency() ), bankAccountId = it.getLong("bank_account_id") ) @@ -493,7 +519,8 @@ class Database(private val dbConfig: String) { debtorName = it.getString("debtor_name"), amount = TalerAmount( it.getLong("amount_val"), - it.getInt("amount_frac") + it.getInt("amount_frac"), + getCurrency() ), accountServicerReference = it.getString("account_servicer_reference"), endToEndId = it.getString("end_to_end_id"), @@ -594,7 +621,8 @@ class Database(private val dbConfig: String) { debtorName = it.getString("debtor_name"), amount = TalerAmount( it.getLong("amount_val"), - it.getInt("amount_frac") + it.getInt("amount_frac"), + getCurrency() ), accountServicerReference = it.getString("account_servicer_reference"), endToEndId = it.getString("end_to_end_id"), @@ -652,7 +680,8 @@ class Database(private val dbConfig: String) { return TalerWithdrawalOperation( amount = TalerAmount( it.getLong("amount_val"), - it.getInt("amount_frac") + it.getInt("amount_frac"), + getCurrency() ), selectionDone = it.getBoolean("selection_done"), selectedExchangePayto = it.getString("selected_exchange_payto"), @@ -871,17 +900,20 @@ class Database(private val dbConfig: String) { return Cashout( amountDebit = TalerAmount( value = it.getLong("amount_debit_val"), - frac = it.getInt("amount_debit_frac") + frac = it.getInt("amount_debit_frac"), + getCurrency() ), amountCredit = TalerAmount( value = it.getLong("amount_credit_val"), - frac = it.getInt("amount_credit_frac") + frac = it.getInt("amount_credit_frac"), + getCurrency() ), bankAccount = it.getLong("bank_account"), buyAtRatio = it.getInt("buy_at_ratio"), buyInFee = TalerAmount( value = it.getLong("buy_in_fee_val"), - frac = it.getInt("buy_in_fee_frac") + frac = it.getInt("buy_in_fee_frac"), + getCurrency() ), credit_payto_uri = it.getString("credit_payto_uri"), cashoutCurrency = it.getString("cashout_currency"), @@ -890,7 +922,8 @@ class Database(private val dbConfig: String) { sellAtRatio = it.getInt("sell_at_ratio"), sellOutFee = TalerAmount( value = it.getLong("sell_out_fee_val"), - frac = it.getInt("sell_out_fee_frac") + frac = it.getInt("sell_out_fee_frac"), + getCurrency() ), subject = it.getString("subject"), tanChannel = it.getString("tan_channel").run { @@ -946,6 +979,7 @@ class Database(private val dbConfig: String) { amount = TalerAmount( value = it.getLong("amount_value"), frac = it.getInt("amount_frac"), + getCurrency() ), creditAccount = it.getString("credit_account_payto"), exchangeBaseUrl = it.getString("exchange_base_url"), diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt @@ -39,7 +39,6 @@ import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.* import kotlinx.serialization.modules.SerializersModule import net.taler.common.errorcodes.TalerErrorCode -import org.jetbrains.exposed.sql.stringLiteral import org.slf4j.Logger import org.slf4j.LoggerFactory import org.slf4j.event.Level @@ -47,11 +46,10 @@ import tech.libeufin.util.* import java.time.Duration // GLOBALS -val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank") -val db = Database(System.getProperty("BANK_DB_CONNECTION_STRING")) +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank.Main") +private val db = Database(System.getProperty("BANK_DB_CONNECTION_STRING")) const val GENERIC_UNDEFINED = -1 // Filler for ECs that don't exist yet. val TOKEN_DEFAULT_DURATION_US = Duration.ofDays(1L).seconds * 1000000 -const val FRACTION_BASE = 100000000 /** @@ -129,8 +127,8 @@ fun ApplicationCall.myAuth(requiredScope: TokenScope): Customer? { TalerErrorCode.TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED ) return when (authDetails.scheme) { - "Basic" -> doBasicAuth(authDetails.content) - "Bearer" -> doTokenAuth(authDetails.content, requiredScope) + "Basic" -> doBasicAuth(db, authDetails.content) + "Bearer" -> doTokenAuth(db, authDetails.content, requiredScope) else -> throw LibeufinBankException( httpStatus = HttpStatusCode.Unauthorized, talerError = TalerError( @@ -245,11 +243,11 @@ val webApp: Application.() -> Unit = { call.respond(Config()) return@get } - this.accountsMgmtHandlers() - this.tokenHandlers() - this.transactionsHandlers() - this.talerWebHandlers() - this.talerIntegrationHandlers() - this.talerWireGatewayHandlers() + this.accountsMgmtHandlers(db) + this.tokenHandlers(db) + this.transactionsHandlers(db) + this.talerWebHandlers(db) + this.talerIntegrationHandlers(db) + this.talerWireGatewayHandlers(db) } } \ No newline at end of file diff --git a/bank/src/main/kotlin/tech/libeufin/bank/accountsMgmtHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/accountsMgmtHandlers.kt @@ -6,15 +6,19 @@ import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* import net.taler.common.errorcodes.TalerErrorCode +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.libeufin.util.CryptoUtil import tech.libeufin.util.maybeUriComponent +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank.accountsMgmtHandlers") + /** * This function collects all the /accounts handlers that * create, update, delete, show bank accounts. No histories * and wire transfers should belong here. */ -fun Routing.accountsMgmtHandlers() { +fun Routing.accountsMgmtHandlers(db: Database) { post("/accounts") { // check if only admin. val maybeOnlyAdmin = db.configGet("only_admin_registrations") diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt @@ -27,11 +27,17 @@ import io.ktor.server.request.* import io.ktor.server.util.* import net.taler.common.errorcodes.TalerErrorCode import net.taler.wallet.crypto.Base32Crockford +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.libeufin.util.* import java.lang.NumberFormatException import java.net.URL import java.util.* +const val FRACTION_BASE = 100000000 + +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank.helpers") + fun ApplicationCall.expectUriComponent(componentName: String) = this.maybeUriComponent(componentName) ?: throw badRequest( hint = "No username found in the URI", @@ -58,7 +64,7 @@ fun ApplicationCall.getAuthToken(): String? { * Performs the HTTP basic authentication. Returns the * authenticated customer on success, or null otherwise. */ -fun doBasicAuth(encodedCredentials: String): Customer? { +fun doBasicAuth(db: Database, encodedCredentials: String): Customer? { val plainUserAndPass = String(base64ToBytes(encodedCredentials), Charsets.UTF_8) // :-separated val userAndPassSplit = plainUserAndPass.split( ":", @@ -102,6 +108,7 @@ private fun splitBearerToken(tok: String): String? { /* Performs the bearer-token authentication. Returns the * authenticated customer on success, null otherwise. */ fun doTokenAuth( + db: Database, token: String, requiredScope: TokenScope, ): Customer? { @@ -261,7 +268,7 @@ fun parseTalerAmount( return TalerAmount( value = value, frac = fraction, - maybeCurrency = match.destructured.component1() + currency = match.destructured.component1() ) } @@ -272,7 +279,7 @@ private fun normalizeAmount(amt: TalerAmount): TalerAmount { return TalerAmount( value = normalValue, frac = normalFrac, - maybeCurrency = amt.currency + currency = amt.currency ) } return amt @@ -295,7 +302,7 @@ private fun amountAdd(first: TalerAmount, second: TalerAmount): TalerAmount { return normalizeAmount(TalerAmount( value = valueAdd, frac = fracAdd, - maybeCurrency = first.currency + currency = first.currency )) } @@ -343,7 +350,7 @@ fun isBalanceEnough( (normalDiff.frac > normalMaxDebt.frac)) return false return true } -fun getBankCurrency(): String = db.configGet("internal_currency") ?: throw internalServerError("Bank lacks currency") +fun getBankCurrency(db: Database): String = db.configGet("internal_currency") ?: throw internalServerError("Bank lacks currency") /** * Builds the taler://withdraw-URI. Such URI will serve the requests @@ -403,7 +410,7 @@ fun getWithdrawalConfirmUrl( * if the query param doesn't parse into a UUID. Currently * used by the Taler Web/SPA and Integration API handlers. */ -fun getWithdrawal(opIdParam: String): TalerWithdrawalOperation { +fun getWithdrawal(db: Database, opIdParam: String): TalerWithdrawalOperation { val opId = try { UUID.fromString(opIdParam) } catch (e: Exception) { @@ -412,7 +419,7 @@ fun getWithdrawal(opIdParam: String): TalerWithdrawalOperation { } val op = db.talerWithdrawalGet(opId) ?: throw notFound( - hint = "Withdrawal operation ${opIdParam} not found", + hint = "Withdrawal operation $opIdParam not found", talerEc = TalerErrorCode.TALER_EC_END ) return op diff --git a/bank/src/main/kotlin/tech/libeufin/bank/talerIntegrationHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/talerIntegrationHandlers.kt @@ -28,7 +28,7 @@ import io.ktor.server.routing.* import net.taler.common.errorcodes.TalerErrorCode import tech.libeufin.util.getBaseUrl -fun Routing.talerIntegrationHandlers() { +fun Routing.talerIntegrationHandlers(db: Database) { get("/taler-integration/config") { val internalCurrency: String = db.configGet("internal_currency") ?: throw internalServerError("Currency not found") @@ -38,7 +38,7 @@ fun Routing.talerIntegrationHandlers() { // Note: wopid acts as an authentication token. get("/taler-integration/withdrawal-operation/{wopid}") { val wopid = call.expectUriComponent("wopid") - val op = getWithdrawal(wopid) // throws 404 if not found. + val op = getWithdrawal(db, wopid) // throws 404 if not found. val relatedBankAccount = db.bankAccountGetFromOwnerId(op.walletBankAccount) if (relatedBankAccount == null) throw internalServerError("Bank has a withdrawal not related to any bank account.") @@ -66,7 +66,7 @@ fun Routing.talerIntegrationHandlers() { post("/taler-integration/withdrawal-operation/{wopid}") { val wopid = call.expectUriComponent("wopid") val req = call.receive<BankWithdrawalOperationPostRequest>() - val op = getWithdrawal(wopid) // throws 404 if not found. + val op = getWithdrawal(db, wopid) // throws 404 if not found. if (op.selectionDone) { // idempotency if (op.selectedExchangePayto != req.selected_exchange && diff --git a/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/talerWebHandlers.kt @@ -36,7 +36,7 @@ import tech.libeufin.util.getBaseUrl import tech.libeufin.util.getNowUs import java.util.* -fun Routing.talerWebHandlers() { +fun Routing.talerWebHandlers(db: Database) { post("/accounts/{USERNAME}/withdrawals") { val c = call.myAuth(TokenScope.readwrite) ?: throw unauthorized() // Admin not allowed to withdraw in the name of customers: @@ -84,7 +84,7 @@ fun Routing.talerWebHandlers() { // Admin allowed to see the details if (c.login != accountName && c.login != "admin") throw forbidden() // Permissions passed, get the information. - val op = getWithdrawal(call.expectUriComponent("withdrawal_id")) + val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id")) call.respond(BankAccountGetWithdrawalResponse( amount = op.amount.toString(), aborted = op.aborted, @@ -99,7 +99,7 @@ fun Routing.talerWebHandlers() { val c = call.myAuth(TokenScope.readonly) ?: throw unauthorized() // Admin allowed to abort. if (!call.getResourceName("USERNAME").canI(c)) throw forbidden() - val op = getWithdrawal(call.expectUriComponent("withdrawal_id")) + val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id")) // Idempotency: if (op.aborted) { call.respondText("{}", ContentType.Application.Json) @@ -117,7 +117,7 @@ fun Routing.talerWebHandlers() { val c = call.myAuth(TokenScope.readwrite) ?: throw unauthorized() // No admin allowed. if(!call.getResourceName("USERNAME").canI(c, withAdmin = false)) throw forbidden() - val op = getWithdrawal(call.expectUriComponent("withdrawal_id")) + val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id")) // Checking idempotency: if (op.confirmationDone) { call.respondText("{}", ContentType.Application.Json) diff --git a/bank/src/main/kotlin/tech/libeufin/bank/talerWireGatewayHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/talerWireGatewayHandlers.kt @@ -29,7 +29,7 @@ import io.ktor.server.routing.* import net.taler.common.errorcodes.TalerErrorCode import tech.libeufin.util.getNowUs -fun Routing.talerWireGatewayHandlers() { +fun Routing.talerWireGatewayHandlers(db: Database) { get("/taler-wire-gateway/config") { val internalCurrency = db.configGet("internal_currency") ?: throw internalServerError("Could not find bank own currency.") diff --git a/bank/src/main/kotlin/tech/libeufin/bank/tokenHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/tokenHandlers.kt @@ -25,10 +25,14 @@ import io.ktor.server.response.* import io.ktor.server.routing.* import net.taler.common.errorcodes.TalerErrorCode import net.taler.wallet.crypto.Base32Crockford +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.libeufin.util.maybeUriComponent import tech.libeufin.util.getNowUs -fun Routing.tokenHandlers() { +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank.accountsMgmtHandlers") + +fun Routing.tokenHandlers(db: Database) { delete("/accounts/{USERNAME}/token") { throw internalServerError("Token deletion not implemented.") } @@ -66,7 +70,7 @@ fun Routing.tokenHandlers() { return@run try { this.toLong() } catch (e: Exception) { - tech.libeufin.bank.logger.error("Could not convert config's token_max_duration to Long") + logger.error("Could not convert config's token_max_duration to Long") throw internalServerError(e.message) } } diff --git a/bank/src/main/kotlin/tech/libeufin/bank/transactionsHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/transactionsHandlers.kt @@ -7,11 +7,16 @@ import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* import net.taler.common.errorcodes.TalerErrorCode +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.libeufin.util.getNowUs import tech.libeufin.util.parsePayto import kotlin.math.abs -fun Routing.transactionsHandlers() { + +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank.transactionHandlers") + +fun Routing.transactionsHandlers(db: Database) { get("/accounts/{USERNAME}/transactions") { val c = call.myAuth(TokenScope.readonly) ?: throw unauthorized() val resourceName = call.expectUriComponent("USERNAME") @@ -65,7 +70,7 @@ fun Routing.transactionsHandlers() { TalerErrorCode.TALER_EC_END // FIXME: define this EC. ) val amount = parseTalerAmount(txData.amount) - if (amount.currency != getBankCurrency()) + if (amount.currency != getBankCurrency(db)) throw badRequest( "Wrong currency: ${amount.currency}", talerErrorCode = TalerErrorCode.TALER_EC_GENERIC_CURRENCY_MISMATCH diff --git a/bank/src/main/kotlin/tech/libeufin/bank/types.kt b/bank/src/main/kotlin/tech/libeufin/bank/types.kt @@ -160,14 +160,8 @@ data class Customer( class TalerAmount( val value: Long, val frac: Int, - maybeCurrency: String? = null + val currency: String ) { - val currency: String = if (maybeCurrency == null) { - val internalCurrency = db.configGet("internal_currency") - ?: throw internalServerError("internal_currency not found in the config") - internalCurrency - } else maybeCurrency - override fun equals(other: Any?): Boolean { return other is TalerAmount && other.value == this.value && diff --git a/bank/src/test/kotlin/AmountTest.kt b/bank/src/test/kotlin/AmountTest.kt @@ -19,10 +19,7 @@ import org.junit.Test -import tech.libeufin.bank.FracDigits -import tech.libeufin.bank.TalerAmount -import tech.libeufin.bank.isBalanceEnough -import tech.libeufin.bank.parseTalerAmount +import tech.libeufin.bank.* class AmountTest { @Test @@ -79,7 +76,7 @@ class AmountTest { fun testAutoCurrency() { val db = initDb() db.configSet("internal_currency", "KUDOS") - val a = TalerAmount(1L, 0) + val a = TalerAmount(1L, 0, getBankCurrency(db)) assert(a.currency == "KUDOS") } diff --git a/bank/src/test/kotlin/DatabaseTest.kt b/bank/src/test/kotlin/DatabaseTest.kt @@ -131,14 +131,15 @@ class DatabaseTest { assert(db.bankAccountCreate(bankAccountBar)) var fooAccount = db.bankAccountGetFromOwnerId(fooId!!) assert(fooAccount?.hasDebt == false) // Foo has NO debit. + val currency = "KUDOS" // Preparing the payment data. db.bankAccountSetMaxDebt( fooId, - TalerAmount(100, 0) + TalerAmount(100, 0, currency) ) db.bankAccountSetMaxDebt( barId!!, - TalerAmount(50, 0) + TalerAmount(50, 0, currency) ) val firstSpending = db.bankTransactionCreate(fooPaysBar) // Foo pays Bar and goes debit. assert(firstSpending == Database.BankTransactionResult.SUCCESS) @@ -167,7 +168,7 @@ class DatabaseTest { creditorAccountId = 1, debtorAccountId = 2, subject = "test", - amount = TalerAmount(10, 0), + amount = TalerAmount(10, 0, currency), accountServicerReference = "acct-svcr-ref", endToEndId = "end-to-end-id", paymentInformationId = "pmtinfid", @@ -220,17 +221,19 @@ class DatabaseTest { @Test fun bankAccountTest() { val db = initDb() + val currency = "KUDOS" assert(db.bankAccountGetFromOwnerId(1L) == null) assert(db.customerCreate(customerFoo) != null) assert(db.bankAccountCreate(bankAccountFoo)) assert(!db.bankAccountCreate(bankAccountFoo)) // Triggers conflict. - assert(db.bankAccountGetFromOwnerId(1L)?.balance?.equals(TalerAmount(0, 0)) == true) + assert(db.bankAccountGetFromOwnerId(1L)?.balance?.equals(TalerAmount(0, 0, currency)) == true) } @Test fun withdrawalTest() { val db = initDb() val uuid = UUID.randomUUID() + val currency = "KUDOS" assert(db.customerCreate(customerFoo) != null) assert(db.bankAccountCreate(bankAccountFoo)) assert(db.customerCreate(customerBar) != null) // plays the exchange. @@ -239,7 +242,7 @@ class DatabaseTest { assert(db.talerWithdrawalCreate( uuid, 1L, - TalerAmount(1, 0) + TalerAmount(1, 0, currency) )) // get it. val op = db.talerWithdrawalGet(uuid) @@ -260,9 +263,10 @@ class DatabaseTest { @Test fun historyTest() { val db = initDb() + val currency = "KUDOS" db.customerCreate(customerFoo); db.bankAccountCreate(bankAccountFoo) db.customerCreate(customerBar); db.bankAccountCreate(bankAccountBar) - assert(db.bankAccountSetMaxDebt(1L, TalerAmount(10000000, 0))) + assert(db.bankAccountSetMaxDebt(1L, TalerAmount(10000000, 0, currency))) // Foo pays Bar 100 times: for (i in 1..100) { db.bankTransactionCreate(genTx("test-$i")) } // Testing positive delta: @@ -282,15 +286,16 @@ class DatabaseTest { @Test fun cashoutTest() { val db = initDb() + val currency = "KUDOS" val op = Cashout( cashoutUuid = UUID.randomUUID(), - amountDebit = TalerAmount(1, 0), - amountCredit = TalerAmount(2, 0), + amountDebit = TalerAmount(1, 0, currency), + amountCredit = TalerAmount(2, 0, currency), bankAccount = 1L, buyAtRatio = 3, - buyInFee = TalerAmount(0, 22), + buyInFee = TalerAmount(0, 22, currency), sellAtRatio = 2, - sellOutFee = TalerAmount(0, 44), + sellOutFee = TalerAmount(0, 44, currency), credit_payto_uri = "IBAN", cashoutCurrency = "KUDOS", creationTime = 3L, @@ -310,14 +315,14 @@ class DatabaseTest { assert(db.cashoutCreate(op)) db.bankAccountSetMaxDebt( fooId!!, - TalerAmount(100, 0) + TalerAmount(100, 0, currency) ) assert(db.bankTransactionCreate( BankInternalTransaction( creditorAccountId = 2, debtorAccountId = 1, subject = "backing the cash-out", - amount = TalerAmount(10, 0), + amount = TalerAmount(10, 0, currency), accountServicerReference = "acct-svcr-ref", endToEndId = "end-to-end-id", paymentInformationId = "pmtinfid", diff --git a/bank/src/test/kotlin/LibeuFinApiTest.kt b/bank/src/test/kotlin/LibeuFinApiTest.kt @@ -31,10 +31,11 @@ class LibeuFinApiTest { cashoutPayto = "payto://external-IBAN", cashoutCurrency = "KUDOS" ) + private fun genBankAccount(rowId: Long) = BankAccount( hasDebt = false, internalPaytoUri = "payto://iban/SANDBOXX/${rowId}-IBAN", - maxDebt = TalerAmount(100, 0), + maxDebt = TalerAmount(100, 0, "KUDOS"), owningCustomerId = rowId ) @@ -164,7 +165,7 @@ class LibeuFinApiTest { BankAccount( hasDebt = false, internalPaytoUri = "payto://iban/SANDBOXX/FOO-IBAN", - maxDebt = TalerAmount(100, 0), + maxDebt = TalerAmount(100, 0, "KUDOS"), owningCustomerId = customerRowId!! ) )) @@ -186,7 +187,7 @@ class LibeuFinApiTest { assert(db.bankAccountCreate(BankAccount( hasDebt = false, internalPaytoUri = "payto://iban/SANDBOXX/ADMIN-IBAN", - maxDebt = TalerAmount(100, 0), + maxDebt = TalerAmount(100, 0, "KUDOS"), owningCustomerId = adminRowId!! ))) client.get("/accounts/foo") { diff --git a/bank/src/test/kotlin/TalerApiTest.kt b/bank/src/test/kotlin/TalerApiTest.kt @@ -56,7 +56,7 @@ class TalerApiTest { // Give the exchange reasonable debt allowance: assert(db.bankAccountSetMaxDebt( 1L, - TalerAmount(1000, 0) + TalerAmount(1000, 0, "KUDOS") )) // Do POST /transfer. testApplication { @@ -131,7 +131,7 @@ class TalerApiTest { // Give Foo reasonable debt allowance: assert(db.bankAccountSetMaxDebt( 1L, - TalerAmount(1000, 0) + TalerAmount(1000, 0, "KUDOS") )) // Foo pays Bar (the exchange) twice. assert(db.bankTransactionCreate(genTx("withdrawal 1")) == Database.BankTransactionResult.SUCCESS) @@ -164,7 +164,7 @@ class TalerApiTest { // Give Bar reasonable debt allowance: assert(db.bankAccountSetMaxDebt( 2L, - TalerAmount(1000, 0) + TalerAmount(1000, 0, "KUDOS") )) testApplication { application(webApp) @@ -196,7 +196,7 @@ class TalerApiTest { assert(db.talerWithdrawalCreate( opUUID = uuid, walletBankAccount = 1L, - amount = TalerAmount(1, 0) + amount = TalerAmount(1, 0, "KUDOS") )) testApplication { application(webApp) @@ -226,7 +226,7 @@ class TalerApiTest { assert(db.talerWithdrawalCreate( opUUID = uuid, walletBankAccount = 1L, - amount = TalerAmount(1, 0) + amount = TalerAmount(1, 0, "KUDOS") )) testApplication { application(webApp) @@ -247,7 +247,7 @@ class TalerApiTest { assert(db.talerWithdrawalCreate( opUUID = uuid, walletBankAccount = 1L, - amount = TalerAmount(1, 0) + amount = TalerAmount(1, 0, "KUDOS") )) val op = db.talerWithdrawalGet(uuid) assert(op?.aborted == false) @@ -301,7 +301,7 @@ class TalerApiTest { assert(db.talerWithdrawalCreate( opUUID = uuid, walletBankAccount = 1L, - amount = TalerAmount(1, 0) + amount = TalerAmount(1, 0, "KUDOS") )) // Specifying Bar as the exchange, via its Payto URI. assert(db.talerWithdrawalSetDetails(