gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated (0ee8d69d -> 7dfdeeda)


From: gnunet
Subject: [libeufin] branch master updated (0ee8d69d -> 7dfdeeda)
Date: Mon, 13 Mar 2023 10:25:11 +0100

This is an automated email from the git hooks/post-receive script.

ms pushed a change to branch master
in repository libeufin.

    from 0ee8d69d readme
     new c094c812 /history/incoming: no negative start param.
     new d13ac7ba Adapt tests to #7515.
     new 14c2af86 Testing #7515.
     new 0e7ebe41 Using #7515 constructs.
     new 7dfdeeda Addressing #7515 (core change).

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt    |   1 -
 nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt |   9 +-
 .../tech/libeufin/nexus/server/NexusServer.kt      |   2 +-
 nexus/src/test/kotlin/MakeEnv.kt                   |  58 +++-----
 nexus/src/test/kotlin/SandboxAccessApiTest.kt      |   5 +-
 nexus/src/test/kotlin/SandboxCircuitApiTest.kt     |  14 +-
 nexus/src/test/kotlin/TalerTest.kt                 |   4 +-
 .../kotlin/tech/libeufin/sandbox/CircuitApi.kt     |   8 +-
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    | 131 +++++++++++++++--
 .../tech/libeufin/sandbox/EbicsProtocolBackend.kt  |   6 +-
 .../main/kotlin/tech/libeufin/sandbox/Helpers.kt   |  40 ++++-
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 163 +++++++++++----------
 .../kotlin/tech/libeufin/sandbox/bankAccount.kt    |  19 +--
 sandbox/src/test/kotlin/BalanceTest.kt             |  21 ++-
 sandbox/src/test/kotlin/DBTest.kt                  |  67 ++++++---
 15 files changed, 358 insertions(+), 190 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index f8ce7bd4..b1e47d3d 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -387,7 +387,6 @@ class FacadeEntity(id: EntityID<Long>) : LongEntity(id) {
             return find { FacadesTable.facadeName eq name}.firstOrNull()
         }
     }
-
     var facadeName by FacadesTable.facadeName
     var type by FacadesTable.type
     var creator by NexusUserEntity referencedOn FacadesTable.creator
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
index c9e73662..8f1d32f2 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
@@ -131,11 +131,14 @@ fun getComparisonOperator(delta: Int, start: Long, table: 
IdTable<Long>): Op<Boo
     }
 }
 
-fun expectLong(param: String?): Long {
+fun expectLong(param: String?, allowNegative: Boolean = false): Long {
     if (param == null) throw badRequest("'$param' is not Long")
-    return try { param.toLong() } catch (e: Exception) {
+    val maybeLong = try { param.toLong() } catch (e: Exception) {
         throw badRequest("'$param' is not Long")
     }
+    if (!allowNegative && maybeLong < 0)
+        throw badRequest("Not expecting a negative: $param")
+    return maybeLong
 }
 
 // Helper handling 'start' being optional and its dependence on 'delta'.
@@ -480,7 +483,7 @@ private suspend fun historyOutgoing(call: ApplicationCall) {
 // Handle a /taler-wire-gateway/history/incoming request.
 private suspend fun historyIncoming(call: ApplicationCall) {
     val facadeId = expectNonNull(call.parameters["fcid"])
-    val username = call.request.requirePermission(
+    call.request.requirePermission(
         PermissionQuery(
             "facade",
             facadeId,
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
index 2632100f..12b0dd26 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
@@ -190,7 +190,7 @@ val nexusApp: Application.() -> Unit = {
             )
         }
         exception<UtilError> { call, cause ->
-            logger.error("Exception while handling '${call.request.uri}'", 
cause.message)
+            logger.error("Exception while handling '${call.request.uri}': 
${cause.message}")
             call.respond(
                 cause.statusCode,
                 message = ErrorResponse(
diff --git a/nexus/src/test/kotlin/MakeEnv.kt b/nexus/src/test/kotlin/MakeEnv.kt
index 31cc5704..9f8f5249 100644
--- a/nexus/src/test/kotlin/MakeEnv.kt
+++ b/nexus/src/test/kotlin/MakeEnv.kt
@@ -22,8 +22,6 @@ data class EbicsKeys(
     val enc: CryptoUtil.RsaCrtKeyPair,
     val sig: CryptoUtil.RsaCrtKeyPair
 )
-const val TEST_DB_FILE = "/tmp/nexus-test.sqlite3"
-// const val TEST_DB_CONN = "jdbc:sqlite:$TEST_DB_FILE"
 // Convenience DB connection to switch to Postgresql:
 val currentUser = System.getProperty("user.name")
 val TEST_DB_CONN = 
"jdbc:postgresql://localhost:5432/libeufincheck?user=$currentUser"
@@ -63,23 +61,11 @@ inline fun <reified ExceptionType> assertException(
  * Cleans up the DB file afterwards.
  */
 fun withTestDatabase(f: () -> Unit) {
-    File(TEST_DB_FILE).also {
-        if (it.exists()) {
-            it.delete()
-        }
-    }
     Database.connect(TEST_DB_CONN)
     TransactionManager.manager.defaultIsolationLevel = 
java.sql.Connection.TRANSACTION_SERIALIZABLE
     dbDropTables(TEST_DB_CONN)
     tech.libeufin.sandbox.dbDropTables(TEST_DB_CONN)
-    try { f() }
-    finally {
-        File(TEST_DB_FILE).also {
-            if (it.exists()) {
-                it.delete()
-            }
-        }
-    }
+    f()
 }
 
 val reportSpec: String = jacksonObjectMapper().
@@ -169,19 +155,21 @@ fun prepNexusDb() {
     }
 }
 
-fun prepSandboxDb() {
+fun prepSandboxDb(usersDebtLimit: Int = 1000) {
     tech.libeufin.sandbox.dbCreateTables(TEST_DB_CONN)
     transaction {
-        val demoBank = DemobankConfigEntity.new {
-            currency = "TESTKUDOS"
-            bankDebtLimit = 10000
-            usersDebtLimit = 1000
-            allowRegistrations = true
-            name = "default"
-            this.withSignupBonus = false
-            captchaUrl = "http://example.com/"; // unused
+        val config = DemobankConfig(
+            currency = "TESTKUDOS",
+            bankDebtLimit = 10000,
+            usersDebtLimit = usersDebtLimit,
+            allowRegistrations = true,
+            demobankName = "default",
+            withSignupBonus = false,
+            captchaUrl = "http://example.com/";,
             suggestedExchangePayto = "payto://iban/${BAR_USER_IBAN}"
-        }
+        )
+        insertConfigPairs(config)
+        val demoBank = DemobankConfigEntity.new { name = "default" }
         BankAccountEntity.new {
             iban = BANK_IBAN
             label = "admin" // used by the wire helper
@@ -275,15 +263,17 @@ fun withSandboxTestDatabase(f: () -> Unit) {
     withTestDatabase {
         tech.libeufin.sandbox.dbCreateTables(TEST_DB_CONN)
         transaction {
-            val d = DemobankConfigEntity.new {
-                currency = "TESTKUDOS"
-                bankDebtLimit = 10000
-                usersDebtLimit = 1000
-                allowRegistrations = true
-                name = "default"
-                this.withSignupBonus = false
+            val config = DemobankConfig(
+                currency = "TESTKUDOS",
+                bankDebtLimit = 10000,
+                usersDebtLimit = 1000,
+                allowRegistrations = true,
+                demobankName = "default",
+                withSignupBonus = false,
                 captchaUrl = "http://example.com/"; // unused
-            }
+            )
+            insertConfigPairs(config)
+            val d = DemobankConfigEntity.new { name = "default" }
             // admin's bank account.
             BankAccountEntity.new {
                 iban = BANK_IBAN
@@ -299,7 +289,7 @@ fun withSandboxTestDatabase(f: () -> Unit) {
 
 fun newNexusBankTransaction(currency: String, value: String, subject: String) {
     transaction {
-        val inc = NexusBankTransactionEntity.new {
+        NexusBankTransactionEntity.new {
             bankAccount = NexusBankAccountEntity.findByName("foo")!!
             accountTransactionId = "mock"
             creditDebitIndicator = "CRDT"
diff --git a/nexus/src/test/kotlin/SandboxAccessApiTest.kt 
b/nexus/src/test/kotlin/SandboxAccessApiTest.kt
index e163a1bb..f924a5fe 100644
--- a/nexus/src/test/kotlin/SandboxAccessApiTest.kt
+++ b/nexus/src/test/kotlin/SandboxAccessApiTest.kt
@@ -5,6 +5,7 @@ import io.ktor.client.statement.*
 import io.ktor.http.*
 import io.ktor.server.testing.*
 import kotlinx.coroutines.runBlocking
+import org.jetbrains.exposed.sql.and
 import org.jetbrains.exposed.sql.transactions.transaction
 import org.junit.Test
 import tech.libeufin.sandbox.*
@@ -66,11 +67,9 @@ class SandboxAccessApiTest {
     @Test
     fun highAmountWithdraw() {
         withTestDatabase {
-            prepSandboxDb()
-            val b = getDefaultDemobank()
+            prepSandboxDb(usersDebtLimit = 900000000)
             testApplication {
                 application(sandboxApp)
-                transaction { b.usersDebtLimit = 900000000 }
                 // Create the operation.
                 val r = 
client.post("/demobanks/default/access-api/accounts/foo/withdrawals") {
                     expectSuccess = true
diff --git a/nexus/src/test/kotlin/SandboxCircuitApiTest.kt 
b/nexus/src/test/kotlin/SandboxCircuitApiTest.kt
index 350c5e8e..0de4121c 100644
--- a/nexus/src/test/kotlin/SandboxCircuitApiTest.kt
+++ b/nexus/src/test/kotlin/SandboxCircuitApiTest.kt
@@ -5,6 +5,7 @@ import io.ktor.client.statement.*
 import io.ktor.http.*
 import io.ktor.server.testing.*
 import kotlinx.coroutines.runBlocking
+import org.jetbrains.exposed.sql.and
 import org.jetbrains.exposed.sql.lowerCase
 import org.jetbrains.exposed.sql.transactions.transaction
 import org.junit.Ignore
@@ -221,8 +222,15 @@ class SandboxCircuitApiTest {
                         """.trimIndent())
                 }
                 // Give initial balance to the new account.
-                val demobank = getDefaultDemobank()
-                transaction { demobank.usersDebtLimit = 0 }
+                // Forcing different debt limit:
+                transaction {
+                    val configRaw = DemobankConfigPairEntity.find {
+                        DemobankConfigPairsTable.demobankName eq "default" and(
+                                DemobankConfigPairsTable.configKey eq 
"usersDebtLimit"
+                                )
+                    }.first()
+                    configRaw.configValue = 0.toString()
+                }
                 val initialBalance = "TESTKUDOS:50.00"
                 val balanceAfterCashout = "TESTKUDOS:30.00"
                 wireTransfer(
@@ -514,7 +522,7 @@ class SandboxCircuitApiTest {
                     uCustomerProfile.delete()
                 }
                 val barBalanceUpdate = getBalance("bar")
-                assert(barBalance == BigDecimal("3"))
+                assert(barBalanceUpdate == BigDecimal("3"))
             }
         }
     }
diff --git a/nexus/src/test/kotlin/TalerTest.kt 
b/nexus/src/test/kotlin/TalerTest.kt
index 7eadeb25..22493606 100644
--- a/nexus/src/test/kotlin/TalerTest.kt
+++ b/nexus/src/test/kotlin/TalerTest.kt
@@ -32,11 +32,13 @@ class TalerTest {
                 application(nexusApp)
                 runBlocking {
                     launch {
-                        val r = 
client.get("/facades/taler/taler-wire-gateway/history/incoming?delta=5") {
+                        val r = 
client.get("/facades/taler/taler-wire-gateway/history/incoming?delta=5&start=0&long_poll_ms=3000")
 {
                             expectSuccess = false
                             contentType(ContentType.Application.Json)
                             basicAuth("foo", "foo")
                         }
+                        println("maybe response body: ${r.bodyAsText()}")
+                        assert(r.status.value == HttpStatusCode.OK.value)
                         val j = mapper.readTree(r.readBytes())
                         val reservePubFromTwg = 
j.get("incoming_transactions").get(0).get("reserve_pub").asText()
                         assert(reservePubFromTwg == reservePub)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt
index 2c325ccc..8ddbab47 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt
@@ -371,9 +371,9 @@ fun circuitApi(circuitRoute: Route) {
         val amountCredit = parseAmount(req.amount_credit) // amount after 
rates, as expected by the client
         val demobank = ensureDemobank(call)
         // Currency check of the cash-out's circuit part.
-        if (amountDebit.currency != demobank.currency)
+        if (amountDebit.currency != demobank.config.currency)
             throw badRequest("'${req::amount_debit.name}' 
(${req.amount_debit})" +
-                    " doesn't match the regional currency 
(${demobank.currency})"
+                    " doesn't match the regional currency 
(${demobank.config.currency})"
             )
         // Currency check of the cash-out's fiat part.
         if (amountCredit.currency != FIAT_CURRENCY)
@@ -415,7 +415,7 @@ fun circuitApi(circuitRoute: Route) {
         // check that the balance is sufficient
         val balance = getBalance(user, withPending = true)
         val balanceCheck = balance - amountDebitAsNumber
-        if (balanceCheck < BigDecimal.ZERO && balanceCheck.abs() > 
BigDecimal(demobank.usersDebtLimit))
+        if (balanceCheck < BigDecimal.ZERO && balanceCheck.abs() > 
BigDecimal(demobank.config.usersDebtLimit))
             throw SandboxError(
                 HttpStatusCode.PreconditionFailed,
                 "Cash-out not possible due to insufficient funds.  Balance 
${balance.toPlainString()} would reach ${balanceCheck.toPlainString()}"
@@ -567,7 +567,7 @@ fun circuitApi(circuitRoute: Route) {
                     val name = it.name
                     val balance = getBalanceForJson(
                         getBalance(it.username),
-                        getDefaultDemobank().currency
+                        getDefaultDemobank().config.currency
                     )
                     val debitThreshold = getMaxDebitForUser(it.username)
                 })
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 3281b67a..00688082 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -19,6 +19,7 @@
 
 package tech.libeufin.sandbox
 
+import io.ktor.http.*
 import org.jetbrains.exposed.dao.Entity
 import org.jetbrains.exposed.dao.EntityClass
 import org.jetbrains.exposed.dao.IntEntity
@@ -32,7 +33,10 @@ import org.jetbrains.exposed.dao.id.LongIdTable
 import org.jetbrains.exposed.sql.*
 import org.jetbrains.exposed.sql.transactions.TransactionManager
 import org.jetbrains.exposed.sql.transactions.transaction
+import tech.libeufin.util.internalServerError
 import java.sql.Connection
+import kotlin.reflect.*
+import kotlin.reflect.full.*
 
 /**
  * All the states to give a subscriber.
@@ -87,29 +91,122 @@ enum class KeyState {
     RELEASED
 }
 
+/**
+ * Stores one config object to the database.  Each field
+ * name and value populate respectively the configKey and
+ * configValue columns.  Rows are defined in the following way:
+ * demobankName | configKey | configValue
+ */
+fun insertConfigPairs(config: DemobankConfig, override: Boolean = false) {
+    // Fill the config key-value pairs in the DB.
+    config::class.declaredMemberProperties.forEach { configField ->
+        val maybeValue = configField.getter.call(config)
+        if (override) {
+            val maybeConfigPair = DemobankConfigPairEntity.find {
+                DemobankConfigPairsTable.configKey eq configField.name
+            }.firstOrNull()
+            if (maybeConfigPair == null)
+                throw internalServerError("Cannot override config value 
'${configField.name}' not found.")
+            maybeConfigPair.configValue = maybeValue?.toString()
+            return@forEach
+        }
+        DemobankConfigPairEntity.new {
+            this.demobankName = config.demobankName
+            this.configKey = configField.name
+            this.configValue = maybeValue?.toString()
+        }
+    }
+}
+
+object DemobankConfigPairsTable : LongIdTable() {
+    val demobankName = text("demobankName")
+    val configKey = text("configKey")
+    val configValue = text("configValue").nullable()
+}
+
+class DemobankConfigPairEntity(id: EntityID<Long>) : LongEntity(id) {
+    companion object : 
LongEntityClass<DemobankConfigPairEntity>(DemobankConfigPairsTable)
+    var demobankName by DemobankConfigPairsTable.demobankName
+    var configKey by DemobankConfigPairsTable.configKey
+    var configValue by DemobankConfigPairsTable.configValue
+}
+
 object DemobankConfigsTable : LongIdTable() {
-    val currency = text("currency")
-    val allowRegistrations = bool("allowRegistrations")
-    val withSignupBonus = bool("withSignupBonus")
-    val bankDebtLimit = integer("bankDebtLimit")
-    val usersDebtLimit = integer("usersDebtLimit")
     val name = text("hostname")
-    val suggestedExchangeBaseUrl = text("suggestedExchangeBaseUrl").nullable()
-    val suggestedExchangePayto = text("suggestedExchangePayto").nullable()
-    val captchaUrl = text("captchaUrl").nullable()
+}
+
+// Helpers for handling config values in memory.
+typealias DemobankConfigKey = String
+typealias DemobankConfigValue = String?
+fun Pair<DemobankConfigKey, DemobankConfigValue>.expectValue(): String {
+    if (this.second == null) throw internalServerError("Config value for 
'${this.first}' is null in the database.")
+    return this.second as String
 }
 
 class DemobankConfigEntity(id: EntityID<Long>) : LongEntity(id) {
     companion object : 
LongEntityClass<DemobankConfigEntity>(DemobankConfigsTable)
-    var currency by DemobankConfigsTable.currency
-    var allowRegistrations by DemobankConfigsTable.allowRegistrations
-    var withSignupBonus by DemobankConfigsTable.withSignupBonus
-    var bankDebtLimit by DemobankConfigsTable.bankDebtLimit
-    var usersDebtLimit by DemobankConfigsTable.usersDebtLimit
     var name by DemobankConfigsTable.name
-    var captchaUrl by DemobankConfigsTable.captchaUrl
-    var suggestedExchangeBaseUrl by 
DemobankConfigsTable.suggestedExchangeBaseUrl
-    var suggestedExchangePayto by DemobankConfigsTable.suggestedExchangePayto
+    /**
+     * This object gets defined by parsing all the configuration
+     * values found in the DB for one demobank.  Those values are
+     * retrieved from _another_ table.
+     */
+    val config: DemobankConfig by lazy {
+        // Getting all the values for this demobank.
+        val configPairs: List<Pair<DemobankConfigKey, DemobankConfigValue>> = 
transaction {
+            val maybeConfigPairs = DemobankConfigPairEntity.find {
+                DemobankConfigPairsTable.demobankName.eq(name)
+            }
+            if (maybeConfigPairs.empty()) throw SandboxError(
+                HttpStatusCode.InternalServerError,
+                "No config values of $name were found in the database"
+            )
+            // Copying results to a DB-agnostic list, to later operate out of 
"transaction {}"
+            maybeConfigPairs.map { Pair(it.configKey, it.configValue) }
+        }
+        // Building the args to instantiate a DemobankConfig (non-Exposed) 
object.
+        val args = mutableMapOf<KParameter, Any?>()
+        // For each constructor parameter name, find the same-named database 
entry.
+        val configClass = DemobankConfig::class
+        if (configClass.primaryConstructor == null) {
+            throw SandboxError(
+                HttpStatusCode.InternalServerError,
+                "${configClass.simpleName} primaryConstructor is null."
+            )
+        }
+        if (configClass.primaryConstructor?.parameters == null) {
+            throw SandboxError(
+                HttpStatusCode.InternalServerError,
+                "${configClass.simpleName} primaryConstructor" +
+                        " arguments is null.  Cannot set any config value."
+            )
+        }
+        // For each field in the config object, find the respective DB row.
+        configClass.primaryConstructor?.parameters?.forEach { par: KParameter 
->
+            val configPairFromDb: Pair<DemobankConfigKey, DemobankConfigValue>?
+            = configPairs.firstOrNull {
+                    configPair: Pair<DemobankConfigKey, DemobankConfigValue> ->
+                configPair.first == par.name
+            }
+            if (configPairFromDb == null) {
+                throw SandboxError(
+                    HttpStatusCode.InternalServerError,
+                    "Config key '${par.name}' not found in the database."
+                )
+            }
+            when(par.type) {
+                // non-nullable
+                typeOf<Boolean>() -> { args[par] = 
configPairFromDb.expectValue().toBoolean() }
+                typeOf<Int>() -> { args[par] = 
configPairFromDb.expectValue().toInt() }
+                // nullable
+                typeOf<Boolean?>() -> { args[par] = 
configPairFromDb.second?.toBoolean() }
+                typeOf<Int?>() -> { args[par] = 
configPairFromDb.second?.toInt() }
+                else -> args[par] = configPairFromDb.second
+            }
+        }
+        // Proceeding now to instantiate the config class, and make it a field 
of this type.
+        configClass.primaryConstructor!!.callBy(args)
+    }
 }
 
 /**
@@ -538,6 +635,7 @@ fun dbDropTables(dbConnectionString: String) {
             BankAccountReportsTable,
             BankAccountStatementsTable,
             DemobankConfigsTable,
+            DemobankConfigPairsTable,
             TalerWithdrawalsTable,
             DemobankCustomersTable,
             CashoutOperationsTable
@@ -551,6 +649,7 @@ fun dbCreateTables(dbConnectionString: String) {
     transaction {
         SchemaUtils.create(
             DemobankConfigsTable,
+            DemobankConfigPairsTable,
             EbicsSubscribersTable,
             EbicsHostsTable,
             EbicsDownloadTransactionsTable,
diff --git 
a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index 86bb8f18..798a7761 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -300,7 +300,7 @@ fun buildCamtString(
     val zonedDateTime = camtCreationTime.toZonedString()
     val creationTimeMillis = camtCreationTime.toInstant().toEpochMilli()
     val messageId = "sandbox-${creationTimeMillis / 
1000}-${getRandomString(10)}"
-    val currency = getDefaultDemobank().currency
+    val currency = getDefaultDemobank().config.currency
 
     val camtMessage = constructXml(indent = true) {
         root("Document") {
@@ -741,7 +741,7 @@ private fun handleCct(paymentRequest: String,
             return@transaction
         }
         val bankAccount = getBankAccountFromIban(parseResult.debtorIban)
-        if (parseResult.currency != bankAccount.demoBank.currency) throw 
EbicsRequestError(
+        if (parseResult.currency != bankAccount.demoBank.config.currency) 
throw EbicsRequestError(
             "[EBICS_PROCESSING_ERROR] Currency (${parseResult.currency}) not 
supported.",
             "091116"
         )
@@ -1034,7 +1034,7 @@ private fun makePartnerInfo(subscriber: 
EbicsSubscriberEntity): EbicsTypes.Partn
                         this.value = bankAccount.iban
                     }
                 )
-                this.currency = bankAccount.demoBank.currency
+                this.currency = bankAccount.demoBank.config.currency
                 this.description = "Ordinary Bank Account"
                 this.bankCodeList = listOf(
                     EbicsTypes.GeneralBankCode().apply {
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
index 16f1d6b9..912c2ee6 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
@@ -19,6 +19,8 @@
 
 package tech.libeufin.sandbox
 
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.SerializationFeature
 import io.ktor.server.application.*
 import io.ktor.http.HttpStatusCode
 import io.ktor.server.request.*
@@ -30,6 +32,20 @@ import java.security.interfaces.RSAPublicKey
 import java.util.*
 import java.util.zip.DeflaterInputStream
 
+data class DemobankConfig(
+    val allowRegistrations: Boolean,
+    val currency: String,
+    val bankDebtLimit: Int,
+    val usersDebtLimit: Int,
+    val withSignupBonus: Boolean,
+    val demobankName: String, // demobank name.
+    val captchaUrl: String? = null,
+    val smsTan: String? = null, // fixme: move the config subcommand
+    val emailTan: String? = null, // fixme: same as above.
+    val suggestedExchangeBaseUrl: String? = null,
+    val suggestedExchangePayto: String? = null
+)
+
 /**
  * Helps to communicate Camt values without having
  * to parse the XML each time one is needed.
@@ -120,8 +136,8 @@ fun insertNewAccount(username: String,
             this.demoBank = demobankFromDb
             this.isPublic = isPublic
         }
-        if (demobankFromDb.withSignupBonus)
-            newBankAccount.bonus("${demobankFromDb.currency}:100")
+        if (demobankFromDb.config.withSignupBonus)
+            newBankAccount.bonus("${demobankFromDb.config.currency}:100")
         AccountPair(customer = newCustomer, bankAccount = newBankAccount)
     }
 }
@@ -220,6 +236,24 @@ fun getHistoryElementFromTransactionRow(dbRow: 
BankAccountTransactionEntity): Ra
     )
 }
 
+fun printConfig(demobank: DemobankConfigEntity) {
+    val ret = ObjectMapper()
+    ret.configure(SerializationFeature.INDENT_OUTPUT, true)
+    println(
+        ret.writeValueAsString(object {
+            val currency = demobank.config.currency
+            val bankDebtLimit = demobank.config.bankDebtLimit
+            val usersDebtLimit = demobank.config.usersDebtLimit
+            val allowRegistrations = demobank.config.allowRegistrations
+            val name = demobank.name // always 'default'
+            val withSignupBonus = demobank.config.withSignupBonus
+            val captchaUrl = demobank.config.captchaUrl
+            val suggestedExchangeBaseUrl = 
demobank.config.suggestedExchangeBaseUrl
+            val suggestedExchangePayto = demobank.config.suggestedExchangePayto
+        })
+    )
+}
+
 fun getHistoryElementFromTransactionRow(
     dbRow: BankAccountFreshTransactionEntity
 ): RawPayment {
@@ -440,4 +474,4 @@ fun prepareEbicsPayload(
     val enc = CryptoUtil.encryptEbicsE002(compressedResponse, pub)
     return Pair(Base64.getEncoder().encodeToString(enc.encryptedData), enc)
 
-}
\ No newline at end of file
+}
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 1f02728c..a266ccdd 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -61,6 +61,10 @@ import java.math.BigDecimal
 import java.net.URL
 import java.security.interfaces.RSAPublicKey
 import javax.xml.bind.JAXBContext
+import kotlin.reflect.KProperty0
+import kotlin.reflect.KProperty1
+import kotlin.reflect.full.declaredMemberProperties
+import kotlin.reflect.full.declaredMembers
 import kotlin.system.exitProcess
 
 val logger: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox")
@@ -81,11 +85,7 @@ data class SandboxErrorJson(val error: 
SandboxErrorDetailJson)
 data class SandboxErrorDetailJson(val type: String, val description: String)
 
 class DefaultExchange : CliktCommand("Set default Taler exchange for a 
demobank.") {
-    init {
-        context {
-            helpFormatter = CliktHelpFormatter(showDefaultValues = true)
-        }
-    }
+    init { context { helpFormatter = CliktHelpFormatter(showDefaultValues = 
true) } }
     private val exchangeBaseUrl by argument("EXCHANGE-BASEURL", "base URL of 
the default exchange")
     private val exchangePayto by argument("EXCHANGE-PAYTO", "default 
exchange's payto-address")
     private val demobank by option("--demobank", help = "Which demobank 
defaults to EXCHANGE").default("default")
@@ -102,20 +102,40 @@ class DefaultExchange : CliktCommand("Set default Taler 
exchange for a demobank.
                     System.err.println("Error, demobank $demobank not found.")
                     exitProcess(1)
                 }
-                maybeDemobank.suggestedExchangeBaseUrl = exchangeBaseUrl
-                maybeDemobank.suggestedExchangePayto = exchangePayto
+                val config = maybeDemobank.config
+                /**
+                 * Iterating over the config object's field that hold the 
exchange
+                 * base URL and Payto.  The iteration is only used to retrieve 
the
+                 * correct names of the DB column 'configKey', because this is 
named
+                 * after such fields.
+                 */
+                listOf(
+                    Pair(config::suggestedExchangeBaseUrl, exchangeBaseUrl),
+                    Pair(config::suggestedExchangePayto, exchangePayto)
+                ).forEach {
+                    val maybeConfigPair = DemobankConfigPairEntity.find {
+                        DemobankConfigPairsTable.demobankName eq demobank and(
+                                DemobankConfigPairsTable.configKey eq 
it.first.name)
+                    }.firstOrNull()
+                    /**
+                     * The DB doesn't contain any column to hold the exchange 
URL
+                     * or Payto, fail.  That should never happen, because the 
DB row
+                     * are created _after_ the DemobankConfig object that 
_does_ contain
+                     * such fields.
+                     */
+                    if (maybeConfigPair == null) {
+                        System.err.println("Config key '${it.first.name}' for 
demobank '$demobank' not found in DB.")
+                        exitProcess(1)
+                    }
+                    maybeConfigPair.configValue = it.second
+                }
             }
         }
     }
 }
 
 class Config : CliktCommand("Insert one configuration (a.k.a. demobank) into 
the database.") {
-    init {
-        context {
-            helpFormatter = CliktHelpFormatter(showDefaultValues = true)
-        }
-    }
-
+    init { context { helpFormatter = CliktHelpFormatter(showDefaultValues = 
true) } }
     private val nameArgument by argument(
         "NAME", help = "Name of this configuration.  Currently, only 'default' 
is admitted."
     )
@@ -147,40 +167,40 @@ class Config : CliktCommand("Insert one configuration 
(a.k.a. demobank) into the
         }
         execThrowableOrTerminate {
             dbCreateTables(dbConnString)
-            transaction {
-                val maybeDemobank = BankAccountEntity.find(
-                    BankAccountsTable.label eq "admin"
-                ).firstOrNull()
-                if (showOption) {
-                    if (maybeDemobank != null) {
-                        val ret = ObjectMapper()
-                        ret.configure(SerializationFeature.INDENT_OUTPUT, true)
-                        println(
-                            ret.writeValueAsString(object {
-                                val currency = maybeDemobank.demoBank.currency
-                                val bankDebtLimit = 
maybeDemobank.demoBank.bankDebtLimit
-                                val usersDebtLimit = 
maybeDemobank.demoBank.usersDebtLimit
-                                val allowRegistrations = 
maybeDemobank.demoBank.allowRegistrations
-                                val name = maybeDemobank.demoBank.name // 
always 'default'
-                                val withSignupBonus = 
maybeDemobank.demoBank.withSignupBonus
-                                val captchaUrl = 
maybeDemobank.demoBank.captchaUrl
-                            })
-                        )
-                        return@transaction
-                    }
-                    println("Nothing to show.")
-                    return@transaction
+            val maybeDemobank = transaction { getDemobank(nameArgument) }
+            if (showOption) {
+                if (maybeDemobank != null) {
+                    printConfig(maybeDemobank)
+                } else {
+                    println("Demobank: $nameArgument not found.")
+                    System.exit(1)
                 }
-                if (maybeDemobank == null) {
-                    val demoBank = DemobankConfigEntity.new {
-                        currency = currencyOption
-                        bankDebtLimit = bankDebtLimitOption
-                        usersDebtLimit = usersDebtLimitOption
-                        allowRegistrations = allowRegistrationsOption
-                        name = nameArgument
-                        this.withSignupBonus = withSignupBonusOption
-                        captchaUrl = captchaUrlOption
-                    }
+                return@execThrowableOrTerminate
+            }
+            if (bankDebtLimitOption < 0 || usersDebtLimitOption < 0) {
+                System.err.println("Debt numbers can't be negative.")
+                exitProcess(1)
+            }
+            // The user asks to _set_ values, regardless of overriding or 
creating.
+            val config = DemobankConfig(
+                currency = currencyOption,
+                bankDebtLimit = bankDebtLimitOption,
+                usersDebtLimit = usersDebtLimitOption,
+                allowRegistrations = allowRegistrationsOption,
+                demobankName = nameArgument,
+                withSignupBonus = withSignupBonusOption,
+                captchaUrl = captchaUrlOption
+            )
+            /**
+             * The demobank didn't exist.  Now:
+             *   1, Store the config values in the database.
+             *   2, Store the demobank name in the database.
+             *   3, Create the admin bank account under this demobank.
+             */
+            if (maybeDemobank == null) {
+                transaction {
+                    insertConfigPairs(config)
+                    val demoBank = DemobankConfigEntity.new { this.name = 
nameArgument }
                     BankAccountEntity.new {
                         iban = getIban()
                         label = "admin"
@@ -188,16 +208,10 @@ class Config : CliktCommand("Insert one configuration 
(a.k.a. demobank) into the
                         // For now, the model assumes always one demobank
                         this.demoBank = demoBank
                     }
-                    return@transaction
                 }
-                maybeDemobank.demoBank.currency = currencyOption
-                maybeDemobank.demoBank.bankDebtLimit = bankDebtLimitOption
-                maybeDemobank.demoBank.usersDebtLimit = usersDebtLimitOption
-                maybeDemobank.demoBank.allowRegistrations = 
allowRegistrationsOption
-                maybeDemobank.demoBank.withSignupBonus = withSignupBonusOption
-                maybeDemobank.demoBank.name = nameArgument
-                maybeDemobank.demoBank.captchaUrl = captchaUrlOption
             }
+            // Demobank exists: update its config values in the database.
+            else transaction { insertConfigPairs(config, override = true) }
         }
     }
 }
@@ -391,10 +405,10 @@ class Serve : CliktCommand("Run sandbox HTTP server") {
 
 private fun getJsonFromDemobankConfig(fromDb: DemobankConfigEntity): Demobank {
     return Demobank(
-        currency = fromDb.currency,
-        userDebtLimit = fromDb.usersDebtLimit,
-        bankDebtLimit = fromDb.bankDebtLimit,
-        allowRegistrations = fromDb.allowRegistrations,
+        currency = fromDb.config.currency,
+        userDebtLimit = fromDb.config.usersDebtLimit,
+        bankDebtLimit = fromDb.config.bankDebtLimit,
+        allowRegistrations = fromDb.config.allowRegistrations,
         name = fromDb.name
     )
 }
@@ -710,7 +724,7 @@ val sandboxApp: Application.() -> Unit = {
                     throw unauthorized("'${username}' has no rights over 
'$label'")
                 val balance = getBalance(bankAccount, withPending = true)
                 object {
-                    val balance = "${bankAccount.demoBank.currency}:${balance}"
+                    val balance = 
"${bankAccount.demoBank.config.currency}:${balance}"
                     val iban = bankAccount.iban
                     val bic = bankAccount.bic
                     val label = bankAccount.label
@@ -755,7 +769,7 @@ val sandboxApp: Application.() -> Unit = {
                     this.account = account
                     direction = "CRDT"
                     this.demobank = demobank
-                    this.currency = demobank.currency
+                    this.currency = demobank.config.currency
                 }
             }
             call.respond(object {})
@@ -901,7 +915,7 @@ val sandboxApp: Application.() -> Unit = {
                         this.account = account
                         direction = "CRDT"
                         this.demobank = demobank
-                        currency = demobank.currency
+                        currency = demobank.config.currency
                     }
                 }
 
@@ -922,7 +936,7 @@ val sandboxApp: Application.() -> Unit = {
                         this.account = account
                         direction = "DBIT"
                         this.demobank = demobank
-                        currency = demobank.currency
+                        currency = demobank.config.currency
                     }
                 }
             }
@@ -939,7 +953,7 @@ val sandboxApp: Application.() -> Unit = {
             val body = call.receive<EbicsSubscriberObsoleteApi>()
             transaction {
                 // Check the host ID exists.
-                val maybeHostId = EbicsHostEntity.find {
+                EbicsHostEntity.find {
                     EbicsHostsTable.hostID eq body.hostID
                 }.firstOrNull() ?: throw notFound("Host ID ${body.hostID} not 
found.")
                 // Check it exists first.
@@ -1086,6 +1100,7 @@ val sandboxApp: Application.() -> Unit = {
                 throw e
             }
             catch (e: Exception) {
+                logger.error(e.stackTraceToString())
                 throw EbicsProcessingError("Could not map error to EBICS code: 
$e")
             }
             return@post
@@ -1168,7 +1183,7 @@ val sandboxApp: Application.() -> Unit = {
                     call.respond(SandboxConfig(
                         name = "taler-bank-integration",
                         version = SANDBOX_VERSION,
-                        currency = demobank.currency
+                        currency = demobank.config.currency
                     ))
                     return@get
                 }
@@ -1223,13 +1238,13 @@ val sandboxApp: Application.() -> Unit = {
                         )
                     }
                     val demobank = ensureDemobank(call)
-                    var captcha_page = demobank.captchaUrl
+                    var captcha_page = demobank.config.captchaUrl
                     if (captcha_page == null) logger.warn("CAPTCHA URL not 
found")
                     val ret = TalerWithdrawalStatus(
                         selection_done = maybeWithdrawalOp.selectionDone,
                         transfer_done = maybeWithdrawalOp.confirmationDone,
                         amount = maybeWithdrawalOp.amount,
-                        suggested_exchange = demobank.suggestedExchangeBaseUrl,
+                        suggested_exchange = 
demobank.config.suggestedExchangeBaseUrl,
                         aborted = maybeWithdrawalOp.aborted,
                         confirm_transfer_url = captcha_page
                     )
@@ -1305,8 +1320,8 @@ val sandboxApp: Application.() -> Unit = {
                     val req = call.receive<WithdrawalRequest>()
                     // Check for currency consistency
                     val amount = parseAmount(req.amount)
-                    if (amount.currency != demobank.currency)
-                        throw badRequest("Currency ${amount.currency} differs 
from Demobank's: ${demobank.currency}")
+                    if (amount.currency != demobank.config.currency)
+                        throw badRequest("Currency ${amount.currency} differs 
from Demobank's: ${demobank.config.currency}")
                     // Check funds are sufficient.
                     if (maybeDebit(maybeOwnedAccount.label, 
BigDecimal(amount.amount))) {
                         logger.error("Account ${maybeOwnedAccount.label} would 
surpass debit threshold.  Not withdrawing")
@@ -1373,7 +1388,7 @@ val sandboxApp: Application.() -> Unit = {
                          */
                         val demobank = ensureDemobank(call)
                         if (wo.selectedExchangePayto == null) {
-                            wo.selectedExchangePayto = 
demobank.suggestedExchangePayto
+                            wo.selectedExchangePayto = 
demobank.config.suggestedExchangePayto
                         }
                         val exchangeBankAccount = getBankAccountFromPayto(
                             wo.selectedExchangePayto ?: throw 
internalServerError(
@@ -1422,7 +1437,7 @@ val sandboxApp: Application.() -> Unit = {
                     val balance = getBalance(bankAccount, withPending = true)
                     call.respond(object {
                         val balance = object {
-                            val amount = 
"${demobank.currency}:${balance.abs(). toPlainString()}"
+                            val amount = 
"${demobank.config.currency}:${balance.abs(). toPlainString()}"
                             val credit_debit_indicator = if (balance < 
BigDecimal.ZERO) "debit" else "credit"
                         }
                         val paytoUri = buildIbanPaytoUri(
@@ -1524,7 +1539,7 @@ val sandboxApp: Application.() -> Unit = {
                             val balanceIter = getBalance(it, withPending = 
true)
                             ret.publicAccounts.add(
                                 PublicAccountInfo(
-                                    balance = 
"${demobank.currency}:$balanceIter",
+                                    balance = 
"${demobank.config.currency}:$balanceIter",
                                     iban = it.iban,
                                     accountLabel = it.label
                                 )
@@ -1550,7 +1565,7 @@ val sandboxApp: Application.() -> Unit = {
                 post("/testing/register") {
                     // Check demobank was created.
                     val demobank = ensureDemobank(call)
-                    if (!demobank.allowRegistrations) {
+                    if (!demobank.config.allowRegistrations) {
                         throw SandboxError(
                             HttpStatusCode.UnprocessableEntity,
                             "The bank doesn't allow new registrations at the 
moment."
@@ -1566,7 +1581,7 @@ val sandboxApp: Application.() -> Unit = {
                     )
                     val balance = getBalance(newAccount.bankAccount, 
withPending = true)
                     call.respond(object {
-                        val balance = getBalanceForJson(balance, 
demobank.currency)
+                        val balance = getBalanceForJson(balance, 
demobank.config.currency)
                         val paytoUri = buildIbanPaytoUri(
                             iban = newAccount.bankAccount.iban,
                             bic = newAccount.bankAccount.bic,
@@ -1618,4 +1633,4 @@ val sandboxApp: Application.() -> Unit = {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
index 5bd45a6f..0e7e64f6 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
@@ -21,8 +21,8 @@ fun maybeDebit(
     )
     val balance = getBalance(accountLabel, withPending = true)
     val maxDebt = if (accountLabel == "admin") {
-        demobank.bankDebtLimit
-    } else demobank.usersDebtLimit
+        demobank.config.bankDebtLimit
+    } else demobank.config.usersDebtLimit
     val balanceCheck = balance - requestedAmount
     if (balanceCheck < BigDecimal.ZERO && balanceCheck.abs() > 
BigDecimal.valueOf(maxDebt.toLong())) {
         logger.warn("User '$accountLabel' would surpass the debit" +
@@ -34,10 +34,8 @@ fun maybeDebit(
 
 fun getMaxDebitForUser(username: String): Int {
     val bank = getDefaultDemobank()
-    if (username == "admin") return bank.bankDebtLimit
-    return bank.usersDebtLimit
-
-
+    if (username == "admin") return bank.config.bankDebtLimit
+    return bank.config.usersDebtLimit
 }
 
 fun getBalanceForJson(value: BigDecimal, currency: String): BalanceJson {
@@ -45,7 +43,6 @@ fun getBalanceForJson(value: BigDecimal, currency: String): 
BalanceJson {
         amount = "${currency}:${value.abs()}",
         credit_debit_indicator = if (value < BigDecimal.ZERO) "DBIT" else 
"CRDT"
     )
-
 }
 
 /**
@@ -147,10 +144,10 @@ fun wireTransfer(
     val amountAsNumber = BigDecimal(parsedAmount.amount)
     if (amountAsNumber == BigDecimal.ZERO)
         throw badRequest("Wire transfers of zero not possible.")
-    if (parsedAmount.currency != demobank.currency)
+    if (parsedAmount.currency != demobank.config.currency)
         throw badRequest(
             "Won't wire transfer with currency: ${parsedAmount.currency}." +
-                    "  Only ${demobank.currency} allowed."
+                    "  Only ${demobank.config.currency} allowed."
         )
     // Check funds are sufficient.
     if (maybeDebit(debitAccount.label, amountAsNumber)) {
@@ -169,7 +166,7 @@ fun wireTransfer(
             debtorName = getPersonNameFromCustomer(debitAccount.owner)
             this.subject = subject
             this.amount = parsedAmount.amount
-            this.currency = demobank.currency
+            this.currency = demobank.config.currency
             date = timeStamp
             accountServicerReference = transactionRef
             account = creditAccount
@@ -186,7 +183,7 @@ fun wireTransfer(
             debtorName = getPersonNameFromCustomer(debitAccount.owner)
             this.subject = subject
             this.amount = parsedAmount.amount
-            this.currency = demobank.currency
+            this.currency = demobank.config.currency
             date = timeStamp
             accountServicerReference = transactionRef
             account = debitAccount
diff --git a/sandbox/src/test/kotlin/BalanceTest.kt 
b/sandbox/src/test/kotlin/BalanceTest.kt
index 48497e98..cf9f3918 100644
--- a/sandbox/src/test/kotlin/BalanceTest.kt
+++ b/sandbox/src/test/kotlin/BalanceTest.kt
@@ -8,24 +8,21 @@ import java.math.BigDecimal
 import java.time.LocalDateTime
 
 class BalanceTest {
-
     @Test
     fun balanceTest() {
+        val config = DemobankConfig(
+            currency = "EUR",
+            bankDebtLimit = 1000000,
+            usersDebtLimit = 10000,
+            allowRegistrations = true,
+            demobankName = "default",
+            withSignupBonus = false
+        )
         withTestDatabase {
             transaction {
-                SchemaUtils.create(
-                    BankAccountsTable,
-                    BankAccountTransactionsTable,
-                    BankAccountFreshTransactionsTable,
-                    BankAccountStatementsTable
-                )
+                insertConfigPairs(config)
                 val demobank = DemobankConfigEntity.new {
-                    currency = "EUR"
-                    bankDebtLimit = 1000000
-                    usersDebtLimit = 10000
-                    allowRegistrations = true
                     name = "default"
-                    withSignupBonus = false
                 }
                 val one = BankAccountEntity.new {
                     iban = "IBAN 1"
diff --git a/sandbox/src/test/kotlin/DBTest.kt 
b/sandbox/src/test/kotlin/DBTest.kt
index c74ebce1..c63efd6f 100644
--- a/sandbox/src/test/kotlin/DBTest.kt
+++ b/sandbox/src/test/kotlin/DBTest.kt
@@ -30,47 +30,72 @@ import java.time.LocalDateTime
  * Cleans up the DB file afterwards.
  */
 fun withTestDatabase(f: () -> Unit) {
-    val dbfile = "jdbc:sqlite:/tmp/nexus-test.sqlite3"
-    File(dbfile).also {
+    val dbFile = "/tmp/sandbox-test.sqlite3"
+    val dbConn = "jdbc:sqlite:${dbFile}"
+    File(dbFile).also {
         if (it.exists()) {
             it.delete()
         }
     }
-    Database.connect("$dbfile")
-    dbDropTables(dbfile)
-    try {
-        f()
-    }
+    Database.connect(dbConn)
+    dbDropTables(dbConn)
+    dbCreateTables(dbConn)
+    try { f() }
     finally {
-        File(dbfile).also {
-            if (it.exists()) {
+        File(dbFile).also {
+            if (it.exists())
                 it.delete()
-            }
         }
     }
 }
 
 class DBTest {
+    private var config = DemobankConfig(
+        currency = "EUR",
+        bankDebtLimit = 1000000,
+        usersDebtLimit = 10000,
+        allowRegistrations = true,
+        demobankName = "default",
+        withSignupBonus = false,
+    )
+
+    /**
+     * Storing configuration values into the database,
+     * then extract them and check that they equal the
+     * configuration model object.
+     */
     @Test
-    fun exist() {
-        println("x")
+    fun insertPairsTest() {
+        withTestDatabase {
+            // Config model.
+            val config = DemobankConfig(
+                currency = "EUR",
+                bankDebtLimit = 1,
+                usersDebtLimit = 2,
+                allowRegistrations = true,
+                demobankName = "default",
+                withSignupBonus = true
+            )
+            transaction {
+                DemobankConfigEntity.new { name = "default" }
+                insertConfigPairs(config)
+                val db = getDefaultDemobank()
+                /**
+                 * db.config extracts config values from the database
+                 * and puts them in a fresh config model object.
+                 */
+                assert(config.hashCode() == db.config.hashCode())
+            }
+        }
     }
 
     @Test
     fun betweenDates() {
         withTestDatabase {
             transaction {
-                SchemaUtils.create(
-                    BankAccountTransactionsTable,
-                    BankAccountFreshTransactionsTable
-                )
+                insertConfigPairs(config)
                 val demobank = DemobankConfigEntity.new {
-                    currency = "EUR"
-                    bankDebtLimit = 1000000
-                    usersDebtLimit = 10000
-                    allowRegistrations = true
                     name = "default"
-                    withSignupBonus = false
                 }
                 val bankAccount = BankAccountEntity.new {
                     iban = "iban"

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]