summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-01-17 01:18:37 +0100
committerFlorian Dold <florian@dold.me>2021-01-17 01:18:37 +0100
commit9aa9742d0e909609f9ce22bc1db8364ab7076db8 (patch)
treeb3247f0a50e4961d38ae302a344920488eeb0c27
parent94431fc6d2ad0f003dd12c100b1c7a53980f72f3 (diff)
downloadwallet-core-9aa9742d0e909609f9ce22bc1db8364ab7076db8.tar.gz
wallet-core-9aa9742d0e909609f9ce22bc1db8364ab7076db8.tar.bz2
wallet-core-9aa9742d0e909609f9ce22bc1db8364ab7076db8.zip
implement the big LibEuFin integration test
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/harness.ts70
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/libeufin.ts442
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts293
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/testrunner.ts12
4 files changed, 794 insertions, 23 deletions
diff --git a/packages/taler-wallet-cli/src/integrationtests/harness.ts b/packages/taler-wallet-cli/src/integrationtests/harness.ts
index 4a856cea4..3434b5e71 100644
--- a/packages/taler-wallet-cli/src/integrationtests/harness.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/harness.ts
@@ -76,8 +76,8 @@ import {
codecForPrepareTipResult,
AcceptTipRequest,
AbortPayWithRefundRequest,
- handleWorkerError,
openPromise,
+ parsePaytoUri,
} from "taler-wallet-core";
import { URL } from "url";
import axios, { AxiosError } from "axios";
@@ -352,6 +352,10 @@ export class GlobalTestState {
if (this.inShutdown) {
return;
}
+ if (shouldLingerInTest()) {
+ console.log("refusing to shut down, lingering was requested");
+ return;
+ }
this.inShutdown = true;
console.log("shutting down");
for (const s of this.servers) {
@@ -368,6 +372,10 @@ export class GlobalTestState {
}
}
+export function shouldLingerInTest(): boolean {
+ return !!process.env["TALER_TEST_LINGER"];
+}
+
export interface TalerConfigSection {
options: Record<string, string | undefined>;
}
@@ -427,7 +435,11 @@ function setCoin(config: Configuration, c: CoinConfig) {
config.setString(s, "rsa_keysize", `${c.rsaKeySize}`);
}
-async function pingProc(
+/**
+ * Send an HTTP request until it succeeds or the
+ * process dies.
+ */
+export async function pingProc(
proc: ProcessWrapper | undefined,
url: string,
serviceName: string,
@@ -814,6 +826,15 @@ export class ExchangeService implements ExchangeServiceInterface {
);
}
+ async runTransferOnce() {
+ await runCommand(
+ this.globalState,
+ `exchange-${this.name}-transfer-once`,
+ "taler-exchange-transfer",
+ [...this.timetravelArgArr, "-c", this.configFilename, "-t"],
+ );
+ }
+
changeConfig(f: (config: Configuration) => void) {
const config = Configuration.load(this.configFilename);
f(config);
@@ -1006,11 +1027,18 @@ export class ExchangeService implements ExchangeServiceInterface {
);
const accounts: string[] = [];
+ const accountTargetTypes: Set<string> = new Set();
const config = Configuration.load(this.configFilename);
for (const sectionName of config.getSectionNames()) {
if (sectionName.startsWith("exchange-account")) {
- accounts.push(config.getString(sectionName, "payto_uri").required());
+ const paytoUri = config.getString(sectionName, "payto_uri").required();
+ const p = parsePaytoUri(paytoUri);
+ if (!p) {
+ throw Error(`invalid payto uri in exchange config: ${paytoUri}`);
+ }
+ accountTargetTypes.add(p?.targetType);
+ accounts.push(paytoUri);
}
}
@@ -1032,22 +1060,24 @@ export class ExchangeService implements ExchangeServiceInterface {
}
const year = new Date().getFullYear();
- for (let i = year; i < year + 5; i++) {
- await runCommand(
- this.globalState,
- "exchange-offline",
- "taler-exchange-offline",
- [
- "-c",
- this.configFilename,
- "wire-fee",
- `${i}`,
- "x-taler-bank",
- `${this.exchangeConfig.currency}:0.01`,
- `${this.exchangeConfig.currency}:0.01`,
- "upload",
- ],
- );
+ for (const accTargetType of accountTargetTypes.values()) {
+ for (let i = year; i < year + 5; i++) {
+ await runCommand(
+ this.globalState,
+ "exchange-offline",
+ "taler-exchange-offline",
+ [
+ "-c",
+ this.configFilename,
+ "wire-fee",
+ `${i}`,
+ accTargetType,
+ `${this.exchangeConfig.currency}:0.01`,
+ `${this.exchangeConfig.currency}:0.01`,
+ "upload",
+ ],
+ );
+ }
}
}
@@ -1451,10 +1481,10 @@ export async function runTestWithState(
let status: TestStatus;
const handleSignal = (s: string) => {
- gc.shutdownSync();
console.warn(
`**** received fatal proces event, terminating test ${testName}`,
);
+ gc.shutdownSync();
process.exit(1);
};
diff --git a/packages/taler-wallet-cli/src/integrationtests/libeufin.ts b/packages/taler-wallet-cli/src/integrationtests/libeufin.ts
new file mode 100644
index 000000000..cb739f52d
--- /dev/null
+++ b/packages/taler-wallet-cli/src/integrationtests/libeufin.ts
@@ -0,0 +1,442 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import axios from "axios";
+import { URL } from "taler-wallet-core";
+import {
+ GlobalTestState,
+ pingProc,
+ ProcessWrapper,
+ runCommand,
+} from "./harness";
+
+export interface LibeufinSandboxServiceInterface {
+ baseUrl: string;
+}
+
+export interface LibeufinNexusServiceInterface {
+ baseUrl: string;
+}
+
+export interface LibeufinSandboxConfig {
+ httpPort: number;
+ databaseJdbcUri: string;
+}
+
+export interface LibeufinNexusConfig {
+ httpPort: number;
+ databaseJdbcUri: string;
+}
+
+export class LibeufinSandboxService implements LibeufinSandboxServiceInterface {
+ static async create(
+ gc: GlobalTestState,
+ sandboxConfig: LibeufinSandboxConfig,
+ ): Promise<LibeufinSandboxService> {
+ return new LibeufinSandboxService(gc, sandboxConfig);
+ }
+
+ sandboxProc: ProcessWrapper | undefined;
+ globalTestState: GlobalTestState;
+
+ constructor(
+ gc: GlobalTestState,
+ private sandboxConfig: LibeufinSandboxConfig,
+ ) {
+ this.globalTestState = gc;
+ }
+
+ get baseUrl(): string {
+ return `http://localhost:${this.sandboxConfig.httpPort}/`;
+ }
+
+ async start(): Promise<void> {
+ this.sandboxProc = this.globalTestState.spawnService(
+ "libeufin-sandbox",
+ [
+ "serve",
+ "--port",
+ `${this.sandboxConfig.httpPort}`,
+ "--db-conn-string",
+ this.sandboxConfig.databaseJdbcUri,
+ ],
+ "libeufin-sandbox",
+ );
+ }
+
+ async pingUntilAvailable(): Promise<void> {
+ const url = `${this.baseUrl}config`;
+ await pingProc(this.sandboxProc, url, "libeufin-sandbox");
+ }
+}
+
+export class LibeufinNexusService {
+ static async create(
+ gc: GlobalTestState,
+ nexusConfig: LibeufinNexusConfig,
+ ): Promise<LibeufinNexusService> {
+ return new LibeufinNexusService(gc, nexusConfig);
+ }
+
+ nexusProc: ProcessWrapper | undefined;
+ globalTestState: GlobalTestState;
+
+ constructor(gc: GlobalTestState, private nexusConfig: LibeufinNexusConfig) {
+ this.globalTestState = gc;
+ }
+
+ get baseUrl(): string {
+ return `http://localhost:${this.nexusConfig.httpPort}/`;
+ }
+
+ async start(): Promise<void> {
+ await runCommand(
+ this.globalTestState,
+ "libeufin-nexus-superuser",
+ "libeufin-nexus",
+ [
+ "superuser",
+ "admin",
+ "--password",
+ "test",
+ "--db-conn-string",
+ this.nexusConfig.databaseJdbcUri,
+ ],
+ );
+
+ this.nexusProc = this.globalTestState.spawnService(
+ "libeufin-nexus",
+ [
+ "serve",
+ "--port",
+ `${this.nexusConfig.httpPort}`,
+ "--db-conn-string",
+ this.nexusConfig.databaseJdbcUri,
+ ],
+ "libeufin-nexus",
+ );
+ }
+
+ async pingUntilAvailable(): Promise<void> {
+ const url = `${this.baseUrl}config`;
+ await pingProc(this.nexusProc, url, "libeufin-nexus");
+ }
+}
+
+export interface CreateEbicsSubscriberRequest {
+ hostID: string;
+ userID: string;
+ partnerID: string;
+ systemID?: string;
+}
+
+interface CreateEbicsBankAccountRequest {
+ subscriber: {
+ hostID: string;
+ partnerID: string;
+ userID: string;
+ systemID?: string;
+ };
+ // IBAN
+ iban: string;
+ // BIC
+ bic: string;
+ // human name
+ name: string;
+ currency: string;
+ label: string;
+}
+
+export interface SimulateIncomingTransactionRequest {
+ debtorIban: string;
+ debtorBic: string;
+ debtorName: string;
+
+ /**
+ * Subject / unstructured remittance info.
+ */
+ subject: string;
+
+ /**
+ * Decimal amount without currency.
+ */
+ amount: string;
+ currency: string;
+}
+
+export namespace LibeufinSandboxApi {
+ export async function createEbicsHost(
+ libeufinSandboxService: LibeufinSandboxServiceInterface,
+ hostID: string,
+ ) {
+ const baseUrl = libeufinSandboxService.baseUrl;
+ let url = new URL("admin/ebics/hosts", baseUrl);
+ await axios.post(url.href, {
+ hostID,
+ ebicsVersion: "2.5",
+ });
+ }
+
+ export async function createEbicsSubscriber(
+ libeufinSandboxService: LibeufinSandboxServiceInterface,
+ req: CreateEbicsSubscriberRequest,
+ ) {
+ const baseUrl = libeufinSandboxService.baseUrl;
+ let url = new URL("admin/ebics/subscribers", baseUrl);
+ await axios.post(url.href, req);
+ }
+
+ export async function createEbicsBankAccount(
+ libeufinSandboxService: LibeufinSandboxServiceInterface,
+ req: CreateEbicsBankAccountRequest,
+ ) {
+ const baseUrl = libeufinSandboxService.baseUrl;
+ let url = new URL("admin/ebics/bank-accounts", baseUrl);
+ await axios.post(url.href, req);
+ }
+
+ export async function simulateIncomingTransaction(
+ libeufinSandboxService: LibeufinSandboxServiceInterface,
+ accountLabel: string,
+ req: SimulateIncomingTransactionRequest,
+ ) {
+ const baseUrl = libeufinSandboxService.baseUrl;
+ let url = new URL(
+ `admin/bank-accounts/${accountLabel}/simulate-incoming-transaction`,
+ baseUrl,
+ );
+ await axios.post(url.href, req);
+ }
+
+ export async function getAccountTransactions(
+ libeufinSandboxService: LibeufinSandboxServiceInterface,
+ accountLabel: string,
+ ): Promise<SandboxAccountTransactions> {
+ const baseUrl = libeufinSandboxService.baseUrl;
+ let url = new URL(
+ `admin/bank-accounts/${accountLabel}/transactions`,
+ baseUrl,
+ );
+ const res = await axios.get(url.href);
+ return res.data as SandboxAccountTransactions;
+ }
+}
+
+export interface SandboxAccountTransactions {
+ payments: {
+ accountLabel: string;
+ creditorIban: string;
+ creditorBic?: string;
+ creditorName: string;
+ debtorIban: string;
+ debtorBic: string;
+ debtorName: string;
+ amount: string;
+ currency: string;
+ subject: string;
+ date: string;
+ creditDebitIndicator: "debit" | "credit";
+ accountServicerReference: string;
+ }[];
+}
+
+export interface CreateEbicsBankConnectionRequest {
+ name: string;
+ ebicsURL: string;
+ hostID: string;
+ userID: string;
+ partnerID: string;
+ systemID?: string;
+}
+
+export interface CreateTalerWireGatewayFacadeRequest {
+ name: string;
+ connectionName: string;
+ accountName: string;
+ currency: string;
+ reserveTransferLevel: "report" | "statement" | "notification";
+}
+
+export namespace LibeufinNexusApi {
+ export async function createEbicsBankConnection(
+ libeufinNexusService: LibeufinNexusServiceInterface,
+ req: CreateEbicsBankConnectionRequest,
+ ): Promise<void> {
+ const baseUrl = libeufinNexusService.baseUrl;
+ let url = new URL("bank-connections", baseUrl);
+ await axios.post(
+ url.href,
+ {
+ source: "new",
+ type: "ebics",
+ name: req.name,
+ data: {
+ ebicsURL: req.ebicsURL,
+ hostID: req.hostID,
+ userID: req.userID,
+ partnerID: req.partnerID,
+ systemID: req.systemID,
+ },
+ },
+ {
+ auth: {
+ username: "admin",
+ password: "test",
+ },
+ },
+ );
+ }
+
+ export async function fetchAccounts(
+ libeufinNexusService: LibeufinNexusServiceInterface,
+ connectionName: string,
+ ): Promise<void> {
+ const baseUrl = libeufinNexusService.baseUrl;
+ let url = new URL(
+ `bank-connections/${connectionName}/fetch-accounts`,
+ baseUrl,
+ );
+ await axios.post(
+ url.href,
+ {},
+ {
+ auth: {
+ username: "admin",
+ password: "test",
+ },
+ },
+ );
+ }
+
+ export async function importConnectionAccount(
+ libeufinNexusService: LibeufinNexusServiceInterface,
+ connectionName: string,
+ offeredAccountId: string,
+ nexusBankAccountId: string,
+ ): Promise<void> {
+ const baseUrl = libeufinNexusService.baseUrl;
+ let url = new URL(
+ `bank-connections/${connectionName}/import-account`,
+ baseUrl,
+ );
+ await axios.post(
+ url.href,
+ {
+ offeredAccountId,
+ nexusBankAccountId,
+ },
+ {
+ auth: {
+ username: "admin",
+ password: "test",
+ },
+ },
+ );
+ }
+
+ export async function connectBankConnection(
+ libeufinNexusService: LibeufinNexusServiceInterface,
+ connectionName: string,
+ ) {
+ const baseUrl = libeufinNexusService.baseUrl;
+ let url = new URL(`bank-connections/${connectionName}/connect`, baseUrl);
+ await axios.post(
+ url.href,
+ {},
+ {
+ auth: {
+ username: "admin",
+ password: "test",
+ },
+ },
+ );
+ }
+
+ export async function fetchAllTransactions(
+ libeufinNexusService: LibeufinNexusService,
+ accountName: string,
+ ): Promise<void> {
+ const baseUrl = libeufinNexusService.baseUrl;
+ let url = new URL(
+ `/bank-accounts/${accountName}/fetch-transactions`,
+ baseUrl,
+ );
+ await axios.post(
+ url.href,
+ {
+ rangeType: "all",
+ level: "report",
+ },
+ {
+ auth: {
+ username: "admin",
+ password: "test",
+ },
+ },
+ );
+ }
+
+ export async function createTwgFacade(
+ libeufinNexusService: LibeufinNexusServiceInterface,
+ req: CreateTalerWireGatewayFacadeRequest,
+ ) {
+ const baseUrl = libeufinNexusService.baseUrl;
+ let url = new URL("facades", baseUrl);
+ await axios.post(
+ url.href,
+ {
+ name: req.name,
+ type: "taler-wire-gateway",
+ config: {
+ bankAccount: req.accountName,
+ bankConnection: req.connectionName,
+ currency: req.currency,
+ reserveTransferLevel: req.reserveTransferLevel,
+ },
+ },
+ {
+ auth: {
+ username: "admin",
+ password: "test",
+ },
+ },
+ );
+ }
+
+ export async function submitAllPaymentInitiations(
+ libeufinNexusService: LibeufinNexusServiceInterface,
+ accountId: string,
+ ) {
+ const baseUrl = libeufinNexusService.baseUrl;
+ let url = new URL(
+ `/bank-accounts/${accountId}/submit-all-payment-initiations`,
+ baseUrl,
+ );
+ await axios.post(
+ url.href,
+ {},
+ {
+ auth: {
+ username: "admin",
+ password: "test",
+ },
+ },
+ );
+ }
+}
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
new file mode 100644
index 000000000..39980dac9
--- /dev/null
+++ b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
@@ -0,0 +1,293 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import { CoreApiResponse } from "taler-wallet-core";
+import { CoinConfig, defaultCoinConfig } from "./denomStructures";
+import {
+ BankService,
+ DbInfo,
+ delayMs,
+ ExchangeBankAccount,
+ ExchangeService,
+ GlobalTestState,
+ MerchantService,
+ setupDb,
+ WalletCli,
+} from "./harness";
+import { makeTestPayment } from "./helpers";
+import {
+ LibeufinNexusApi,
+ LibeufinNexusService,
+ LibeufinSandboxApi,
+ LibeufinSandboxService,
+} from "./libeufin";
+
+const exchangeIban = "DE71500105179674997361";
+const customerIban = "DE84500105176881385584";
+const customerBic = "BELADEBEXXX";
+const merchantIban = "DE42500105171245624648";
+
+export interface LibeufinTestEnvironment {
+ commonDb: DbInfo;
+ exchange: ExchangeService;
+ exchangeBankAccount: ExchangeBankAccount;
+ merchant: MerchantService;
+ wallet: WalletCli;
+ libeufinSandbox: LibeufinSandboxService;
+ libeufinNexus: LibeufinNexusService;
+}
+
+/**
+ * Create a Taler environment with LibEuFin and an EBICS account.
+ */
+export async function createLibeufinTestEnvironment(
+ t: GlobalTestState,
+ coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("EUR")),
+): Promise<LibeufinTestEnvironment> {
+ const db = await setupDb(t);
+
+ const libeufinSandbox = await LibeufinSandboxService.create(t, {
+ httpPort: 5010,
+ databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`,
+ });
+
+ await libeufinSandbox.start();
+ await libeufinSandbox.pingUntilAvailable();
+
+ const libeufinNexus = await LibeufinNexusService.create(t, {
+ httpPort: 5011,
+ databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
+ });
+
+ await libeufinNexus.start();
+ await libeufinNexus.pingUntilAvailable();
+
+ await LibeufinSandboxApi.createEbicsHost(libeufinSandbox, "host01");
+ // Subscriber and bank Account for the exchange
+ await LibeufinSandboxApi.createEbicsSubscriber(libeufinSandbox, {
+ hostID: "host01",
+ partnerID: "partner01",
+ userID: "user01",
+ });
+ await LibeufinSandboxApi.createEbicsBankAccount(libeufinSandbox, {
+ bic: "DEUTDEBB101",
+ iban: exchangeIban,
+ label: "exchangeacct",
+ name: "Taler Exchange",
+ subscriber: {
+ hostID: "host01",
+ partnerID: "partner01",
+ userID: "user01",
+ },
+ currency: "EUR",
+ });
+ // Subscriber and bank Account for the merchant
+ // (Merchant doesn't need EBICS access, but sandbox right now only supports EBICS
+ // accounts.)
+ await LibeufinSandboxApi.createEbicsSubscriber(libeufinSandbox, {
+ hostID: "host01",
+ partnerID: "partner02",
+ userID: "user02",
+ });
+ await LibeufinSandboxApi.createEbicsBankAccount(libeufinSandbox, {
+ bic: "COBADEFXXX",
+ iban: merchantIban,
+ label: "merchantacct",
+ name: "Merchant",
+ subscriber: {
+ hostID: "host01",
+ partnerID: "partner02",
+ userID: "user02",
+ },
+ currency: "EUR",
+ });
+
+ await LibeufinNexusApi.createEbicsBankConnection(libeufinNexus, {
+ name: "myconn",
+ ebicsURL: "http://localhost:5010/ebicsweb",
+ hostID: "host01",
+ partnerID: "partner01",
+ userID: "user01",
+ });
+ await LibeufinNexusApi.connectBankConnection(libeufinNexus, "myconn");
+ await LibeufinNexusApi.fetchAccounts(libeufinNexus, "myconn");
+ await LibeufinNexusApi.importConnectionAccount(
+ libeufinNexus,
+ "myconn",
+ "exchangeacct",
+ "myacct",
+ );
+
+ await LibeufinNexusApi.createTwgFacade(libeufinNexus, {
+ name: "twg1",
+ accountName: "myacct",
+ connectionName: "myconn",
+ currency: "EUR",
+ reserveTransferLevel: "report",
+ });
+
+ const exchange = ExchangeService.create(t, {
+ name: "testexchange-1",
+ currency: "EUR",
+ httpPort: 8081,
+ database: db.connStr,
+ });
+
+ const merchant = await MerchantService.create(t, {
+ name: "testmerchant-1",
+ currency: "EUR",
+ httpPort: 8083,
+ database: db.connStr,
+ });
+
+ const exchangeBankAccount: ExchangeBankAccount = {
+ accountName: "twg-user",
+ accountPassword: "123",
+ accountPaytoUri: `payto://iban/${exchangeIban}?receiver-name=Exchange`,
+ wireGatewayApiBaseUrl:
+ "http://localhost:5011/facades/twg1/taler-wire-gateway/",
+ };
+
+ exchange.addBankAccount("1", exchangeBankAccount);
+
+ exchange.addCoinConfigList(coinConfig);
+
+ await exchange.start();
+ await exchange.pingUntilAvailable();
+
+ merchant.addExchange(exchange);
+
+ await merchant.start();
+ await merchant.pingUntilAvailable();
+
+ await merchant.addInstance({
+ id: "default",
+ name: "Default Instance",
+ paytoUris: [`payto://iban/${merchantIban}?receiver-name=Merchant`],
+ defaultWireTransferDelay: { d_ms: 0 },
+ });
+
+ console.log("setup done!");
+
+ const wallet = new WalletCli(t);
+
+ return {
+ commonDb: db,
+ exchange,
+ merchant,
+ wallet,
+ exchangeBankAccount,
+ libeufinNexus,
+ libeufinSandbox,
+ };
+}
+
+/**
+ * Run basic test with LibEuFin.
+ */
+export async function runLibeufinBasicTest(t: GlobalTestState) {
+ // Set up test environment
+
+ const {
+ wallet,
+ exchange,
+ merchant,
+ libeufinSandbox,
+ libeufinNexus,
+ } = await createLibeufinTestEnvironment(t);
+
+ let wresp: CoreApiResponse;
+
+ // FIXME: add nicer api in the harness wallet for this.
+ wresp = await wallet.apiRequest("addExchange", {
+ exchangeBaseUrl: exchange.baseUrl,
+ });
+
+ t.assertTrue(wresp.type === "response");
+
+ // FIXME: add nicer api in the harness wallet for this.
+ wresp = await wallet.apiRequest("acceptManualWithdrawal", {
+ exchangeBaseUrl: exchange.baseUrl,
+ amount: "EUR:10",
+ });
+
+ t.assertTrue(wresp.type === "response");
+
+ const reservePub: string = (wresp.result as any).reservePub;
+
+ await LibeufinSandboxApi.simulateIncomingTransaction(
+ libeufinSandbox,
+ "exchangeacct",
+ {
+ amount: "15.00",
+ currency: "EUR",
+ debtorBic: customerBic,
+ debtorIban: customerIban,
+ debtorName: "Jane Customer",
+ subject: `Taler Top-up ${reservePub}`,
+ },
+ );
+
+ await LibeufinNexusApi.fetchAllTransactions(libeufinNexus, "myacct");
+
+ await exchange.runWirewatchOnce();
+
+ await wallet.runUntilDone();
+
+ const bal = await wallet.getBalances();
+ console.log("balances", JSON.stringify(bal, undefined, 2));
+ t.assertAmountEquals(bal.balances[0].available, "EUR:14.7");
+
+ const order = {
+ summary: "Buy me!",
+ amount: "EUR:5",
+ fulfillment_url: "taler://fulfillment-success/thx",
+ };
+
+ await makeTestPayment(t, { wallet, merchant, order });
+
+ await exchange.runAggregatorOnce();
+ await exchange.runTransferOnce();
+
+ await LibeufinNexusApi.submitAllPaymentInitiations(libeufinNexus, "myacct");
+
+ const exchangeTransactions = await LibeufinSandboxApi.getAccountTransactions(
+ libeufinSandbox,
+ "exchangeacct",
+ );
+
+ console.log(
+ "exchange transactions:",
+ JSON.stringify(exchangeTransactions, undefined, 2),
+ );
+
+ t.assertDeepEqual(
+ exchangeTransactions.payments[0].creditDebitIndicator,
+ "credit",
+ );
+ t.assertDeepEqual(
+ exchangeTransactions.payments[1].creditDebitIndicator,
+ "debit",
+ );
+ t.assertDeepEqual(exchangeTransactions.payments[1].debtorIban, exchangeIban);
+ t.assertDeepEqual(
+ exchangeTransactions.payments[1].creditorIban,
+ merchantIban,
+ );
+}
diff --git a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
index 2acec0627..04e803b74 100644
--- a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
@@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { GlobalTestState, runTestWithState, TestRunResult } from "./harness";
+import { GlobalTestState, runTestWithState, shouldLingerInTest, TestRunResult } from "./harness";
import { runPaymentTest } from "./test-payment";
import * as fs from "fs";
import * as path from "path";
@@ -48,6 +48,7 @@ import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank";
import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated";
import M from "minimatch";
import { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion";
+import { runLibeufinBasicTest } from "./test-libeufin-basic";
/**
* Test runner.
@@ -65,6 +66,8 @@ const allTests: TestMainFunction[] = [
runClaimLoopTest,
runExchangeManagementTest,
runFeeRegressionTest,
+ runLibeufinBasicTest,
+ runMerchantExchangeConfusionTest,
runMerchantLongpollingTest,
runMerchantRefundApiTest,
runPayAbortTest,
@@ -81,14 +84,13 @@ const allTests: TestMainFunction[] = [
runRefundIncrementalTest,
runRefundTest,
runRevocationTest,
+ runTestWithdrawalManualTest,
runTimetravelAutorefreshTest,
runTimetravelWithdrawTest,
runTippingTest,
runWallettestingTest,
- runTestWithdrawalManualTest,
runWithdrawalAbortBankTest,
runWithdrawalBankIntegratedTest,
- runMerchantExchangeConfusionTest,
];
export interface TestRunSpec {
@@ -301,6 +303,10 @@ if (runTestInstrStr && process.argv.includes("__TWCLI_TESTWORKER")) {
runTest()
.then(() => {
console.log(`test ${testName} finished in worker`);
+ if (shouldLingerInTest()) {
+ console.log("lingering ...");
+ return;
+ }
process.exit(0);
})
.catch((e) => {