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:
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 {