diff options
author | Antoine A <> | 2024-01-07 16:37:57 +0000 |
---|---|---|
committer | Antoine A <> | 2024-01-07 16:37:57 +0000 |
commit | 0233fe3890721f8b726d9b5ab7c09f3cc832e899 (patch) | |
tree | fa3a5e26052d2f555fd2bd5a828a189a8ad47044 | |
parent | 0684a2c0097bbaef65f569d8dae14fdb4a1aa965 (diff) | |
download | libeufin-0233fe3890721f8b726d9b5ab7c09f3cc832e899.tar.gz libeufin-0233fe3890721f8b726d9b5ab7c09f3cc832e899.tar.bz2 libeufin-0233fe3890721f8b726d9b5ab7c09f3cc832e899.zip |
Semi automated test for postfinance ebics setup
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | integration/build.gradle | 28 | ||||
-rw-r--r-- | integration/conf/postfinance.conf | 13 | ||||
-rw-r--r-- | integration/src/main/kotlin/Main.kt | 94 | ||||
-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.kt | 1 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt | 102 | ||||
-rw-r--r-- | util/src/main/kotlin/Cli.kt | 13 |
8 files changed, 176 insertions, 77 deletions
@@ -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) } |