diff --git a/contrib/integrationtest.conf b/contrib/integrationtest.conf new file mode 100644 index 000000000..5b4f4e85c --- /dev/null +++ b/contrib/integrationtest.conf @@ -0,0 +1,10 @@ +[integrationtest] +walletdb = testwalletdb.json +bank_base_url = https://bank.test.taler.net/ +exchange_base_url = https://exchange.test.taler.net/ +merchant_base_url = https://backend.test.taler.net/ +merchant_api_key = sandbox + +[integrationtest-basic] +amount_withdraw = TESTKUDOS:10 +amount_spend = TESTKUDOS:5 diff --git a/src/headless/integrationtest.ts b/src/headless/integrationtest.ts index 984ef9c39..fbc6223fc 100644 --- a/src/headless/integrationtest.ts +++ b/src/headless/integrationtest.ts @@ -24,6 +24,7 @@ import { Logger } from "../util/logging"; import { NodeHttpLib } from "./NodeHttpLib"; import * as Amounts from "../util/amounts"; import { Wallet } from "../wallet"; +import { Configuration } from "../util/talerconfig"; const logger = new Logger("integrationtest.ts"); @@ -189,5 +190,143 @@ export async function runIntegrationTest(args: IntegrationTestArgs) { const history = await myWallet.getHistory({ verboseDetails: true }); - console.log("history after integration test:", JSON.stringify(history, undefined, 2)); + console.log( + "history after integration test:", + JSON.stringify(history, undefined, 2), + ); +} + +export async function runIntegrationTestBasic(cfg: Configuration) { + const walletDbPath = cfg.getString("integrationtest", "walletdb").required(); + + const bankBaseUrl = cfg + .getString("integrationtest", "bank_base_url") + .required(); + + const exchangeBaseUrl = cfg + .getString("integrationtest", "exchange_base_url") + .required(); + + const merchantBaseUrl = cfg + .getString("integrationtest", "merchant_base_url") + .required(); + + const merchantApiKey = cfg + .getString("integrationtest", "merchant_api_key") + .required(); + + const parsedWithdrawAmount = cfg + .getAmount("integrationtest-basic", "amount_withdraw") + .required(); + + const parsedSpendAmount = cfg + .getAmount("integrationtest-basic", "amount_spend") + .required(); + + const currency = parsedSpendAmount.currency; + + const myHttpLib = new NodeHttpLib(); + myHttpLib.setThrottling(false); + + const myWallet = await getDefaultNodeWallet({ + httpLib: myHttpLib, + persistentStoragePath: walletDbPath, + }); + + myWallet.runRetryLoop().catch(e => { + console.error("exception during retry loop:", e); + }); + + logger.info("withdrawing test balance"); + await withdrawTestBalance( + myWallet, + Amounts.toString(parsedWithdrawAmount), + bankBaseUrl, + exchangeBaseUrl, + ); + logger.info("done withdrawing test balance"); + + const balance = await myWallet.getBalances(); + + console.log(JSON.stringify(balance, null, 2)); + + const myMerchant = new MerchantBackendConnection( + merchantBaseUrl, + merchantApiKey, + ); + + await makePayment(myWallet, myMerchant, Amounts.toString(parsedSpendAmount), "hello world"); + + // Wait until the refresh is done + await myWallet.runUntilDone(); + + console.log("withdrawing test balance for refund"); + const withdrawAmountTwo: Amounts.AmountJson = { + currency, + value: 18, + fraction: 0, + }; + const spendAmountTwo: Amounts.AmountJson = { + currency, + value: 7, + fraction: 0, + }; + + const refundAmount: Amounts.AmountJson = { + currency, + value: 6, + fraction: 0, + }; + + const spendAmountThree: Amounts.AmountJson = { + currency, + value: 3, + fraction: 0, + }; + + await withdrawTestBalance( + myWallet, + Amounts.toString(withdrawAmountTwo), + bankBaseUrl, + exchangeBaseUrl, + ); + + // Wait until the withdraw is done + await myWallet.runUntilDone(); + + let { orderId: refundOrderId } = await makePayment( + myWallet, + myMerchant, + Amounts.toString(spendAmountTwo), + "order that will be refunded", + ); + + const refundUri = await myMerchant.refund( + refundOrderId, + "test refund", + Amounts.toString(refundAmount), + ); + + console.log("refund URI", refundUri); + + await myWallet.applyRefund(refundUri); + + // Wait until the refund is done + await myWallet.runUntilDone(); + + await makePayment( + myWallet, + myMerchant, + Amounts.toString(spendAmountThree), + "payment after refund", + ); + + await myWallet.runUntilDone(); + + const history = await myWallet.getHistory({ verboseDetails: true }); + + console.log( + "history after integration test:", + JSON.stringify(history, undefined, 2), + ); } diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts index 4518d601e..c4d8664d9 100644 --- a/src/headless/taler-wallet-cli.ts +++ b/src/headless/taler-wallet-cli.ts @@ -18,7 +18,7 @@ import os = require("os"); import fs = require("fs"); import { getDefaultNodeWallet, withdrawTestBalance } from "./helpers"; import { MerchantBackendConnection } from "./merchant"; -import { runIntegrationTest } from "./integrationtest"; +import { runIntegrationTest, runIntegrationTestBasic } from "./integrationtest"; import { Wallet } from "../wallet"; import qrcodeGenerator = require("qrcode-generator"); import * as clk from "./clk"; @@ -30,6 +30,7 @@ import { OperationFailedAndReportedError } from "../operations/errors"; import { Bank } from "./bank"; import { classifyTalerUri, TalerUriType } from "../util/taleruri"; import util = require("util"); +import { Configuration } from "../util/talerconfig"; // Backwards compatibility with nodejs<0.11, where TextEncoder and TextDecoder // are not globals yet. @@ -38,7 +39,7 @@ import util = require("util"); const logger = new Logger("taler-wallet-cli.ts"); -const walletDbPath = os.homedir + "/" + ".talerwalletdb.json"; +const defaultWalletDbPath = os.homedir + "/" + ".talerwalletdb.json"; function assertUnreachable(x: never): never { throw new Error("Didn't expect to get here"); @@ -115,6 +116,9 @@ const walletCli = clk .program("wallet", { help: "Command line interface for the GNU Taler wallet.", }) + .maybeOption("walletDbFile", ["--wallet-db"], clk.STRING, { + help: "location of the wallet database file" + }) .maybeOption("inhibit", ["--inhibit"], clk.STRING, { help: "Inhibit running certain operations, useful for debugging and testing.", @@ -132,8 +136,9 @@ async function withWallet( walletCliArgs: WalletCliArgsType, f: (w: Wallet) => Promise, ): Promise { + const dbPath = walletCliArgs.wallet.walletDbFile ?? defaultWalletDbPath; const wallet = await getDefaultNodeWallet({ - persistentStoragePath: walletDbPath, + persistentStoragePath: dbPath, }); applyVerbose(walletCliArgs.wallet.verbose); try { @@ -189,7 +194,9 @@ walletCli } else { for (const h of history.history) { console.log( - `event at ${new Date(h.timestamp.t_ms).toISOString()} with type ${h.type}:`, + `event at ${new Date(h.timestamp.t_ms).toISOString()} with type ${ + h.type + }:`, ); console.log(JSON.stringify(h, undefined, 2)); console.log(); @@ -402,6 +409,23 @@ const testCli = walletCli.subcommand("testingArgs", "testing", { help: "Subcommands for testing GNU Taler deployments.", }); +testCli + .subcommand("integrationtestBasic", "integrationtest-basic") + .requiredArgument("cfgfile", clk.STRING) + .action(async args => { + const cfgStr = fs.readFileSync(args.integrationtestBasic.cfgfile, "utf8"); + const cfg = new Configuration(); + cfg.loadFromString(cfgStr); + try { + await runIntegrationTestBasic(cfg); + } catch (e) { + console.log("integration test failed"); + console.log(e) + process.exit(1); + } + process.exit(0); + }); + testCli .subcommand("testPayCmd", "test-pay", { help: "create contract and pay" }) .requiredOption("amount", ["-a", "--amount"], clk.STRING) diff --git a/tsconfig.json b/tsconfig.json index a6cd7b260..6808b3374 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -86,6 +86,7 @@ "src/util/payto.ts", "src/util/promiseUtils.ts", "src/util/query.ts", + "src/util/talerconfig.ts", "src/util/taleruri-test.ts", "src/util/taleruri.ts", "src/util/time.ts",