From c53264869451ccbfbaf1976e01df8c7636163068 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 14 Oct 2021 11:36:43 +0200 Subject: implement fakebank withdrawal --- packages/taler-wallet-cli/src/index.ts | 24 +++++- .../src/integrationtests/harness.ts | 61 ++++++++++++++ .../src/integrationtests/helpers.ts | 33 +++++--- .../integrationtests/test-withdrawal-fakebank.ts | 96 ++++++++++++++++++++++ .../src/integrationtests/test-withdrawal-manual.ts | 4 - .../src/integrationtests/testrunner.ts | 4 +- 6 files changed, 206 insertions(+), 16 deletions(-) create mode 100644 packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts (limited to 'packages/taler-wallet-cli') diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts index 0985ba884..a5e129d92 100644 --- a/packages/taler-wallet-cli/src/index.ts +++ b/packages/taler-wallet-cli/src/index.ts @@ -634,6 +634,29 @@ const advancedCli = walletCli.subcommand("advancedArgs", "advanced", { "Subcommands for advanced operations (only use if you know what you're doing!).", }); +advancedCli + .subcommand("withdrawFakebank", "withdraw-fakebank", { + help: "Withdraw via a fakebank.", + }) + .requiredOption("exchange", ["--exchange"], clk.STRING, { + help: "Base URL of the exchange to use", + }) + .requiredOption("amount", ["--amount"], clk.STRING, { + help: "Amount to withdraw (before fees)." + }) + .requiredOption("bank", ["--bank"], clk.STRING, { + help: "Base URL of the Taler fakebank service.", + }) + .action(async (args) => { + await withWallet(args, async (wallet) => { + await wallet.client.call(WalletApiOperation.WithdrawFakebank, { + amount: args.withdrawFakebank.amount, + bank: args.withdrawFakebank.bank, + exchange: args.withdrawFakebank.exchange, + }); + }); + }); + advancedCli .subcommand("manualWithdrawalDetails", "manual-withdrawal-details", { help: "Query withdrawal fees.", @@ -1064,6 +1087,5 @@ export function main() { logger.warn("Allowing withdrawal of late denominations for debugging"); walletCoreDebugFlags.denomselAllowLate = true; } - logger.trace(`running wallet-cli with`, process.argv); walletCli.run(); } diff --git a/packages/taler-wallet-cli/src/integrationtests/harness.ts b/packages/taler-wallet-cli/src/integrationtests/harness.ts index a3a6e9e1c..6644e567f 100644 --- a/packages/taler-wallet-cli/src/integrationtests/harness.ts +++ b/packages/taler-wallet-cli/src/integrationtests/harness.ts @@ -395,6 +395,11 @@ export interface BankConfig { maxDebt?: string; } +export interface FakeBankConfig { + currency: string; + httpPort: number; +} + function setTalerPaths(config: Configuration, home: string) { config.setString("paths", "taler_home", home); // We need to make sure that the path of taler_runtime_dir isn't too long, @@ -714,6 +719,62 @@ export class BankService implements BankServiceInterface { } } +export class FakeBankService { + proc: ProcessWrapper | undefined; + + static fromExistingConfig(gc: GlobalTestState): FakeBankService { + const cfgFilename = gc.testDir + "/bank.conf"; + console.log("reading fakebank config from", cfgFilename); + const config = Configuration.load(cfgFilename); + const bc: FakeBankConfig = { + currency: config.getString("taler", "currency").required(), + httpPort: config.getNumber("bank", "http_port").required(), + }; + return new FakeBankService(gc, bc, cfgFilename); + } + + static async create( + gc: GlobalTestState, + bc: FakeBankConfig, + ): Promise { + const config = new Configuration(); + setTalerPaths(config, gc.testDir + "/talerhome"); + config.setString("taler", "currency", bc.currency); + config.setString("bank", "http_port", `${bc.httpPort}`); + const cfgFilename = gc.testDir + "/bank.conf"; + config.write(cfgFilename); + return new FakeBankService(gc, bc, cfgFilename); + } + + get baseUrl(): string { + return `http://localhost:${this.bankConfig.httpPort}/`; + } + + get port() { + return this.bankConfig.httpPort; + } + + private constructor( + private globalTestState: GlobalTestState, + private bankConfig: FakeBankConfig, + private configFile: string, + ) {} + + async start(): Promise { + this.proc = this.globalTestState.spawnService( + "taler-fakebank-run", + ["-c", this.configFile], + "fakebank", + ); + } + + async pingUntilAvailable(): Promise { + // Fakebank doesn't have "/config", so we ping just "/". + const url = `http://localhost:${this.bankConfig.httpPort}/`; + await pingProc(this.proc, url, "bank"); + } +} + export interface BankUser { username: string; password: string; diff --git a/packages/taler-wallet-cli/src/integrationtests/helpers.ts b/packages/taler-wallet-cli/src/integrationtests/helpers.ts index 1fdc36788..3b4e1643f 100644 --- a/packages/taler-wallet-cli/src/integrationtests/helpers.ts +++ b/packages/taler-wallet-cli/src/integrationtests/helpers.ts @@ -353,13 +353,22 @@ export async function makeTestPayment( const { wallet, merchant } = args; const instance = args.instance ?? "default"; - const orderResp = await MerchantPrivateApi.createOrder(merchant, instance, { - order: args.order, - }, auth); + const orderResp = await MerchantPrivateApi.createOrder( + merchant, + instance, + { + order: args.order, + }, + auth, + ); - let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { - orderId: orderResp.order_id, - }, auth); + let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( + merchant, + { + orderId: orderResp.order_id, + }, + auth, + ); t.assertTrue(orderStatus.order_status === "unpaid"); @@ -384,10 +393,14 @@ export async function makeTestPayment( // Check if payment was successful. - orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { - orderId: orderResp.order_id, - instance, - }, auth); + orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( + merchant, + { + orderId: orderResp.order_id, + instance, + }, + auth, + ); t.assertTrue(orderStatus.order_status === "paid"); } diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts new file mode 100644 index 000000000..bfe29b322 --- /dev/null +++ b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts @@ -0,0 +1,96 @@ +/* + 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 + */ + +/** + * Imports. + */ +import { + GlobalTestState, + BankApi, + WalletCli, + setupDb, + ExchangeService, + FakeBankService, +} from "./harness"; +import { createSimpleTestkudosEnvironment } from "./helpers"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { CoinConfig, defaultCoinConfig } from "./denomStructures.js"; +import { URL } from "@gnu-taler/taler-util"; + +/** + * Run test for basic, bank-integrated withdrawal. + */ +export async function runTestWithdrawalFakebankTest(t: GlobalTestState) { + // Set up test environment + + const db = await setupDb(t); + + const bank = await FakeBankService.create(t, { + currency: "TESTKUDOS", + httpPort: 8082, + }); + + const exchange = ExchangeService.create(t, { + name: "testexchange-1", + currency: "TESTKUDOS", + httpPort: 8081, + database: db.connStr, + }); + + exchange.addBankAccount("1", { + accountName: "exchange", + accountPassword: "x", + wireGatewayApiBaseUrl: new URL("/exchange/", bank.baseUrl).href, + accountPaytoUri: "payto://x-taler-bank/localhost/exchange", + }); + + await bank.start(); + + await bank.pingUntilAvailable(); + + const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")); + exchange.addCoinConfigList(coinConfig); + + await exchange.start(); + await exchange.pingUntilAvailable(); + + console.log("setup done!"); + + const wallet = new WalletCli(t); + + await wallet.client.call(WalletApiOperation.AddExchange, { + exchangeBaseUrl: exchange.baseUrl, + }); + + await wallet.client.call(WalletApiOperation.WithdrawFakebank, { + exchange: exchange.baseUrl, + amount: "TESTKUDOS:10", + bank: bank.baseUrl, + }); + + await exchange.runWirewatchOnce(); + + await wallet.runUntilDone(); + + // Check balance + + const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {}); + t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available); + + await t.shutdown(); +} + +runTestWithdrawalFakebankTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts index 613618071..fe8fd3c56 100644 --- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts +++ b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts @@ -19,8 +19,6 @@ */ import { GlobalTestState, BankApi } from "./harness"; import { createSimpleTestkudosEnvironment } from "./helpers"; -import { CoreApiResponse } from "@gnu-taler/taler-util"; -import { codecForBalancesResponse } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; /** @@ -40,8 +38,6 @@ export async function runTestWithdrawalManualTest(t: GlobalTestState) { const user = await BankApi.createRandomBankUser(bank); - let wresp: CoreApiResponse; - await wallet.client.call(WalletApiOperation.AddExchange, { exchangeBaseUrl: exchange.baseUrl, }); diff --git a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts index 720dd8b80..bcb0dd271 100644 --- a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts +++ b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts @@ -87,6 +87,7 @@ import { runPaymentZeroTest } from "./test-payment-zero.js"; import { runMerchantSpecPublicOrdersTest } from "./test-merchant-spec-public-orders.js"; import { runExchangeTimetravelTest } from "./test-exchange-timetravel.js"; import { runDenomUnofferedTest } from "./test-denom-unoffered.js"; +import { runTestWithdrawalFakebankTest } from "./test-withdrawal-fakebank.js"; /** * Test runner. @@ -154,6 +155,7 @@ const allTests: TestMainFunction[] = [ runRefundTest, runRevocationTest, runTestWithdrawalManualTest, + runTestWithdrawalFakebankTest, runTimetravelAutorefreshTest, runTimetravelWithdrawTest, runTippingTest, @@ -340,7 +342,7 @@ export async function runTests(spec: TestRunSpec) { try { result = await token.racePromise(resultPromise); - } catch (e) { + } catch (e: any) { console.error(`test ${testName} timed out`); if (token.isCancelled) { result = { -- cgit v1.2.3