taler-typescript-core

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

commit d296533bda998b44e64bdda8b579be0d4618db8a
parent 4e4ee87b17b693437e9d11ce19f7329ceab8ad19
Author: Florian Dold <florian@dold.me>
Date:   Sun, 23 Feb 2025 14:39:44 +0100

harness: test for https://bugs.taler.net/n/7814

Diffstat:
Apackages/taler-harness/src/integrationtests/test-deposit-fault.ts | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-harness/src/integrationtests/testrunner.ts | 4+++-
2 files changed, 205 insertions(+), 1 deletion(-)

diff --git a/packages/taler-harness/src/integrationtests/test-deposit-fault.ts b/packages/taler-harness/src/integrationtests/test-deposit-fault.ts @@ -0,0 +1,202 @@ +/* + 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/> + */ + +/** + * Fault injection test for deposits. + */ + +/** + * Imports. + */ +import { + j2s, + openPromise, + TalerCorebankApiClient, +} from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { defaultCoinConfig } from "../harness/denomStructures.js"; +import { + createWalletDaemonWithClient, + withdrawViaBankV3, +} from "../harness/environments.js"; +import { + FaultInjectedExchangeService, + FaultInjectionRequestContext, + FaultInjectionResponseContext, +} from "../harness/faultInjection.js"; +import { + BankService, + ExchangeService, + getTestHarnessPaytoForLabel, + GlobalTestState, + harnessHttpLib, + setupDb, +} from "../harness/harness.js"; + +/** + * Run test for basic, bank-integrated withdrawal. + */ +export async function runDepositFaultTest(t: GlobalTestState) { + // Set up test environment + + const db = await setupDb(t); + + const bank = await BankService.create(t, { + allowRegistrations: true, + currency: "TESTKUDOS", + database: db.connStr, + httpPort: 8082, + }); + + const exchange = ExchangeService.create(t, { + name: "testexchange-1", + currency: "TESTKUDOS", + httpPort: 8081, + database: db.connStr, + }); + + let receiverName = "Exchange"; + let exchangeBankUsername = "exchange"; + let exchangeBankPassword = "mypw-password"; + let exchangePaytoUri = getTestHarnessPaytoForLabel(exchangeBankUsername); + + await exchange.addBankAccount("1", { + accountName: exchangeBankUsername, + accountPassword: exchangeBankPassword, + wireGatewayApiBaseUrl: new URL( + "accounts/exchange/taler-wire-gateway/", + bank.baseUrl, + ).href, + accountPaytoUri: exchangePaytoUri, + }); + + const faultyExchange = new FaultInjectedExchangeService(t, exchange, 8091); + // Base URL must contain port that the proxy is listening on. + await exchange.modifyConfig(async (config) => { + config.setString("exchange", "base_url", "http://localhost:8091/"); + }); + + bank.setSuggestedExchange(faultyExchange, exchangePaytoUri); + + await bank.start(); + + await bank.pingUntilAvailable(); + + const bankClient = new TalerCorebankApiClient(bank.corebankApiBaseUrl, { + auth: { + username: "admin", + password: "admin-password", + }, + }); + + await bankClient.registerAccountExtended({ + name: receiverName, + password: exchangeBankPassword, + username: exchangeBankUsername, + is_taler_exchange: true, + payto_uri: exchangePaytoUri, + }); + + exchange.addOfferedCoins(defaultCoinConfig); + + await exchange.start(); + await exchange.pingUntilAvailable(); + + // Print all requests to the exchange + faultyExchange.faultProxy.addFault({ + async modifyRequest(ctx: FaultInjectionRequestContext) { + console.log("got request", ctx); + }, + async modifyResponse(ctx: FaultInjectionResponseContext) { + console.log("got response", ctx); + }, + }); + + console.log("setup done!"); + + const { walletClient } = await createWalletDaemonWithClient(t, { + name: "default", + }); + + await walletClient.call(WalletApiOperation.GetBalances, {}); + + const wres = await withdrawViaBankV3(t, { + walletClient, + bankClient, + exchange: faultyExchange, + amount: "TESTKUDOS:20", + }); + + await wres.withdrawalFinishedCond; + + const caughtDeposit = openPromise<any>(); + const caughtRefresh = openPromise<any>(); + + let allowDeposit = false; + let allowRefresh = false; + + faultyExchange.faultProxy.addFault({ + async modifyRequest(ctx: FaultInjectionRequestContext) { + if (ctx.requestUrl.endsWith("/batch-deposit")) { + if (allowDeposit) { + return; + } + ctx.dropRequest = true; + const td = new TextDecoder(); + caughtDeposit.resolve(JSON.parse(td.decode(ctx.requestBody))); + } + // if (ctx.requestUrl.includes("/melt/")) { + // if (allowRefresh) { + // return; + // } + // ctx.dropRequest = true; + // const td = new TextDecoder(); + // caughtRefresh.resolve(JSON.parse(td.decode(ctx.requestBody))); + // } + }, + async modifyResponse(ctx: FaultInjectionResponseContext) {}, + }); + + const depositResp = await walletClient.call( + WalletApiOperation.CreateDepositGroup, + { + amount: "TESTKUDOS:10", + depositPaytoUri: getTestHarnessPaytoForLabel("foo"), + }, + ); + + const caughtDepositBody = await caughtDeposit.promise; + console.log(`caught deposit request: ${j2s(caughtDepositBody)}`); + + await walletClient.call(WalletApiOperation.AbortTransaction, { + transactionId: depositResp.transactionId, + }); + + allowDeposit = true; + await harnessHttpLib.fetch( + new URL("/batch-deposit", faultyExchange.baseUrl).href, + { + method: "POST", + body: caughtDepositBody, + }, + ); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); + await walletClient.call(WalletApiOperation.TestingWaitRefreshesFinal, {}); +} + +runDepositFaultTest.suites = ["wallet"]; +runDepositFaultTest.timeoutMs = 120000; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -40,6 +40,7 @@ import { runClauseSchnorrTest } from "./test-clause-schnorr.js"; import { runCurrencyScopeTest } from "./test-currency-scope.js"; import { runDenomLostTest } from "./test-denom-lost.js"; import { runDenomUnofferedTest } from "./test-denom-unoffered.js"; +import { runDepositFaultTest } from "./test-deposit-fault.js"; import { runDepositTest } from "./test-deposit.js"; import { runExchangeDepositTest } from "./test-exchange-deposit.js"; import { runExchangeManagementFaultTest } from "./test-exchange-management-fault.js"; @@ -53,6 +54,7 @@ import { runKnownAccountsTest } from "./test-known-accounts.js"; import { runKycAmpFailureTest } from "./test-kyc-amp-failure.js"; import { runKycAmpTimeoutTest } from "./test-kyc-amp-timeout.js"; import { runKycBalanceWithdrawalTest } from "./test-kyc-balance-withdrawal.js"; +import { runKycDecisionAttrTest } from "./test-kyc-decision-attr.js"; import { runKycDecisionsTest } from "./test-kyc-decisions.js"; import { runKycDepositAggregateImplicitAuthTest } from "./test-kyc-deposit-aggregate-implicit-auth.js"; import { runKycDepositAggregateTest } from "./test-kyc-deposit-aggregate.js"; @@ -157,7 +159,6 @@ import { runWithdrawalHugeTest } from "./test-withdrawal-huge.js"; import { runWithdrawalIdempotentTest } from "./test-withdrawal-idempotent.js"; import { runWithdrawalManualTest } from "./test-withdrawal-manual.js"; import { runWithdrawalPrepareTest } from "./test-withdrawal-prepare.js"; -import { runKycDecisionAttrTest } from "./test-kyc-decision-attr.js"; /** * Test runner. @@ -306,6 +307,7 @@ const allTests: TestMainFunction[] = [ runBankWopTest, runKycNewMeasuresProgTest, runKycDecisionAttrTest, + runDepositFaultTest, ]; export interface TestRunSpec {