taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit f4d83b7c66eca946af7fc6a94cee5e5f107e28d1
parent 592bbe8eda310b531f38105f666c8dabc341cad9
Author: Antoine A <>
Date:   Thu, 10 Apr 2025 17:16:48 +0200

harness: fixme for #9683

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts | 217++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
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)}`); }