taler-typescript-core

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

commit d2e770e0ff271cc0477fa317b5572d726a51d6cb
parent 4cf0b13414deb1579453bd72a4c785a045c74c1d
Author: Florian Dold <dold@taler.net>
Date:   Wed, 22 Jul 2026 17:10:16 +0200

harness: drive a withdrawal over a failing network

Uses the start-tc dev experiment to fail and delay a share of the requests
once the withdrawal is running as a background task, so that the scheduler has
to work through repeated errors, backoffs and the wakeups that follow them.

Diffstat:
Apackages/taler-harness/src/integrationtests/test-wallet-bad-network.ts | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-harness/src/integrationtests/testrunner.ts | 2++
2 files changed, 95 insertions(+), 0 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-wallet-bad-network.ts b/packages/taler-harness/src/integrationtests/test-wallet-bad-network.ts @@ -0,0 +1,93 @@ +/* + This file is part of GNU Taler + (C) 2026 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, + TransactionMajorState, + TransactionType, +} from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { + createSimpleTestkudosEnvironmentV3, + createWalletDaemonWithClient, + withdrawViaBankV3, +} from "../harness/environments.js"; +import { GlobalTestState } from "../harness/harness.js"; + +/** + * Drive a withdrawal while a good share of the HTTP traffic fails, so that the + * task scheduler has to work through repeated errors, backoffs and the wakeups + * that follow each stored result. + * + * The scheduler is otherwise only exercised on paths where everything answers + * first time, which is the half of it that cannot go wrong. + */ +export async function runWalletBadNetworkTest(t: GlobalTestState) { + const { bankClient, exchange } = await createSimpleTestkudosEnvironmentV3(t); + + const { walletClient } = await createWalletDaemonWithClient(t, { + name: "w-bad-net", + config: { + testing: { + devModeActive: true, + }, + }, + }); + + // Set the withdrawal up over a working network: these are foreground + // requests from the test itself, and nothing retries those. + const withdrawRes = await withdrawViaBankV3(t, { + walletClient, + bankClient: bankClient as TalerCorebankApiClient, + exchange, + amount: "TESTKUDOS:20", + }); + + // From here on the withdrawal runs as a background task, so every failure + // below has to be absorbed by the scheduler. The delay makes each handler + // run long enough for the results of other requests to land while one is + // still in flight. + await walletClient.call(WalletApiOperation.ApplyDevExperiment, { + devExperimentUri: + "taler://dev-experiment/start-tc?fake_500=0.3&delay_resp=100", + }); + + // The wallet has to get here on its own, by retrying. + await withdrawRes.withdrawalFinishedCond; + + await walletClient.call(WalletApiOperation.ApplyDevExperiment, { + devExperimentUri: "taler://dev-experiment/stop-tc", + }); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); + + const txs = await walletClient.call(WalletApiOperation.GetTransactionsV2, {}); + const withdrawal = txs.transactions.find( + (x) => x.type === TransactionType.Withdrawal, + ); + t.assertTrue(!!withdrawal); + t.assertDeepEqual(withdrawal.txState.major, TransactionMajorState.Done); + + // Compared against what the withdrawal itself says rather than a constant, + // so that a change in fees does not look like a scheduler regression. + const bal = await walletClient.call(WalletApiOperation.GetBalances, {}); + t.assertAmountEquals(bal.balances[0].available, withdrawal.amountEffective); +} + +runWalletBadNetworkTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -192,6 +192,7 @@ import { runWalletDblessTest } from "./test-wallet-dbless.js"; import { runWalletDd48Test } from "./test-wallet-dd48.js"; import { runWalletDenomExpireTest } from "./test-wallet-denom-expire.js"; import { runWalletDepositLargeTest } from "./test-wallet-deposit-large.js"; +import { runWalletBadNetworkTest } from "./test-wallet-bad-network.js"; import { runWalletDevExperimentsTest } from "./test-wallet-dev-experiments.js"; import { runWalletDevexpFakeprotoverTest } from "./test-wallet-devexp-fakeprotover.js"; import { runWalletExchangeFeaturesTest } from "./test-wallet-exchange-features.js"; @@ -321,6 +322,7 @@ const allTests: TestMainFunction[] = [ runWalletBalanceNotificationsTest, runExchangeManagementTest, runWalletObservabilityTest, + runWalletBadNetworkTest, runWalletDevExperimentsTest, runWalletBalanceZeroTest, runWalletInsufficientBalanceTest,