From 69b51897d0fd1351cb82f5de29fdbf3a714410cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Reynoza?= Date: Tue, 13 Feb 2024 14:15:45 -0600 Subject: [PATCH] feat:(mojaloop/#3670): add sdk (#49) * add scheme adapter to compose * add env variables * k6 test get party inbound * fix: fix harness running tests without sim running (#46) * fix: increase fundsin check delay (#47) * add outbout server k6 tests * add outbout server k6 tests * fix k6 tests * add outboud server k6 transfer flow * finish outbound server k6 tests, debug inbound ws * finish tests * update tests * fix(monitoring): parametrize dashboard-quoting-service.json for prometheus (#48) * setup new headers for sdk * add small doc * commit suggestions * change sdk image version --------- Co-authored-by: Kevin Leyow Co-authored-by: Muzammil --- .env | 183 +++++++++++++++++- README.md | 22 +++ docker-compose-perf.yml | 102 +++++++++- docker/prometheus/prometheus.yml | 1 + envs/fspiop-sim.env | 11 ++ .../callback-handler-svc/handlers/backend.js | 164 ++++++++++++++++ .../callback-handler-svc/handlers/fspiop.js | 68 ++++--- .../k6-tests/config/inboundSDKDiscovery.json | 21 ++ .../k6-tests/config/inboundSDKQuotes.json | 18 ++ .../k6-tests/config/inboundSDKTransfer.json | 22 +++ .../k6-tests/config/outboundSDKDiscovery.json | 21 ++ .../k6-tests/config/outboundSDKQuotes.json | 18 ++ .../k6-tests/config/outboundSDKTransfer.json | 21 ++ packages/k6-tests/config/test.json | 20 +- packages/k6-tests/index.js | 7 + .../k6-tests/scenarios/inboundSDKDiscovery.js | 5 + .../k6-tests/scenarios/inboundSDKQuotes.js | 6 + .../k6-tests/scenarios/inboundSDKTransfers.js | 6 + .../scenarios/outboundSDKDiscovery.js | 5 + .../k6-tests/scenarios/outboundSDKQuotes.js | 6 + .../scenarios/outboundSDKTransfers.js | 6 + .../k6-tests/scripts/inboundSDKGetParties.js | 96 +++++++++ packages/k6-tests/scripts/inboundSDKQuotes.js | 131 +++++++++++++ .../k6-tests/scripts/inboundSDKTransfers.js | 130 +++++++++++++ .../k6-tests/scripts/outboundSDKGetParties.js | 47 +++++ .../k6-tests/scripts/outboundSDKQuotes.js | 84 ++++++++ .../k6-tests/scripts/outboundSDKTransfers.js | 91 +++++++++ perf.env | 37 +++- 28 files changed, 1291 insertions(+), 58 deletions(-) create mode 100644 envs/fspiop-sim.env create mode 100644 packages/callback-handler-svc/handlers/backend.js create mode 100644 packages/k6-tests/config/inboundSDKDiscovery.json create mode 100644 packages/k6-tests/config/inboundSDKQuotes.json create mode 100644 packages/k6-tests/config/inboundSDKTransfer.json create mode 100644 packages/k6-tests/config/outboundSDKDiscovery.json create mode 100644 packages/k6-tests/config/outboundSDKQuotes.json create mode 100644 packages/k6-tests/config/outboundSDKTransfer.json create mode 100644 packages/k6-tests/scenarios/inboundSDKDiscovery.js create mode 100644 packages/k6-tests/scenarios/inboundSDKQuotes.js create mode 100644 packages/k6-tests/scenarios/inboundSDKTransfers.js create mode 100644 packages/k6-tests/scenarios/outboundSDKDiscovery.js create mode 100644 packages/k6-tests/scenarios/outboundSDKQuotes.js create mode 100644 packages/k6-tests/scenarios/outboundSDKTransfers.js create mode 100644 packages/k6-tests/scripts/inboundSDKGetParties.js create mode 100644 packages/k6-tests/scripts/inboundSDKQuotes.js create mode 100644 packages/k6-tests/scripts/inboundSDKTransfers.js create mode 100644 packages/k6-tests/scripts/outboundSDKGetParties.js create mode 100644 packages/k6-tests/scripts/outboundSDKQuotes.js create mode 100644 packages/k6-tests/scripts/outboundSDKTransfers.js diff --git a/.env b/.env index 6ac00f19..67390ad3 100644 --- a/.env +++ b/.env @@ -17,7 +17,7 @@ CENTRAL_LEDGER_VERSION=v17.2.1 # CENTRAL_LEDGER_VERSION=v17.0.4-snapshot.0 ## Central Ledger version with batch processing capability CENTRAL_LEDGER_BATCH_VERSION=v17.3.0-snapshot.5 - +SDK_SCHEME_ADAPTER_VERSION=v23.3.0 ## Testing & Sims TEST_SIMULATOR_VERSION=v11.1.3 TEST_TTK_SVC_VERSION=v16.1.1 @@ -41,7 +41,7 @@ KAFKA_EXPORTER_VERSION=latest CADVISER_VERSION=latest ## Load Docker Image Versions -K6_VERSION=0.45.0 +K6_VERSION=0.47.0 ## Performance Testing Replicas QS_REPLICAS=1 @@ -51,3 +51,182 @@ CENTRAL_LEDGER_POSITION_REPLICAS=1 CENTRAL_LEDGER_POSITION_BATCH_REPLICAS=0 ML_API_ADAPTER_REPLICAS=1 ML_NOTIFICATION_REPLICAS=1 + +## ---------------------------------------------- + + + +# Port number that the inbound (Mojaloop API) HTTP server will listen on +INBOUND_LISTEN_PORT=4000 + +# Port number that the outbound (simplified DFSP outbound API) HTTP server will listen on +OUTBOUND_LISTEN_PORT=4001 + +# Enable mutual TLS authentication. Useful when not running in a secure +# environment, i.e. when you're running it locally against your own implementation. +INBOUND_MUTUAL_TLS_ENABLED=false +OUTBOUND_MUTUAL_TLS_ENABLED=false + +# Enable verification or incoming JWS signatures +# Note that signatures will be required on incoming messages +# and will be validated against a public key. +VALIDATE_INBOUND_JWS=false + +# applicable only if VALIDATE_INBOUND_JWS is "true" +# allows disabling of validation on incoming PUT /parties/{idType}/{idValue} requests +VALIDATE_INBOUND_PUT_PARTIES_JWS=false + +# Enable signing of outgoing requests +JWS_SIGN=false +VALIDATE_JWS_SIGN=false + +# applicable only if JWS_SIGN is "true" +# allows disabling of signing on outgoing PUT /parties/{idType}/{idValue} requests +JWS_SIGN_PUT_PARTIES=false + +# Path to JWS signing key (private key of THIS DFSP) +JWS_SIGNING_KEY_PATH=secrets/jwsSigningKey.key +JWS_VERIFICATION_KEYS_DIRECTORY=secrets/jwsVerificationKeys + +# Location of certs and key required for TLS +# IN_CA_CERT_PATH=./secrets/cacert.pem +# IN_SERVER_CERT_PATH=./secrets/servercert.pem +# IN_SERVER_KEY_PATH=./secrets/serverkey.pem + +# OUT_CA_CERT_PATH=./secrets/cacert.pem +# OUT_CLIENT_CERT_PATH=./secrets/servercert.pem +# OUT_CLIENT_KEY_PATH=./secrets/serverkey.pem + +# The number of space characters by which to indent pretty-printed logs. If set to zero, log events +# will each be printed on a single line. +LOG_INDENT=0 + +# REDIS CACHE CONNECTION +CACHE_URL=redis://redis:6379 + +# expiry period in seconds for quote and transfers issued by the SDK +EXPIRY_SECONDS=60 + +# if set to false the SDK will not automatically accept all returned quotes +# but will halt the transfer after a quote response is received. A further +# confirmation call will be required to complete the final transfer stage. +AUTO_ACCEPT_QUOTES=false + +# if set to false the SDK will not automatically accept a resolved party +# but will halt the transer after a party lookup response is received. A further +# cnofirmation call will be required to progress the transfer to quotes state. +AUTO_ACCEPT_PARTY=false + +# if set to false the SDK will not automatically accept a resolved party on a requestToPay operation +# but will halt the operation after a party lookup response is received. A further +# confirmation call will be required to progress the requestToPay to transactionRequest state. +AUTO_ACCEPT_R2P_PARTY=true + +# This parameter is only for the requestToPay transfers when the initiator is of type BUSINESS. +# if set to false the SDK will not automatically accept quote on a requestToPayTransfer. +# but will halt the operation after a quote response is received. A further +# confirmation call will be required to progress the requestToPayTransfer to authorization step. +AUTO_ACCEPT_R2P_BUSINESS_QUOTES=false + +# if set to false the SDK will not automatically accept OTP on a requestToPay operation +# but will halt the operation after receiving entered OTP from payee. A further +# confirmation call will be required to progress the requestToPayTransfer to transfer step. +AUTO_ACCEPT_R2P_DEVICE_OTP=false + +# this flag is for testing purpose only. sdk-scheme-adapter is not supposed to receive PUT /participants/{Type}/{ID}, +# but for testing we can enable it by setting this flag to true +AUTO_ACCEPT_PARTICIPANTS_PUT=false + +# when set to true, when sending money via the outbound API, the SDK will use the value +# of FSPIOP-Source header from the received quote response as the payeeFsp value in the +# transfer prepare request body instead of the value received in the payee party lookup. +# This behaviour should be enabled when the SDK user DFSP is in a forex enabled switch +# ecosystem and expects quotes and transfers to be rerouted by the switch to forex +# entities i.e. forex providing DFSPs. Please see the SDK documentation and switch +# operator documentation for more information on forex use cases. +USE_QUOTE_SOURCE_FSP_AS_TRANSFER_PAYEE_FSP=false + +# set to true to validate ILP, otherwise false to ignore ILP +CHECK_ILP=false + +# set to true to enable test features such as request cacheing and retrieval endpoints +ENABLE_TEST_FEATURES=false + +# set to true to mock WSO2 oauth2 token endpoint +ENABLE_OAUTH_TOKEN_ENDPOINT=false +OAUTH_TOKEN_ENDPOINT_CLIENT_KEY=test-client-key +OAUTH_TOKEN_ENDPOINT_CLIENT_SECRET=test-client-secret +OAUTH_TOKEN_ENDPOINT_LISTEN_PORT=6000 + +# WSO2 Bearer Token specific to golden-fsp instance and environment +WSO2_BEARER_TOKEN=7718fa9b-be13-3fe7-87f0-a12cf1628168 + +# OAuth2 data used to obtain WSO2 bearer token +OAUTH_TOKEN_ENDPOINT= +OAUTH_CLIENT_KEY= +OAUTH_CLIENT_SECRET= +OAUTH_REFRESH_SECONDS=3600 + +# Set to true to respect expirity timestamps +REJECT_EXPIRED_QUOTE_RESPONSES=false +REJECT_TRANSFERS_ON_EXPIRED_QUOTES=false +REJECT_EXPIRED_TRANSFER_FULFILS=false + +# Timeout for GET/POST/DELETE - PUT flow processing +REQUEST_PROCESSING_TIMEOUT_SECONDS=4 + +# Common Account Lookup System (ALS) +# ALS_ENDPOINT=ttkhubsim:4040 + +# # QUOTES_ENDPOINT +# QUOTES_ENDPOINT=ttkhubsim:4040 + +# # TRANSFERS_ENDPOINT +# TRANSFERS_ENDPOINT=ttkhubsim:4040 + +# To allow transfer without a previous quote request, set this value to true. +# The incoming transfer request should consists of an ILP packet and a matching condition in this case. +# The fulfilment will be generated from the provided ILP packet, and must hash to the provided condition. +ALLOW_TRANSFER_WITHOUT_QUOTE=false + +# To enable request for notification on fulfiled transfer +RESERVE_NOTIFICATION=true +# resources API versions should be string in format: "resourceOneName=1.0,resourceTwoName=1.1" +RESOURCE_VERSIONS="transfers=1.1,participants=1.1" + +# Management API websocket connection settings. +# The Management API uses this for exchanging connector management messages. +MGMT_API_WS_URL=mock-management-svc +MGMT_API_WS_PORT=4005 + +# Set to true to enable the use of PM4ML-related services e.g MCM, Management API service +# when running the scheme-adapter as a mojaloop connector component within Payment Manager for Mojaloop. +PM4ML_ENABLED=false + +BACKEND_EVENT_CONSUMER_BROKER_LIST=kafka:29092 +BACKEND_EVENT_PRODUCER_BROKER_LIST=kafka:29092 +FSPIOP_EVENT_CONSUMER_BROKER_LIST=kafka:29092 +FSPIOP_EVENT_PRODUCER_BROKER_LIST=kafka:29092 + +# Maximum payload limits +FSPIOP_API_SERVER_MAX_REQUEST_BYTES=209715200 +BACKEND_API_SERVER_MAX_REQUEST_BYTES=209715200 + +ENABLE_FSPIOP_EVENT_HANDLER=false +ENABLE_BACKEND_EVENT_HANDLER=false + +# Port number that the inbound (Mojaloop API) HTTP server will listen on +INBOUND_LISTEN_PORT=4000 + +# Port number that the outbound (simplified DFSP outbound API) HTTP server will listen on +OUTBOUND_LISTEN_PORT=4001 + +# ---- SDK Config ---- +# The option 'PEER_ENDPOINT' has no effect if the remaining options 'ALS_ENDPOINT', 'QUOTES_ENDPOINT', +# 'BULK_QUOTES_ENDPOINT', 'TRANSFERS_ENDPOINT', 'BULK_TRANSFERS_ENDPOINT', 'TRANSACTION_REQUESTS_ENDPOINT' are specified. +# ALS_ENDPOINT=callback-handler-svc-cl-sim:3001/backend +# QUOTES_ENDPOINT=callback-handler-svc-cl-sim:3001/backend +# BULK_QUOTES_ENDPOINT=callback-handler-svc-cl-sim:3001/backend +# TRANSFERS_ENDPOINT=callback-handler-svc-cl-sim:3001/backend +# BULK_TRANSFERS_ENDPOINT=callback-handler-svc-cl-sim:3001/backend +# TRANSACTION_REQUESTS_ENDPOINT=callback-handler-svc-cl-sim:3001/backend diff --git a/README.md b/README.md index 8ddfb227..2e78c8fa 100644 --- a/README.md +++ b/README.md @@ -243,11 +243,27 @@ docker compose --project-name ml-core -f docker-compose-perf.yml --profile all-s > NOTE: `-v` argument is optional, and it will delete any volume data created by the monitoring docker compose +### Running Services for SDK characterization + +```bash +docker compose --project-name ml-core -f docker-compose-perf.yml --profile sdk-scheme-adapter up -d +``` + +Stop Services + +```bash +docker compose --project-name ml-core -f docker-compose-perf.yml --profile sdk-scheme-adapter down -v +``` + +#### Setting up the Inbound/Outbound Server variables +- Go to `perf.env` and comment out the inboundSDK variables. You'll need to do the same and restart the `docker-compose` in order to change test suite. + ### Configuration for Transfers with batch support - Set CENTRAL_LEDGER_POSITION_BATCH_REPLICAS to desired count in `.env` file - Enable line `CLEDG_KAFKA__EVENT_TYPE_ACTION_TOPIC_MAP__POSITION__PREPARE=topic-transfer-position-batch` in `perf.env` file - Set `CENTRAL_LEDGER_VERSION` to `v17.2.0` or higher + ### Monitoring Start Monitoring Services stack which uses: @@ -339,6 +355,12 @@ env K6_SCRIPT_CONFIG_FILE_NAME=fspiopDiscovery.json docker compose --project-nam env K6_SCRIPT_CONFIG_FILE_NAME=fspiopQuotes.json docker compose --project-name load -f docker-compose-load.yml up ( or ) env K6_SCRIPT_CONFIG_FILE_NAME=fspiopE2E.json docker compose --project-name load -f docker-compose-load.yml up +( or ) +env K6_SCRIPT_CONFIG_FILE_NAME=inboundSDKDiscovery.json docker compose --project-name load -f docker-compose-load.yml up +( or ) +env K6_SCRIPT_CONFIG_FILE_NAME=inboundSDKQuotes.json docker compose --project-name load -f docker-compose-load.yml up +( or ) +env K6_SCRIPT_CONFIG_FILE_NAME=inboundSDKTransfer.json docker compose --project-name load -f docker-compose-load.yml up ``` Cleanup tests diff --git a/docker-compose-perf.yml b/docker-compose-perf.yml index f7596f5e..ebd0875a 100644 --- a/docker-compose-perf.yml +++ b/docker-compose-perf.yml @@ -565,8 +565,8 @@ services: - mojaloop-net image: docker.io/bitnami/kafka:${DEP_KAFKA_VERSION} container_name: kafka - # ports: - # - "9092:9092" + ports: + - "9092:9092" environment: # BITNAMI_DEBUG: "yes" ALLOW_PLAINTEXT_LISTENER: "yes" @@ -593,6 +593,9 @@ services: - transfer - discovery - agreement + - sdk-scheme-adapter + - inbound-sdk-scheme-adapter + - outbound-sdk-scheme-adapter - all-services - transfers-test - quotes-test @@ -904,6 +907,40 @@ services: - all-services - als-test + sdk-scheme-adapter-api-svc: + image: mojaloop/sdk-scheme-adapter:${SDK_SCHEME_ADAPTER_VERSION} + env_file: + - .env + - perf.env + container_name: sdk-scheme-adapter + command: yarn nx run modules-api-svc:start + ports: + - "4000:4000" + - "4001:4001" + networks: + - mojaloop-net + depends_on: + redis: + condition: service_healthy + volumes: + - ./docker/wait4:/opt/app/wait4 + - ./docker/config-modifier:/opt/app/config-modifier + healthcheck: + test: [ + "CMD" , + "apk", "add", "--no-cache", "curl", # Install curl + "&&", + "curl", "-f", "http://localhost:4001" # Check if api is up + ] + timeout: 20s + retries: 10 + start_period: 40s + interval: 30s + user: root + profiles: + - sdk-scheme-adapter + - all-services + callback-handler-svc-cl-sim: image: mojaloop/callback-handler-svc:${TEST_CALLBACK_HAND_SVC_VERSION} deploy: @@ -916,8 +953,8 @@ services: context: ./packages/callback-handler-svc cache_from: - mojaloop/callback-handler-svc:${TEST_CALLBACK_HAND_SVC_VERSION} - # ports: - # - "3001:3001" + ports: + - "3001:3001" networks: - mojaloop-net volumes: @@ -938,6 +975,20 @@ services: - als-test - transfers-test - quotes-test + + redis: + networks: + - mojaloop-net + image: "redis:6.2.4-alpine" + container_name: redis + ports: + - "6379:6379" + healthcheck: + test: ["CMD" ,"sh", "-c", "redis-cli","ping"] + timeout: 20s + retries: 10 + start_period: 40s + interval: 30s sim-perffsp1: image: mojaloop/callback-handler-svc:${TEST_CALLBACK_HAND_SVC_VERSION} @@ -968,7 +1019,6 @@ services: user: root profiles: - simulators - - callback-handler - all-services - als-test - 2dfsp @@ -977,9 +1027,9 @@ services: sim-perffsp2: image: mojaloop/callback-handler-svc:${TEST_CALLBACK_HAND_SVC_VERSION} - deploy: - mode: replicated - replicas: 1 + # deploy: + # mode: replicated + # replicas: 1 env_file: - .env - ./envs/perffsp2.env @@ -1004,7 +1054,6 @@ services: user: root profiles: - simulators - - callback-handler - all-services - als-test - 2dfsp @@ -1192,3 +1241,38 @@ services: user: root profiles: - 8dfsp + + sim-fspiop: + image: mojaloop/callback-handler-svc:${TEST_CALLBACK_HAND_SVC_VERSION} + # deploy: + # mode: replicated + # replicas: 5 + env_file: + - .env + - ./envs/fspiop-sim.env + build: + context: ./packages/callback-handler-svc + cache_from: + - mojaloop/callback-handler-svc:${TEST_CALLBACK_HAND_SVC_VERSION} + ports: + - "3001:3001" + - "3002:3002" + networks: + - mojaloop-net + volumes: + - ./packages/callback-handler-svc/config:/opt/app/config + - ./packages/callback-handler-svc/handlers:/opt/app/handlers + environment: {} + # - LOG_LEVEL=error + healthcheck: + test: wget -q http://localhost:3001/health -O /dev/null || exit 1 + timeout: 20s + retries: 30 + interval: 15s + user: root + profiles: + - simulators + - callback-handler + - sdk-scheme-adapter + - all-services + - als-test diff --git a/docker/prometheus/prometheus.yml b/docker/prometheus/prometheus.yml index b41944e7..a01928db 100644 --- a/docker/prometheus/prometheus.yml +++ b/docker/prometheus/prometheus.yml @@ -137,6 +137,7 @@ scrape_configs: 'sim-perffsp6:3001', 'sim-perffsp7:3001', 'sim-perffsp8:3001', + 'sim-fspiop:3001', 'callback-handler-svc-oracle-sim:3001', 'ml-core-callback-handler-svc-cl-sim-1:3001', 'ml-core-callback-handler-svc-cl-sim-2:3001', diff --git a/envs/fspiop-sim.env b/envs/fspiop-sim.env new file mode 100644 index 00000000..e284f02a --- /dev/null +++ b/envs/fspiop-sim.env @@ -0,0 +1,11 @@ +CBH_FSPIOP_FSP_ID=fspiopsim +CBH_FSPIOP_CALLBACK_HTTP_KEEPALIVE=true + +### FSPIOP Handlers +CBH_FSPIOP_ALS_ENDPOINT_URL=http://sdk-scheme-adapter:4000 +CBH_FSPIOP_QUOTES_ENDPOINT_URL=http://sdk-scheme-adapter:4000 +CBH_FSPIOP_TRANSFERS_ENDPOINT_URL=http://sdk-scheme-adapter:4000 + +CBH_FSPIOP_QUOTES_ILPPACKET='AYIDGQAAAAAAACcQIWcuZ3JlZW5iYW5rZnNwLm1zaXNkbi4yNzcxMzgwMzkxMoIC62V5SjBjbUZ1YzJGamRHbHZia2xrSWpvaU1ERXhaR1EyTldZdE5UQXpNeTAwTVdNMkxUazFaR1l0T1RFeFl6WTRPVFExWWpobUlpd2ljWFZ2ZEdWSlpDSTZJbVF3TXpJMU1EVTJMVE0xTldFdE5EUmxNUzFpT1RnMExXWXdZVFExTmpFMFkyRXpPQ0lzSW5CaGVXVmxJanA3SW5CaGNuUjVTV1JKYm1adklqcDdJbkJoY25SNVNXUlVlWEJsSWpvaVRWTkpVMFJPSWl3aWNHRnlkSGxKWkdWdWRHbG1hV1Z5SWpvaU1qYzNNVE00TURNNU1USWlMQ0ptYzNCSlpDSTZJbWR5WldWdVltRnVhMlp6Y0NKOWZTd2ljR0Y1WlhJaU9uc2ljR0Z5ZEhsSlpFbHVabThpT25zaWNHRnlkSGxKWkZSNWNHVWlPaUpOVTBsVFJFNGlMQ0p3WVhKMGVVbGtaVzUwYVdacFpYSWlPaUkwTkRFeU16UTFOamM0T1NJc0ltWnpjRWxrSWpvaWNHbHVhMkpoYm10bWMzQWlmU3dpY0dWeWMyOXVZV3hKYm1adklqcDdJbU52YlhCc1pYaE9ZVzFsSWpwN0ltWnBjbk4wVG1GdFpTSTZJa1pwY25OMGJtRnRaUzFVWlhOMElpd2liR0Z6ZEU1aGJXVWlPaUpNWVhOMGJtRnRaUzFVWlhOMEluMHNJbVJoZEdWUFprSnBjblJvSWpvaU1UazROQzB3TVMwd01TSjlmU3dpWVcxdmRXNTBJanA3SW1OMWNuSmxibU41SWpvaVZWTkVJaXdpWVcxdmRXNTBJam9pTVRBd0luMHNJblJ5WVc1ellXTjBhVzl1Vkhsd1pTSTZleUp6WTJWdVlYSnBieUk2SWxSU1FVNVRSa1ZTSWl3aWFXNXBkR2xoZEc5eUlqb2lVRUZaUlZJaUxDSnBibWwwYVdGMGIzSlVlWEJsSWpvaVEwOU9VMVZOUlZJaWZYMAA' +CBH_FSPIOP_QUOTES_CONDITION='5m0gq_5dLQlTSSRKQmLpj0MZ1MtWLWgSu1oLGVTJyYs' +CBH_QUOTE_EXPIRATION_WINDOW=60000 diff --git a/packages/callback-handler-svc/handlers/backend.js b/packages/callback-handler-svc/handlers/backend.js new file mode 100644 index 00000000..6f9a4b86 --- /dev/null +++ b/packages/callback-handler-svc/handlers/backend.js @@ -0,0 +1,164 @@ +const express = require('express') +const env = require('env-var') +const { TraceUtils } = require('@callback-handler-svc/utils') + +const TRACESTATE_KEY_END2END_START_TS = 'tx_end2end_start_ts' +const TRACESTATE_KEY_CALLBACK_START_TS = 'tx_callback_start_ts' + + +const init = (config, logger, options = undefined) => { + const router = express.Router() + + const handleCallback = (resource, req, res) => { + const histTimerEnd = options.metrics.getHistogram( + 'ing_callbackHandler', + 'Ingress - Operation handler', + ['success', 'operation'] + ).startTimer() + const currentTime = Date.now() + const path = req.path + const httpMethod = req.method.toLowerCase() + const isErrorOperation = path.endsWith('error') + const operation = `fspiop_${httpMethod}_${resource}` + const operationE2e = `${operation}_end2end` + const operationRequest = `${operation}_request` + const operationResponse = `${operation}_response` + const tracestate = TraceUtils.getTraceStateMap(req.headers) + + if (tracestate === undefined || tracestate[TRACESTATE_KEY_END2END_START_TS] === undefined || tracestate[TRACESTATE_KEY_CALLBACK_START_TS] === undefined) { + return res.status(400).send(`${TRACESTATE_KEY_END2END_START_TS} or ${TRACESTATE_KEY_CALLBACK_START_TS} key/values not found in tracestate`) + } + + const e2eDelta = currentTime - tracestate[TRACESTATE_KEY_END2END_START_TS] + const requestDelta = tracestate[TRACESTATE_KEY_CALLBACK_START_TS] - tracestate[TRACESTATE_KEY_END2END_START_TS] + const responseDelta = currentTime - tracestate[TRACESTATE_KEY_CALLBACK_START_TS] + + const performanceHistogram = options.metrics.getHistogram( + 'tx_cb_perf', + 'Metrics for callbacks', + ['success', 'operation'] + ) + + performanceHistogram.observe({ + success: (!isErrorOperation).toString(), + operation: operationE2e + }, e2eDelta / 1000) + performanceHistogram.observe({ + success: (!isErrorOperation).toString(), + operation: operationRequest + }, requestDelta / 1000) + performanceHistogram.observe({ + success: (!isErrorOperation).toString(), + operation: operationResponse + }, responseDelta / 1000) + + logger.isDebugEnabled && logger.debug( + { + traceparent: req.headers.traceparent, + tracestate, + operation, + path, + isErrorOperation, + serverHandlingTime: currentTime, + [operationE2e]: e2eDelta, + [operationRequest]: requestDelta, + [operationResponse]: responseDelta + } + ) + const traceId = TraceUtils.getTraceId(req.headers) + const channel = '/' + traceId + '/' + req.method + req.path + console.log('Handled PUT Callback request', channel) + options.wsServer.notify(channel, isErrorOperation ? 'ERROR_CALLBACK_RECEIVED' : 'SUCCESS_CALLBACK_RECEIVED') + histTimerEnd({ success: true, operation }) + return res.status(202).end() + } + + + // Handle Oracle GET Participants request + router.get('/parties/:type/:id', (req, res) => { + const histTimerEnd = options.metrics.getHistogram( + 'ing_callbackHandler', + 'Ingress - Operation handler', + ['success', 'operation'] + ).startTimer() + + res.status(202).json({ + "fsp": "string" + }) + console.log('Handled GET request') + histTimerEnd({ success: true, operation: 'oracle_get_parties'}) + }) + + // Handle Quote Request + router.post('/quoterequests', (req, res) => { + const histTimerEnd = options.metrics.getHistogram( + 'ing_callbackHandler', + 'Ingress - Operation handler', + ['success', 'operation'] + ).startTimer() + + const quotesRequest = req.body + + const quotesResponse = { + payeeFspCommissionAmount: quotesRequest.feesCurrency, + payeeFspCommissionAmountCurrency: quotesRequest.feesCurrency, + payeeFspFeeAmount: quotesRequest.feesAmount, + payeeFspFeeAmountCurrency: quotesRequest.feesCurrency, + // Fee currency and currency should be the same in order to have the right value + payeeReceiveAmount: (Number(quotesRequest.amount) - Number(quotesRequest.feesAmount)), + payeeReceiveAmountCurrency: quotesRequest.currency, + quoteId: quotesRequest.quoteId, + transactionId: quotesRequest.transactionId, + transferAmount: quotesRequest.amount, + transferAmountCurrency: quotesRequest.currency, + expiration: new Date(new Date().getTime() + 10000) + } + + res.status(202).json(quotesResponse) + + histTimerEnd({ success: true, operation: 'quoting_service_post_quote' }) + }) + + // Handle Transfer Request + router.post('/transfers', (req, res) => { + const histTimerEnd = options.metrics.getHistogram( + 'ing_callbackHandler', + 'Ingress - Operation handler', + ['success', 'operation'] + ).startTimer() + + const quotesResponse = { + completedTimestamp: new Date(new Date().getTime() + 10000), + fulfilment: 'string', + homeTransactionId: req.body.homeR2PTransactionId, + transferState: 'RECEIVED' + } + + res.status(202).json(quotesResponse) + + histTimerEnd({ success: true, operation: 'transfers_post_transfer' }) + }) + + router.put('/parties/:type/:id', (req, res) => { + return handleCallback('parties', req, res) + }) + + router.put('/quotes/:id', (req, res) => { + return handleCallback('quotes', req, res) + }) + + router.put('/transfers/:id', (req, res) => { + return handleCallback('transfers', req, res) + }) + + return { + name: 'backend', + basepath: '/backend', + router + } +} + +// require-glob has no ES support +module.exports = { + init +} diff --git a/packages/callback-handler-svc/handlers/fspiop.js b/packages/callback-handler-svc/handlers/fspiop.js index 713f9fda..0a5db49a 100644 --- a/packages/callback-handler-svc/handlers/fspiop.js +++ b/packages/callback-handler-svc/handlers/fspiop.js @@ -50,37 +50,43 @@ const init = (config, logger, options = undefined) => { 'Egress - Operation handler', ['success', 'operation'] ).startTimer() - await instance.put(`${FSPIOP_ALS_ENDPOINT_URL}/parties/${type}/${id}`, { - "party": { - "partyIdInfo": { - "partyIdType": "MSISDN", - "partyIdentifier": id, - "fspId": FSP_ID, - "partySubIdOrType": "HEALTH_CARD" - }, - "personalInfo": { - "dateOfBirth": "1971-12-25", - "complexName": { - "lastName": "Trudeau", - "middleName": "Pierre", - "firstName": "Justin" - } - }, - "name": "Justin Pierre" - } - }, - { - headers: { - 'Content-Type': 'application/vnd.interoperability.parties+json;version=1.1', - 'Accept': 'application/vnd.interoperability.parties+json;version=1.1', - Date: new Date(), - 'FSPIOP-Source': FSP_ID, - 'FSPIOP-Destination': fspiopSourceHeader, - 'traceparent': traceparentHeader, - 'tracestate': tracestateHeader + `,${TRACESTATE_KEY_CALLBACK_START_TS}=${Date.now()}` + try { + await instance.put(`${FSPIOP_ALS_ENDPOINT_URL}/parties/${type}/${id}`, { + "party": { + "partyIdInfo": { + "type": "CONSUMER", + "partyIdType": "MSISDN", + "partyIdentifier": id, + "fspId": FSP_ID + }, + "personalInfo": { + "dateOfBirth": "1971-12-25", + "complexName": { + "lastName": "Trudeau", + "middleName": "Pierre", + "firstName": "Justin" + } + }, + "name": "Justin Pierre" + } }, - httpAgent, - }) + { + headers: { + 'Content-Type': 'application/vnd.interoperability.parties+json;version=1.1', + 'Accept': 'application/vnd.interoperability.parties+json;version=1.1', + Date: new Date(), + 'FSPIOP-Source': FSP_ID, + 'FSPIOP-Destination': fspiopSourceHeader, + 'traceparent': traceparentHeader, + 'tracestate': tracestateHeader + `,${TRACESTATE_KEY_CALLBACK_START_TS}=${Date.now()}` + }, + httpAgent, + }) + } catch (e) { + console.log('failed here: ', `${FSPIOP_ALS_ENDPOINT_URL}/parties/${type}/${id}`) + logger.error(e) + + } egressHistTimerEnd({ success: true, operation: 'fspiop_put_parties'}) histTimerEnd1({ success: true, operation: 'fspiop_get_parties_with_callback'}) })(); @@ -199,7 +205,7 @@ const init = (config, logger, options = undefined) => { const operationE2e = `${operation}_end2end` const operationRequest = `${operation}_request` const operationResponse = `${operation}_response` - const tracestate = TraceUtils.getTraceStateMap(req.headers) + const tracestate = TraceUtils.getTraceStateMap(req.headers) if (tracestate === undefined || tracestate[TRACESTATE_KEY_END2END_START_TS] === undefined || tracestate[TRACESTATE_KEY_CALLBACK_START_TS] === undefined) { return res.status(400).send(`${TRACESTATE_KEY_END2END_START_TS} or ${TRACESTATE_KEY_CALLBACK_START_TS} key/values not found in tracestate`) diff --git a/packages/k6-tests/config/inboundSDKDiscovery.json b/packages/k6-tests/config/inboundSDKDiscovery.json new file mode 100644 index 00000000..d77b5c1c --- /dev/null +++ b/packages/k6-tests/config/inboundSDKDiscovery.json @@ -0,0 +1,21 @@ +{ + "scenarios": { + "inboundSDKDiscovery": { + "executor": "ramping-vus", + "exec": "inboundSDKDiscoveryScenarios", + "env": { + "UNIDIRECTIONAL": "false" + }, + "startVUs": 1, + "stages": [ + { "duration": "30s", "target": 5 } + ] + } + }, + "thresholds": { + "iteration_duration": [ "p(95)<1000" ], + "http_req_failed": [ "rate<0.01" ], + "http_req_duration": [ "p(95)<1000" ] + } + } + \ No newline at end of file diff --git a/packages/k6-tests/config/inboundSDKQuotes.json b/packages/k6-tests/config/inboundSDKQuotes.json new file mode 100644 index 00000000..f63e8aa9 --- /dev/null +++ b/packages/k6-tests/config/inboundSDKQuotes.json @@ -0,0 +1,18 @@ +{ + "scenarios": { + "inboundSDKQuotes": { + "executor": "ramping-vus", + "exec": "inboundSDKQuotesScenarios", + "startVUs": 1, + "stages": [ + { "duration": "30s", "target": 15 }, + { "duration": "1m", "target": 15 } + ] + } + }, + "thresholds": { + "iteration_duration": [ "p(95)<1000" ], + "http_req_failed": [ "rate<0.01" ], + "http_req_duration": [ "p(95)<1000" ] + } +} diff --git a/packages/k6-tests/config/inboundSDKTransfer.json b/packages/k6-tests/config/inboundSDKTransfer.json new file mode 100644 index 00000000..1a48ec0f --- /dev/null +++ b/packages/k6-tests/config/inboundSDKTransfer.json @@ -0,0 +1,22 @@ +{ + "scenarios": { + "inboundSDKTransfers": { + "executor": "ramping-vus", + "exec": "inboundSDKTransfersScenarios", + "env": { + "UNIDIRECTIONAL": "true" + }, + "startVUs": 1, + "stages": [ + { "duration": "30s", "target": 10 }, + { "duration": "1m", "target": 10 } + ] + } + }, + "thresholds": { + "iteration_duration": [ "p(95)<1000" ], + "http_req_failed": [ "rate<0.01" ], + "http_req_duration": [ "p(95)<1000" ] + } + } + \ No newline at end of file diff --git a/packages/k6-tests/config/outboundSDKDiscovery.json b/packages/k6-tests/config/outboundSDKDiscovery.json new file mode 100644 index 00000000..8c706837 --- /dev/null +++ b/packages/k6-tests/config/outboundSDKDiscovery.json @@ -0,0 +1,21 @@ +{ + "scenarios": { + "outboundSDKDiscovery": { + "executor": "ramping-vus", + "exec": "outboundSDKDiscoveryScenarios", + "env": { + "UNIDIRECTIONAL": "true" + }, + "startVUs": 1, + "stages": [ + { "duration": "2m", "target": 5 } + ] + } + }, + "thresholds": { + "iteration_duration": [ "p(95)<1000" ], + "http_req_failed": [ "rate<0.01" ], + "http_req_duration": [ "p(95)<1000" ] + } + } + \ No newline at end of file diff --git a/packages/k6-tests/config/outboundSDKQuotes.json b/packages/k6-tests/config/outboundSDKQuotes.json new file mode 100644 index 00000000..ec13d0ed --- /dev/null +++ b/packages/k6-tests/config/outboundSDKQuotes.json @@ -0,0 +1,18 @@ +{ + "scenarios": { + "outboundSDKQuotes": { + "executor": "ramping-vus", + "exec": "outboundSDKQuotesScenarios", + "startVUs": 1, + "stages": [ + { "duration": "30s", "target": 15 }, + { "duration": "1m", "target": 15 } + ] + } + }, + "thresholds": { + "iteration_duration": [ "p(95)<1000" ], + "http_req_failed": [ "rate<0.01" ], + "http_req_duration": [ "p(95)<1000" ] + } +} diff --git a/packages/k6-tests/config/outboundSDKTransfer.json b/packages/k6-tests/config/outboundSDKTransfer.json new file mode 100644 index 00000000..e9bc55aa --- /dev/null +++ b/packages/k6-tests/config/outboundSDKTransfer.json @@ -0,0 +1,21 @@ +{ + "scenarios": { + "outboundSDKTransfers": { + "executor": "ramping-vus", + "exec": "outboundSDKTransfersScenarios", + "env": { + "UNIDIRECTIONAL": "true" + }, + "startVUs": 1, + "stages": [ + { "duration": "2m", "target": 5 } + ] + } + }, + "thresholds": { + "iteration_duration": [ "p(95)<1000" ], + "http_req_failed": [ "rate<0.01" ], + "http_req_duration": [ "p(95)<1000" ] + } + } + \ No newline at end of file diff --git a/packages/k6-tests/config/test.json b/packages/k6-tests/config/test.json index 43be4c64..7884a667 100644 --- a/packages/k6-tests/config/test.json +++ b/packages/k6-tests/config/test.json @@ -1,17 +1,13 @@ { "scenarios": { "test": { - "executor": "ramping-vus", - "exec": "testScenarios", - "startVUs": 1, - "stages": [ - { "duration": "10s", "target": 6 } - ] + "executor": "shared-iterations", + "exec": "inboundSDKTransfersScenarios", + "env": { + "UNIDIRECTIONAL": "false" + }, + "vus": 1, + "iterations": 1 + } } - }, - "thresholds": { - "iteration_duration": [ "p(95)<1000" ], - "http_req_failed": [ "rate<0.01" ], - "http_req_duration": [ "p(95)<1000" ] - } } diff --git a/packages/k6-tests/index.js b/packages/k6-tests/index.js index 8ac741c0..3ec8cc5a 100644 --- a/packages/k6-tests/index.js +++ b/packages/k6-tests/index.js @@ -4,6 +4,13 @@ export { fspiopTransfersNoCallbackScenarios } from './scenarios/fspiopTransfersN export { fspiopQuotesScenarios } from './scenarios/fspiopQuotes.js'; export { fspiopQuotesPersonalInfoExtensionsScenarios } from './scenarios/fspiopQuotesPersonalInfoExtensions.js'; export { fspiopE2EScenarios } from './scenarios/fspiopE2E.js'; +export { inboundSDKDiscoveryScenarios } from './scenarios/inboundSDKDiscovery.js'; +export { inboundSDKQuotesScenarios } from './scenarios/inboundSDKQuotes.js'; +export { inboundSDKTransfersScenarios } from './scenarios/inboundSDKTransfers.js'; +export { outboundSDKDiscoveryScenarios } from './scenarios/outboundSDKDiscovery.js'; +export { outboundSDKQuotesScenarios } from './scenarios/outboundSDKQuotes.js'; +export { outboundSDKTransfersScenarios } from './scenarios/outboundSDKTransfers.js'; + const configFile = __ENV.K6_SCRIPT_CONFIG_FILE_NAME ? './config/' + __ENV.K6_SCRIPT_CONFIG_FILE_NAME : './config/test.json'; const testConfig = JSON.parse(open(configFile)); diff --git a/packages/k6-tests/scenarios/inboundSDKDiscovery.js b/packages/k6-tests/scenarios/inboundSDKDiscovery.js new file mode 100644 index 00000000..1d8a9c95 --- /dev/null +++ b/packages/k6-tests/scenarios/inboundSDKDiscovery.js @@ -0,0 +1,5 @@ +import { getParties } from "../scripts/inboundSDKGetParties.js"; + +export function inboundSDKDiscoveryScenarios() { + getParties(); +} diff --git a/packages/k6-tests/scenarios/inboundSDKQuotes.js b/packages/k6-tests/scenarios/inboundSDKQuotes.js new file mode 100644 index 00000000..7fe5e66c --- /dev/null +++ b/packages/k6-tests/scenarios/inboundSDKQuotes.js @@ -0,0 +1,6 @@ +import { postQuotes } from "../scripts/inboundSDKQuotes.js"; + + +export function inboundSDKQuotesScenarios() { + postQuotes(); +} diff --git a/packages/k6-tests/scenarios/inboundSDKTransfers.js b/packages/k6-tests/scenarios/inboundSDKTransfers.js new file mode 100644 index 00000000..dea244b2 --- /dev/null +++ b/packages/k6-tests/scenarios/inboundSDKTransfers.js @@ -0,0 +1,6 @@ +import { postTransfers } from "../scripts/inboundSDKTransfers.js"; + + +export function inboundSDKTransfersScenarios() { + postTransfers(); +} diff --git a/packages/k6-tests/scenarios/outboundSDKDiscovery.js b/packages/k6-tests/scenarios/outboundSDKDiscovery.js new file mode 100644 index 00000000..0ce4e482 --- /dev/null +++ b/packages/k6-tests/scenarios/outboundSDKDiscovery.js @@ -0,0 +1,5 @@ +import { getParties } from "../scripts/outboundSDKGetParties.js"; + +export function outboundSDKDiscoveryScenarios() { + getParties(); +} diff --git a/packages/k6-tests/scenarios/outboundSDKQuotes.js b/packages/k6-tests/scenarios/outboundSDKQuotes.js new file mode 100644 index 00000000..76975f63 --- /dev/null +++ b/packages/k6-tests/scenarios/outboundSDKQuotes.js @@ -0,0 +1,6 @@ +import { postQuotes } from "../scripts/outboundSDKQuotes.js"; + + +export function outboundSDKQuotesScenarios() { + postQuotes(); +} diff --git a/packages/k6-tests/scenarios/outboundSDKTransfers.js b/packages/k6-tests/scenarios/outboundSDKTransfers.js new file mode 100644 index 00000000..bca0196b --- /dev/null +++ b/packages/k6-tests/scenarios/outboundSDKTransfers.js @@ -0,0 +1,6 @@ +import { postTransfers } from "../scripts/outboundSDKTransfers.js"; + + +export function outboundSDKTransfersScenarios() { + postTransfers(); +} diff --git a/packages/k6-tests/scripts/inboundSDKGetParties.js b/packages/k6-tests/scripts/inboundSDKGetParties.js new file mode 100644 index 00000000..8599b4af --- /dev/null +++ b/packages/k6-tests/scripts/inboundSDKGetParties.js @@ -0,0 +1,96 @@ +import http from 'k6/http'; +import { check, fail, sleep, group } from 'k6'; +import { WebSocket } from 'k6/experimental/websockets'; +import { setTimeout, clearTimeout } from 'k6/experimental/timers'; +import { randomItem } from "https://jslib.k6.io/k6-utils/1.1.0/index.js"; +import { Trace } from "../common/trace.js"; + + +console.log(`Env Vars --> + K6_SCRIPT_WS_TIMEOUT_MS=${__ENV.K6_SCRIPT_WS_TIMEOUT_MS}, + K6_SCRIPT_SDK_ENDPOINT_URL=${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}, + K6_SCRIPT_FSPIOP_FSP_POOL=${__ENV.K6_SCRIPT_FSPIOP_FSP_POOL} +`); + +const fspList = JSON.parse(__ENV.K6_SCRIPT_FSPIOP_FSP_POOL) + +export function getParties() { + group("Get Parties", function () { + let payerFsp + let payeeFsp + if (__ENV.UNIDIRECTIONAL === "true" || __ENV.UNIDIRECTIONAL === "TRUE") { + payerFsp = fspList[0] + payeeFsp = fspList[1] + } else { + const randomSortedFsp = fspList.concat().sort(() => randomItem([-1,1])).slice(0, 2); + payerFsp = randomSortedFsp[0] + payeeFsp = randomSortedFsp[1] + } + + const startTs = Date.now(); + const payeeId = payeeFsp['partyId']; + const payerFspId = payerFsp['fspId']; + const payeeFspId = payeeFsp['fspId']; + const wsUrl = payerFsp['wsUrl']; + const traceParent = Trace(); + const traceId = traceParent.traceId; + const wsChannel = `${traceParent.traceId}/PUT/parties/MSISDN/${payeeId}`; + const wsURL = `${wsUrl}/${wsChannel}` + const ws = new WebSocket(wsURL); + + const wsTimeoutMs = Number(__ENV.K6_SCRIPT_WS_TIMEOUT_MS) || 2000; // user session between 5s and 1m + + var wsTimeoutId = null; + + const clearTimers = () => { + if (wsTimeoutId) { clearTimeout(wsTimeoutId); wsTimeoutId=null } + } + + ws.onclose(() => { + clearTimers(); + }); + + ws.onerror((err) => { + console.error(traceId, err); + check(err, { 'SDK_GET_PARTIES_SUCCESS': (cbMessage) => false }); + clearTimers(); + ws.close(); + }); + + ws.onmessage = (event) => { + console.info(traceId, `WS message received [${wsChannel}]: ${event.data}`); + check(event.data, { 'SDK_GET_PARTIES_SUCCESS': (cbMessage) => cbMessage == 'SUCCESS_CALLBACK_RECEIVED' }); + clearTimers(); + ws.close(); + // sleep(1); + }; + + ws.onopen = () => { + const params = { + tags: { + payerFspId, + payeeFspId + }, + headers: { + 'Accept': 'application/vnd.interoperability.parties+json;version=1.1', + 'Content-Type': 'application/vnd.interoperability.parties+json;version=1.1', + 'FSPIOP-Source': payerFspId, + 'Date': (new Date()).toUTCString(), + 'traceparent': traceParent.toString(), + 'tracestate': `tx_end2end_start_ts=${startTs}` + }, + }; + + // Lets send the GET /parties request to the SDK + const res = http.get(`${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}/parties/MSISDN/${payeeId}`, params); + check(res, { 'SDK_GET_PARTIES_RESPONSE_IS_202' : (r) => r.status == 202 }); + + wsTimeoutId = setTimeout(() => { + const errorMsg = `WS timed-out on URL: ${wsURL}` + console.error(traceId, errorMsg); + check(res, { 'SDK_PARTIES_SUCCESS': (cbMessage) => false }); + ws.close(); + }, wsTimeoutMs); + }; + }); +} diff --git a/packages/k6-tests/scripts/inboundSDKQuotes.js b/packages/k6-tests/scripts/inboundSDKQuotes.js new file mode 100644 index 00000000..4dc4b436 --- /dev/null +++ b/packages/k6-tests/scripts/inboundSDKQuotes.js @@ -0,0 +1,131 @@ +import http from 'k6/http'; +import { crypto } from "k6/experimental/webcrypto"; +import { check, fail, sleep, group } from 'k6'; +import { WebSocket } from 'k6/experimental/websockets'; +import { setTimeout, clearTimeout, setInterval, clearInterval } from 'k6/experimental/timers'; +import { Trace } from "../common/trace.js"; +import { getTwoItemsFromArray } from "../common/utils.js"; + +console.log(`Env Vars --> + K6_SCRIPT_WS_TIMEOUT_MS=${__ENV.K6_SCRIPT_WS_TIMEOUT_MS}, + K6_SCRIPT_SDK_ENDPOINT_URL=${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}, + K6_SCRIPT_FSPIOP_FSP_POOL=${__ENV.K6_SCRIPT_FSPIOP_FSP_POOL} +`); + +const fspList = JSON.parse(__ENV.K6_SCRIPT_FSPIOP_FSP_POOL) +const amount = __ENV.K6_SCRIPT_FSPIOP_QUOTES_AMOUNT.toString() +const currency = __ENV.K6_SCRIPT_FSPIOP_QUOTES_CURRENCY + +export function postQuotes() { + group("Post Quotes", function () { + let payerFsp + let payeeFsp + + if (__ENV.UNIDIRECTIONAL === "true" || __ENV.UNIDIRECTIONAL === "TRUE") { + payerFsp = fspList[0] + payeeFsp = fspList[1] + } else { + const selectedFsps = getTwoItemsFromArray(fspList) + payerFsp = selectedFsps[0] + payeeFsp = selectedFsps[1] + } + + const startTs = Date.now(); + const quoteId = crypto.randomUUID(); + const transactionId = crypto.randomUUID(); + const payerFspId = payerFsp['fspId']; + const payeeFspId = payeeFsp['fspId']; + const wsUrl = payerFsp['wsUrl']; + const traceParent = Trace(); + const traceId = traceParent.traceId; + const wsChannel = `${traceParent.traceId}/PUT/quotes/${quoteId}`; + const wsURL = `${wsUrl}/${wsChannel}` + console.log('ws open on: ', wsURL) + const ws = new WebSocket(wsURL); + + const wsTimeoutMs = Number(__ENV.K6_SCRIPT_WS_TIMEOUT_MS) || 2000; // user session between 5s and 1m + + var wsTimeoutId = null; + + const clearTimers = () => { + if (wsTimeoutId) { clearTimeout(wsTimeoutId); wsTimeoutId=null } + } + + ws.onclose(() => { + clearTimers(); + }); + + ws.onerror((err) => { + console.error(traceId, err); + check(err, { 'QUOTES_E2E_FSPIOP_POST_QUOTES_SUCCESS': (cbMessage) => false }); + clearTimers(); + ws.close(); + }); + + ws.onmessage = (event) => { + console.info(traceId, `WS message received [${wsChannel}]: ${event.data}`); + check(event.data, { 'QUOTES_E2E_FSPIOP_POST_QUOTES_SUCCESS': (cbMessage) => cbMessage == 'SUCCESS_CALLBACK_RECEIVED' }); + clearTimers(); + ws.close(); + // sleep(1); + }; + + ws.onopen = () => { + const params = { + tags: { + payerFspId, + payeeFspId + }, + headers: { + 'accept': 'application/vnd.interoperability.quotes+json;version=1.0', + 'Content-Type': 'application/vnd.interoperability.quotes+json;version=1.0', + 'FSPIOP-Source': payerFspId, + 'FSPIOP-Destination': payeeFspId, + 'Date': (new Date()).toUTCString(), + 'traceparent': traceParent.toString(), + 'tracestate': `tx_end2end_start_ts=${startTs}` + }, + }; + + const body = { + "quoteId": quoteId, + "transactionId": transactionId, + "payer": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": `${payerFsp['partyId']}`, + "fspId": payerFspId + } + }, + "payee": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": `${payeeFsp['partyId']}`, + "fspId": payeeFspId + } + }, + "amountType": "SEND", + "amount": { + "amount": `${amount}`, + "currency": `${currency}` + }, + "transactionType": { + "scenario": "TRANSFER", + "initiator": "PAYER", + "initiatorType": "CONSUMER" + } + } + + // Lets send the FSPIOP POST /quotes request + const res = http.post(`${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}/quotes`, JSON.stringify(body), params); + check(res, { 'QUOTES_FSPIOP_POST_QUOTES_RESPONSE_IS_202' : (r) => r.status == 202 }); + + wsTimeoutId = setTimeout(() => { + const errorMsg = `WS timed-out on URL: ${wsURL}` + console.error(traceId, errorMsg); + check(res, { 'QUOTES_E2E_FSPIOP_POST_QUOTES_SUCCESS': (cbMessage) => false }); + ws.close(); + }, wsTimeoutMs); + }; + }); +} diff --git a/packages/k6-tests/scripts/inboundSDKTransfers.js b/packages/k6-tests/scripts/inboundSDKTransfers.js new file mode 100644 index 00000000..b4f551da --- /dev/null +++ b/packages/k6-tests/scripts/inboundSDKTransfers.js @@ -0,0 +1,130 @@ +import http from 'k6/http'; +import { crypto } from "k6/experimental/webcrypto"; +import { check, fail, sleep, group } from 'k6'; +import exec from 'k6/execution'; +import { WebSocket } from 'k6/experimental/websockets'; +import { setTimeout, clearTimeout, setInterval, clearInterval } from 'k6/experimental/timers'; +import { Trace } from "../common/trace.js"; +import { getTwoItemsFromArray } from "../common/utils.js"; + +console.log(`Env Vars --> + K6_SCRIPT_WS_TIMEOUT_MS=${__ENV.K6_SCRIPT_WS_TIMEOUT_MS}, + K6_SCRIPT_SDK_ENDPOINT_URL=${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}, + K6_SCRIPT_FSPIOP_FSP_POOL=${__ENV.K6_SCRIPT_FSPIOP_FSP_POOL}, + K6_SCRIPT_ABORT_ON_ERROR=${__ENV.K6_SCRIPT_ABORT_ON_ERROR} +`); + +const fspList = JSON.parse(__ENV.K6_SCRIPT_FSPIOP_FSP_POOL) + +const ilpPacket = __ENV.K6_SCRIPT_FSPIOP_TRANSFERS_ILPPACKET +const condition = __ENV.K6_SCRIPT_FSPIOP_TRANSFERS_CONDITION +const amount = __ENV.K6_SCRIPT_FSPIOP_TRANSFERS_AMOUNT.toString() +const currency = __ENV.K6_SCRIPT_FSPIOP_TRANSFERS_CURRENCY +const abortOnError = (__ENV.K6_SCRIPT_ABORT_ON_ERROR && __ENV.K6_SCRIPT_ABORT_ON_ERROR.toLowerCase() === 'true') ? true : false + +export function postTransfers() { + group("Post Transfers", function () { + let payerFsp + let payeeFsp + + if (__ENV.UNIDIRECTIONAL === "true" || __ENV.UNIDIRECTIONAL === "TRUE") { + payerFsp = fspList[0] + payeeFsp = fspList[1] + } else { + const selectedFsps = getTwoItemsFromArray(fspList) + payerFsp = selectedFsps[0] + payeeFsp = selectedFsps[1] + } + + const startTs = Date.now(); + const transferId = crypto.randomUUID(); + const payerFspId = payerFsp['fspId']; + const payeeFspId = payeeFsp['fspId']; + const wsUrl = payerFsp['wsUrl']; + const traceParent = Trace(); + const traceId = traceParent.traceId; + const wsChannel = `${traceParent.traceId}/PUT/transfers/${transferId}`; + const wsURL = `${wsUrl}/${wsChannel}` + const ws = new WebSocket(wsURL); + const wsTimeoutMs = Number(__ENV.K6_SCRIPT_WS_TIMEOUT_MS) || 2000; // user session between 5s and 1m + + var wsTimeoutId = null; + + const clearTimers = () => { + if (wsTimeoutId) { clearTimeout(wsTimeoutId); wsTimeoutId=null } + } + + ws.onclose(() => { + clearTimers(); + }); + + ws.onerror((err) => { + console.error(traceId, err); + check(err, { 'TRANSFERS_E2E_FSPIOP_POST_TRANSFERS_SUCCESS': (cbMessage) => false }); + clearTimers(); + ws.close(); + }); + + ws.onmessage = (event) => { + console.info(traceId, `WS message received [${wsChannel}]: ${event.data}`); + check(event.data, { 'TRANSFERS_E2E_FSPIOP_POST_TRANSFERS_SUCCESS': (cbMessage) => cbMessage == 'SUCCESS_CALLBACK_RECEIVED' }); + clearTimers(); + ws.close(); + // sleep(1); + }; + + ws.onopen = () => { + console.info(traceId, `WS open on URL: ${wsURL}`); + const params = { + tags: { + payerFspId, + payeeFspId + }, + headers: { + 'Accept': 'application/vnd.interoperability.transfers+json;version=1.1', + 'Content-Type': 'application/vnd.interoperability.transfers+json;version=1.1', + 'FSPIOP-Source': payerFspId, + 'FSPIOP-Destination': payeeFspId, + 'Date': (new Date()).toUTCString(), + 'traceparent': traceParent.toString(), + 'tracestate': `tx_end2end_start_ts=${startTs}` + }, + }; + + const body = { + "transferId": transferId, + "payerFsp": payerFspId, + "payeeFsp": payeeFspId, + "amount": { + amount, + currency + }, + "expiration": "2030-01-01T00:00:00.000Z", + ilpPacket, + condition + } + + // Lets send the FSPIOP POST /transfers request + const res = http.post(`${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}/transfers`, JSON.stringify(body), params); + check(res, { 'TRANSFERS_FSPIOP_POST_TRANSFERS_RESPONSE_IS_202' : (r) => r.status == 202 }); + + if (abortOnError && res.status != 202) { + // Abort the entire k6 test exection runner + console.error(traceId, `FSPIOP POST /transfers returned status: ${res.status}`); + ws.close(); + exec.test.abort() + } + + wsTimeoutId = setTimeout(() => { + const errorMsg = `WS timed-out on URL: ${wsURL}` + check(res, { 'TRANSFERS_E2E_FSPIOP_POST_TRANSFERS_SUCCESS': (cbMessage) => false }); + ws.close(); + if (abortOnError) { + // Abort the entire k6 test exection runner + console.error(traceId, 'Aborting k6 test execution!') + exec.test.abort() + } + }, wsTimeoutMs); + }; + }); +} diff --git a/packages/k6-tests/scripts/outboundSDKGetParties.js b/packages/k6-tests/scripts/outboundSDKGetParties.js new file mode 100644 index 00000000..382d18b5 --- /dev/null +++ b/packages/k6-tests/scripts/outboundSDKGetParties.js @@ -0,0 +1,47 @@ +import http from 'k6/http'; +import { check, group } from 'k6'; +import { getTwoItemsFromArray } from "../common/utils.js"; + +console.log(`Env Vars --> + K6_SCRIPT_FSPIOP_FSP_POOL=${__ENV.K6_SCRIPT_FSPIOP_FSP_POOL}, + K6_SCRIPT_SDK_ENDPOINT_URL=${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}, +`); + +const fspList = JSON.parse(__ENV.K6_SCRIPT_FSPIOP_FSP_POOL) + +export function getParties() { + group("Get Parties", function () { + let payerFsp + let payeeFsp + + if (__ENV.UNIDIRECTIONAL === "true" || __ENV.UNIDIRECTIONAL === "TRUE") { + payerFsp = fspList[0] + payeeFsp = fspList[1] + } else { + const selectedFsps = getTwoItemsFromArray(fspList) + payerFsp = selectedFsps[0] + payeeFsp = selectedFsps[1] + } + + const payeeId = payeeFsp['partyId']; + const payerFspId = payerFsp['fspId']; + const payeeFspId = payeeFsp['fspId']; + + const params = { + tags: { + payerFspId, + payeeFspId + }, + headers: { + 'Accept': 'application/vnd.interoperability.parties+json;version=1.1', + 'Content-Type': 'application/vnd.interoperability.parties+json;version=1.1', + 'FSPIOP-Source': payerFspId, + 'Date': (new Date()).toUTCString(), + }, + }; + + const res = http.get(`${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}/parties/MSISDN/${payeeId}`, params); + check(res, { 'SDK_GET_PARTIES_RESPONSE_IS_200' : (r) => r.status == 200 }); + + }); +} diff --git a/packages/k6-tests/scripts/outboundSDKQuotes.js b/packages/k6-tests/scripts/outboundSDKQuotes.js new file mode 100644 index 00000000..ca949fa0 --- /dev/null +++ b/packages/k6-tests/scripts/outboundSDKQuotes.js @@ -0,0 +1,84 @@ +import http from 'k6/http'; +import { crypto } from "k6/experimental/webcrypto"; +import { check, group } from 'k6'; +import { getTwoItemsFromArray } from "../common/utils.js"; + +console.log(`Env Vars --> + K6_SCRIPT_FSPIOP_FSP_POOL=${__ENV.K6_SCRIPT_FSPIOP_FSP_POOL}, + K6_SCRIPT_SDK_ENDPOINT_URL=${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}, +`); + +const fspList = JSON.parse(__ENV.K6_SCRIPT_FSPIOP_FSP_POOL) +const amount = __ENV.K6_SCRIPT_FSPIOP_QUOTES_AMOUNT.toString() +const currency = __ENV.K6_SCRIPT_FSPIOP_QUOTES_CURRENCY + +export function postQuotes() { + group("Post Quotes", function () { + let payerFsp + let payeeFsp + + if (__ENV.UNIDIRECTIONAL === "true" || __ENV.UNIDIRECTIONAL === "TRUE") { + payerFsp = fspList[0] + payeeFsp = fspList[1] + } else { + const selectedFsps = getTwoItemsFromArray(fspList) + payerFsp = selectedFsps[0] + payeeFsp = selectedFsps[1] + } + + const quoteId = crypto.randomUUID(); + const transactionId = crypto.randomUUID(); + const payerFspId = payerFsp['fspId']; + const payeeFspId = payeeFsp['fspId']; + + const params = { + tags: { + payerFspId, + payeeFspId + }, + headers: { + // 'accept': 'application/vnd.interoperability.quotes+json;version=1.0', + 'Content-Type': 'application/json', + 'FSPIOP-Source': payerFspId, + 'FSPIOP-Destination': payeeFspId, + 'Date': (new Date()).toUTCString() + }, + }; + + const body = { + "fspId": payerFspId, + "quotesPostRequest": { + "quoteId": quoteId, + "transactionId": transactionId, + "payee": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": `${payeeFsp['partyId']}`, + "fspId": payeeFspId + } + }, + "payer": { + "partyIdInfo": { + "partyIdType": "MSISDN", + "partyIdentifier": `${payerFsp['partyId']}`, + "fspId": payerFspId + } + }, + "amountType": "SEND", + "amount": { + "amount": `${amount}`, + "currency": `${currency}` + }, + "transactionType": { + "scenario": "DEPOSIT", + "initiator": "PAYER", + "initiatorType": "AGENT", + } + } + } + + // Lets send the FSPIOP POST /quotes request + const res = http.post(`${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}/quotes`, JSON.stringify(body), params); + check(res, { 'QUOTES_FSPIOP_POST_QUOTES_RESPONSE_IS_200' : (r) => r.status == 200 }); + }); +} diff --git a/packages/k6-tests/scripts/outboundSDKTransfers.js b/packages/k6-tests/scripts/outboundSDKTransfers.js new file mode 100644 index 00000000..c0782e70 --- /dev/null +++ b/packages/k6-tests/scripts/outboundSDKTransfers.js @@ -0,0 +1,91 @@ +import http from 'k6/http'; +import { check, group } from 'k6'; +import exec from 'k6/execution'; +import { getTwoItemsFromArray } from "../common/utils.js"; + +console.log(`Env Vars --> + K6_SCRIPT_FSPIOP_FSP_POOL=${__ENV.K6_SCRIPT_FSPIOP_FSP_POOL}, + K6_SCRIPT_ABORT_ON_ERROR=${__ENV.K6_SCRIPT_ABORT_ON_ERROR}, + K6_SCRIPT_SDK_ENDPOINT_URL=${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL} +`); + +const fspList = JSON.parse(__ENV.K6_SCRIPT_FSPIOP_FSP_POOL) + +const abortOnError = (__ENV.K6_SCRIPT_ABORT_ON_ERROR && __ENV.K6_SCRIPT_ABORT_ON_ERROR.toLowerCase() === 'true') ? true : false + +export function postTransfers() { + group("Post Transfers", function () { + let payerFsp + let payeeFsp + + if (__ENV.UNIDIRECTIONAL === "true" || __ENV.UNIDIRECTIONAL === "TRUE") { + payerFsp = fspList[0] + payeeFsp = fspList[1] + } else { + const selectedFsps = getTwoItemsFromArray(fspList) + payerFsp = selectedFsps[0] + payeeFsp = selectedFsps[1] + } + + const payerFspId = payerFsp['fspId']; + const payeeFspId = payeeFsp['fspId']; + const params = { + tags: { + payerFspId, + payeeFspId + }, + headers: { + 'Date': (new Date()).toUTCString(), + 'Content-Type': 'application/json', + }, + }; + + const body = { + "homeTransactionId": "string", + "from": { + "type": "CONSUMER", + "idType": "MSISDN", + "idValue": "payerFspId", + "displayName": "test payer", + "firstName": "Henrik", + "lastName": "Karlsson", + "fspId": "string" + }, + "to": { + "type": "CONSUMER", + "idType": "MSISDN", + "idValue": "payeeFspId" + }, + "amountType": "SEND", + "currency": "AED", + "amount": "123.45", + "transactionType": "TRANSFER" + } + + // Lets send the FSPIOP POST /transfers request + const postTransferResponse = http.post(`${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}/transfers`, JSON.stringify(body), params); + check(postTransferResponse, { 'TRANSFERS__POST_TRANSFERS_RESPONSE_IS_200' : (r) => r.status == 200 }); + + const transferId = JSON.parse(postTransferResponse.body).transferId + + if (postTransferResponse.status == 200) { + const putTransferacceptPartyResponse = http.put(`${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}/transfers/${transferId}`, JSON.stringify({ + "acceptParty": true + }), params); + check(putTransferacceptPartyResponse, { 'TRANSFERS__PUT_TRANSFERS_ACCEPT_PARTY_RESPONSE_IS_200' : (r) => r.status == 200 }); + + if (putTransferacceptPartyResponse.status == 200) { + const putTransferAcceptQuoteResponse = http.put(`${__ENV.K6_SCRIPT_SDK_ENDPOINT_URL}/transfers/${transferId}`, JSON.stringify({ + "acceptQuote": true + }), params); + check(putTransferAcceptQuoteResponse, { 'TRANSFERS__PUT_TRANSFERS_ACCEPT_QUOTE_RESPONSE_IS_200' : (r) => r.status == 200 }); + } + } + + if (abortOnError && res.status != 200) { + // Abort the entire k6 test exection runner + console.error(traceId, `POST /transfers returned status: ${res.status}`); + exec.test.abort() + } + }); +} diff --git a/perf.env b/perf.env index 30a5ed75..dae628a8 100644 --- a/perf.env +++ b/perf.env @@ -14,9 +14,11 @@ K6_OUT=experimental-prometheus-rw # Ref: https://k6.io/docs/results-output/real- K6_SCRIPT_WS_TIMEOUT_MS=5000 # K6_SCRIPT_ABORT_ON_ERROR=true #### Use this if we want to hit the ALS -K6_SCRIPT_FSPIOP_ALS_ENDPOINT_URL=http://account-lookup-service:4002 +# K6_SCRIPT_FSPIOP_ALS_ENDPOINT_URL=http://account-lookup-service:4002 #### Use this if we want to bypass ALS -# K6_SCRIPT_FSPIOP_ALS_ENDPOINT_URL=http://perffsp2:3001/fspiop +# K6_SCRIPT_FSPIOP_ALS_ENDPOINT_URL=http://sim-perffsp2:3001/fspiop + +REQUEST_PROCESSING_TIMEOUT_SECONDS=5 # [ @@ -32,12 +34,15 @@ K6_SCRIPT_FSPIOP_ALS_ENDPOINT_URL=http://account-lookup-service:4002 ## IMPORTANT: Update pool depending 2dfsp, 4dfsp or 8dfsp docker profile ## NOTE: Unidirectional will take the first two DFSP's in the pool # 8 DFSPs -K6_SCRIPT_FSPIOP_FSP_POOL='[{"partyId":19012345001,"fspId":"perffsp1","wsUrl":"ws://sim-perffsp1:3002"},{"partyId":19012345002,"fspId":"perffsp2","wsUrl":"ws://sim-perffsp2:3002"},{"partyId":19012345003,"fspId":"perffsp3","wsUrl":"ws://sim-perffsp3:3002"},{"partyId":19012345004,"fspId":"perffsp4","wsUrl":"ws://sim-perffsp4:3002"},{"partyId":19012345005,"fspId":"perffsp5","wsUrl":"ws://sim-perffsp5:3002"},{"partyId":19012345006,"fspId":"perffsp6","wsUrl":"ws://sim-perffsp6:3002"},{"partyId":19012345007,"fspId":"perffsp7","wsUrl":"ws://sim-perffsp7:3002"},{"partyId":19012345008,"fspId":"perffsp8","wsUrl":"ws://sim-perffsp8:3002"}]' +# K6_SCRIPT_FSPIOP_FSP_POOL='[{"partyId":19012345001,"fspId":"perffsp1","wsUrl":"ws://sim-perffsp1:3002"},{"partyId":19012345002,"fspId":"perffsp2","wsUrl":"ws://sim-perffsp2:3002"},{"partyId":19012345003,"fspId":"perffsp3","wsUrl":"ws://sim-perffsp3:3002"},{"partyId":19012345004,"fspId":"perffsp4","wsUrl":"ws://sim-perffsp4:3002"},{"partyId":19012345005,"fspId":"perffsp5","wsUrl":"ws://sim-perffsp5:3002"},{"partyId":19012345006,"fspId":"perffsp6","wsUrl":"ws://sim-perffsp6:3002"},{"partyId":19012345007,"fspId":"perffsp7","wsUrl":"ws://sim-perffsp7:3002"},{"partyId":19012345008,"fspId":"perffsp8","wsUrl":"ws://sim-perffsp8:3002"}]' # 2 DFSPs -# K6_SCRIPT_FSPIOP_FSP_POOL='[{"partyId":19012345001,"fspId":"perffsp1","wsUrl":"ws://sim-perffsp1:3002"},{"partyId":19012345002,"fspId":"perffsp2","wsUrl":"ws://sim-perffsp2:3002"}]' +K6_SCRIPT_FSPIOP_FSP_POOL='[{"partyId":19012345001,"fspId":"perffsp1","wsUrl":"ws://sim-perffsp1:3002"},{"partyId":19012345002,"fspId":"perffsp2","wsUrl":"ws://sim-perffsp2:3002"}]' # 4 DFSPs # K6_SCRIPT_FSPIOP_FSP_POOL='[{"partyId":19012345001,"fspId":"perffsp1","wsUrl":"ws://sim-perffsp1:3002"},{"partyId":19012345002,"fspId":"perffsp2","wsUrl":"ws://sim-perffsp2:3002"},{"partyId":19012345003,"fspId":"perffsp3","wsUrl":"ws://sim-perffsp3:3002"},{"partyId":19012345004,"fspId":"perffsp4","wsUrl":"ws://sim-perffsp4:3002"}]' +# SDK DFSPs +# K6_SCRIPT_FSPIOP_FSP_POOL='[{"partyId":19012345001,"fspId":"perffsp1","wsUrl":"ws://sim-fspiop:3002"},{"partyId":19012345002,"fspId":"perffsp2","wsUrl":"ws://sim-fspiop:3002"}]' + K6_SCRIPT_ADMIN_ENDPOINT_URL=http://callback-handler-svc-cl-sim:3001/admin K6_SCRIPT_ORACLE_ENDPOINT_URL=http://callback-handler-svc-oracle-sim:3001/oracle @@ -45,6 +50,7 @@ K6_SCRIPT_ORACLE_ENDPOINT_URL=http://callback-handler-svc-oracle-sim:3001/oracle K6_SCRIPT_FSPIOP_TRANSFERS_ENDPOINT_URL=http://ml-api-adapter:3000 # K6_SCRIPT_FSPIOP_TRANSFERS_ENDPOINT_URL=http://sim-perffsp2:3001/fspiop + K6_SCRIPT_FSPIOP_QUOTES_ENDPOINT_URL=http://quoting-service:3002 K6_SCRIPT_FSPIOP_QUOTES_AMOUNT=2 K6_SCRIPT_FSPIOP_QUOTES_CURRENCY=USD @@ -121,3 +127,26 @@ QS_SIMPLE_ROUTING_MODE=false # Use this for E2E testing # ALS_SWITCH_ENDPOINT="http://central-ledger:3001" + +CHECK_ILP=false +JWS_SIGN=false +VALIDATE_JWS_SIGN=false + +# ---- SDK Config ---- +ALLOW_DIFFERENT_TRANSFER_TRANSACTION_ID=true +ALLOW_TRANSFER_WITHOUT_QUOTE=true +JWS_SIGN=false +VALIDATE_JWS_SIGN=false +VALIDATE_INBOUND_JWS=false +DFSP_ID=mojaloop-sdk + +# Inbound SDK config +K6_SCRIPT_SDK_ENDPOINT_URL=http://sdk-scheme-adapter:4000 +PEER_ENDPOINT=sim-fspiop:3001/backend +BACKEND_ENDPOINT=sim-fspiop:3001/backend + +# Outbound SDK config +# K6_SCRIPT_SDK_ENDPOINT_URL=http://sdk-scheme-adapter:4001 +# PEER_ENDPOINT=sim-fspiop:3001/fspiop +# BACKEND_ENDPOINT=sim-fspiop:3001 +