diff --git a/packages/taler-integrationtests/src/harness.ts b/packages/taler-integrationtests/src/harness.ts index d092a9733..027869d15 100644 --- a/packages/taler-integrationtests/src/harness.ts +++ b/packages/taler-integrationtests/src/harness.ts @@ -69,8 +69,7 @@ export async function sh( logName: string, command: string, ): Promise { - console.log("runing command"); - console.log(command); + console.log("runing command", command); return new Promise((resolve, reject) => { const stdoutChunks: Buffer[] = []; const proc = spawn(command, { @@ -89,8 +88,8 @@ export async function sh( flags: "a", }); proc.stderr.pipe(stderrLog); - proc.on("exit", (code) => { - console.log("child process exited"); + proc.on("exit", (code, signal) => { + console.log(`child process exited (${code} / ${signal})`); if (code != 0) { reject(Error(`Unexpected exit code ${code} for '${command}'`)); return; @@ -419,6 +418,13 @@ async function pingProc( } } +export interface ExchangeBankAccount { + accountName: string; + accountPassword: string; + accountPaytoUri: string; + wireGatewayApiBaseUrl: string; +} + export class BankService { proc: ProcessWrapper | undefined; @@ -454,6 +460,18 @@ export class BankService { ); const cfgFilename = gc.testDir + "/bank.conf"; config.write(cfgFilename); + + await sh( + gc, + "taler-bank-manage_django", + `taler-bank-manage -c '${cfgFilename}' django migrate`, + ); + await sh( + gc, + "taler-bank-manage_django", + `taler-bank-manage -c '${cfgFilename}' django provide_accounts`, + ); + return new BankService(gc, bc, cfgFilename); } @@ -463,6 +481,33 @@ export class BankService { config.setString("bank", "suggested_exchange_payto", exchangePayto); } + async createExchangeAccount( + accountName: string, + password: string, + ): Promise { + await sh( + this.globalTestState, + "taler-bank-manage_django", + `taler-bank-manage -c '${this.configFile}' django add_bank_account ${accountName}`, + ); + await sh( + this.globalTestState, + "taler-bank-manage_django", + `taler-bank-manage -c '${this.configFile}' django changepassword_unsafe ${accountName} ${password}`, + ); + await sh( + this.globalTestState, + "taler-bank-manage_django", + `taler-bank-manage -c '${this.configFile}' django top_up ${accountName} ${this.bankConfig.currency}:100000`, + ); + return { + accountName: accountName, + accountPassword: password, + accountPaytoUri: `payto://x-taler-bank/${accountName}`, + wireGatewayApiBaseUrl: `http://localhost:${this.bankConfig.httpPort}/taler-wire-gateway/${accountName}/`, + }; + } + get port() { return this.bankConfig.httpPort; } @@ -495,10 +540,12 @@ export class BankService { } async createRandomBankUser(): Promise { + const username = + "user-" + talerCrypto.encodeCrock(talerCrypto.getRandomBytes(10)); const bankUser: BankUser = { - username: - "user-" + talerCrypto.encodeCrock(talerCrypto.getRandomBytes(10)), + username, password: "pw-" + talerCrypto.encodeCrock(talerCrypto.getRandomBytes(10)), + accountPaytoUri: `payto://x-taler-bank/localhost/${username}`, }; await this.createAccount(bankUser.username, bankUser.password); return bankUser; @@ -521,6 +568,29 @@ export class BankService { return codecForWithdrawalOperationInfo().decode(resp.data); } + async adminAddIncoming(params: { + exchangeBankAccount: ExchangeBankAccount; + amount: string; + reservePub: string; + debitAccountPayto: string; + }) { + const url = `http://localhost:${this.bankConfig.httpPort}/taler-wire-gateway/${params.exchangeBankAccount.accountName}/admin/add-incoming`; + await axios.post( + url, + { + amount: params.amount, + reserve_pub: params.reservePub, + debit_account: params.debitAccountPayto, + }, + { + auth: { + username: params.exchangeBankAccount.accountName, + password: params.exchangeBankAccount.accountPassword, + }, + }, + ); + } + async confirmWithdrawalOperation( bankUser: BankUser, wopi: WithdrawalOperationInfo, @@ -539,6 +609,7 @@ export class BankService { export interface BankUser { username: string; password: string; + accountPaytoUri: string; } export interface WithdrawalOperationInfo { @@ -600,6 +671,14 @@ export class ExchangeService implements ExchangeServiceInterface { return new ExchangeService(gc, ec, cfgFilename, keyPair); } + async runWirewatchOnce() { + await sh( + this.globalState, + "wirewatch-test", + `taler-exchange-wirewatch -c '${this.configFilename}' -t`, + ); + } + static create(gc: GlobalTestState, e: ExchangeConfig) { const config = new Configuration(); config.setString("taler", "currency", e.currency); @@ -686,13 +765,10 @@ export class ExchangeService implements ExchangeServiceInterface { return this.exchangeConfig.httpPort; } - async setupTestBankAccount( - bc: BankService, + async addBankAccount( localName: string, - accountName: string, - password: string, + exchangeBankAccount: ExchangeBankAccount, ): Promise { - await bc.createAccount(accountName, password); const config = Configuration.load(this.configFilename); config.setString( `exchange-account-${localName}`, @@ -702,22 +778,30 @@ export class ExchangeService implements ExchangeServiceInterface { config.setString( `exchange-account-${localName}`, "payto_uri", - `payto://x-taler-bank/localhost/${accountName}`, + exchangeBankAccount.accountPaytoUri, ); config.setString(`exchange-account-${localName}`, "enable_credit", "yes"); config.setString(`exchange-account-${localName}`, "enable_debit", "yes"); config.setString( `exchange-account-${localName}`, "wire_gateway_url", - `http://localhost:${bc.port}/taler-wire-gateway/${accountName}/`, + exchangeBankAccount.wireGatewayApiBaseUrl, ); config.setString( `exchange-account-${localName}`, "wire_gateway_auth_method", "basic", ); - config.setString(`exchange-account-${localName}`, "username", accountName); - config.setString(`exchange-account-${localName}`, "password", password); + config.setString( + `exchange-account-${localName}`, + "username", + exchangeBankAccount.accountName, + ); + config.setString( + `exchange-account-${localName}`, + "password", + exchangeBankAccount.accountPassword, + ); config.write(this.configFilename); } diff --git a/packages/taler-integrationtests/src/helpers.ts b/packages/taler-integrationtests/src/helpers.ts index 9afb66428..41cd39d77 100644 --- a/packages/taler-integrationtests/src/helpers.ts +++ b/packages/taler-integrationtests/src/helpers.ts @@ -32,6 +32,7 @@ import { setupDb, BankService, defaultCoinConfig, + ExchangeBankAccount, } from "./harness"; import { AmountString } from "taler-wallet-core/lib/types/talerTypes"; @@ -39,6 +40,7 @@ export interface SimpleTestEnvironment { commonDb: DbInfo; bank: BankService; exchange: ExchangeService; + exchangeBankAccount: ExchangeBankAccount; merchant: MerchantService; wallet: WalletCli; } @@ -73,13 +75,15 @@ export async function createSimpleTestkudosEnvironment( database: db.connStr, }); - bank.setSuggestedExchange(exchange, "payto://x-taler-bank/MyExchange"); + const exchangeBankAccount = await bank.createExchangeAccount("MyExchange", "x"); + exchange.addBankAccount("1", exchangeBankAccount); + + bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); await bank.start(); await bank.pingUntilAvailable(); - await exchange.setupTestBankAccount(bank, "1", "MyExchange", "x"); exchange.addOfferedCoins(defaultCoinConfig); await exchange.start(); @@ -112,6 +116,7 @@ export async function createSimpleTestkudosEnvironment( merchant, wallet, bank, + exchangeBankAccount, }; } diff --git a/packages/taler-integrationtests/src/test-payment-fault.ts b/packages/taler-integrationtests/src/test-payment-fault.ts index f0b17a7fc..0db6770bf 100644 --- a/packages/taler-integrationtests/src/test-payment-fault.ts +++ b/packages/taler-integrationtests/src/test-payment-fault.ts @@ -56,13 +56,15 @@ runTest(async (t: GlobalTestState) => { database: db.connStr, }); - bank.setSuggestedExchange(exchange, "payto://x-taler-bank/MyExchange"); + const exchangeBankAccount = await bank.createExchangeAccount("MyExchange", "x"); + + bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); await bank.start(); await bank.pingUntilAvailable(); - await exchange.setupTestBankAccount(bank, "1", "MyExchange", "x"); + await exchange.addBankAccount("1", exchangeBankAccount); exchange.addOfferedCoins(defaultCoinConfig); await exchange.start(); diff --git a/packages/taler-integrationtests/src/test-payment-multiple.ts b/packages/taler-integrationtests/src/test-payment-multiple.ts index 84aab4c81..5bbeecbd9 100644 --- a/packages/taler-integrationtests/src/test-payment-multiple.ts +++ b/packages/taler-integrationtests/src/test-payment-multiple.ts @@ -51,15 +51,17 @@ async function setupTest(t: GlobalTestState): Promise<{ database: db.connStr, }); + const exchangeBankAccount = await bank.createExchangeAccount("MyExchange", "x"); + exchange.addOfferedCoins([coin_ct10, coin_u1]); - bank.setSuggestedExchange(exchange, "payto://x-taler-bank/MyExchange"); + bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); await bank.start(); await bank.pingUntilAvailable(); - await exchange.setupTestBankAccount(bank, "1", "MyExchange", "x"); + await exchange.addBankAccount("1", exchangeBankAccount); await exchange.start(); await exchange.pingUntilAvailable(); diff --git a/packages/taler-integrationtests/src/test-withdrawal.ts b/packages/taler-integrationtests/src/test-withdrawal-bank-integrated.ts similarity index 100% rename from packages/taler-integrationtests/src/test-withdrawal.ts rename to packages/taler-integrationtests/src/test-withdrawal-bank-integrated.ts diff --git a/packages/taler-integrationtests/src/test-withdrawal-manual.ts b/packages/taler-integrationtests/src/test-withdrawal-manual.ts new file mode 100644 index 000000000..afaf41e63 --- /dev/null +++ b/packages/taler-integrationtests/src/test-withdrawal-manual.ts @@ -0,0 +1,74 @@ +/* + 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 { runTest, GlobalTestState } from "./harness"; +import { createSimpleTestkudosEnvironment } from "./helpers"; +import { walletTypes } from "taler-wallet-core"; +import { CoreApiResponse } from "taler-wallet-core/lib/walletCoreApiHandler"; + +/** + * Run test for basic, bank-integrated withdrawal. + */ +runTest(async (t: GlobalTestState) => { + + // Set up test environment + + const { wallet, bank, exchange, exchangeBankAccount } = await createSimpleTestkudosEnvironment(t); + + // Create a withdrawal operation + + const user = await bank.createRandomBankUser(); + + let wresp: CoreApiResponse; + + wresp = await wallet.apiRequest("addExchange", { + exchangeBaseUrl: exchange.baseUrl, + }); + + t.assertTrue(wresp.type === "response"); + + wresp = await wallet.apiRequest("acceptManualWithdrawal", { + exchangeBaseUrl: exchange.baseUrl, + amount: "TESTKUDOS:10", + }); + + t.assertTrue(wresp.type === "response"); + + const reservePub: string = (wresp.result as any).reservePub; + + await bank.adminAddIncoming({ + exchangeBankAccount, + amount: "TESTKUDOS:10", + debitAccountPayto: user.accountPaytoUri, + reservePub: reservePub, + }); + + await exchange.runWirewatchOnce(); + + await wallet.runUntilDone(); + + // Check balance + + const balApiResp = await wallet.apiRequest("getBalances", {}); + t.assertTrue(balApiResp.type === "response"); + const balResp = walletTypes.codecForBalancesResponse().decode(balApiResp.result); + t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available) + + await t.shutdown(); +});