diff options
Diffstat (limited to 'packages/taler-harness')
3 files changed, 255 insertions, 0 deletions
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts index 1253e3dd5..37e0b02a7 100644 --- a/packages/taler-harness/src/harness/harness.ts +++ b/packages/taler-harness/src/harness/harness.ts @@ -45,6 +45,7 @@ import { j2s, parsePaytoUri, stringToBytes, + AccountRestriction, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, @@ -589,6 +590,11 @@ export interface HarnessExchangeBankAccount { accountPassword: string; accountPaytoUri: string; wireGatewayApiBaseUrl: string; + + conversionUrl?: string; + + debitRestrictions?: AccountRestriction[]; + creditRestrictions?: AccountRestriction[]; } /** diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts new file mode 100644 index 000000000..2a9dd5800 --- /dev/null +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts @@ -0,0 +1,247 @@ +/* + 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/> + */ + +/** + * Imports. + */ +import { + AbsoluteTime, + AmountString, + Duration, + Logger, + TalerCorebankApiClient, + WireGatewayApiClient, + j2s, +} from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { + BankService, + ExchangeService, + GlobalTestState, + MerchantService, + generateRandomPayto, + setupDb, +} from "../harness/harness.js"; +import { createWalletDaemonWithClient } from "../harness/helpers.js"; +import { defaultCoinConfig } from "../harness/denomStructures.js"; +import * as http from "node:http"; + +const logger = new Logger("test-withdrawal-conversion.ts"); + +interface TestfakeConversionService { + stop: () => void; +} + +function splitInTwoAt(s: string, separator: string): [string, string] { + const idx = s.indexOf(separator); + if (idx === -1) { + return [s, ""]; + } + return [s.slice(0, idx), s.slice(idx + 1)]; +} + +/** + * Testfake for the kyc service that the exchange talks to. + */ +async function runTestfakeConversionService(): Promise<TestfakeConversionService> { + const server = http.createServer((req, res) => { + const requestUrl = req.url!; + logger.info(`kyc: got ${req.method} request, ${requestUrl}`); + + const [path, query] = splitInTwoAt(requestUrl, "?"); + + const qp = new URLSearchParams(query); + + if (path === "/config") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + version: "0:0:0", + name: "taler-conversion-info", + regional_currency: {}, + fiat_currency: {}, + }), + ); + } else if (path === "/cashin-rate") { + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + amount_debit: "FOO:123", + amount_credit: "BAR:123", + }), + ); + } else { + res.writeHead(400, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ code: 1, message: "bad request" })); + } + }); + await new Promise<void>((resolve, reject) => { + server.listen(8071, () => resolve()); + }); + return { + stop() { + server.close(); + }, + }; +} + +/** + * Test for currency conversion during manual withdrawal. + */ +export async function runWithdrawalConversionTest(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, + }); + + const merchant = await MerchantService.create(t, { + name: "testmerchant-1", + currency: "TESTKUDOS", + httpPort: 8083, + database: db.connStr, + }); + + const exchangeBankAccount = await bank.createExchangeAccount( + "myexchange", + "x", + ); + exchangeBankAccount.conversionUrl = "http://localhost:8071/"; + await exchange.addBankAccount("1", exchangeBankAccount); + + await bank.start(); + + await bank.pingUntilAvailable(); + + exchange.addOfferedCoins(defaultCoinConfig); + + await exchange.start(); + await exchange.pingUntilAvailable(); + + merchant.addExchange(exchange); + await merchant.start(); + await merchant.pingUntilAvailable(); + + await merchant.addInstanceWithWireAccount({ + id: "default", + name: "Default Instance", + paytoUris: [generateRandomPayto("merchant-default")], + defaultWireTransferDelay: Duration.toTalerProtocolDuration( + Duration.fromSpec({ minutes: 1 }), + ), + }); + + await merchant.addInstanceWithWireAccount({ + id: "minst1", + name: "minst1", + paytoUris: [generateRandomPayto("minst1")], + defaultWireTransferDelay: Duration.toTalerProtocolDuration( + Duration.fromSpec({ minutes: 1 }), + ), + }); + + const { walletClient, walletService } = await createWalletDaemonWithClient( + t, + { name: "wallet" }, + ); + + await runTestfakeConversionService(); + + // Create a withdrawal operation + + const bankAccessApiClient = new TalerCorebankApiClient( + bank.corebankApiBaseUrl, + ); + + const user = await bankAccessApiClient.createRandomBankUser(); + + await walletClient.call(WalletApiOperation.AddExchange, { + exchangeBaseUrl: exchange.baseUrl, + }); + + const infoRes = walletClient.call( + WalletApiOperation.GetWithdrawalDetailsForAmount, + { + exchangeBaseUrl: exchange.baseUrl, + amount: "EXTCOIN:20" as AmountString, + }, + ); + + console.log(`withdrawal details: ${j2s(infoRes)}`); + + const tStart = AbsoluteTime.now(); + + logger.info("starting AcceptManualWithdrawal request"); + // We expect this to return immediately. + + const wres = await walletClient.call( + WalletApiOperation.AcceptManualWithdrawal, + { + exchangeBaseUrl: exchange.baseUrl, + amount: "TESTKUDOS:10" as AmountString, + }, + ); + + logger.info("AcceptManualWithdrawal finished"); + logger.info(`result: ${j2s(wres)}`); + + // Check that the request did not go into long-polling. + const duration = AbsoluteTime.difference(tStart, AbsoluteTime.now()); + if (typeof duration.d_ms !== "number" || duration.d_ms > 5 * 1000) { + throw Error("withdrawal took too long (longpolling issue)"); + } + + const reservePub: string = wres.reservePub; + + const wireGatewayApiClient = new WireGatewayApiClient( + exchangeBankAccount.wireGatewayApiBaseUrl, + { + auth: { + username: exchangeBankAccount.accountName, + password: exchangeBankAccount.accountPassword, + }, + }, + ); + + await wireGatewayApiClient.adminAddIncoming({ + amount: "TESTKUDOS:10", + debitAccountPayto: user.accountPaytoUri, + reservePub: reservePub, + }); + + await exchange.runWirewatchOnce(); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); + + // Check balance + + const balResp = await walletClient.call(WalletApiOperation.GetBalances, {}); + t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available); +} + +runWithdrawalConversionTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts index 25bce5712..b363e58a9 100644 --- a/packages/taler-harness/src/integrationtests/testrunner.ts +++ b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -95,6 +95,7 @@ import { runWalletGenDbTest } from "./test-wallet-gendb.js"; import { runLibeufinBankTest } from "./test-libeufin-bank.js"; import { runMultiExchangeTest } from "./test-multiexchange.js"; import { runAgeRestrictionsDepositTest } from "./test-age-restrictions-deposit.js"; +import { runWithdrawalConversionTest } from "./test-withdrawal-conversion.js"; /** * Test runner. @@ -173,6 +174,7 @@ const allTests: TestMainFunction[] = [ runWithdrawalBankIntegratedTest, runWithdrawalFakebankTest, runWithdrawalFeesTest, + runWithdrawalConversionTest, runWithdrawalHugeTest, runTermOfServiceFormatTest, runStoredBackupsTest, |