libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 3dbf7c87943e8ff7c1194d65ac60c1665d2d1a90
parent ce7049b28e8809ffb2ea9fe86b05a05bcfc166c5
Author: Florian Dold <florian.dold@gmail.com>
Date:   Mon,  8 Jun 2020 17:22:49 +0530

spawn test environment via util

Diffstat:
Dcontrib/start-testenv.py | 293-------------------------------------------------------------------------------
Aintegration-tests/start-testenv.py | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 4++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 1+
Mnexus/src/test/kotlin/DBTest.kt | 2+-
Msandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt | 2+-
6 files changed, 212 insertions(+), 297 deletions(-)

diff --git a/contrib/start-testenv.py b/contrib/start-testenv.py @@ -1,293 +0,0 @@ -#!/usr/bin/env python3 - -from requests import post, get -from subprocess import call, Popen, PIPE -from time import sleep -import os -import socket -import hashlib -import base64 - -# Steps implemented in this test. -# -# 0 Prepare sandbox. -# -> (a) Make a EBICS host, (b) make a EBICS subscriber -# for the test runner, and (c) assign a IBAN to such -# subscriber. -# -# 1 Prepare nexus. -# -> (a) Make a Nexus user, (b) make a EBICS subscriber -# associated to that user -# -# 2 Prepare the Ebics bank connection for the nexus user. -# -> (a) Upload keys from Nexus to the Bank (INI & HIA), -# (b) Download key from the Bank (HPB) to the Nexus, -# and (c) Fetch the bank account owned by that subscriber -# at the bank. - -# 3 Request history from the Nexus to the Bank (C53). -# 4 Verify that history is empty. -# 5 Issue a payment from Nexus -# -> (a) Prepare & (b) trigger CCT. -# 6 Request history after submitting the payment, -# from Nexus to Bank. -# 7 Verify that previous payment shows up. - -# Nexus user details -USERNAME = "person" -PASSWORD = "y" -USER_AUTHORIZATION_HEADER = "basic {}".format( - base64.b64encode(b"person:y").decode("utf-8") -) - -# Admin authentication -ADMIN_AUTHORIZATION_HEADER = "basic {}".format( - base64.b64encode(b"admin:x").decode("utf-8") -) - -# EBICS details -EBICS_URL = "http://localhost:5000/ebicsweb" -HOST_ID = "HOST01" -PARTNER_ID = "PARTNER1" -USER_ID = "USER1" -EBICS_VERSION = "H004" - -# Subscriber's bank account -SUBSCRIBER_IBAN = "GB33BUKB20201555555555" -SUBSCRIBER_BIC = "BUKBGB22" -SUBSCRIBER_NAME = "Oliver Smith" -BANK_ACCOUNT_LABEL = "savings" - -# Databases -NEXUS_DB="test-nexus.sqlite3" - -def fail(msg): - print(msg) - nexus.terminate() - sandbox.terminate() - exit(1) - - -def checkPorts(ports): - for i in ports: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - s.bind(("0.0.0.0", i)) - s.close() - except: - print("Port {} is not available".format(i)) - exit(77) - - -def assertResponse(response): - if response.status_code != 200: - print("Test failed on URL: {}".format(response.url)) - # stdout/stderr from both services is A LOT of text. - # Confusing to dump all that to console. - print("Check nexus.log and sandbox.log, probably under /tmp") - nexus.terminate() - sandbox.terminate() - exit(1) - # Allows for finer grained checks. - return response - - -# -1 Clean databases and start services. -os.chdir("..") -assert 0 == call(["rm", "-f", "sandbox/libeufin-sandbox.sqlite3"]) -assert 0 == call(["rm", "-f", "nexus/{}".format(NEXUS_DB)]) -DEVNULL = open(os.devnull, "w") - -assert 0 == call( - ["./gradlew", "nexus:run", "--console=plain", "--args=superuser admin --password x --db-name={}".format(NEXUS_DB)] -) - -# Start nexus -checkPorts([5001]) -nexus = Popen( - ["./gradlew", "nexus:run", "--console=plain", "--args=serve --db-name={}".format(NEXUS_DB)], - stdout=PIPE, - stderr=PIPE, -) -for i in range(10): - try: - get("http://localhost:5001/") - except: - if i == 9: - nexus.terminate() - stdout, stderr = nexus.communicate() - print("Nexus timed out") - print("{}\n{}".format(stdout.decode(), stderr.decode())) - exit(77) - sleep(2) - continue - break -# Start sandbox -checkPorts([5000]) -sandbox = Popen(["./gradlew", "sandbox:run"], stdout=PIPE, stderr=PIPE) -for i in range(10): - try: - get("http://localhost:5000/") - except: - if i == 9: - nexus.terminate() - sandbox.terminate() - stdout, stderr = nexus.communicate() - print("Sandbox timed out") - print("{}\n{}".format(stdout.decode(), stderr.decode())) - exit(77) - sleep(2) - continue - break - -# 0.a -assertResponse( - post( - "http://localhost:5000/admin/ebics/host", - json=dict(hostID=HOST_ID, ebicsVersion=EBICS_VERSION), - ) -) - -# 0.b -assertResponse( - post( - "http://localhost:5000/admin/ebics/subscribers", - json=dict(hostID=HOST_ID, partnerID=PARTNER_ID, userID=USER_ID), - ) -) - -# 0.c -assertResponse( - post( - "http://localhost:5000/admin/ebics/bank-accounts", - json=dict( - subscriber=dict(hostID=HOST_ID, partnerID=PARTNER_ID, userID=USER_ID), - iban=SUBSCRIBER_IBAN, - bic=SUBSCRIBER_BIC, - name=SUBSCRIBER_NAME, - label=BANK_ACCOUNT_LABEL, - ), - ) -) - -# 1.a, make a new nexus user. - -assertResponse( - post( - "http://localhost:5001/users", - headers=dict(Authorization=ADMIN_AUTHORIZATION_HEADER), - json=dict(username=USERNAME, password=PASSWORD), - ) -) - -print("creating bank connection") - -# 1.b, make a ebics bank connection for the new user. -assertResponse( - post( - "http://localhost:5001/bank-connections", - json=dict( - name="my-ebics", - source="new", - type="ebics", - data=dict( - ebicsURL=EBICS_URL, hostID=HOST_ID, partnerID=PARTNER_ID, userID=USER_ID - ), - ), - headers=dict(Authorization=USER_AUTHORIZATION_HEADER), - ) -) - -print("connecting") - -assertResponse( - post( - "http://localhost:5001/bank-connections/my-ebics/connect", - json=dict(), - headers=dict(Authorization=USER_AUTHORIZATION_HEADER), - ) -) - - -# 2.c, fetch bank account information -assertResponse( - post( - "http://localhost:5001/bank-connections/my-ebics/ebics/import-accounts", - json=dict(), - headers=dict(Authorization=USER_AUTHORIZATION_HEADER), - ) -) - -# 3, ask nexus to download history -assertResponse( - post( - f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/fetch-transactions", - json=dict(), - headers=dict(Authorization=USER_AUTHORIZATION_HEADER), - ) -) - -# 4, make sure history is empty -resp = assertResponse( - get( - f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/transactions", - headers=dict(Authorization=USER_AUTHORIZATION_HEADER), - ) -) -if len(resp.json().get("transactions")) != 0: - fail("unexpected number of transactions") - -# 5.a, prepare a payment -resp = assertResponse( - post( - "http://localhost:5001/bank-accounts/{}/prepared-payments".format( - BANK_ACCOUNT_LABEL - ), - json=dict( - iban="FR7630006000011234567890189", - bic="AGRIFRPP", - name="Jacques La Fayette", - subject="integration test", - amount="EUR:1", - ), - headers=dict(Authorization=USER_AUTHORIZATION_HEADER), - ) -) -PREPARED_PAYMENT_UUID = resp.json().get("uuid") -if PREPARED_PAYMENT_UUID == None: - fail("Payment UUID not received") - -# 5.b, submit prepared statement -assertResponse( - post( - f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/prepared-payments/{PREPARED_PAYMENT_UUID}/submit", - json=dict(), - headers=dict(Authorization=USER_AUTHORIZATION_HEADER), - ) -) - -# 6, request history after payment submission -assertResponse( - post( - f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/fetch-transactions", - json=dict(), - headers=dict(Authorization=USER_AUTHORIZATION_HEADER), - ) -) - -resp = assertResponse( - get( - f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/transactions", - headers=dict(Authorization=USER_AUTHORIZATION_HEADER), - ) -) - -if len(resp.json().get("transactions")) != 1: - fail("Unexpected number of transactions; should be 1") - - -try: - input("press enter to stop LibEuFin test environment ...") -finally: - nexus.terminate() - sandbox.terminate() diff --git a/integration-tests/start-testenv.py b/integration-tests/start-testenv.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 + +from requests import post, get +from subprocess import call, Popen, PIPE +from time import sleep +import os +import socket +import hashlib +import base64 +from util import startNexus, startSandbox + +# Nexus user details +USERNAME = "person" +PASSWORD = "y" +USER_AUTHORIZATION_HEADER = "basic {}".format( + base64.b64encode(b"person:y").decode("utf-8") +) + +# Admin authentication +ADMIN_AUTHORIZATION_HEADER = "basic {}".format( + base64.b64encode(b"admin:x").decode("utf-8") +) + +# EBICS details +EBICS_URL = "http://localhost:5000/ebicsweb" +HOST_ID = "HOST01" +PARTNER_ID = "PARTNER1" +USER_ID = "USER1" +EBICS_VERSION = "H004" + +# Subscriber's bank account +SUBSCRIBER_IBAN = "GB33BUKB20201555555555" +SUBSCRIBER_BIC = "BUKBGB22" +SUBSCRIBER_NAME = "Oliver Smith" +BANK_ACCOUNT_LABEL = "savings" + +# Databases +NEXUS_DB="test-nexus.sqlite3" + +def fail(msg): + print(msg) + exit(1) + + +def assertResponse(response): + if response.status_code != 200: + print("Test failed on URL: {}".format(response.url)) + # stdout/stderr from both services is A LOT of text. + # Confusing to dump all that to console. + print("Check nexus.log and sandbox.log, probably under /tmp") + exit(1) + # Allows for finer grained checks. + return response + + +os.chdir("..") + +startNexus("nexus-testenv.sqlite3") +startSandbox() + +# 0.a +assertResponse( + post( + "http://localhost:5000/admin/ebics/host", + json=dict(hostID=HOST_ID, ebicsVersion=EBICS_VERSION), + ) +) + +# 0.b +assertResponse( + post( + "http://localhost:5000/admin/ebics/subscribers", + json=dict(hostID=HOST_ID, partnerID=PARTNER_ID, userID=USER_ID), + ) +) + +# 0.c +assertResponse( + post( + "http://localhost:5000/admin/ebics/bank-accounts", + json=dict( + subscriber=dict(hostID=HOST_ID, partnerID=PARTNER_ID, userID=USER_ID), + iban=SUBSCRIBER_IBAN, + bic=SUBSCRIBER_BIC, + name=SUBSCRIBER_NAME, + label=BANK_ACCOUNT_LABEL, + ), + ) +) + +# 1.a, make a new nexus user. + +assertResponse( + post( + "http://localhost:5001/users", + headers=dict(Authorization=ADMIN_AUTHORIZATION_HEADER), + json=dict(username=USERNAME, password=PASSWORD), + ) +) + +print("creating bank connection") + +# 1.b, make a ebics bank connection for the new user. +assertResponse( + post( + "http://localhost:5001/bank-connections", + json=dict( + name="my-ebics", + source="new", + type="ebics", + data=dict( + ebicsURL=EBICS_URL, hostID=HOST_ID, partnerID=PARTNER_ID, userID=USER_ID + ), + ), + headers=dict(Authorization=USER_AUTHORIZATION_HEADER), + ) +) + +print("connecting") + +assertResponse( + post( + "http://localhost:5001/bank-connections/my-ebics/connect", + json=dict(), + headers=dict(Authorization=USER_AUTHORIZATION_HEADER), + ) +) + + +# 2.c, fetch bank account information +assertResponse( + post( + "http://localhost:5001/bank-connections/my-ebics/ebics/import-accounts", + json=dict(), + headers=dict(Authorization=USER_AUTHORIZATION_HEADER), + ) +) + +# 3, ask nexus to download history +assertResponse( + post( + f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/fetch-transactions", + json=dict(), + headers=dict(Authorization=USER_AUTHORIZATION_HEADER), + ) +) + +# 4, make sure history is empty +resp = assertResponse( + get( + f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/transactions", + headers=dict(Authorization=USER_AUTHORIZATION_HEADER), + ) +) +if len(resp.json().get("transactions")) != 0: + fail("unexpected number of transactions") + +# 5.a, prepare a payment +resp = assertResponse( + post( + "http://localhost:5001/bank-accounts/{}/prepared-payments".format( + BANK_ACCOUNT_LABEL + ), + json=dict( + iban="FR7630006000011234567890189", + bic="AGRIFRPP", + name="Jacques La Fayette", + subject="integration test", + amount="EUR:1", + ), + headers=dict(Authorization=USER_AUTHORIZATION_HEADER), + ) +) +PREPARED_PAYMENT_UUID = resp.json().get("uuid") +if PREPARED_PAYMENT_UUID == None: + fail("Payment UUID not received") + +# 5.b, submit prepared statement +assertResponse( + post( + f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/prepared-payments/{PREPARED_PAYMENT_UUID}/submit", + json=dict(), + headers=dict(Authorization=USER_AUTHORIZATION_HEADER), + ) +) + +# 6, request history after payment submission +assertResponse( + post( + f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/fetch-transactions", + json=dict(), + headers=dict(Authorization=USER_AUTHORIZATION_HEADER), + ) +) + +resp = assertResponse( + get( + f"http://localhost:5001/bank-accounts/{BANK_ACCOUNT_LABEL}/transactions", + headers=dict(Authorization=USER_AUTHORIZATION_HEADER), + ) +) + +if len(resp.json().get("transactions")) != 1: + fail("Unexpected number of transactions; should be 1") + + +input("press enter to stop LibEuFin test environment ...") diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -265,7 +265,6 @@ fun schedulePeriodicWork() { while (true) { logger.debug("Outer background job") try { - delay(Duration.ofSeconds(1)) downloadTalerFacadesTransactions() // ingestTalerTransactions() submitPreparedPaymentsViaEbics() @@ -275,6 +274,7 @@ fun schedulePeriodicWork() { e.printStackTrace(pw) logger.info("==== Background job exception ====\n${sw}======") } + delay(Duration.ofSeconds(1)) } } } @@ -430,7 +430,7 @@ fun serverMain(dbName: String) { return@intercept } - //schedulePeriodicWork() + schedulePeriodicWork() routing { /** * Shows information about the requesting user. diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt @@ -411,6 +411,7 @@ suspend fun submitPreparedPaymentsViaEbics() { } val httpClient = HttpClient() workQueue.forEach { + println("submitting prepared payment via EBICS"); doEbicsUploadTransaction( httpClient, it.subscriberDetails, diff --git a/nexus/src/test/kotlin/DBTest.kt b/nexus/src/test/kotlin/DBTest.kt @@ -52,7 +52,7 @@ class DBTest { type = "any" creator = user } - val talerConfig = TalerFacadeStateEntity.new { + TalerFacadeStateEntity.new { bankAccount = "b" bankConnection = "b" reserveTransferLevel = "any" diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt @@ -870,7 +870,7 @@ private fun handleEbicsUploadTransactionInitialization(requestContext: RequestCo this.orderID = orderID this.numSegments = numSegments.toInt() this.transactionKeyEnc = ExposedBlob(transactionKeyEnc) - }.flush() + } logger.debug("after SQL flush") val sigObj = XMLUtil.convertStringToJaxb<UserSignatureData>(plainSigData.toString(Charsets.UTF_8)) logger.debug("got UserSignatureData: ${plainSigData.toString(Charsets.UTF_8)}")