commit f4d83b7c66eca946af7fc6a94cee5e5f107e28d1
parent 592bbe8eda310b531f38105f666c8dabc341cad9
Author: Antoine A <>
Date: Thu, 10 Apr 2025 17:16:48 +0200
harness: fixme for #9683
Diffstat:
1 file changed, 133 insertions(+), 84 deletions(-)
diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
+ (C) 2020-2025 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
@@ -19,6 +19,8 @@
*/
import {
NotificationType,
+ TalerError,
+ TalerErrorCode,
TransactionMajorState,
TransactionMinorState,
TransactionType,
@@ -26,7 +28,7 @@ import {
j2s,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js";
+import { createSimpleTestkudosEnvironmentV3, createWalletDaemonWithClient } from "../harness/environments.js";
import { GlobalTestState } from "../harness/harness.js";
/**
@@ -34,102 +36,105 @@ import { GlobalTestState } from "../harness/harness.js";
*/
export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
// Set up test environment
-
- const { walletClient, bankClient, exchange } =
- await createSimpleTestkudosEnvironmentV3(t);
-
- // Create a withdrawal operation
+ const [
+ { walletClient: wallet1, bankClient, exchange },
+ { walletClient: wallet2 },
+ ] = await Promise.all([
+ createSimpleTestkudosEnvironmentV3(t),
+ createWalletDaemonWithClient(t, {
+ name: "w2",
+ }),
+ createWalletDaemonWithClient(t, {
+ name: "w3",
+ }),
+ ]);
const user = await bankClient.createRandomBankUser();
bankClient.setAuth(user);
- const wop = await bankClient.createWithdrawalOperation(
+
+ // Create a withdrawal operation
+ const withdrawal = await bankClient.createWithdrawalOperation(
user.username,
"TESTKUDOS:10",
);
t.logStep("Hand it to the wallet");
- const r1 = await walletClient.client.call(
+ const withdraw_info = await wallet1.call(
WalletApiOperation.GetWithdrawalDetailsForUri,
{
- talerWithdrawUri: wop.taler_withdraw_uri,
+ talerWithdrawUri: withdrawal.taler_withdraw_uri,
},
);
t.logStep("Withdraw");
- const r2 = await walletClient.client.call(
+ const { transactionId } = await wallet1.call(
WalletApiOperation.AcceptBankIntegratedWithdrawal,
{
exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
+ talerWithdrawUri: withdrawal.taler_withdraw_uri,
},
);
t.logStep("wait confirmed");
- const withdrawalBankConfirmedCond = walletClient.waitForNotificationCond(
- (x) => {
- return (
- x.type === NotificationType.TransactionStateTransition &&
- x.transactionId === r2.transactionId &&
- x.newTxState.major === TransactionMajorState.Pending &&
- x.newTxState.minor === TransactionMinorState.ExchangeWaitReserve
- );
- },
+ const withdrawalBankConfirmedCond = wallet1.waitForNotificationCond(
+ (x) =>
+ x.type === NotificationType.TransactionStateTransition &&
+ x.transactionId === transactionId &&
+ x.newTxState.major === TransactionMajorState.Pending &&
+ x.newTxState.minor === TransactionMinorState.ExchangeWaitReserve,
);
t.logStep("wait finished");
- const withdrawalFinishedCond = walletClient.waitForNotificationCond((x) => {
- return (
+ const withdrawalFinishedCond = wallet1.waitForNotificationCond(
+ (x) =>
x.type === NotificationType.TransactionStateTransition &&
- x.transactionId === r2.transactionId &&
- x.newTxState.major === TransactionMajorState.Done
- );
- });
+ x.transactionId === transactionId &&
+ x.newTxState.major === TransactionMajorState.Done,
+ );
t.logStep("wait withdraw coins");
- const withdrawalReserveReadyCond = walletClient.waitForNotificationCond(
- (x) => {
- return (
- x.type === NotificationType.TransactionStateTransition &&
- x.transactionId === r2.transactionId &&
- x.newTxState.major === TransactionMajorState.Pending &&
- x.newTxState.minor === TransactionMinorState.WithdrawCoins
- );
- },
+ const withdrawalReserveReadyCond = wallet1.waitForNotificationCond(
+ (x) =>
+ x.type === NotificationType.TransactionStateTransition &&
+ x.transactionId === transactionId &&
+ x.newTxState.major === TransactionMajorState.Pending &&
+ x.newTxState.minor === TransactionMinorState.WithdrawCoins,
);
t.logStep("Do it twice to check idempotency");
- const r3 = await walletClient.client.call(
- WalletApiOperation.AcceptBankIntegratedWithdrawal,
- {
- exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- },
- );
+ {
+ const idempotent = await wallet1.call(
+ WalletApiOperation.AcceptBankIntegratedWithdrawal,
+ {
+ exchangeBaseUrl: exchange.baseUrl,
+ talerWithdrawUri: withdrawal.taler_withdraw_uri,
+ },
+ );
+ t.assertTrue(idempotent.transactionId === transactionId);
+ }
t.logStep("stop wirewatch");
await exchange.stopWirewatch();
t.logStep("Check status before withdrawal is confirmed by bank.");
{
- const txn = await walletClient.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
- console.log("transactions before confirmation:", j2s(txn));
- const tx0 = txn.transactions[0];
- t.assertTrue(tx0.type === TransactionType.Withdrawal);
+ const tx = await wallet1.call(WalletApiOperation.GetTransactionById, {
+ transactionId,
+ });
+ console.log("transaction before confirmation:", j2s(tx));
+ t.assertTrue(tx.type === TransactionType.Withdrawal);
t.assertTrue(
- tx0.withdrawalDetails.type === WithdrawalType.TalerBankIntegrationApi,
+ tx.withdrawalDetails.type === WithdrawalType.TalerBankIntegrationApi,
);
- t.assertTrue(tx0.withdrawalDetails.confirmed === false);
- t.assertTrue(tx0.withdrawalDetails.reserveIsReady === false);
+ t.assertTrue(tx.withdrawalDetails.confirmed === false);
+ t.assertTrue(tx.withdrawalDetails.reserveIsReady === false);
}
t.logStep("Confirm it");
- await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: r3.transactionId,
+ await wallet1.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId,
txState: {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.BankConfirmTransfer,
@@ -137,7 +142,7 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
});
await bankClient.confirmWithdrawalOperation(user.username, {
- withdrawalOperationId: wop.withdrawal_id,
+ withdrawalOperationId: withdrawal.withdrawal_id,
});
await withdrawalBankConfirmedCond;
@@ -146,18 +151,16 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
// but before funds are wired to the exchange.
t.logStep("Check status after withdrawal");
{
- const txn = await walletClient.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
- console.log("transactions after confirmation:", j2s(txn));
- const tx0 = txn.transactions[0];
- t.assertTrue(tx0.type === TransactionType.Withdrawal);
+ const tx = await wallet1.call(WalletApiOperation.GetTransactionById, {
+ transactionId,
+ });
+ console.log("transactions after confirmation:", j2s(tx));
+ t.assertTrue(tx.type === TransactionType.Withdrawal);
t.assertTrue(
- tx0.withdrawalDetails.type === WithdrawalType.TalerBankIntegrationApi,
+ tx.withdrawalDetails.type === WithdrawalType.TalerBankIntegrationApi,
);
- t.assertTrue(tx0.withdrawalDetails.confirmed === true);
- t.assertTrue(tx0.withdrawalDetails.reserveIsReady === false);
+ t.assertTrue(tx.withdrawalDetails.confirmed === true);
+ t.assertTrue(tx.withdrawalDetails.reserveIsReady === false);
}
t.logStep("start wirewatch");
@@ -168,34 +171,80 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
t.logStep("Check status after funds were wired.");
{
- const txn = await walletClient.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
- console.log("transactions after reserve ready:", j2s(txn));
- const tx0 = txn.transactions[0];
- t.assertTrue(tx0.type === TransactionType.Withdrawal);
+ const tx = await wallet1.call(WalletApiOperation.GetTransactionById, {
+ transactionId,
+ });
+ console.log("transactions after reserve ready:", j2s(tx));
+ t.assertTrue(tx.type === TransactionType.Withdrawal);
t.assertTrue(
- tx0.withdrawalDetails.type === WithdrawalType.TalerBankIntegrationApi,
+ tx.withdrawalDetails.type === WithdrawalType.TalerBankIntegrationApi,
);
- t.assertTrue(tx0.withdrawalDetails.confirmed === true);
- t.assertTrue(tx0.withdrawalDetails.reserveIsReady === true);
+ t.assertTrue(tx.withdrawalDetails.confirmed === true);
+ t.assertTrue(tx.withdrawalDetails.reserveIsReady === true);
}
await withdrawalFinishedCond;
+ t.logStep("Check rescan idempotent");
+
+ {
+ await wallet1.call(
+ WalletApiOperation.GetWithdrawalDetailsForUri,
+ {
+ talerWithdrawUri: withdrawal.taler_withdraw_uri,
+ },
+ );
+ // FIXME #9683 wallet should already know withdrawal already completed by this wallet
+
+ try {
+ await wallet1.call(
+ WalletApiOperation.AcceptBankIntegratedWithdrawal,
+ {
+ exchangeBaseUrl: exchange.baseUrl,
+ talerWithdrawUri: withdrawal.taler_withdraw_uri,
+ },
+ )
+ throw "unreachable"
+ } catch (e) {
+ t.assertTrue(e instanceof TalerError)
+ t.assertTrue(e.errorDetail.code === TalerErrorCode.WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED)
+ // FIXME #9683 We should not fail here
+ }
+ }
+
+ t.logStep("Check other wallet scan after completion");
+
+ {
+ await wallet2.call(
+ WalletApiOperation.GetWithdrawalDetailsForUri,
+ {
+ talerWithdrawUri: withdrawal.taler_withdraw_uri,
+ },
+ );
+ // FIXME #9683 wallet should already know withdrawal already completed by wallet1
+
+ try {
+ await wallet2.call(
+ WalletApiOperation.AcceptBankIntegratedWithdrawal,
+ {
+ exchangeBaseUrl: exchange.baseUrl,
+ talerWithdrawUri: withdrawal.taler_withdraw_uri,
+ },
+ )
+ throw "unreachable"
+ } catch (e) {
+ t.assertTrue(e instanceof TalerError)
+ t.assertTrue(e.errorDetail.code === TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK)
+ // FIXME #9683 can we have a more proper error code than this ?
+ }
+ }
+
t.logStep("Check balance");
- const balResp = await walletClient.client.call(
- WalletApiOperation.GetBalances,
- {},
- );
+ const balResp = await wallet1.call(WalletApiOperation.GetBalances, {});
t.assertAmountEquals(balResp.balances[0].available, "TESTKUDOS:9.72");
- const txn = await walletClient.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
+ const txn = await wallet1.call(WalletApiOperation.GetTransactions, {});
console.log(`transactions: ${j2s(txn)}`);
}