aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine A <>2024-01-07 16:37:57 +0000
committerAntoine A <>2024-01-07 16:37:57 +0000
commit0233fe3890721f8b726d9b5ab7c09f3cc832e899 (patch)
treefa3a5e26052d2f555fd2bd5a828a189a8ad47044
parent0684a2c0097bbaef65f569d8dae14fdb4a1aa965 (diff)
downloadlibeufin-0233fe3890721f8b726d9b5ab7c09f3cc832e899.tar.gz
libeufin-0233fe3890721f8b726d9b5ab7c09f3cc832e899.tar.bz2
libeufin-0233fe3890721f8b726d9b5ab7c09f3cc832e899.zip
Semi automated test for postfinance ebics setup
-rw-r--r--.gitignore2
-rw-r--r--integration/build.gradle28
-rw-r--r--integration/conf/postfinance.conf13
-rw-r--r--integration/src/main/kotlin/Main.kt94
-rw-r--r--integration/src/main/test/IntegrationTest.kt (renamed from integration/test/IntegrationTest.kt)0
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt1
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt102
-rw-r--r--util/src/main/kotlin/Cli.kt13
8 files changed, 176 insertions, 77 deletions
diff --git a/.gitignore b/.gitignore
index 3b7e2827..a680a8b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,8 @@
/sandbox/bin/
/util/bin/
nexus/libeufin-nexus-dev
+integration/test
+integration/config.json
sandbox/libeufin-sandbox-dev
configure
build/
diff --git a/integration/build.gradle b/integration/build.gradle
index ee9f7da6..4ac5bede 100644
--- a/integration/build.gradle
+++ b/integration/build.gradle
@@ -1,5 +1,6 @@
plugins {
id("kotlin")
+ id("application")
}
java {
@@ -10,18 +11,27 @@ java {
compileKotlin.kotlinOptions.jvmTarget = "17"
compileTestKotlin.kotlinOptions.jvmTarget = "17"
-sourceSets.test.java.srcDirs = ["test"]
+sourceSets.main.java.srcDirs = ["src/main/kotlin"]
dependencies {
- testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
- testImplementation(project(":util"))
- testImplementation(project(":bank"))
- testImplementation(project(":nexus"))
+ implementation(project(":util"))
+ implementation(project(":bank"))
+ implementation(project(":nexus"))
- testImplementation("com.github.ajalt.clikt:clikt:$clikt_version")
+ implementation("com.github.ajalt.clikt:clikt:$clikt_version")
- testImplementation("io.ktor:ktor-server-test-host:$ktor_version")
- testImplementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
- testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlin_version")
+ implementation("io.ktor:ktor-server-test-host:$ktor_version")
+ implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
+ implementation("org.jetbrains.kotlin:kotlin-test:$kotlin_version")
+}
+
+application {
+ mainClass = "tech.libeufin.integration.MainKt"
+ applicationName = "libeufin-integration-test"
+}
+
+run {
+ standardInput = System.in
} \ No newline at end of file
diff --git a/integration/conf/postfinance.conf b/integration/conf/postfinance.conf
new file mode 100644
index 00000000..e3eca878
--- /dev/null
+++ b/integration/conf/postfinance.conf
@@ -0,0 +1,13 @@
+[nexus-ebics]
+currency = CHF
+BANK_DIALECT = postfinance
+HOST_BASE_URL = https://isotest.postfinance.ch/ebicsweb/ebicsweb
+BANK_PUBLIC_KEYS_FILE = test/postfinance/bank-keys.json
+CLIENT_PRIVATE_KEYS_FILE = test/postfinance/client-keys.json
+IBAN = CH9589144624243597634
+HOST_ID = PFEBICS
+USER_ID = PFC00563
+PARTNER_ID = PFC00563
+
+[nexus-postgres]
+CONFIG = postgres:///libeufincheck
diff --git a/integration/src/main/kotlin/Main.kt b/integration/src/main/kotlin/Main.kt
new file mode 100644
index 00000000..da1aa633
--- /dev/null
+++ b/integration/src/main/kotlin/Main.kt
@@ -0,0 +1,94 @@
+/*
+ * This file is part of LibEuFin.
+ * Copyright (C) 2023 Taler Systems S.A.
+ *
+ * LibEuFin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3, or
+ * (at your option) any later version.
+ *
+ * LibEuFin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with LibEuFin; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>
+ */
+
+package tech.libeufin.integration
+
+import tech.libeufin.nexus.*
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.core.subcommands
+import com.github.ajalt.clikt.testing.test
+import io.ktor.client.*
+import io.ktor.client.engine.cio.*
+import kotlin.test.*
+import java.io.File
+import java.nio.file.*
+import kotlinx.coroutines.runBlocking
+import io.ktor.client.request.*
+
+fun CliktCommand.run(cmd: String) {
+ val result = test(cmd)
+ if (result.statusCode != 0)
+ throw Exception(result.output)
+ println(result.output)
+}
+
+val nexusCmd = LibeufinNexusCommand()
+val client = HttpClient(CIO)
+
+class PostFinanceCli : CliktCommand("Run tests on postfinance", name="postfinance") {
+ override fun run() {
+ runBlocking {
+ Files.createDirectories(Paths.get("test/postfinance"))
+ val clientKeysPath = Paths.get("test/postfinance/client-keys.json")
+ val bankKeysPath = Paths.get("test/postfinance/bank-keys.json")
+
+ var hasClientKeys = Files.exists(clientKeysPath)
+ var hasBankKeys = Files.exists(bankKeysPath)
+
+ if (hasClientKeys || hasBankKeys) {
+ println("Reset keys ? y/n>")
+ if (readlnOrNull() == "y") {
+ if (hasClientKeys) Files.deleteIfExists(clientKeysPath)
+ if (hasBankKeys) Files.deleteIfExists(bankKeysPath)
+ hasClientKeys = false
+ hasBankKeys = false
+ }
+ }
+
+ if (!hasClientKeys) {
+ // Test INI order
+ println("Got to https://testplattform.postfinance.ch/corporates/user/settings/ebics and click on 'Reset EBICS user'.\nPress Enter when done>")
+ readlnOrNull()
+ nexusCmd.test("ebics-setup -c conf/postfinance.conf").run {
+ assertEquals(1, statusCode, "ebics-setup should failed the first time")
+ }
+ }
+
+ if (!hasBankKeys) {
+ // Test HIA order
+ println("Got to https://testplattform.postfinance.ch/corporates/user/settings/ebics and click on 'Activate EBICS user'.\nPress Enter when done>")
+ readlnOrNull()
+ val out = nexusCmd.test("ebics-setup -c conf/postfinance.conf --auto-accept-keys").run {
+ assertEquals(0, statusCode, "ebics-setup should succeed the second time")
+ }
+ }
+ }
+ }
+}
+
+class Cli : CliktCommand() {
+ init {
+ subcommands(PostFinanceCli())
+ }
+ override fun run() = Unit
+}
+
+fun main(args: Array<String>) {
+ Cli().main(args)
+}
diff --git a/integration/test/IntegrationTest.kt b/integration/src/main/test/IntegrationTest.kt
index e5766a61..e5766a61 100644
--- a/integration/test/IntegrationTest.kt
+++ b/integration/src/main/test/IntegrationTest.kt
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt
index af7954cc..6a254a79 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DbInit.kt
@@ -4,7 +4,6 @@ import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.groups.*
import tech.libeufin.util.*
-import kotlin.system.exitProcess
/**
* This subcommand tries to load the SQL files that define
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
index a3f025d9..11819e87 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
@@ -26,7 +26,6 @@ import io.ktor.client.*
import kotlinx.coroutines.runBlocking
import tech.libeufin.util.ebics_h004.EbicsTypes
import java.io.File
-import kotlin.system.exitProcess
import TalerConfigError
import kotlinx.serialization.encodeToString
import tech.libeufin.nexus.ebics.*
@@ -132,8 +131,7 @@ fun maybeCreatePrivateKeysFile(filename: String): Boolean {
*/
private fun preparePrivateKeys(location: String): ClientPrivateKeysFile? {
if (!maybeCreatePrivateKeysFile(location)) {
- logger.error("Could not create client keys at $location")
- exitProcess(1)
+ throw Error("Could not create client keys at $location")
}
return loadPrivateKeysFromDisk(location) // loads what found at location.
}
@@ -297,8 +295,7 @@ fun extractEbicsConfig(configFile: String?): EbicsSetupConfig {
val cfg = try {
EbicsSetupConfig(config)
} catch (e: TalerConfigError) {
- logger.error(e.message)
- exitProcess(1)
+ throw Error(e.message)
}
return cfg
}
@@ -314,14 +311,12 @@ private fun makePdf(privs: ClientPrivateKeysFile, cfg: EbicsSetupConfig) {
val pdf = generateKeysPdf(privs, cfg)
val pdfFile = File("/tmp/libeufin-nexus-keys-${Instant.now().epochSecond}.pdf")
if (pdfFile.exists()) {
- logger.error("PDF file exists already at: ${pdfFile.path}, not overriding it")
- exitProcess(1)
+ throw Error("PDF file exists already at: ${pdfFile.path}, not overriding it")
}
try {
pdfFile.writeBytes(pdf)
} catch (e: Exception) {
- logger.error("Could not write PDF to ${pdfFile}, detail: ${e.message}")
- exitProcess(1)
+ throw Error("Could not write PDF to ${pdfFile}, detail: ${e.message}")
}
println("PDF file with keys hex encoding created at: $pdfFile")
}
@@ -346,45 +341,42 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
/**
* This function collects the main steps of setting up an EBICS access.
*/
- override fun run() {
- val cfg = doOrFail { extractEbicsConfig(common.config) }
+ override fun run() = cliCmd(logger) {
+ val cfg = extractEbicsConfig(common.config)
if (checkFullConfig) {
- doOrFail {
- cfg.config.requireString("nexus-submit", "frequency").apply {
- if (getFrequencyInSeconds(this) == null)
- throw Exception("frequency value of nexus-submit section is not valid: $this")
- }
- cfg.config.requireString("nexus-fetch", "frequency").apply {
- if (getFrequencyInSeconds(this) == null)
- throw Exception("frequency value of nexus-fetch section is not valid: $this")
- }
- cfg.config.requirePath("nexus-fetch", "statement_log_directory")
- cfg.config.requireNumber("nexus-httpd", "port")
- cfg.config.requirePath("nexus-httpd", "unixpath")
- cfg.config.requireString("nexus-httpd", "serve")
- cfg.config.requireString("nexus-httpd-wire-gateway-facade", "enabled")
- cfg.config.requireString("nexus-httpd-wire-gateway-facade", "auth_method")
- cfg.config.requireString("nexus-httpd-wire-gateway-facade", "auth_token")
- cfg.config.requireString("nexus-httpd-revenue-facade", "enabled")
- cfg.config.requireString("nexus-httpd-revenue-facade", "auth_method")
- cfg.config.requireString("nexus-httpd-revenue-facade", "auth_token")
+ cfg.config.requireString("nexus-submit", "frequency").apply {
+ if (getFrequencyInSeconds(this) == null)
+ throw Exception("frequency value of nexus-submit section is not valid: $this")
}
- return
+ cfg.config.requireString("nexus-fetch", "frequency").apply {
+ if (getFrequencyInSeconds(this) == null)
+ throw Exception("frequency value of nexus-fetch section is not valid: $this")
+ }
+ cfg.config.requirePath("nexus-fetch", "statement_log_directory")
+ cfg.config.requireNumber("nexus-httpd", "port")
+ cfg.config.requirePath("nexus-httpd", "unixpath")
+ cfg.config.requireString("nexus-httpd", "serve")
+ cfg.config.requireString("nexus-httpd-wire-gateway-facade", "enabled")
+ cfg.config.requireString("nexus-httpd-wire-gateway-facade", "auth_method")
+ cfg.config.requireString("nexus-httpd-wire-gateway-facade", "auth_token")
+ cfg.config.requireString("nexus-httpd-revenue-facade", "enabled")
+ cfg.config.requireString("nexus-httpd-revenue-facade", "auth_method")
+ cfg.config.requireString("nexus-httpd-revenue-facade", "auth_token")
+ return@cliCmd
}
// Config is sane. Go (maybe) making the private keys.
val privsMaybe = preparePrivateKeys(cfg.clientPrivateKeysFilename)
if (privsMaybe == null) {
- logger.error("Private keys preparation failed.")
- exitProcess(1)
+ throw Error("Private keys preparation failed.")
}
val httpClient = HttpClient()
// Privs exist. Upload their pubs
val keysNotSub = !privsMaybe.submitted_ini || !privsMaybe.submitted_hia
runBlocking {
if ((!privsMaybe.submitted_ini) || forceKeysResubmission)
- doKeysRequestAndUpdateState(cfg, privsMaybe, httpClient, KeysOrderType.INI).apply { if (!this) exitProcess(1) }
+ doKeysRequestAndUpdateState(cfg, privsMaybe, httpClient, KeysOrderType.INI).apply { if (!this) throw Error() }
if ((!privsMaybe.submitted_hia) || forceKeysResubmission)
- doKeysRequestAndUpdateState(cfg, privsMaybe, httpClient, KeysOrderType.HIA).apply { if (!this) exitProcess(1) }
+ doKeysRequestAndUpdateState(cfg, privsMaybe, httpClient, KeysOrderType.HIA).apply { if (!this) throw Error() }
}
// Reloading new state from disk if any upload (and therefore a disk write) actually took place
val haveSubmitted = forceKeysResubmission || keysNotSub
@@ -393,13 +385,11 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
loadPrivateKeysFromDisk(cfg.clientPrivateKeysFilename)
} else privsMaybe
if (privs == null) {
- logger.error("Could not reload private keys from disk after submission")
- exitProcess(1)
+ throw Error("Could not reload private keys from disk after submission")
}
// Really both must be submitted here.
if ((!privs.submitted_hia) || (!privs.submitted_ini)) {
- logger.error("Cannot continue with non-submitted client keys.")
- exitProcess(1)
+ throw Error("Cannot continue with non-submitted client keys.")
}
// Eject PDF if the keys were submitted for the first time, or the user asked.
if (keysNotSub || generateRegistrationPdf) makePdf(privs, cfg)
@@ -415,35 +405,29 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
)
}
if (!areKeysOnDisk) {
- logger.error("Could not download bank keys. Send client keys (and/or related PDF document with --generate-registration-pdf) to the bank.")
- exitProcess(1)
+ throw Error("Could not download bank keys. Send client keys (and/or related PDF document with --generate-registration-pdf) to the bank.")
}
logger.info("Bank keys stored at ${cfg.bankPublicKeysFilename}")
}
// bank keys made it to the disk, check if they're accepted.
val bankKeysMaybe = loadBankKeys(cfg.bankPublicKeysFilename)
if (bankKeysMaybe == null) {
- logger.error("Although previous checks, could not load the bank keys file from: ${cfg.bankPublicKeysFilename}")
- exitProcess(1)
- }
- val printOk = { println("setup ready") }
-
- if (bankKeysMaybe.accepted) {
- printOk()
- return
+ throw Error("Although previous checks, could not load the bank keys file from: ${cfg.bankPublicKeysFilename}")
}
- // Finishing the setup by accepting the bank keys.
- if (autoAcceptKeys) bankKeysMaybe.accepted = true
- else bankKeysMaybe.accepted = askUserToAcceptKeys(bankKeysMaybe)
if (!bankKeysMaybe.accepted) {
- logger.error("Cannot successfully finish the setup without accepting the bank keys.")
- exitProcess(1)
- }
- if (!syncJsonToDisk(bankKeysMaybe, cfg.bankPublicKeysFilename)) {
- logger.error("Could not set bank keys as accepted on disk.")
- exitProcess(1)
+ // Finishing the setup by accepting the bank keys.
+ if (autoAcceptKeys) bankKeysMaybe.accepted = true
+ else bankKeysMaybe.accepted = askUserToAcceptKeys(bankKeysMaybe)
+
+ if (!bankKeysMaybe.accepted) {
+ throw Error("Cannot successfully finish the setup without accepting the bank keys.")
+ }
+ if (!syncJsonToDisk(bankKeysMaybe, cfg.bankPublicKeysFilename)) {
+ throw Error("Could not set bank keys as accepted on disk.")
+ }
}
- printOk()
+
+ println("setup ready")
}
} \ No newline at end of file
diff --git a/util/src/main/kotlin/Cli.kt b/util/src/main/kotlin/Cli.kt
index 6d618473..6da7fd4f 100644
--- a/util/src/main/kotlin/Cli.kt
+++ b/util/src/main/kotlin/Cli.kt
@@ -22,8 +22,7 @@ package tech.libeufin.util
import ConfigSource
import TalerConfig
import TalerConfigError
-import com.github.ajalt.clikt.core.CliktCommand
-import com.github.ajalt.clikt.core.subcommands
+import com.github.ajalt.clikt.core.*
import com.github.ajalt.clikt.parameters.types.*
import com.github.ajalt.clikt.parameters.arguments.*
import com.github.ajalt.clikt.parameters.options.*
@@ -37,9 +36,9 @@ private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.ConfigC
fun cliCmd(logger: Logger, lambda: () -> Unit) {
try {
lambda()
- } catch (e: Exception) {
+ } catch (e: Throwable) {
logger.error(e.message)
- exitProcess(1)
+ throw ProgramResult(1)
}
}
@@ -83,15 +82,13 @@ private class CliConfigGet(private val configSource: ConfigSource) : CliktComman
if (isPath) {
val res = config.lookupPath(sectionName, optionName)
if (res == null) {
- logger.error("value not found in config")
- exitProcess(2)
+ throw Error("value not found in config")
}
println(res)
} else {
val res = config.lookupString(sectionName, optionName)
if (res == null) {
- logger.error("value not found in config")
- exitProcess(2)
+ throw Error("value not found in config")
}
println(res)
}