gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated (fb40741b -> 58b39bd5)


From: gnunet
Subject: [libeufin] branch master updated (fb40741b -> 58b39bd5)
Date: Sun, 24 Sep 2023 17:00:19 +0200

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

dold pushed a change to branch master
in repository libeufin.

    from fb40741b refactoring, adapt to core bank API withdrawal change
     new 12015ed6 basic support for variables in config
     new 58b39bd5 native dbinit

The 2 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:
 Makefile                                           |  13 +--
 .../tech/libeufin/bank/CorebankApiHandlers.kt      |   6 --
 .../src/main/kotlin/tech/libeufin/bank/Database.kt |  72 +++++++++++++-
 bank/src/main/kotlin/tech/libeufin/bank/Main.kt    |  34 ++++++-
 bank/src/test/kotlin/Common.kt                     |  20 ++--
 bank/src/test/kotlin/TalerApiTest.kt               |   6 +-
 contrib/libeufin-bank-dbinit                       | 107 ---------------------
 ...ibeufin-bank.sample.conf => libeufin-bank.conf} |   6 +-
 util/src/main/kotlin/TalerConfig.kt                |  93 +++++++++++++++++-
 util/src/test/kotlin/TalerConfigTest.kt            |  16 +++
 10 files changed, 230 insertions(+), 143 deletions(-)
 delete mode 100755 contrib/libeufin-bank-dbinit
 rename contrib/{libeufin-bank.sample.conf => libeufin-bank.conf} (67%)

diff --git a/Makefile b/Makefile
index ce3a1cc3..7fd05bf0 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,9 @@ define versions_check =
     then echo WARNING: Project version from Gradle: $(gradle_version) differs 
from current Git tag: $(git_tag); fi
 endef
 
+sql_dir=$(prefix)/share/taler/sql/libeufin-bank
+config_dir=$(prefix)/share/taler/config.d
+
 .PHONY: dist
 dist:
        @$(call versions_check)
@@ -37,6 +40,7 @@ deb: exec-arch copy-spa
 
 .PHONY: install-bank
 install-bank:
+       @install -D contrib/libeufin-bank.conf $(config_dir)
        @./gradlew -q -Pprefix=$(prefix) bank:installToPrefix; cd ..
 
 # To reactivate after the refactoring.
@@ -51,12 +55,9 @@ install-cli:
 
 .PHONY: install-db-versioning
 install-db-versioning:
-       $(eval BANK_DBINIT_SCRIPT := libeufin-bank-dbinit)
-       @sed 
"s|__BANK_STATIC_PATCHES_LOCATION__|$(prefix)/share/libeufin/sql/bank|" < 
contrib/$(BANK_DBINIT_SCRIPT) > build/$(BANK_DBINIT_SCRIPT)
-       @install -D database-versioning/libeufin-bank*.sql -t 
$(prefix)/share/libeufin/sql/bank
-       @install -D database-versioning/versioning.sql -t 
$(prefix)/share/libeufin/sql/bank
-       @install -D database-versioning/procedures.sql -t 
$(prefix)/share/libeufin/sql/bank
-       @install -D build/$(BANK_DBINIT_SCRIPT) -t $(prefix)/bin
+       @install -D database-versioning/libeufin-bank*.sql -t $(sql_dir)
+       @install -D database-versioning/versioning.sql -t $(sql_dir)
+       @install -D database-versioning/procedures.sql -t $(sql_dir)
 
 .PHONY: assemble
 assemble:
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
index bfae12c4..e1939073 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
@@ -272,9 +272,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
     }
 
     post("/withdrawals/{withdrawal_id}/abort") {
-        val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw 
unauthorized()
-        // Admin allowed to abort.
-        if (!call.getResourceName("USERNAME").canI(c)) throw forbidden()
         val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id"))
         // Idempotency:
         if (op.aborted) {
@@ -290,9 +287,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
     }
 
     post("/withdrawals/{withdrawal_id}/confirm") {
-        val c = call.authenticateBankRequest(db, TokenScope.readwrite) ?: 
throw unauthorized()
-        // No admin allowed.
-        if (!call.getResourceName("USERNAME").canI(c, withAdmin = false)) 
throw forbidden()
         val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id"))
         // Checking idempotency:
         if (op.confirmationDone) {
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
index 8bcb5e56..44d0b61a 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
@@ -24,9 +24,10 @@ import org.postgresql.jdbc.PgConnection
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import tech.libeufin.util.getJdbcConnectionFromPg
-import java.net.URI
+import java.io.File
 import java.sql.DriverManager
 import java.sql.PreparedStatement
+import java.sql.ResultSet
 import java.sql.SQLException
 import java.util.*
 import kotlin.math.abs
@@ -41,6 +42,75 @@ fun BankAccountTransaction.expectRowId(): Long = 
this.dbRowId ?: throw internalS
 
 private val logger: Logger = 
LoggerFactory.getLogger("tech.libeufin.bank.Database")
 
+fun initializeDatabaseTables(dbConfig: String, sqlDir: String) {
+    logger.info("doing DB initialization, sqldir $sqlDir, dbConfig $dbConfig")
+    val jdbcConnStr = getJdbcConnectionFromPg(dbConfig)
+    logger.info("connecting to database via JDBC string '$jdbcConnStr'")
+    val dbConn = 
DriverManager.getConnection(jdbcConnStr).unwrap(PgConnection::class.java)
+    if (dbConn == null) {
+        throw Error("could not open database")
+    }
+    val sqlVersioning = File("$sqlDir/versioning.sql").readText()
+    dbConn.execSQLUpdate(sqlVersioning)
+
+    val checkStmt = dbConn.prepareStatement("SELECT count(*) as n FROM 
_v.patches where patch_name = ?")
+
+    for (n in 1..9999) {
+        val numStr = n.toString().padStart(4, '0')
+        val patchName = "libeufin-bank-$numStr"
+
+        checkStmt.setString(1, patchName)
+        val res = checkStmt.executeQuery()
+        if (!res.next()) {
+            throw Error("unable to query patches")
+        }
+
+        val patchCount = res.getInt("n")
+        if (patchCount >= 1) {
+            logger.info("patch $patchName already applied")
+            continue
+        }
+
+        val path = File("$sqlDir/libeufin-bank-$numStr.sql")
+        if (!path.exists()) {
+            logger.info("path $path doesn't exist anymore, stopping")
+            break
+        }
+        logger.info("applying patch $path")
+        val sqlPatchText = path.readText()
+        dbConn.execSQLUpdate(sqlPatchText)
+    }
+    val sqlProcedures = File("$sqlDir/procedures.sql").readText()
+    dbConn.execSQLUpdate(sqlProcedures)
+}
+
+private fun countRows(rs: ResultSet): Int {
+    var size = 0
+    while (rs.next()) {
+        size++
+    }
+    return size
+}
+
+fun resetDatabaseTables(dbConfig: String, sqlDir: String) {
+    logger.info("doing DB initialization, sqldir $sqlDir, dbConfig $dbConfig")
+    val jdbcConnStr = getJdbcConnectionFromPg(dbConfig)
+    logger.info("connecting to database via JDBC string '$jdbcConnStr'")
+    val dbConn = 
DriverManager.getConnection(jdbcConnStr).unwrap(PgConnection::class.java)
+    if (dbConn == null) {
+        throw Error("could not open database")
+    }
+
+    val queryRes = dbConn.execSQLQuery("SELECT schema_name FROM 
information_schema.schemata WHERE schema_name='_v'")
+    if (countRows(queryRes) == 0) {
+        logger.info("versioning schema not present, not running drop sql")
+        return
+    }
+
+    val sqlDrop = File("$sqlDir/libeufin-bank-drop.sql").readText()
+    dbConn.execSQLUpdate(sqlDrop)
+}
+
 class Database(private val dbConfig: String, private val bankCurrency: String) 
{
     private var dbConn: PgConnection? = null
     private var dbCtr: Int = 0
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index cb4bb7f9..025ff5e8 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -287,6 +287,7 @@ fun Application.corebankWebApp(db: Database, ctx: 
BankApplicationContext) {
 class LibeufinBankCommand : CliktCommand() {
     init {
         versionOption(getVersion())
+        subcommands(ServeBank(), BankDbInit())
     }
 
     override fun run() = Unit
@@ -374,6 +375,35 @@ fun readBankApplicationContextFromConfig(cfg: 
TalerConfig): BankApplicationConte
     )
 }
 
+
+class BankDbInit : CliktCommand("Initialize the libeufin-bank database", name 
= "dbinit") {
+    private val configFile by option(
+        "--config", "-c",
+        help = "set the configuration file"
+    )
+
+    private val requestReset by option(
+        "--reset", "-r",
+        help = "reset database (DANGEROUS: All existing data is lost)"
+    ).flag()
+
+    init {
+        context {
+            helpFormatter = CliktHelpFormatter(showDefaultValues = true)
+        }
+    }
+
+    override fun run() {
+        val config = TalerConfig.load(this.configFile)
+        val dbConnStr = config.requireValueString("libeufin-bankdb", "config")
+        val sqlDir = config.requireValuePath("libeufin-bankdb-postgres", 
"sql_dir")
+        if (requestReset) {
+            resetDatabaseTables(dbConnStr, sqlDir)
+        }
+        initializeDatabaseTables(dbConnStr, sqlDir)
+    }
+}
+
 class ServeBank : CliktCommand("Run libeufin-bank HTTP server", name = 
"serve") {
     private val configFile by option(
         "--config", "-c",
@@ -388,7 +418,7 @@ class ServeBank : CliktCommand("Run libeufin-bank HTTP 
server", name = "serve")
     override fun run() {
         val config = TalerConfig.load(this.configFile)
         val ctx = readBankApplicationContextFromConfig(config)
-        val dbConnStr = config.requireValueString("libeufin-bank-db-postgres", 
"config")
+        val dbConnStr = config.requireValueString("libeufin-bankdb", "config")
         logger.info("using database '$dbConnStr'")
         val serveMethod = config.requireValueString("libeufin-bank", "serve")
         if (serveMethod.lowercase() != "tcp") {
@@ -407,5 +437,5 @@ class ServeBank : CliktCommand("Run libeufin-bank HTTP 
server", name = "serve")
 }
 
 fun main(args: Array<String>) {
-    LibeufinBankCommand().subcommands(ServeBank()).main(args)
+    LibeufinBankCommand().main(args)
 }
diff --git a/bank/src/test/kotlin/Common.kt b/bank/src/test/kotlin/Common.kt
index 8206c93d..01a36985 100644
--- a/bank/src/test/kotlin/Common.kt
+++ b/bank/src/test/kotlin/Common.kt
@@ -17,25 +17,19 @@
  * <http://www.gnu.org/licenses/>
  */
 
-import tech.libeufin.bank.BankApplicationContext
-import tech.libeufin.bank.Database
-import tech.libeufin.bank.TalerAmount
+import tech.libeufin.bank.*
 import tech.libeufin.util.execCommand
 
 /**
  * Init the database and sets the currency to KUDOS.
  */
 fun initDb(): Database {
-    execCommand(
-        listOf(
-            "libeufin-bank-dbinit",
-            "-d",
-            "libeufincheck",
-            "-r"
-        ),
-        throwIfFails = true
-    )
-    return Database("postgresql:///libeufincheck", "KUDOS")
+    val config = TalerConfig.load()
+    val sqlPath = config.requireValuePath("libeufin-bankdb-postgres", 
"SQL_DIR")
+    val dbConnStr = "postgresql:///libeufincheck"
+    resetDatabaseTables(dbConnStr, sqlPath)
+    initializeDatabaseTables(dbConnStr, sqlPath)
+    return Database(dbConnStr, "KUDOS")
 }
 
 fun getTestContext(
diff --git a/bank/src/test/kotlin/TalerApiTest.kt 
b/bank/src/test/kotlin/TalerApiTest.kt
index 64a33bac..e76c367d 100644
--- a/bank/src/test/kotlin/TalerApiTest.kt
+++ b/bank/src/test/kotlin/TalerApiTest.kt
@@ -262,7 +262,7 @@ class TalerApiTest {
             application {
                 corebankWebApp(db, ctx)
             }
-            client.post("/accounts/foo/withdrawals/${uuid}/abort") {
+            client.post("/withdrawals/${uuid}/abort") {
                 expectSuccess = true
                 basicAuth("foo", "pw")
             }
@@ -292,7 +292,7 @@ class TalerApiTest {
             }
             val opId = 
Json.decodeFromString<BankAccountCreateWithdrawalResponse>(r.bodyAsText())
             // Getting the withdrawal from the bank.  Throws (failing the 
test) if not found.
-            client.get("/accounts/foo/withdrawals/${opId.withdrawal_id}") {
+            client.get("/withdrawals/${opId.withdrawal_id}") {
                 expectSuccess = true
                 basicAuth("foo", "pw")
             }
@@ -328,7 +328,7 @@ class TalerApiTest {
             application {
                 corebankWebApp(db, ctx)
             }
-            client.post("/accounts/foo/withdrawals/${uuid}/confirm") {
+            client.post("/withdrawals/${uuid}/confirm") {
                 expectSuccess = true // Sufficient to assert on success.
                 basicAuth("foo", "pw")
             }
diff --git a/contrib/libeufin-bank-dbinit b/contrib/libeufin-bank-dbinit
deleted file mode 100755
index 8425070c..00000000
--- a/contrib/libeufin-bank-dbinit
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/bin/bash
-
-set -eu
-
-# The only CLI argument is 'nexus' or 'sandbox',
-# indicating which service will get its database prepared.
-
-fail () {
-  echo $1
-  exit 1
-}
-
-usage_and_exit () {
-  echo Usage: libeufin-bank-dbinit OPTIONS
-  echo
-  echo By default, this command creates and/or patches the Sandbox tables.
-  echo Pass '-r' to drop the tables before creating them again.
-  echo
-  echo 'Supported options:'
-  echo '  -d DB_CONN  -- required.  Pass DB_CONN as the postgres connection 
string.  Passed verbatim to Psql'
-  echo '  -l LOC      -- required.  Pass LOC as the SQL files location.  
Typically $prefix/share/libeufin/sql/bank'
-  echo '  -h           -- print this help'
-  echo '  -r           -- drop all the tables before creating them again'
-  exit 0
-}
-
-run_sql_file () {
-  # -q doesn't hide all the output, hence the
-  # redirection to /dev/null.
-  psql -d $DB_CONNECTION \
-    -q \
-    -f $1 \
-    --set ON_ERROR_STOP=1 > /dev/null
-}
-
-get_patch_path () {
-  echo "$PATCHES_LOCATION/$1"
-}
-
-# The real check happens (by the caller)
-# by checking the returned text.
-check_patch_applied () {
-  psql -d $DB_CONNECTION \
-  -t \
-  -c "SELECT applied_by FROM _v.patches WHERE patch_name = '$1' LIMIT 1"
-}
-
-# Iterates over the .sql migration files and applies
-# the new ones.
-iterate_over_patches () {
-  cd $PATCHES_LOCATION
-  for patch_filename in $(ls -1 -v libeufin-bank-[0-9][0-9][0-9][0-9].sql); do
-    patch_name=$(echo $patch_filename | cut -f1 -d.) # drops the final .sql
-    echo Checking patch: "$patch_name"
-    maybe_applied=$(check_patch_applied "$patch_name")
-    if test -n "$maybe_applied"; then continue; fi
-    # patch not applied, apply it.
-    echo Patch $patch_name not applied, applying it.
-    run_sql_file $patch_filename
-  done
-  cd - > /dev/null # cd to previous location.
-}
-
-if test $# -eq 0; then
-  usage_and_exit
-fi
-
-while getopts ":d:l:hr" OPTION; do
-  case "$OPTION" in 
-    d)
-      DB_CONNECTION="$OPTARG" # only one required.
-      ;;
-    l)
-      PATCHES_LOCATION="$OPTARG"
-      ;;
-    r)
-      DROP="YES"
-      ;;
-    h)
-      usage_and_exit
-      ;;
-    ?)
-      fail 'Unrecognized command line option'
-    ;;
-  esac
-done
-
-# Checking required options.
-if test -z "${PATCHES_LOCATION:-}"; then
-  # This value is substituted by GNU make at installation time.
-  PATCHES_LOCATION=__BANK_STATIC_PATCHES_LOCATION__
-fi
-if test -z "${DB_CONNECTION:-}"; then
-  fail "Required option '-d' was missing."
-fi
-
-run_sql_file $(get_patch_path "versioning.sql")
-if test "${DROP:-}" = "YES"; then
-  maybe_applied=$(check_patch_applied "libeufin-bank-0001")
-  if test -n "$maybe_applied"; then 
-    run_sql_file $(get_patch_path "libeufin-bank-drop.sql")
-  else
-    echo "Nothing to drop"
-  fi
-fi
-iterate_over_patches
-run_sql_file $(get_patch_path "procedures.sql")
diff --git a/contrib/libeufin-bank.sample.conf b/contrib/libeufin-bank.conf
similarity index 67%
rename from contrib/libeufin-bank.sample.conf
rename to contrib/libeufin-bank.conf
index 00317b8e..04be761f 100644
--- a/contrib/libeufin-bank.sample.conf
+++ b/contrib/libeufin-bank.conf
@@ -9,5 +9,9 @@ MAX_AUTH_TOKEN_DURATION = 1d
 SERVE = tcp
 PORT = 8080
 
-[libeufin-bank-db-postgres]
+[libeufin-bankdb]
 CONFIG = postgresql:///libeufinbank
+
+[libeufin-bankdb-postgres]
+# Where are the SQL files to setup our tables?
+SQL_DIR = $DATADIR/sql/libeufin-bank/
diff --git a/util/src/main/kotlin/TalerConfig.kt 
b/util/src/main/kotlin/TalerConfig.kt
index 94a001a4..a0f5bf91 100644
--- a/util/src/main/kotlin/TalerConfig.kt
+++ b/util/src/main/kotlin/TalerConfig.kt
@@ -19,7 +19,6 @@
 
 import java.io.File
 import java.nio.file.Paths
-import java.util.*
 import kotlin.io.path.Path
 import kotlin.io.path.listDirectoryEntries
 
@@ -92,12 +91,13 @@ class TalerConfig {
     }
 
     private fun provideSection(name: String): Section {
-        val existingSec = this.sectionMap[name]
+        val canonSecName = name.uppercase()
+        val existingSec = this.sectionMap[canonSecName]
         if (existingSec != null) {
             return existingSec
         }
         val newSection = Section(entries = mutableMapOf())
-        this.sectionMap[name] = newSection
+        this.sectionMap[canonSecName] = newSection
         return newSection
     }
 
@@ -151,6 +151,33 @@ class TalerConfig {
         throw TalerConfigError("expected yes/no in configuration section 
$section option $option but got $v")
     }
 
+    private fun setSystemDefault(section: String, option: String, value: 
String) {
+        // FIXME: The value should be marked as a system default for 
diagnostics pretty printing
+        val sec = provideSection(section)
+        sec.entries[option.uppercase()] = Entry(value = value)
+    }
+
+    fun putValueString(section: String, option: String, value: String) {
+        val sec = provideSection(section)
+        sec.entries[option.uppercase()] = Entry(value = value)
+    }
+
+    fun lookupValuePath(section: String, option: String): String? {
+        val entry = lookupEntry(section, option)
+        if (entry == null) {
+            return null
+        }
+        return pathsub(entry.value)
+    }
+
+    fun requireValuePath(section: String, option: String): String {
+        val res = lookupValuePath(section, option)
+        if (res == null) {
+            throw TalerConfigError("expected path for section $section option 
$option")
+        }
+        return res
+    }
+
     /**
      * Create a string representation of the loaded configuration.
      */
@@ -187,9 +214,63 @@ class TalerConfig {
     fun loadDefaults() {
         val installDir = getTalerInstallPath()
         val baseConfigDir = Paths.get(installDir, 
"share/taler/config.d").toString()
+        setSystemDefault("PATHS", "PREFIX", "${installDir}/")
+        setSystemDefault("PATHS", "BINDIR", "${installDir}/bin/")
+        setSystemDefault("PATHS", "LIBEXECDIR", "${installDir}/taler/libexec/")
+        setSystemDefault("PATHS", "DOCDIR", "${installDir}/share/doc/taler/")
+        setSystemDefault("PATHS", "ICONDIR", "${installDir}/share/icons/")
+        setSystemDefault("PATHS", "LOCALEDIR", "${installDir}/share/locale/")
+        setSystemDefault("PATHS", "LIBDIR", "${installDir}/lib/taler/")
+        setSystemDefault("PATHS", "DATADIR", "${installDir}/share/taler/")
         loadDefaultsFromDir(baseConfigDir)
     }
 
+    fun variableLookup(x: String, recursionDepth: Int = 0): String? {
+        val pathRes = this.lookupValueString("PATHS", x)
+        if (pathRes != null) {
+            return pathsub(pathRes, recursionDepth + 1)
+        }
+        val envVal = System.getenv(x)
+        if (envVal != null) {
+            return envVal
+        }
+        return null
+    }
+
+    fun pathsub(x: String, recursionDepth: Int = 0): String {
+        if (recursionDepth > 128) {
+            throw TalerConfigError("recursion limit in path substitution 
exceeded")
+        }
+        val result = StringBuilder()
+        var l = 0
+        val s = x
+        while (l < s.length) {
+            if (s[l] != '$') {
+                // normal character
+                result.append(s[l])
+                l++;
+                continue
+            }
+            if (l + 1 < s.length && s[l + 1] == '{') {
+                // ${var}
+                throw NotImplementedError("bracketed variables not yet 
supported")
+            } else {
+                // $var
+                var varEnd = l + 1
+                while (varEnd < s.length && (s[varEnd].isLetterOrDigit() || 
s[varEnd] == '_')) {
+                    varEnd++
+                }
+                val varName = s.substring(l + 1, varEnd)
+                val res = variableLookup(varName)
+                if (res != null) {
+                    result.append(res)
+                }
+                l = varEnd
+            }
+        }
+        return result.toString()
+    }
+
     companion object {
         /**
          * Load configuration values from the file system.
@@ -241,10 +322,14 @@ class TalerConfig {
         }
 
         fun getTalerInstallPath(): String {
+            return getInstallPathFromBinary("taler-config")
+        }
+
+        fun getInstallPathFromBinary(name: String): String {
             val pathEnv = System.getenv("PATH")
             val paths = pathEnv.split(":")
             for (p in paths) {
-                val possiblePath = Paths.get(p, "taler-config").toString()
+                val possiblePath = Paths.get(p, name).toString()
                 if (File(possiblePath).exists()) {
                     return Paths.get(p, "..").toRealPath().toString()
                 }
diff --git a/util/src/test/kotlin/TalerConfigTest.kt 
b/util/src/test/kotlin/TalerConfigTest.kt
index 39977599..4587c535 100644
--- a/util/src/test/kotlin/TalerConfigTest.kt
+++ b/util/src/test/kotlin/TalerConfigTest.kt
@@ -18,6 +18,7 @@
  */
 
 import org.junit.Test
+import java.nio.file.FileSystems
 import kotlin.test.assertEquals
 
 class TalerConfigTest {
@@ -42,4 +43,19 @@ class TalerConfigTest {
 
         println(TalerConfig.getTalerInstallPath())
     }
+
+    @Test
+    fun substitution() {
+        val conf = TalerConfig()
+        conf.putValueString("PATHS", "DATADIR", "mydir")
+        conf.putValueString("foo", "bar", "baz")
+        conf.putValueString("foo", "bar2", "baz")
+
+        assertEquals("baz", conf.lookupValueString("foo", "bar"))
+        assertEquals("baz", conf.lookupValuePath("foo", "bar"))
+
+        conf.putValueString("foo", "dir1", "foo/\$DATADIR/bar")
+
+        assertEquals("foo/mydir/bar", conf.lookupValuePath("foo", "dir1"))
+    }
 }

-- 
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]