summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-09-24 21:03:22 +0200
committerFlorian Dold <florian@dold.me>2023-09-24 21:03:22 +0200
commit7b93938e710c8673ae9a0381b8867705ae872d6f (patch)
tree8aca0b1e5208497dc20e73355a192e9ad22b0baf
parentbdd906c88707b2ec8d6d1d4afbd0bba8e3a9a3cd (diff)
downloadwallet-core-7b93938e710c8673ae9a0381b8867705ae872d6f.tar.gz
wallet-core-7b93938e710c8673ae9a0381b8867705ae872d6f.tar.bz2
wallet-core-7b93938e710c8673ae9a0381b8867705ae872d6f.zip
harness: add libeufin-bank integration testv0.9.3-dev.23
-rw-r--r--packages/taler-harness/src/env-full.ts6
-rw-r--r--packages/taler-harness/src/harness/harness.ts166
-rw-r--r--packages/taler-harness/src/harness/helpers.ts14
-rw-r--r--packages/taler-harness/src/integrationtests/test-bank-api.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-deposit.ts4
-rw-r--r--packages/taler-harness/src/integrationtests/test-exchange-management.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-fee-regression.ts4
-rw-r--r--packages/taler-harness/src/integrationtests/test-kyc.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-libeufin-bank.ts222
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-instances.ts8
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-fault.ts4
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-multiple.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-revocation.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-notifications.ts4
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallettesting.ts4
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts10
-rw-r--r--packages/taler-harness/src/integrationtests/testrunner.ts2
-rw-r--r--packages/taler-util/src/bank-api-client.ts41
-rw-r--r--packages/taler-util/src/talerconfig.ts6
-rw-r--r--packages/taler-util/src/wallet-types.ts13
-rw-r--r--packages/taler-wallet-core/src/operations/testing.ts45
-rw-r--r--packages/taler-wallet-core/src/wallet-api-types.ts12
-rw-r--r--packages/taler-wallet-core/src/wallet.ts7
28 files changed, 552 insertions, 80 deletions
diff --git a/packages/taler-harness/src/env-full.ts b/packages/taler-harness/src/env-full.ts
index 210d38e32..bb2cb8c47 100644
--- a/packages/taler-harness/src/env-full.ts
+++ b/packages/taler-harness/src/env-full.ts
@@ -25,7 +25,7 @@ import {
ExchangeService,
FakebankService,
MerchantService,
- getPayto,
+ generateRandomPayto,
} from "./harness/harness.js";
/**
@@ -82,7 +82,7 @@ export async function runEnvFull(t: GlobalTestState): Promise<void> {
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
@@ -91,7 +91,7 @@ export async function runEnvFull(t: GlobalTestState): Promise<void> {
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts
index edb0071c8..8f2d40d6e 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -565,7 +565,7 @@ class BankServiceBase {
protected globalTestState: GlobalTestState,
protected bankConfig: BankConfig,
protected configFile: string,
- ) { }
+ ) {}
}
export interface HarnessExchangeBankAccount {
@@ -580,7 +580,8 @@ export interface HarnessExchangeBankAccount {
*/
export class FakebankService
extends BankServiceBase
- implements BankServiceHandle {
+ implements BankServiceHandle
+{
proc: ProcessWrapper | undefined;
http = createPlatformHttpLib({ enableThrottling: false });
@@ -664,7 +665,7 @@ export class FakebankService
return {
accountName: accountName,
accountPassword: password,
- accountPaytoUri: getPayto(accountName),
+ accountPaytoUri: generateRandomPayto(accountName),
wireGatewayApiBaseUrl: `http://localhost:${this.bankConfig.httpPort}/accounts/${accountName}/taler-wire-gateway/`,
};
}
@@ -702,6 +703,140 @@ export class FakebankService
}
}
+/**
+ * Implementation of the bank service using the "taler-fakebank-run" tool.
+ */
+export class LibeufinBankService
+ extends BankServiceBase
+ implements BankServiceHandle
+{
+ proc: ProcessWrapper | undefined;
+
+ http = createPlatformHttpLib({ enableThrottling: false });
+
+ // We store "created" accounts during setup and
+ // register them after startup.
+ private accounts: {
+ accountName: string;
+ accountPassword: string;
+ }[] = [];
+
+ /**
+ * Create a new fakebank service handle.
+ *
+ * First generates the configuration for the fakebank and
+ * then creates a fakebank handle, but doesn't start the fakebank
+ * service yet.
+ */
+ static async create(
+ gc: GlobalTestState,
+ bc: BankConfig,
+ ): Promise<LibeufinBankService> {
+ const config = new Configuration();
+ const testDir = bc.overrideTestDir ?? gc.testDir;
+ setTalerPaths(config, testDir + "/talerhome");
+ config.setString("libeufin-bankdb", "config", bc.database);
+ config.setString("libeufin-bank", "currency", bc.currency);
+ config.setString("libeufin-bank", "port", `${bc.httpPort}`);
+ config.setString("libeufin-bank", "serve", "tcp");
+ config.setString(
+ "libeufin-bank",
+ "DEFAULT_CUSTOMER_DEBT_LIMIT",
+ `${bc.currency}:500`,
+ );
+ config.setString(
+ "libeufin-bank",
+ "DEFAULT_ADMIN_DEBT_LIMIT",
+ `${bc.currency}:999999`,
+ );
+ config.setString(
+ "libeufin-bank",
+ "registration_bonus",
+ `${bc.currency}:100`,
+ );
+ config.setString("libeufin-bank", "registration_bonus_enabled", `yes`);
+ config.setString("libeufin-bank", "max_auth_token_duration", "1h");
+ const cfgFilename = testDir + "/bank.conf";
+ config.write(cfgFilename, { excludeDefaults: true });
+
+ return new LibeufinBankService(gc, bc, cfgFilename);
+ }
+
+ static fromExistingConfig(
+ gc: GlobalTestState,
+ opts: { overridePath?: string },
+ ): FakebankService {
+ const testDir = opts.overridePath ?? gc.testDir;
+ const cfgFilename = testDir + `/bank.conf`;
+ const config = Configuration.load(cfgFilename);
+ const bc: BankConfig = {
+ allowRegistrations:
+ config.getYesNo("libeufin-bank", "allow_registrations").orUndefined() ??
+ true,
+ currency: config.getString("libeufin-bank", "currency").required(),
+ database: config
+ .getString("libeufin-bankdb", "config")
+ .required(),
+ httpPort: config.getNumber("libeufin-bank", "port").required(),
+ maxDebt: config
+ .getString("libeufin-bank", "DEFAULT_CUSTOMER_DEBT_LIMIT")
+ .required(),
+ };
+ return new FakebankService(gc, bc, cfgFilename);
+ }
+
+ setSuggestedExchange(e: ExchangeServiceInterface) {
+ if (!!this.proc) {
+ throw Error("Can't set suggested exchange while bank is running.");
+ }
+ const config = Configuration.load(this.configFile);
+ config.setString("libeufin-bank", "suggested_withdrawal_exchange", e.baseUrl);
+ config.write(this.configFile, { excludeDefaults: true });
+ }
+
+ get baseUrl(): string {
+ return `http://localhost:${this.bankConfig.httpPort}/`;
+ }
+
+ get bankAccessApiBaseUrl(): string {
+ return this.baseUrl;
+ }
+
+ get port() {
+ return this.bankConfig.httpPort;
+ }
+
+ async start(): Promise<void> {
+ logger.info("starting libeufin-bank");
+ if (this.proc) {
+ logger.info("libeufin-bank already running, not starting again");
+ return;
+ }
+
+ await sh(
+ this.globalTestState,
+ "libeufin-bank-dbinit",
+ `libeufin-bank dbinit -r -c "${this.configFile}"`,
+ );
+
+ this.proc = this.globalTestState.spawnService(
+ "libeufin-bank",
+ ["serve", "-c", this.configFile],
+ "libeufin-bank-httpd",
+ );
+ await this.pingUntilAvailable();
+ const bankClient = new TalerCorebankApiClient(this.bankAccessApiBaseUrl);
+ for (const acc of this.accounts) {
+ await bankClient.registerAccount(acc.accountName, acc.accountPassword);
+ }
+ }
+
+ async pingUntilAvailable(): Promise<void> {
+ const url = `http://localhost:${this.bankConfig.httpPort}/config`;
+ await pingProc(this.proc, url, "libeufin-bank");
+ }
+}
+
// Use libeufin bank instead of pybank.
const useLibeufinBank = false;
@@ -1011,7 +1146,7 @@ export class ExchangeService implements ExchangeServiceInterface {
private exchangeConfig: ExchangeConfig,
private configFilename: string,
private keyPair: EddsaKeyPair,
- ) { }
+ ) {}
get name() {
return this.exchangeConfig.name;
@@ -1367,7 +1502,7 @@ export class MerchantService implements MerchantServiceInterface {
private globalState: GlobalTestState,
private merchantConfig: MerchantConfig,
private configFilename: string,
- ) { }
+ ) {}
private currentTimetravelOffsetMs: number | undefined;
@@ -1495,7 +1630,7 @@ export class MerchantService implements MerchantServiceInterface {
return await this.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
auth: {
method: "external",
},
@@ -1658,6 +1793,7 @@ export async function runTestWithState(
e.message,
`error detail: ${j2s(e.errorDetail)}`,
);
+ console.error(e.stack);
} else {
console.error("FATAL: test failed with exception", e);
}
@@ -1705,7 +1841,7 @@ export class WalletService {
constructor(
private globalState: GlobalTestState,
private opts: WalletServiceOptions,
- ) { }
+ ) {}
get socketPath() {
const unixPath = path.join(
@@ -1814,7 +1950,7 @@ export class WalletClient {
return client.call(operation, payload);
}
- constructor(private args: WalletClientArgs) { }
+ constructor(private args: WalletClientArgs) {}
async connect(): Promise<void> {
const waiter = this.waiter;
@@ -1881,9 +2017,11 @@ export class WalletCli {
? `--crypto-worker=${cliOpts.cryptoWorkerType}`
: "";
const logName = `wallet-${self.name}`;
- const command = `taler-wallet-cli ${self.timetravelArg ?? ""
- } ${cryptoWorkerArg} --no-throttle -LTRACE --skip-defaults --wallet-db '${self.dbfile
- }' api '${op}' ${shellWrap(JSON.stringify(payload))}`;
+ const command = `taler-wallet-cli ${
+ self.timetravelArg ?? ""
+ } ${cryptoWorkerArg} --no-throttle -LTRACE --skip-defaults --wallet-db '${
+ self.dbfile
+ }' api '${op}' ${shellWrap(JSON.stringify(payload))}`;
const resp = await sh(self.globalTestState, logName, command);
logger.info("--- wallet core response ---");
logger.info(resp);
@@ -1966,7 +2104,7 @@ export class WalletCli {
}
}
-export function getRandomIban(salt: string | null = null): string {
+export function generateRandomTestIban(salt: string | null = null): string {
function getBban(salt: string | null): string {
if (!salt) return Math.random().toString().substring(2, 6);
let hashed = hash(stringToBytes(salt));
@@ -1998,9 +2136,9 @@ export function getWireMethodForTest(): string {
* Generate a payto address, whose authority depends
* on whether the banking is served by euFin or Pybank.
*/
-export function getPayto(label: string): string {
+export function generateRandomPayto(label: string): string {
if (useLibeufinBank)
- return `payto://iban/SANDBOXX/${getRandomIban(
+ return `payto://iban/SANDBOXX/${generateRandomTestIban(
label,
)}?receiver-name=${label}`;
return `payto://x-taler-bank/localhost/${label}`;
diff --git a/packages/taler-harness/src/harness/helpers.ts b/packages/taler-harness/src/harness/helpers.ts
index 27980857c..68b7d087c 100644
--- a/packages/taler-harness/src/harness/helpers.ts
+++ b/packages/taler-harness/src/harness/helpers.ts
@@ -56,7 +56,7 @@ import {
WalletClient,
WalletService,
WithAuthorization,
- getPayto,
+ generateRandomPayto,
setupDb,
setupSharedDb,
} from "./harness.js";
@@ -236,7 +236,7 @@ export async function useSharedTestkudosEnvironment(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
@@ -245,7 +245,7 @@ export async function useSharedTestkudosEnvironment(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
@@ -368,7 +368,7 @@ export async function createSimpleTestkudosEnvironmentV2(
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
@@ -377,7 +377,7 @@ export async function createSimpleTestkudosEnvironmentV2(
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
@@ -512,13 +512,13 @@ export async function createFaultInjectedMerchantTestkudosEnvironment(
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-bank-api.ts b/packages/taler-harness/src/integrationtests/test-bank-api.ts
index 740e89c30..b87a4043b 100644
--- a/packages/taler-harness/src/integrationtests/test-bank-api.ts
+++ b/packages/taler-harness/src/integrationtests/test-bank-api.ts
@@ -30,7 +30,7 @@ import {
ExchangeService,
GlobalTestState,
MerchantService,
- getPayto,
+ generateRandomPayto,
setupDb,
} from "../harness/harness.js";
@@ -88,13 +88,13 @@ export async function runBankApiTest(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-deposit.ts b/packages/taler-harness/src/integrationtests/test-deposit.ts
index 7e1bb2a5c..d4bfa3da5 100644
--- a/packages/taler-harness/src/integrationtests/test-deposit.ts
+++ b/packages/taler-harness/src/integrationtests/test-deposit.ts
@@ -23,7 +23,7 @@ import {
TransactionMinorState,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, getPayto } from "../harness/harness.js";
+import { GlobalTestState, generateRandomPayto } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
@@ -75,7 +75,7 @@ export async function runDepositTest(t: GlobalTestState) {
WalletApiOperation.CreateDepositGroup,
{
amount: "TESTKUDOS:10",
- depositPaytoUri: getPayto("foo"),
+ depositPaytoUri: generateRandomPayto("foo"),
transactionId: depositTxId,
},
);
diff --git a/packages/taler-harness/src/integrationtests/test-exchange-management.ts b/packages/taler-harness/src/integrationtests/test-exchange-management.ts
index 329012e42..fbee50385 100644
--- a/packages/taler-harness/src/integrationtests/test-exchange-management.ts
+++ b/packages/taler-harness/src/integrationtests/test-exchange-management.ts
@@ -36,7 +36,7 @@ import {
GlobalTestState,
MerchantService,
WalletCli,
- getPayto,
+ generateRandomPayto,
setupDb,
} from "../harness/harness.js";
@@ -105,13 +105,13 @@ export async function runExchangeManagementTest(
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts b/packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts
index 2ef7683b3..efa21e1a0 100644
--- a/packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts
+++ b/packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts
@@ -35,7 +35,7 @@ import { makeNoFeeCoinConfig } from "../harness/denomStructures.js";
import {
BankService,
ExchangeService,
- getPayto,
+ generateRandomPayto,
GlobalTestState,
MerchantService,
setupDb,
@@ -151,13 +151,13 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-fee-regression.ts b/packages/taler-harness/src/integrationtests/test-fee-regression.ts
index 2d84b3a7c..f164606c4 100644
--- a/packages/taler-harness/src/integrationtests/test-fee-regression.ts
+++ b/packages/taler-harness/src/integrationtests/test-fee-regression.ts
@@ -23,7 +23,7 @@ import {
ExchangeService,
GlobalTestState,
MerchantService,
- getPayto,
+ generateRandomPayto,
setupDb,
} from "../harness/harness.js";
import {
@@ -142,7 +142,7 @@ export async function createMyTestkudosEnvironment(
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-kyc.ts b/packages/taler-harness/src/integrationtests/test-kyc.ts
index 4fc725bc3..d646995d6 100644
--- a/packages/taler-harness/src/integrationtests/test-kyc.ts
+++ b/packages/taler-harness/src/integrationtests/test-kyc.ts
@@ -34,7 +34,7 @@ import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
import {
BankService,
ExchangeService,
- getPayto,
+ generateRandomPayto,
GlobalTestState,
MerchantService,
setupDb,
@@ -162,7 +162,7 @@ export async function createKycTestkudosEnvironment(
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
@@ -171,7 +171,7 @@ export async function createKycTestkudosEnvironment(
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
diff --git a/packages/taler-harness/src/integrationtests/test-libeufin-bank.ts b/packages/taler-harness/src/integrationtests/test-libeufin-bank.ts
new file mode 100644
index 000000000..66b4c0b80
--- /dev/null
+++ b/packages/taler-harness/src/integrationtests/test-libeufin-bank.ts
@@ -0,0 +1,222 @@
+/*
+ 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 {
+ TalerCorebankApiClient,
+ CreditDebitIndicator,
+ WireGatewayApiClient,
+ createEddsaKeyPair,
+ encodeCrock,
+ Logger,
+ j2s,
+ NotificationType,
+ TransactionMajorState,
+ TransactionMinorState,
+} from "@gnu-taler/taler-util";
+import { defaultCoinConfig } from "../harness/denomStructures.js";
+import {
+ ExchangeService,
+ GlobalTestState,
+ LibeufinBankService,
+ MerchantService,
+ generateRandomPayto,
+ generateRandomTestIban,
+ setupDb,
+} from "../harness/harness.js";
+import { createWalletDaemonWithClient } from "../harness/helpers.js";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+
+const logger = new Logger("test-libeufin-bank.ts");
+
+/**
+ * Run test for the basic functionality of libeufin-bank.
+ */
+export async function runLibeufinBankTest(t: GlobalTestState) {
+ // Set up test environment
+
+ const db = await setupDb(t);
+
+ const bank = await LibeufinBankService.create(t, {
+ currency: "TESTKUDOS",
+ httpPort: 8082,
+ database: db.connStr,
+ allowRegistrations: true,
+ });
+
+ const exchange = ExchangeService.create(t, {
+ name: "testexchange-1",
+ currency: "TESTKUDOS",
+ httpPort: 8081,
+ database: db.connStr,
+ });
+
+ const merchant = await MerchantService.create(t, {
+ name: "testmerchant-1",
+ currency: "TESTKUDOS",
+ httpPort: 8083,
+ database: db.connStr,
+ });
+
+ const exchangeIban = generateRandomTestIban();
+ const exchangeBankUsername = "exchange";
+ const exchangeBankPw = "mypw";
+ const exchangePlainPayto = `payto://iban/${exchangeIban}`;
+ const exchangeExtendedPayto = `payto://iban/${exchangeIban}?receiver-name=Exchange`;
+ const wireGatewayApiBaseUrl = new URL(
+ "accounts/exchange/taler-wire-gateway/",
+ bank.baseUrl,
+ ).href;
+
+ logger.info("creating bank account for the exchange");
+
+ exchange.addBankAccount("1", {
+ wireGatewayApiBaseUrl,
+ accountName: exchangeBankUsername,
+ accountPassword: exchangeBankPw,
+ accountPaytoUri: exchangeExtendedPayto,
+ });
+
+ bank.setSuggestedExchange(exchange);
+
+ await bank.start();
+
+ await bank.pingUntilAvailable();
+
+ exchange.addOfferedCoins(defaultCoinConfig);
+
+ await exchange.start();
+ await exchange.pingUntilAvailable();
+
+ merchant.addExchange(exchange);
+
+ await merchant.start();
+ await merchant.pingUntilAvailable();
+
+ await merchant.addInstanceWithWireAccount({
+ id: "default",
+ name: "Default Instance",
+ paytoUris: [generateRandomPayto("merchant-default")],
+ });
+
+ await merchant.addInstanceWithWireAccount({
+ id: "minst1",
+ name: "minst1",
+ paytoUris: [generateRandomPayto("minst1")],
+ });
+
+ const { walletClient } = await createWalletDaemonWithClient(t, {
+ name: "wallet",
+ });
+
+ console.log("setup done!");
+
+ const bankClient = new TalerCorebankApiClient(bank.bankAccessApiBaseUrl);
+
+ // register exchange bank account
+ await bankClient.registerAccountExtended({
+ name: "Exchange",
+ password: exchangeBankPw,
+ username: exchangeBankUsername,
+ is_taler_exchange: true,
+ internal_payto_uri: exchangePlainPayto,
+ });
+
+ const bankUser = await bankClient.registerAccount("user1", "pw1");
+ bankClient.setAuth({
+ username: "user1",
+ password: "pw1",
+ });
+
+ // Make sure that registering twice results in a 409 Conflict
+ // {
+ // const e = await t.assertThrowsTalerErrorAsync(async () => {
+ // await bankClient.registerAccount("user1", "pw2");
+ // });
+ // t.assertTrue(e.errorDetail.httpStatusCode === 409);
+ // }
+
+ let balResp = await bankClient.getAccountBalance(bankUser.username);
+
+ console.log(balResp);
+
+ // Check that we got the sign-up bonus.
+ t.assertAmountEquals(balResp.balance.amount, "TESTKUDOS:100");
+ t.assertTrue(
+ balResp.balance.credit_debit_indicator === CreditDebitIndicator.Credit,
+ );
+
+ const res = createEddsaKeyPair();
+
+ const wireGatewayApiClient = new WireGatewayApiClient(wireGatewayApiBaseUrl, {
+ auth: {
+ username: exchangeBankUsername,
+ password: exchangeBankPw,
+ },
+ });
+
+ await wireGatewayApiClient.adminAddIncoming({
+ amount: "TESTKUDOS:115",
+ debitAccountPayto: bankUser.accountPaytoUri,
+ reservePub: encodeCrock(res.eddsaPub),
+ });
+
+ balResp = await bankClient.getAccountBalance(bankUser.username);
+ t.assertAmountEquals(balResp.balance.amount, "TESTKUDOS:15");
+ t.assertTrue(
+ balResp.balance.credit_debit_indicator === CreditDebitIndicator.Debit,
+ );
+
+ const wop = await bankClient.createWithdrawalOperation(
+ bankUser.username,
+ "TESTKUDOS:10",
+ );
+
+ const r1 = await walletClient.client.call(
+ WalletApiOperation.GetWithdrawalDetailsForUri,
+ {
+ talerWithdrawUri: wop.taler_withdraw_uri,
+ },
+ );
+
+ console.log(j2s(r1));
+
+ const r2 = await walletClient.client.call(
+ WalletApiOperation.AcceptBankIntegratedWithdrawal,
+ {
+ exchangeBaseUrl: exchange.baseUrl,
+ talerWithdrawUri: wop.taler_withdraw_uri,
+ },
+ );
+
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: r2.transactionId,
+ txState: {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.BankConfirmTransfer,
+ },
+ });
+
+ await bankClient.confirmWithdrawalOperation(bankUser.username, {
+ withdrawalOperationId: wop.withdrawal_id,
+ });
+
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
+}
+
+runLibeufinBankTest.suites = ["fakebank"];
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts b/packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts
index 2f79041d6..35e3267b1 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts
@@ -33,7 +33,7 @@ import {
import {
BankService,
ExchangeService,
- getPayto,
+ generateRandomPayto,
GlobalTestState,
harnessHttpLib,
MerchantService,
@@ -112,13 +112,13 @@ export async function createConfusedMerchantTestkudosEnvironment(
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts b/packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts
index ff567d33d..4508b9976 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts
@@ -22,7 +22,7 @@ import {
ExchangeService,
GlobalTestState,
MerchantService,
- getPayto,
+ generateRandomPayto,
harnessHttpLib,
setupDb,
} from "../harness/harness.js";
@@ -78,7 +78,7 @@ export async function runMerchantInstancesDeleteTest(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
auth: {
method: "external",
},
@@ -88,7 +88,7 @@ export async function runMerchantInstancesDeleteTest(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "myinst",
name: "Second Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
auth: {
method: "external",
},
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts b/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts
index 071288b0f..a037a01c5 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts
@@ -22,7 +22,7 @@ import {
ExchangeService,
GlobalTestState,
MerchantService,
- getPayto,
+ generateRandomPayto,
harnessHttpLib,
setupDb,
} from "../harness/harness.js";
@@ -74,7 +74,7 @@ export async function runMerchantInstancesUrlsTest(t: GlobalTestState) {
name: "My Default Instance",
accounts: [
{
- payto_uri: getPayto("bar"),
+ payto_uri: generateRandomPayto("bar"),
},
],
auth: {
@@ -97,7 +97,7 @@ export async function runMerchantInstancesUrlsTest(t: GlobalTestState) {
name: "My Second Instance",
accounts: [
{
- payto_uri: getPayto("bar"),
+ payto_uri: generateRandomPayto("bar"),
},
],
auth: {
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-instances.ts b/packages/taler-harness/src/integrationtests/test-merchant-instances.ts
index 27de8a0a0..a77e9ca51 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-instances.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-instances.ts
@@ -23,7 +23,7 @@ import {
GlobalTestState,
MerchantService,
setupDb,
- getPayto,
+ generateRandomPayto,
harnessHttpLib,
} from "../harness/harness.js";
@@ -78,7 +78,7 @@ export async function runMerchantInstancesTest(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
auth: {
method: "external",
},
@@ -88,7 +88,7 @@ export async function runMerchantInstancesTest(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
auth: {
method: "external",
},
@@ -98,7 +98,7 @@ export async function runMerchantInstancesTest(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "myinst",
name: "Second Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
auth: {
method: "external",
},
diff --git a/packages/taler-harness/src/integrationtests/test-payment-fault.ts b/packages/taler-harness/src/integrationtests/test-payment-fault.ts
index 8076e2fb4..63244a4e3 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-fault.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-fault.ts
@@ -39,7 +39,7 @@ import {
GlobalTestState,
MerchantService,
WalletCli,
- getPayto,
+ generateRandomPayto,
setupDb,
} from "../harness/harness.js";
@@ -116,7 +116,7 @@ export async function runPaymentFaultTest(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
diff --git a/packages/taler-harness/src/integrationtests/test-payment-multiple.ts b/packages/taler-harness/src/integrationtests/test-payment-multiple.ts
index 4ef5e3bff..0caa3c3e7 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-multiple.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-multiple.ts
@@ -25,7 +25,7 @@ import {
ExchangeService,
GlobalTestState,
MerchantService,
- getPayto,
+ generateRandomPayto,
setupDb,
} from "../harness/harness.js";
import {
@@ -87,13 +87,13 @@ async function setupTest(t: GlobalTestState): Promise<{
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-revocation.ts b/packages/taler-harness/src/integrationtests/test-revocation.ts
index 0cb6987ad..9ed2d6206 100644
--- a/packages/taler-harness/src/integrationtests/test-revocation.ts
+++ b/packages/taler-harness/src/integrationtests/test-revocation.ts
@@ -27,7 +27,7 @@ import {
setupDb,
BankService,
delayMs,
- getPayto,
+ generateRandomPayto,
WalletClient,
} from "../harness/harness.js";
import {
@@ -125,13 +125,13 @@ async function createTestEnvironment(
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts b/packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts
index b94f7757c..449142809 100644
--- a/packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts
+++ b/packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts
@@ -32,7 +32,7 @@ import { makeNoFeeCoinConfig } from "../harness/denomStructures.js";
import {
BankService,
ExchangeService,
- getPayto,
+ generateRandomPayto,
GlobalTestState,
MerchantService,
setupDb,
@@ -97,13 +97,13 @@ export async function runTimetravelAutorefreshTest(t: GlobalTestState) {
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
- paytoUris: [getPayto("minst1")],
+ paytoUris: [generateRandomPayto("minst1")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts b/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts
index 2496f4887..0b5bc45ef 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts
@@ -32,7 +32,7 @@ import {
MerchantService,
WalletClient,
WalletService,
- getRandomIban,
+ generateRandomTestIban,
setupDb,
} from "../harness/harness.js";
@@ -94,7 +94,7 @@ export async function runWalletNotificationsTest(t: GlobalTestState) {
id: "default",
name: "Default Instance",
paytoUris: [
- `payto://iban/SANDBOXX/${getRandomIban(label)}?receiver-name=${label}`,
+ `payto://iban/SANDBOXX/${generateRandomTestIban(label)}?receiver-name=${label}`,
],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
diff --git a/packages/taler-harness/src/integrationtests/test-wallettesting.ts b/packages/taler-harness/src/integrationtests/test-wallettesting.ts
index 4fa870f1c..6d58ae1f2 100644
--- a/packages/taler-harness/src/integrationtests/test-wallettesting.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallettesting.ts
@@ -32,7 +32,7 @@ import {
MerchantService,
setupDb,
WalletCli,
- getPayto,
+ generateRandomPayto,
} from "../harness/harness.js";
import { SimpleTestEnvironment } from "../harness/helpers.js";
@@ -94,7 +94,7 @@ export async function createMyEnvironment(
await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
+ paytoUris: [generateRandomPayto("merchant-default")],
});
console.log("setup done!");
diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
index 8c8853f4a..817da5865 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
@@ -41,12 +41,12 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
// Create a withdrawal operation
- const bankAccessApiClient = new TalerCorebankApiClient(
+ const corebankApiClient = new TalerCorebankApiClient(
bank.bankAccessApiBaseUrl,
);
- const user = await bankAccessApiClient.createRandomBankUser();
- bankAccessApiClient.setAuth(user);
- const wop = await bankAccessApiClient.createWithdrawalOperation(
+ const user = await corebankApiClient.createRandomBankUser();
+ corebankApiClient.setAuth(user);
+ const wop = await corebankApiClient.createWithdrawalOperation(
user.username,
"TESTKUDOS:10",
);
@@ -129,7 +129,7 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
// Confirm it
- await bankAccessApiClient.confirmWithdrawalOperation(user.username, {
+ await corebankApiClient.confirmWithdrawalOperation(user.username, {
withdrawalOperationId: wop.withdrawal_id,
});
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
index 071871837..cf5691fe3 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -92,6 +92,7 @@ import { runWithdrawalFeesTest } from "./test-withdrawal-fees.js";
import { runWithdrawalHugeTest } from "./test-withdrawal-huge.js";
import { runWithdrawalManualTest } from "./test-withdrawal-manual.js";
import { runWalletGenDbTest } from "./test-wallet-gendb.js";
+import { runLibeufinBankTest } from "./test-libeufin-bank.js";
/**
* Test runner.
@@ -173,6 +174,7 @@ const allTests: TestMainFunction[] = [
runStoredBackupsTest,
runPaymentExpiredTest,
runWalletGenDbTest,
+ runLibeufinBankTest,
];
export interface TestRunSpec {
diff --git a/packages/taler-util/src/bank-api-client.ts b/packages/taler-util/src/bank-api-client.ts
index d42317f91..a8cd4b0da 100644
--- a/packages/taler-util/src/bank-api-client.ts
+++ b/packages/taler-util/src/bank-api-client.ts
@@ -264,7 +264,7 @@ export class TalerCorebankApiClient {
const resp = await this.httpLib.fetch(url.href, {
headers: this.makeAuthHeader(),
});
- return await resp.json();
+ return readSuccessResponseJsonOrThrow(resp, codecForAny());
}
async getTransactions(username: string): Promise<void> {
@@ -295,6 +295,30 @@ export class TalerCorebankApiClient {
return await readSuccessResponseJsonOrThrow(resp, codecForAny());
}
+ async registerAccountExtended(req: RegisterAccountRequest): Promise<void> {
+ const url = new URL("accounts", this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "POST",
+ body: req,
+ });
+
+ if (
+ resp.status !== 200 &&
+ resp.status !== 201 &&
+ resp.status !== 202 &&
+ resp.status !== 204
+ ) {
+ logger.error(`unexpected status ${resp.status} from POST ${url.href}`);
+ logger.error(`${j2s(await resp.json())}`);
+ throw TalerError.fromDetail(
+ TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
+ {
+ httpStatusCode: resp.status,
+ },
+ );
+ }
+ }
+
/**
* Register a new account and return information about it.
*
@@ -311,7 +335,13 @@ export class TalerCorebankApiClient {
name: username,
},
});
- if (resp.status !== 200 && resp.status !== 202 && resp.status !== 204) {
+ if (
+ resp.status !== 200 &&
+ resp.status !== 201 &&
+ resp.status !== 202 &&
+ resp.status !== 204
+ ) {
+ logger.error(`unexpected status ${resp.status} from POST ${url.href}`);
logger.error(`${j2s(await resp.json())}`);
throw TalerError.fromDetail(
TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
@@ -320,8 +350,13 @@ export class TalerCorebankApiClient {
},
);
}
+ // FIXME: Corebank should directly return this info!
const infoUrl = new URL(`accounts/${username}`, this.baseUrl);
- const infoResp = await this.httpLib.fetch(infoUrl.href);
+ const infoResp = await this.httpLib.fetch(infoUrl.href, {
+ headers: {
+ Authorization: makeBasicAuthHeader(username, password),
+ },
+ });
// FIXME: Validate!
const acctInfo: AccountData = await readSuccessResponseJsonOrThrow(
infoResp,
diff --git a/packages/taler-util/src/talerconfig.ts b/packages/taler-util/src/talerconfig.ts
index e9eb71279..f817d9bcb 100644
--- a/packages/taler-util/src/talerconfig.ts
+++ b/packages/taler-util/src/talerconfig.ts
@@ -143,9 +143,9 @@ export function expandPath(path: string): string {
export function pathsub(
x: string,
lookup: (s: string, depth: number) => string | undefined,
- depth = 0,
+ recursionDepth = 0,
): string {
- if (depth >= 128) {
+ if (recursionDepth >= 128) {
throw Error("recursion in path substitution");
}
let s = x;
@@ -201,7 +201,7 @@ export function pathsub(
} else {
const m = /^[a-zA-Z-_][a-zA-Z0-9-_]*/.exec(s.substring(l + 1));
if (m && m[0]) {
- const r = lookup(m[0], depth + 1);
+ const r = lookup(m[0], recursionDepth + 1);
if (r !== undefined) {
s = s.substring(0, l) + r + s.substring(l + 1 + m[0].length);
l = l + r.length;
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts
index 87985fa2a..c5c2c375c 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -73,7 +73,13 @@ import {
codecForAbsoluteTime,
codecForTimestamp,
} from "./time.js";
-import { OrderShortInfo, TransactionType } from "./transactions-types.js";
+import {
+ OrderShortInfo,
+ TransactionMajorState,
+ TransactionMinorState,
+ TransactionState,
+ TransactionType,
+} from "./transactions-types.js";
/**
* Identifier for a transaction in the wallet.
@@ -2715,3 +2721,8 @@ export interface WalletContractData {
maxDepositFee: AmountString;
minimumAge?: number;
}
+
+export interface TestingWaitTransactionRequest {
+ transactionId: string;
+ txState: TransactionState;
+}
diff --git a/packages/taler-wallet-core/src/operations/testing.ts b/packages/taler-wallet-core/src/operations/testing.ts
index 9b5dd2a19..b21f1992c 100644
--- a/packages/taler-wallet-core/src/operations/testing.ts
+++ b/packages/taler-wallet-core/src/operations/testing.ts
@@ -23,6 +23,7 @@ import {
ConfirmPayResultType,
Duration,
IntegrationTestV2Args,
+ j2s,
Logger,
NotificationType,
RegisterAccountRequest,
@@ -31,6 +32,7 @@ import {
TestPayResult,
TransactionMajorState,
TransactionMinorState,
+ TransactionState,
TransactionType,
WithdrawTestBalanceRequest,
} from "@gnu-taler/taler-util";
@@ -494,6 +496,49 @@ async function waitUntilPendingReady(
cancelNotifs();
}
+/**
+ * Wait until a transaction is in a particular state.
+ */
+export async function waitTransactionState(
+ ws: InternalWalletState,
+ transactionId: string,
+ txState: TransactionState,
+): Promise<void> {
+ logger.info(
+ `starting waiting for ${transactionId} to be in ${JSON.stringify(
+ txState,
+ )})`,
+ );
+ ws.ensureTaskLoopRunning();
+ let p: OpenedPromise<void> | undefined = undefined;
+ const cancelNotifs = ws.addNotificationListener((notif) => {
+ if (!p) {
+ return;
+ }
+ if (notif.type === NotificationType.TransactionStateTransition) {
+ p.resolve();
+ }
+ });
+ while (1) {
+ p = openPromise();
+ const tx = await getTransactionById(ws, {
+ transactionId,
+ });
+ if (
+ tx.txState.major === txState.major &&
+ tx.txState.minor === txState.minor
+ ) {
+ break;
+ }
+ // Wait until transaction state changed
+ await p.promise;
+ }
+ logger.info(
+ `done waiting for ${transactionId} to be in ${JSON.stringify(txState)}`,
+ );
+ cancelNotifs();
+}
+
export async function runIntegrationTest2(
ws: InternalWalletState,
args: IntegrationTestV2Args,
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
index 3520a05cb..6d66e7ad3 100644
--- a/packages/taler-wallet-core/src/wallet-api-types.ts
+++ b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -104,6 +104,7 @@ import {
TestPayArgs,
TestPayResult,
TestingSetTimetravelRequest,
+ TestingWaitTransactionRequest,
Transaction,
TransactionByIdRequest,
TransactionsRequest,
@@ -214,6 +215,7 @@ export enum WalletApiOperation {
ValidateIban = "validateIban",
TestingWaitTransactionsFinal = "testingWaitTransactionsFinal",
TestingWaitRefreshesFinal = "testingWaitRefreshesFinal",
+ TestingWaitTransactionState = "testingWaitTransactionState",
TestingSetTimetravel = "testingSetTimetravel",
GetScopedCurrencyInfo = "getScopedCurrencyInfo",
ListStoredBackups = "listStoredBackups",
@@ -1005,6 +1007,15 @@ export type TestingWaitRefreshesFinal = {
};
/**
+ * Wait until a transaction is in a particular state.
+ */
+export type TestingWaitTransactionStateOp = {
+ op: WalletApiOperation.TestingWaitTransactionState;
+ request: TestingWaitTransactionRequest;
+ response: EmptyObject;
+};
+
+/**
* Set a coin as (un-)suspended.
* Suspended coins won't be used for payments.
*/
@@ -1108,6 +1119,7 @@ export type WalletOperations = {
[WalletApiOperation.TestingWaitTransactionsFinal]: TestingWaitTransactionsFinal;
[WalletApiOperation.TestingWaitRefreshesFinal]: TestingWaitRefreshesFinal;
[WalletApiOperation.TestingSetTimetravel]: TestingSetTimetravelOp;
+ [WalletApiOperation.TestingWaitTransactionState]: TestingWaitTransactionStateOp;
[WalletApiOperation.GetScopedCurrencyInfo]: GetScopedCurrencyInfoOp;
[WalletApiOperation.CreateStoredBackup]: CreateStoredBackupsOp;
[WalletApiOperation.ListStoredBackups]: ListStoredBackupsOp;
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 75f1a33a9..ccc7ec094 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -127,6 +127,7 @@ import {
codecForRecoverStoredBackupRequest,
codecForTestingSetTimetravelRequest,
setDangerousTimetravel,
+ TestingWaitTransactionRequest,
} from "@gnu-taler/taler-util";
import type { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
@@ -250,6 +251,7 @@ import {
runIntegrationTest,
runIntegrationTest2,
testPay,
+ waitTransactionState,
waitUntilDone,
waitUntilRefreshesDone,
withdrawTestBalance,
@@ -1414,6 +1416,11 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
const resp = await getBackupRecovery(ws);
return resp;
}
+ case WalletApiOperation.TestingWaitTransactionState: {
+ const req = payload as TestingWaitTransactionRequest;
+ await waitTransactionState(ws, req.transactionId, req.txState);
+ return {};
+ }
case WalletApiOperation.GetScopedCurrencyInfo: {
// Ignore result, just validate in this mock implementation
codecForGetCurrencyInfoRequest().decode(payload);