diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx index d65398765..ea86da518 100644 --- a/packages/demobank-ui/src/components/app.tsx +++ b/packages/demobank-ui/src/components/app.tsx @@ -15,7 +15,7 @@ */ import { - globalLogLevel, + getGlobalLogLevel, setGlobalLogLevelFromString, } from "@gnu-taler/taler-util"; import { TranslationProvider } from "@gnu-taler/web-util/browser"; @@ -62,9 +62,7 @@ const App: FunctionalComponent = () => { ); }; (window as any).setGlobalLogLevelFromString = setGlobalLogLevelFromString; -(window as any).getGlobaLevel = () => { - return globalLogLevel; -}; +(window as any).getGlobalLevel = getGlobalLogLevel; function localStorageProvider(): Map { const map = new Map(JSON.parse(localStorage.getItem("app-cache") || "[]")); diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts index c9202c60e..926a0c93b 100644 --- a/packages/taler-harness/src/harness/harness.ts +++ b/packages/taler-harness/src/harness/harness.ts @@ -467,12 +467,29 @@ export async function setupDb(t: GlobalTestState): Promise { }; } +/** + * Make sure that the taler-integrationtest-shared database exists. + * Don't delete it if it already exists. + */ +export async function setupSharedDb(t: GlobalTestState): Promise { + const dbname = "taler-integrationtest-shared"; + const databases = await runCommand(t, "list-dbs", "psql", ["-Aqtl"]); + if (databases.indexOf("taler-integrationtest-shared") < 0) { + await runCommand(t, "createdb", "createdb", [dbname]); + } + return { + connStr: `postgres:///${dbname}`, + dbname, + }; +} + export interface BankConfig { currency: string; httpPort: number; database: string; allowRegistrations: boolean; maxDebt?: string; + overrideTestDir?: string; } export interface FakeBankConfig { @@ -518,6 +535,14 @@ function setCoin(config: Configuration, c: CoinConfig) { } } +function backoffStart(): number { + return 10; +} + +function backoffIncrement(n: number): number { + return Math.min(Math.floor(n * 1.5), 1000); +} + /** * Send an HTTP request until it succeeds or the process dies. */ @@ -529,6 +554,7 @@ export async function pingProc( if (!proc || proc.proc.exitCode !== null) { throw Error(`service process ${serviceName} not started, can't ping`); } + let nextDelay = backoffStart(); while (true) { try { logger.trace(`pinging ${serviceName} at ${url}`); @@ -537,8 +563,9 @@ export async function pingProc( return; } catch (e: any) { logger.warn(`service ${serviceName} not ready:`, e.toString()); - //console.log(e); - await delayMs(1000); + logger.info(`waiting ${nextDelay}ms on ${serviceName}`); + await delayMs(nextDelay); + nextDelay = backoffIncrement(nextDelay); } if (!proc || proc.proc.exitCode != null || proc.proc.signalCode != null) { throw Error(`service process ${serviceName} stopped unexpectedly`); @@ -857,31 +884,57 @@ export class FakebankService accountPassword: string; }[] = []; + /** + * Create a new fakebank service handle. + * + * First generates the configuration for the fakebank and + * then creates a fakebank handle, but doesn't start the fakebank + * service yet. + */ static async create( gc: GlobalTestState, bc: BankConfig, ): Promise { const config = new Configuration(); - setTalerPaths(config, gc.testDir + "/talerhome"); + const testDir = bc.overrideTestDir ?? gc.testDir; + setTalerPaths(config, testDir + "/talerhome"); config.setString("taler", "currency", bc.currency); config.setString("bank", "http_port", `${bc.httpPort}`); config.setString("bank", "serve", "http"); config.setString("bank", "max_debt_bank", `${bc.currency}:999999`); config.setString("bank", "max_debt", bc.maxDebt ?? `${bc.currency}:100`); config.setString("bank", "ram_limit", `${1024}`); - const cfgFilename = gc.testDir + "/bank.conf"; - config.write(cfgFilename); + const cfgFilename = testDir + "/bank.conf"; + config.write(cfgFilename, { excludeDefaults: true }); return new FakebankService(gc, bc, cfgFilename); } + static fromExistingConfig( + gc: GlobalTestState, + opts: { overridePath?: string }, + ): FakebankService { + const testDir = opts.overridePath ?? gc.testDir; + const cfgFilename = testDir + `/bank.conf`; + const config = Configuration.load(cfgFilename); + const bc: BankConfig = { + allowRegistrations: + config.getYesNo("bank", "allow_registrations").orUndefined() ?? true, + currency: config.getString("taler", "currency").required(), + database: "none", + httpPort: config.getNumber("bank", "http_port").required(), + maxDebt: config.getString("bank", "max_debt").required(), + }; + return new FakebankService(gc, bc, cfgFilename); + } + setSuggestedExchange(e: ExchangeServiceInterface, exchangePayto: string) { if (!!this.proc) { throw Error("Can't set suggested exchange while bank is running."); } const config = Configuration.load(this.configFile); config.setString("bank", "suggested_exchange", e.baseUrl); - config.write(this.configFile); + config.write(this.configFile, { excludeDefaults: true }); } get baseUrl(): string { @@ -958,6 +1011,7 @@ export interface ExchangeConfig { roundUnit?: string; httpPort: number; database: string; + overrideTestDir?: string; } export interface ExchangeServiceInterface { @@ -968,8 +1022,13 @@ export interface ExchangeServiceInterface { } export class ExchangeService implements ExchangeServiceInterface { - static fromExistingConfig(gc: GlobalTestState, exchangeName: string) { - const cfgFilename = gc.testDir + `/exchange-${exchangeName}.conf`; + static fromExistingConfig( + gc: GlobalTestState, + exchangeName: string, + opts: { overridePath?: string }, + ) { + const testDir = opts.overridePath ?? gc.testDir; + const cfgFilename = testDir + `/exchange-${exchangeName}.conf`; const config = Configuration.load(cfgFilename); const ec: ExchangeConfig = { currency: config.getString("taler", "currency").required(), @@ -978,7 +1037,9 @@ export class ExchangeService implements ExchangeServiceInterface { name: exchangeName, roundUnit: config.getString("taler", "currency_round_unit").required(), }; - const privFile = config.getPath("exchange", "master_priv_file").required(); + const privFile = config + .getPath("exchange-offline", "master_priv_file") + .required(); const eddsaPriv = fs.readFileSync(privFile); const keyPair: EddsaKeyPair = { eddsaPriv, @@ -1076,11 +1137,13 @@ export class ExchangeService implements ExchangeServiceInterface { changeConfig(f: (config: Configuration) => void) { const config = Configuration.load(this.configFilename); f(config); - config.write(this.configFilename); + config.write(this.configFilename, { excludeDefaults: true }); } static create(gc: GlobalTestState, e: ExchangeConfig) { + const testDir = e.overrideTestDir ?? gc.testDir; const config = new Configuration(); + setTalerPaths(config, testDir + "/talerhome"); config.setString("taler", "currency", e.currency); // Required by the exchange but not really used yet. config.setString("exchange", "aml_threshold", `${e.currency}:1000000`); @@ -1089,7 +1152,6 @@ export class ExchangeService implements ExchangeServiceInterface { "currency_round_unit", e.roundUnit ?? `${e.currency}:0.01`, ); - setTalerPaths(config, gc.testDir + "/talerhome"); config.setString( "exchange", "revocation_dir", @@ -1124,10 +1186,16 @@ export class ExchangeService implements ExchangeServiceInterface { fs.mkdirSync(path.dirname(masterPrivFile), { recursive: true }); + if (fs.existsSync(masterPrivFile)) { + throw new Error( + "master priv file already exists, can't create new exchange config", + ); + } + fs.writeFileSync(masterPrivFile, Buffer.from(exchangeMasterKey.eddsaPriv)); - const cfgFilename = gc.testDir + `/exchange-${e.name}.conf`; - config.write(cfgFilename); + const cfgFilename = testDir + `/exchange-${e.name}.conf`; + config.write(cfgFilename, { excludeDefaults: true }); return new ExchangeService(gc, e, cfgFilename, exchangeMasterKey); } @@ -1136,13 +1204,13 @@ export class ExchangeService implements ExchangeServiceInterface { offeredCoins.forEach((cc) => setCoin(config, cc(this.exchangeConfig.currency)), ); - config.write(this.configFilename); + config.write(this.configFilename, { excludeDefaults: true }); } addCoinConfigList(ccs: CoinConfig[]) { const config = Configuration.load(this.configFilename); ccs.forEach((cc) => setCoin(config, cc)); - config.write(this.configFilename); + config.write(this.configFilename, { excludeDefaults: true }); } enableAgeRestrictions(maskStr: string) { @@ -1153,7 +1221,7 @@ export class ExchangeService implements ExchangeServiceInterface { "age_groups", maskStr, ); - config.write(this.configFilename); + config.write(this.configFilename, { excludeDefaults: true }); } get masterPub() { @@ -1174,7 +1242,7 @@ export class ExchangeService implements ExchangeServiceInterface { ): Promise { const config = Configuration.load(this.configFilename); await f(config); - config.write(this.configFilename); + config.write(this.configFilename, { excludeDefaults: true }); } async addBankAccount( @@ -1214,7 +1282,7 @@ export class ExchangeService implements ExchangeServiceInterface { "password", exchangeBankAccount.accountPassword, ); - config.write(this.configFilename); + config.write(this.configFilename, { excludeDefaults: true }); } exchangeHttpProc: ProcessWrapper | undefined; @@ -1475,15 +1543,26 @@ export class ExchangeService implements ExchangeServiceInterface { ); } - async start(): Promise { - if (this.isRunning()) { - throw Error("exchange is already running"); - } + async dbinit() { await sh( this.globalState, "exchange-dbinit", `taler-exchange-dbinit -c "${this.configFilename}"`, ); + } + + async start( + opts: { skipDbinit?: boolean; skipKeyup?: boolean } = {}, + ): Promise { + if (this.isRunning()) { + throw Error("exchange is already running"); + } + + const skipDbinit = opts.skipDbinit ?? false; + + if (!skipDbinit) { + await this.dbinit(); + } this.helperCryptoEddsaProc = this.globalState.spawnService( "taler-exchange-secmod-eddsa", @@ -1514,7 +1593,14 @@ export class ExchangeService implements ExchangeServiceInterface { ); await this.pingUntilAvailable(); - await this.keyup(); + + const skipKeyup = opts.skipKeyup ?? false; + + if (!skipKeyup) { + await this.keyup(); + } else { + logger.info("skipping keyup"); + } } async pingUntilAvailable(): Promise { @@ -1530,6 +1616,7 @@ export interface MerchantConfig { currency: string; httpPort: number; database: string; + overrideTestDir?: string; } export interface PrivateOrderStatusQuery { @@ -1775,8 +1862,13 @@ export interface CreateMerchantTippingReserveRequest { } export class MerchantService implements MerchantServiceInterface { - static fromExistingConfig(gc: GlobalTestState, name: string) { - const cfgFilename = gc.testDir + `/merchant-${name}.conf`; + static fromExistingConfig( + gc: GlobalTestState, + name: string, + opts: { overridePath?: string }, + ) { + const testDir = opts.overridePath ?? gc.testDir; + const cfgFilename = testDir + `/merchant-${name}.conf`; const config = Configuration.load(cfgFilename); const mc: MerchantConfig = { currency: config.getString("taler", "currency").required(), @@ -1846,13 +1938,24 @@ export class MerchantService implements MerchantServiceInterface { } } - async start(): Promise { + async dbinit() { await runCommand( this.globalState, "merchant-dbinit", "taler-merchant-dbinit", ["-c", this.configFilename], ); + } + + /** + * Start the merchant, + */ + async start(opts: { skipDbinit?: boolean } = {}): Promise { + const skipSetup = opts.skipDbinit ?? false; + + if (!skipSetup) { + await this.dbinit(); + } this.proc = this.globalState.spawnService( "taler-merchant-httpd", @@ -1871,11 +1974,12 @@ export class MerchantService implements MerchantServiceInterface { gc: GlobalTestState, mc: MerchantConfig, ): Promise { + const testDir = mc.overrideTestDir ?? gc.testDir; const config = new Configuration(); config.setString("taler", "currency", mc.currency); - const cfgFilename = gc.testDir + `/merchant-${mc.name}.conf`; - setTalerPaths(config, gc.testDir + "/talerhome"); + const cfgFilename = testDir + `/merchant-${mc.name}.conf`; + setTalerPaths(config, testDir + "/talerhome"); config.setString("merchant", "serve", "tcp"); config.setString("merchant", "port", `${mc.httpPort}`); config.setString( @@ -1884,7 +1988,7 @@ export class MerchantService implements MerchantServiceInterface { "${TALER_DATA_HOME}/merchant/merchant.priv", ); config.setString("merchantdb-postgres", "config", mc.database); - config.write(cfgFilename); + config.write(cfgFilename, { excludeDefaults: true }); return new MerchantService(gc, mc, cfgFilename); } @@ -1902,7 +2006,7 @@ export class MerchantService implements MerchantServiceInterface { this.merchantConfig.currency, ); config.setString(`merchant-exchange-${e.name}`, "master_key", e.masterPub); - config.write(this.configFilename); + config.write(this.configFilename, { excludeDefaults: true }); } async addDefaultInstance(): Promise { @@ -1935,14 +2039,8 @@ export class MerchantService implements MerchantServiceInterface { name: instanceConfig.name, address: instanceConfig.address ?? {}, jurisdiction: instanceConfig.jurisdiction ?? {}, - default_max_wire_fee: - instanceConfig.defaultMaxWireFee ?? - `${this.merchantConfig.currency}:1.0`, - default_wire_fee_amortization: - instanceConfig.defaultWireFeeAmortization ?? 3, - default_max_deposit_fee: - instanceConfig.defaultMaxDepositFee ?? - `${this.merchantConfig.currency}:1.0`, + // FIXME: In some tests, we might want to make this configurable + use_stefan: true, default_wire_transfer_delay: instanceConfig.defaultWireTransferDelay ?? Duration.toTalerProtocolDuration( @@ -1984,9 +2082,6 @@ export interface PartialMerchantInstanceConfig { paytoUris: string[]; address?: unknown; jurisdiction?: unknown; - defaultMaxWireFee?: string; - defaultMaxDepositFee?: string; - defaultWireFeeAmortization?: number; defaultWireTransferDelay?: TalerProtocolDuration; defaultPayDelay?: TalerProtocolDuration; } @@ -2029,9 +2124,7 @@ export interface MerchantInstanceConfig { name: string; address: unknown; jurisdiction: unknown; - default_max_wire_fee: string; - default_max_deposit_fee: string; - default_wire_fee_amortization: number; + use_stefan: boolean; default_wire_transfer_delay: TalerProtocolDuration; default_pay_delay: TalerProtocolDuration; } @@ -2229,12 +2322,15 @@ export class WalletService { } async pingUntilAvailable(): Promise { + let nextDelay = backoffStart(); while (1) { try { await tryUnixConnect(this.socketPath); } catch (e) { - logger.info(`connection attempt failed: ${e}`); - await delayMs(200); + logger.info(`wallet connection attempt failed: ${e}`); + logger.info(`waiting on wallet for ${nextDelay}ms`); + await delayMs(nextDelay); + nextDelay = backoffIncrement(nextDelay); continue; } logger.info("connection to wallet-core succeeded"); diff --git a/packages/taler-harness/src/harness/helpers.ts b/packages/taler-harness/src/harness/helpers.ts index 3e91c8bd9..d41ffdd00 100644 --- a/packages/taler-harness/src/harness/helpers.ts +++ b/packages/taler-harness/src/harness/helpers.ts @@ -32,6 +32,7 @@ import { NotificationType, WalletNotification, TransactionMajorState, + Logger, } from "@gnu-taler/taler-util"; import { BankAccessApi, @@ -49,18 +50,24 @@ import { DbInfo, ExchangeService, ExchangeServiceInterface, + FakebankService, getPayto, GlobalTestState, MerchantPrivateApi, MerchantService, MerchantServiceInterface, setupDb, + setupSharedDb, WalletCli, WalletClient, WalletService, WithAuthorization, } from "./harness.js"; +import * as fs from "fs"; + +const logger = new Logger("helpers.ts"); + /** * @deprecated */ @@ -94,6 +101,10 @@ export interface EnvOptions { ageMaskSpec?: string; mixedAgeRestriction?: boolean; + + additionalExchangeConfig?(e: ExchangeService): void; + additionalMerchantConfig?(m: MerchantService): void; + additionalBankConfig?(b: BankService): void; } /** @@ -204,6 +215,179 @@ export async function createSimpleTestkudosEnvironment( }; } +export async function useSharedTestkudosEnvironment(t: GlobalTestState) { + const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")); + + const sharedDir = `/tmp/taler-harness@${process.env.USER}`; + + fs.mkdirSync(sharedDir, { recursive: true }); + + const db = await setupSharedDb(t); + + let bank: FakebankService; + + const prevSetupDone = fs.existsSync(sharedDir + "/setup-done"); + + logger.info(`previous setup done: ${prevSetupDone}`); + + + // Wallet has longer startup-time and no dependencies, + // so we start it rather early. + const walletStartProm = createWalletDaemonWithClient(t, { name: "wallet" }) + + if (fs.existsSync(sharedDir + "/bank.conf")) { + logger.info("reusing existing bank"); + bank = BankService.fromExistingConfig(t, { + overridePath: sharedDir, + }); + } else { + logger.info("creating new bank config"); + bank = await BankService.create(t, { + allowRegistrations: true, + currency: "TESTKUDOS", + database: db.connStr, + httpPort: 8082, + overrideTestDir: sharedDir, + }); + } + + logger.info("setting up exchange"); + + const exchangeName = "testexchange-1"; + const exchangeConfigFilename = sharedDir + `/exchange-${exchangeName}.conf`; + + logger.info(`exchange config filename: ${exchangeConfigFilename}`); + + let exchange: ExchangeService; + + if (fs.existsSync(exchangeConfigFilename)) { + logger.info("reusing existing exchange config"); + exchange = ExchangeService.fromExistingConfig(t, exchangeName, { + overridePath: sharedDir, + }); + } else { + logger.info("creating new exchange config"); + exchange = ExchangeService.create(t, { + name: "testexchange-1", + currency: "TESTKUDOS", + httpPort: 8081, + database: db.connStr, + overrideTestDir: sharedDir, + }); + } + + logger.info("setting up merchant"); + + let merchant: MerchantService; + const merchantName = "testmerchant-1"; + const merchantConfigFilename = sharedDir + `/merchant-${merchantName}}`; + + if (fs.existsSync(merchantConfigFilename)) { + merchant = MerchantService.fromExistingConfig(t, merchantName, { + overridePath: sharedDir, + }); + } else { + merchant = await MerchantService.create(t, { + name: "testmerchant-1", + currency: "TESTKUDOS", + httpPort: 8083, + database: db.connStr, + overrideTestDir: sharedDir, + }); + } + + logger.info("creating bank account for exchange"); + + const exchangeBankAccount = await bank.createExchangeAccount( + "myexchange", + "x", + ); + + logger.info("creating exchange bank account"); + await exchange.addBankAccount("1", exchangeBankAccount); + + bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); + + exchange.addCoinConfigList(coinConfig); + + merchant.addExchange(exchange); + + logger.info("basic setup done, starting services"); + + if (!prevSetupDone) { + // Must be done sequentially, due to a concurrency + // issue in the *-dbinit tools. + await exchange.dbinit(); + await merchant.dbinit(); + } + + const bankStart = async () => { + await bank.start(); + await bank.pingUntilAvailable(); + }; + + const exchangeStart = async () => { + await exchange.start({ + skipDbinit: true, + skipKeyup: prevSetupDone, + }); + await exchange.pingUntilAvailable(); + }; + + const merchStart = async () => { + await merchant.start({ + skipDbinit: true, + }); + await merchant.pingUntilAvailable(); + + if (!prevSetupDone) { + await merchant.addInstance({ + id: "default", + name: "Default Instance", + paytoUris: [getPayto("merchant-default")], + defaultWireTransferDelay: Duration.toTalerProtocolDuration( + Duration.fromSpec({ minutes: 1 }), + ), + }); + + await merchant.addInstance({ + id: "minst1", + name: "minst1", + paytoUris: [getPayto("minst1")], + defaultWireTransferDelay: Duration.toTalerProtocolDuration( + Duration.fromSpec({ minutes: 1 }), + ), + }); + } + }; + + await bankStart() + + const res = await Promise.all([ + exchangeStart(), + merchStart(), + undefined, + walletStartProm, + ]); + + const walletClient = res[3].walletClient; + const walletService = res[3].walletService; + + fs.writeFileSync(sharedDir + "/setup-done", "OK"); + + logger.info("setup done!"); + + return { + commonDb: db, + exchange, + merchant, + walletClient, + walletService, + bank, + exchangeBankAccount, + }; +} + /** * Run a test case with a simple TESTKUDOS Taler environment, consisting * of one exchange, one bank and one merchant. @@ -246,6 +430,9 @@ export async function createSimpleTestkudosEnvironmentV2( bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); + if (opts.additionalBankConfig) { + opts.additionalBankConfig(bank); + } await bank.start(); await bank.pingUntilAvailable(); @@ -272,11 +459,17 @@ export async function createSimpleTestkudosEnvironmentV2( exchange.addCoinConfigList(coinConfig); } + if (opts.additionalExchangeConfig) { + opts.additionalExchangeConfig(exchange); + } await exchange.start(); await exchange.pingUntilAvailable(); merchant.addExchange(exchange); + if (opts.additionalMerchantConfig) { + opts.additionalMerchantConfig(merchant); + } await merchant.start(); await merchant.pingUntilAvailable(); @@ -336,7 +529,7 @@ export async function createWalletDaemonWithClient( const walletClient = new WalletClient({ unixPath: walletService.socketPath, onNotification(n) { - console.log("got notification", n); + console.log(`got ${args.name} notification`, n); if (args.handleNotification) { args.handleNotification(n); } @@ -610,6 +803,8 @@ export async function applyTimeTravel( /** * Make a simple payment and check that it succeeded. + * + * @deprecated */ export async function makeTestPayment( t: GlobalTestState, diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts index ec99232f5..841e17dc7 100644 --- a/packages/taler-harness/src/index.ts +++ b/packages/taler-harness/src/index.ts @@ -170,6 +170,39 @@ const sandcastleCli = testingCli.subcommand("sandcastleArgs", "sandcastle", { help: "Subcommands for handling GNU Taler sandcastle deployments.", }); +const configCli = testingCli.subcommand("configArgs", "config", { + help: "Subcommands for handling the Taler configuration.", +}); + +configCli.subcommand("show", "show").action(async (args) => { + const config = Configuration.load(); + const cfgStr = config.stringify({ + diagnostics: true, + }); + console.log(cfgStr); +}); + +configCli + .subcommand("get", "get") + .requiredArgument("section", clk.STRING) + .requiredArgument("option", clk.STRING) + .flag("file", ["-f"]) + .action(async (args) => { + const config = Configuration.load(); + let res; + if (args.get.file) { + res = config.getString(args.get.section, args.get.option); + } else { + res = config.getPath(args.get.section, args.get.option); + } + if (res.isDefined()) { + console.log(res.value); + } else { + console.warn("not found"); + process.exit(1); + } + }); + const deploymentCli = testingCli.subcommand("deploymentArgs", "deployment", { help: "Subcommands for handling GNU Taler deployments.", }); @@ -208,8 +241,10 @@ deploymentCli const bankAccessApiClient = new BankAccessApiClient({ baseUrl: args.tipTopup.bankAccessUrl, - username: args.tipTopup.bankAccount, - password: args.tipTopup.bankPassword, + auth: { + username: args.tipTopup.bankAccount, + password: args.tipTopup.bankPassword, + }, allowHttp: true, }); @@ -219,10 +254,12 @@ deploymentCli console.log("payto URI:", paytoUri); - const transactions = await bankAccessApiClient.getTransactions(); + const transactions = await bankAccessApiClient.getTransactions( + args.tipTopup.bankAccount, + ); console.log("transactions:", j2s(transactions)); - await bankAccessApiClient.createTransaction({ + await bankAccessApiClient.createTransaction(args.tipTopup.bankAccount, { amount, paytoUri, }); diff --git a/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts b/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts index 8bf71b63d..af90ef1c5 100644 --- a/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts +++ b/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts @@ -17,12 +17,14 @@ /** * Imports. */ +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { defaultCoinConfig } from "../harness/denomStructures.js"; -import { GlobalTestState, WalletCli } from "../harness/harness.js"; +import { GlobalTestState } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironment, - withdrawViaBank, - makeTestPayment, + createSimpleTestkudosEnvironmentV2, + createWalletDaemonWithClient, + makeTestPaymentV2, + withdrawViaBankV2, } from "../harness/helpers.js"; /** @@ -32,11 +34,11 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) { // Set up test environment const { - wallet: walletOne, + walletClient: walletOne, bank, exchange, merchant, - } = await createSimpleTestkudosEnvironment( + } = await createSimpleTestkudosEnvironmentV2( t, defaultCoinConfig.map((x) => x("TESTKUDOS")), { @@ -45,20 +47,27 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) { }, ); - const walletTwo = new WalletCli(t, "walletTwo"); - const walletThree = new WalletCli(t, "walletThree"); + const { walletClient: walletTwo } = await createWalletDaemonWithClient(t, { + name: "w2", + }); + + const { walletClient: walletThree } = await createWalletDaemonWithClient(t, { + name: "w3", + }); { - const wallet = walletOne; + const walletClient = walletOne; - await withdrawViaBank(t, { - wallet, + const wres = await withdrawViaBankV2(t, { + walletClient, bank, exchange, amount: "TESTKUDOS:20", restrictAge: 13, }); + await wres.withdrawalFinishedCond; + const order = { summary: "Buy me!", amount: "TESTKUDOS:5", @@ -66,41 +75,46 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) { minimum_age: 9, }; - await makeTestPayment(t, { wallet, merchant, order }); - await wallet.runUntilDone(); + await makeTestPaymentV2(t, { walletClient, merchant, order }); + await walletClient.call( + WalletApiOperation.TestingWaitTransactionsFinal, + {}, + ); } { - const wallet = walletTwo; - - await withdrawViaBank(t, { - wallet, + const wres = await withdrawViaBankV2(t, { + walletClient: walletTwo, bank, exchange, amount: "TESTKUDOS:20", restrictAge: 13, }); + + await wres.withdrawalFinishedCond; + const order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", }; - await makeTestPayment(t, { wallet, merchant, order }); - await wallet.runUntilDone(); + await makeTestPaymentV2(t, { walletClient: walletTwo, merchant, order }); + await walletTwo.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); } { - const wallet = walletThree; - - await withdrawViaBank(t, { - wallet, + const wres = await withdrawViaBankV2(t, { + walletClient: walletThree, bank, exchange, amount: "TESTKUDOS:20", }); + + await wres.withdrawalFinishedCond; + const order = { summary: "Buy me!", amount: "TESTKUDOS:5", @@ -108,8 +122,8 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) { minimum_age: 9, }; - await makeTestPayment(t, { wallet, merchant, order }); - await wallet.runUntilDone(); + await makeTestPaymentV2(t, { walletClient: walletThree, merchant, order }); + await walletThree.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); } } diff --git a/packages/taler-harness/src/integrationtests/test-bank-api.ts b/packages/taler-harness/src/integrationtests/test-bank-api.ts index a0e1c2085..9ac16980b 100644 --- a/packages/taler-harness/src/integrationtests/test-bank-api.ts +++ b/packages/taler-harness/src/integrationtests/test-bank-api.ts @@ -17,23 +17,22 @@ /** * Imports. */ -import { - GlobalTestState, - WalletCli, - ExchangeService, - setupDb, - BankService, - MerchantService, - getPayto, -} from "../harness/harness.js"; import { createEddsaKeyPair, encodeCrock } from "@gnu-taler/taler-util"; -import { defaultCoinConfig } from "../harness/denomStructures.js"; import { - BankApi, BankAccessApi, + BankApi, CreditDebitIndicator, WireGatewayApiClient, } from "@gnu-taler/taler-wallet-core"; +import { defaultCoinConfig } from "../harness/denomStructures.js"; +import { + BankService, + ExchangeService, + GlobalTestState, + MerchantService, + getPayto, + setupDb, +} from "../harness/harness.js"; /** * Run test for basic, bank-integrated withdrawal. diff --git a/packages/taler-harness/src/integrationtests/test-claim-loop.ts b/packages/taler-harness/src/integrationtests/test-claim-loop.ts index 921391283..32706c28b 100644 --- a/packages/taler-harness/src/integrationtests/test-claim-loop.ts +++ b/packages/taler-harness/src/integrationtests/test-claim-loop.ts @@ -17,13 +17,13 @@ /** * Imports. */ +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { URL } from "url"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironment, - withdrawViaBank, + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2, } from "../harness/helpers.js"; -import { URL } from "url"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; /** * Run test for the merchant's order lifecycle. @@ -34,10 +34,15 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; export async function runClaimLoopTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); // Set up order. const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { @@ -61,7 +66,7 @@ export async function runClaimLoopTest(t: GlobalTestState) { // Make wallet claim the unpaid order. t.assertTrue(orderStatusBefore.order_status === "unpaid"); const talerPayUri = orderStatusBefore.taler_pay_uri; - await wallet.client.call(WalletApiOperation.PreparePayForUri, { + await walletClient.call(WalletApiOperation.PreparePayForUri, { talerPayUri, }); diff --git a/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts b/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts index 27c336ea6..5a471b9aa 100644 --- a/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts +++ b/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts @@ -18,22 +18,29 @@ * Imports. */ import { PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util"; -import { Wallet, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironment, - withdrawViaBank, + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2, } from "../harness/helpers.js"; export async function runDenomUnofferedTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + const wres = await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); + + await wres.withdrawalFinishedCond; // Make the exchange forget the denomination. // Effectively we completely reset the exchange, @@ -67,7 +74,7 @@ export async function runDenomUnofferedTest(t: GlobalTestState) { // Make wallet pay for the order - const preparePayResult = await wallet.client.call( + const preparePayResult = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri: orderStatus.taler_pay_uri, @@ -78,11 +85,11 @@ export async function runDenomUnofferedTest(t: GlobalTestState) { preparePayResult.status === PreparePayResultType.PaymentPossible, ); - const confirmResp = await wallet.client.call(WalletApiOperation.ConfirmPay, { + const confirmResp = await walletClient.call(WalletApiOperation.ConfirmPay, { proposalId: preparePayResult.proposalId, }); - const tx = await wallet.client.call(WalletApiOperation.GetTransactionById, { + const tx = await walletClient.call(WalletApiOperation.GetTransactionById, { transactionId: confirmResp.transactionId, }); @@ -96,21 +103,26 @@ export async function runDenomUnofferedTest(t: GlobalTestState) { TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND, ); - await wallet.client.call(WalletApiOperation.AddExchange, { + await walletClient.call(WalletApiOperation.AddExchange, { exchangeBaseUrl: exchange.baseUrl, forceUpdate: true, }); - await wallet.client.call(WalletApiOperation.DeleteTransaction, { + await walletClient.call(WalletApiOperation.DeleteTransaction, { transactionId: confirmResp.transactionId, }); // Now withdrawal should work again. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {}); + const txs = await walletClient.call(WalletApiOperation.GetTransactions, {}); console.log(JSON.stringify(txs, undefined, 2)); } diff --git a/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts b/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts index 6c0d070d0..05bbbfaa1 100644 --- a/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts +++ b/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts @@ -30,14 +30,15 @@ import { depositCoin, downloadExchangeInfo, findDenomOrThrow, - refreshCoin, SynchronousCryptoWorkerFactoryPlain, topupReserveWithDemobank, Wallet, withdrawCoin, } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; +import { + createSimpleTestkudosEnvironmentV2, +} from "../harness/helpers.js"; /** * Run test for basic, bank-integrated withdrawal and payment. @@ -45,7 +46,7 @@ import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; export async function runExchangeDepositTest(t: GlobalTestState) { // Set up test environment - const { bank, exchange } = await createSimpleTestkudosEnvironment(t); + const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t); const http = createPlatformHttpLib({ allowHttp: true, diff --git a/packages/taler-harness/src/integrationtests/test-fee-regression.ts b/packages/taler-harness/src/integrationtests/test-fee-regression.ts index 8c5a5bea4..e0dc4bc3b 100644 --- a/packages/taler-harness/src/integrationtests/test-fee-regression.ts +++ b/packages/taler-harness/src/integrationtests/test-fee-regression.ts @@ -19,18 +19,18 @@ */ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { - GlobalTestState, BankService, ExchangeService, + GlobalTestState, MerchantService, - setupDb, - WalletCli, getPayto, + setupDb, } from "../harness/harness.js"; import { - withdrawViaBank, - makeTestPayment, - SimpleTestEnvironment, + SimpleTestEnvironmentNg, + createWalletDaemonWithClient, + makeTestPaymentV2, + withdrawViaBankV2, } from "../harness/helpers.js"; /** @@ -39,7 +39,7 @@ import { */ export async function createMyTestkudosEnvironment( t: GlobalTestState, -): Promise { +): Promise { const db = await setupDb(t); const bank = await BankService.create(t, { @@ -147,13 +147,19 @@ export async function createMyTestkudosEnvironment( console.log("setup done!"); - const wallet = new WalletCli(t); + const { walletClient, walletService } = await createWalletDaemonWithClient( + t, + { + name: "w1", + }, + ); return { commonDb: db, exchange, merchant, - wallet, + walletClient, + walletService, bank, exchangeBankAccount, }; @@ -165,19 +171,21 @@ export async function createMyTestkudosEnvironment( export async function runFeeRegressionTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = + const { walletClient, bank, exchange, merchant } = await createMyTestkudosEnvironment(t); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { - wallet, + const wres = await withdrawViaBankV2(t, { + walletClient, bank, exchange, amount: "TESTKUDOS:1.92", }); - const coins = await wallet.client.call(WalletApiOperation.DumpCoins, {}); + await wres.withdrawalFinishedCond; + + const coins = await walletClient.call(WalletApiOperation.DumpCoins, {}); // Make sure we really withdraw one 0.64 and one 1.28 coin. t.assertTrue(coins.coins.length === 2); @@ -188,11 +196,11 @@ export async function runFeeRegressionTest(t: GlobalTestState) { fulfillment_url: "taler://fulfillment-success/thx", }; - await makeTestPayment(t, { wallet, merchant, order }); + await makeTestPaymentV2(t, { walletClient, merchant, order }); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {}); + const txs = await walletClient.call(WalletApiOperation.GetTransactions, {}); t.assertAmountEquals(txs.transactions[1].amountEffective, "TESTKUDOS:1.30"); console.log(txs); } diff --git a/packages/taler-harness/src/integrationtests/test-forced-selection.ts b/packages/taler-harness/src/integrationtests/test-forced-selection.ts index d0621f000..3425dadf1 100644 --- a/packages/taler-harness/src/integrationtests/test-forced-selection.ts +++ b/packages/taler-harness/src/integrationtests/test-forced-selection.ts @@ -20,7 +20,7 @@ import { j2s } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; +import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js"; /** * Run test for forced denom/coin selection. @@ -28,14 +28,14 @@ import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; export async function runForcedSelectionTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); - await wallet.client.call(WalletApiOperation.AddExchange, { + await walletClient.call(WalletApiOperation.AddExchange, { exchangeBaseUrl: exchange.baseUrl, }); - await wallet.client.call(WalletApiOperation.WithdrawTestBalance, { + await walletClient.call(WalletApiOperation.WithdrawTestBalance, { exchangeBaseUrl: exchange.baseUrl, amount: "TESTKUDOS:10", bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl, @@ -49,13 +49,13 @@ export async function runForcedSelectionTest(t: GlobalTestState) { }, }); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {}); + const coinDump = await walletClient.call(WalletApiOperation.DumpCoins, {}); console.log(coinDump); t.assertDeepEqual(coinDump.coins.length, 3); - const payResp = await wallet.client.call(WalletApiOperation.TestPay, { + const payResp = await walletClient.call(WalletApiOperation.TestPay, { amount: "TESTKUDOS:3", merchantBaseUrl: merchant.makeInstanceBaseUrl(), summary: "bla", diff --git a/packages/taler-harness/src/integrationtests/test-libeufin-api-bankaccount.ts b/packages/taler-harness/src/integrationtests/test-libeufin-api-bankaccount.ts index a5396a472..f36168301 100644 --- a/packages/taler-harness/src/integrationtests/test-libeufin-api-bankaccount.ts +++ b/packages/taler-harness/src/integrationtests/test-libeufin-api-bankaccount.ts @@ -19,7 +19,6 @@ */ import { GlobalTestState } from "../harness/harness.js"; import { - NexusUserBundle, LibeufinNexusApi, LibeufinNexusService, LibeufinSandboxService, diff --git a/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts b/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts index 5c10c029e..68b0174cc 100644 --- a/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts +++ b/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts @@ -23,7 +23,6 @@ import { NexusUserBundle, launchLibeufinServices, LibeufinNexusApi, - LibeufinCli, } from "../harness/libeufin.js"; /** diff --git a/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts b/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts index 1b5d50fd1..534b35278 100644 --- a/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts +++ b/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts @@ -67,12 +67,10 @@ export async function runMerchantInstancesUrlsTest(t: GlobalTestState) { await clientForDefault.createInstance({ id: "default", address: {}, - default_max_deposit_fee: "TESTKUDOS:1", - default_max_wire_fee: "TESTKUDOS:1", + use_stefan: true, default_pay_delay: Duration.toTalerProtocolDuration( Duration.fromSpec({ seconds: 60 }), ), - default_wire_fee_amortization: 1, default_wire_transfer_delay: Duration.toTalerProtocolDuration( Duration.fromSpec({ seconds: 60 }), ), @@ -92,12 +90,10 @@ export async function runMerchantInstancesUrlsTest(t: GlobalTestState) { await clientForDefault.createInstance({ id: "myinst", address: {}, - default_max_deposit_fee: "TESTKUDOS:1", - default_max_wire_fee: "TESTKUDOS:1", default_pay_delay: Duration.toTalerProtocolDuration( Duration.fromSpec({ seconds: 60 }), ), - default_wire_fee_amortization: 1, + use_stefan: true, default_wire_transfer_delay: Duration.toTalerProtocolDuration( Duration.fromSpec({ seconds: 60 }), ), diff --git a/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts b/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts index 7d2ba99b2..59f23fe5d 100644 --- a/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts +++ b/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts @@ -17,20 +17,20 @@ /** * Imports. */ +import { + ConfirmPayResultType, + PreparePayResultType, + URL, + codecForMerchantOrderStatusUnpaid, +} from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import axiosImp from "axios"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironment, - withdrawViaBank, + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2 } from "../harness/helpers.js"; -import { - PreparePayResultType, - codecForMerchantOrderStatusUnpaid, - ConfirmPayResultType, - URL, -} from "@gnu-taler/taler-util"; -import axiosImp from "axios"; const axios = axiosImp.default; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; /** * Run test for basic, bank-integrated withdrawal. @@ -38,12 +38,19 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; export async function runMerchantLongpollingTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + const wres = await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); + + await wres.withdrawalFinishedCond; /** * ========================================================================= @@ -113,7 +120,7 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) { * ========================================================================= */ - let preparePayResp = await wallet.client.call( + let preparePayResp = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri: pubUnpaidStatus.taler_pay_uri, @@ -148,7 +155,7 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) { publicOrderStatusResp.data, ); - const confirmPayRes = await wallet.client.call( + const confirmPayRes = await walletClient.call( WalletApiOperation.ConfirmPay, { proposalId: proposalId, diff --git a/packages/taler-harness/src/integrationtests/test-payment-abort.ts b/packages/taler-harness/src/integrationtests/test-payment-abort.ts index 0ac953240..40438c583 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-abort.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-abort.ts @@ -24,7 +24,6 @@ import { } from "../harness/helpers.js"; import { FaultInjectionRequestContext, - FaultInjectionResponseContext, } from "../harness/faultInjection.js"; import { codecForMerchantOrderStatusUnpaid, diff --git a/packages/taler-harness/src/integrationtests/test-payment-claim.ts b/packages/taler-harness/src/integrationtests/test-payment-claim.ts index e93d2c44c..eb219c1e7 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-claim.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-claim.ts @@ -17,18 +17,17 @@ /** * Imports. */ +import { PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState, MerchantPrivateApi, WalletCli, } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironment, - withdrawViaBank, + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2, } from "../harness/helpers.js"; -import { PreparePayResultType } from "@gnu-taler/taler-util"; -import { TalerErrorCode } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; /** * Run test for basic, bank-integrated withdrawal. @@ -36,14 +35,19 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; export async function runPaymentClaimTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); const walletTwo = new WalletCli(t, "two"); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); // Set up order. @@ -65,7 +69,7 @@ export async function runPaymentClaimTest(t: GlobalTestState) { // Make wallet pay for the order - const preparePayResult = await wallet.client.call( + const preparePayResult = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri, @@ -82,7 +86,7 @@ export async function runPaymentClaimTest(t: GlobalTestState) { }); }); - await wallet.client.call(WalletApiOperation.ConfirmPay, { + await walletClient.call(WalletApiOperation.ConfirmPay, { proposalId: preparePayResult.proposalId, }); diff --git a/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts b/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts index b66eda2f1..21d76397d 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts @@ -17,11 +17,12 @@ /** * Imports. */ +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironment, - withdrawViaBank, - makeTestPayment, + createSimpleTestkudosEnvironmentV2, + makeTestPaymentV2, + withdrawViaBankV2, } from "../harness/helpers.js"; /** @@ -30,12 +31,19 @@ import { export async function runPaymentForgettableTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + const wres = await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); + + await wres.withdrawalFinishedCond; { const order = { @@ -50,7 +58,7 @@ export async function runPaymentForgettableTest(t: GlobalTestState) { }, }; - await makeTestPayment(t, { wallet, merchant, order }); + await makeTestPaymentV2(t, { walletClient, merchant, order }); } console.log("testing with forgettable field without hash"); @@ -68,10 +76,10 @@ export async function runPaymentForgettableTest(t: GlobalTestState) { }, }; - await makeTestPayment(t, { wallet, merchant, order }); + await makeTestPaymentV2(t, { walletClient, merchant, order }); } - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); } runPaymentForgettableTest.suites = ["wallet", "merchant"]; diff --git a/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts b/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts index 1099a8188..e16cf9dd1 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts @@ -17,13 +17,13 @@ /** * Imports. */ -import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { - createSimpleTestkudosEnvironment, - withdrawViaBank, -} from "../harness/helpers.js"; import { PreparePayResultType } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; +import { + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2, +} from "../harness/helpers.js"; /** * Test the wallet-core payment API, especially that repeated operations @@ -32,12 +32,17 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; export async function runPaymentIdempotencyTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); // Set up order. @@ -59,14 +64,14 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) { // Make wallet pay for the order - const preparePayResult = await wallet.client.call( + const preparePayResult = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri: orderStatus.taler_pay_uri, }, ); - const preparePayResultRep = await wallet.client.call( + const preparePayResultRep = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri: orderStatus.taler_pay_uri, @@ -82,7 +87,7 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) { const proposalId = preparePayResult.proposalId; - const confirmPayResult = await wallet.client.call( + const confirmPayResult = await walletClient.call( WalletApiOperation.ConfirmPay, { proposalId: proposalId, @@ -91,7 +96,7 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) { console.log("confirm pay result", confirmPayResult); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); // Check if payment was successful. @@ -101,7 +106,7 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) { t.assertTrue(orderStatus.order_status === "paid"); - const preparePayResultAfter = await wallet.client.call( + const preparePayResultAfter = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri, diff --git a/packages/taler-harness/src/integrationtests/test-payment-share.ts b/packages/taler-harness/src/integrationtests/test-payment-share.ts index 2f853b91b..156571372 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-share.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-share.ts @@ -17,21 +17,17 @@ /** * Imports. */ +import { + ConfirmPayResultType, + PreparePayResultType, +} from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { createSimpleTestkudosEnvironmentV2, - withdrawViaBankV2, - makeTestPaymentV2, createWalletDaemonWithClient, + withdrawViaBankV2, } from "../harness/helpers.js"; -import { - ConfirmPayResultType, - PreparePayResultType, - j2s, - parsePayUri, - stringifyPayUri, -} from "@gnu-taler/taler-util"; /** * Run test for basic, bank-integrated withdrawal and payment. diff --git a/packages/taler-harness/src/integrationtests/test-payment-template.ts b/packages/taler-harness/src/integrationtests/test-payment-template.ts index 41e43e28a..172791648 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-template.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-template.ts @@ -17,13 +17,12 @@ /** * Imports. */ -import { ConfirmPayResultType, Duration, PreparePayResultType, TalerProtocolTimestamp } from "@gnu-taler/taler-util"; +import { ConfirmPayResultType, Duration, PreparePayResultType } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironment, - withdrawViaBank, - makeTestPayment, + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2 } from "../harness/helpers.js"; /** @@ -32,8 +31,8 @@ import { export async function runPaymentTemplateTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); await MerchantPrivateApi.createTemplate(merchant, "default", { template_id: "template1", @@ -51,11 +50,11 @@ export async function runPaymentTemplateTest(t: GlobalTestState) { // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:20" }); // Request a template payment - const preparePayResult = await wallet.client.call( + const preparePayResult = await walletClient.call( WalletApiOperation.PreparePayForTemplate, { talerPayTemplateUri: `taler+http://pay-template/localhost:${merchant.port}/template1?amount=TESTKUDOS:1`, @@ -71,7 +70,7 @@ export async function runPaymentTemplateTest(t: GlobalTestState) { // Pay for it - const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, { + const r2 = await walletClient.call(WalletApiOperation.ConfirmPay, { proposalId: preparePayResult.proposalId, }); @@ -88,8 +87,7 @@ export async function runPaymentTemplateTest(t: GlobalTestState) { ); t.assertTrue(orderStatus.order_status === "paid"); - - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); } runPaymentTemplateTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/test-payment-zero.ts b/packages/taler-harness/src/integrationtests/test-payment-zero.ts index 264cffd06..7423751a5 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-zero.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-zero.ts @@ -20,9 +20,9 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironment, - withdrawViaBank, - makeTestPayment, + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2, + makeTestPaymentV2, } from "../harness/helpers.js"; import { TransactionMajorState } from "@gnu-taler/taler-util"; @@ -33,19 +33,19 @@ import { TransactionMajorState } from "@gnu-taler/taler-util"; export async function runPaymentZeroTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); // First, make a "free" payment when we don't even have // any money in the // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:20" }); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - await makeTestPayment(t, { - wallet, + await makeTestPaymentV2(t, { + walletClient, merchant, order: { summary: "I am free!", @@ -54,9 +54,9 @@ export async function runPaymentZeroTest(t: GlobalTestState) { }, }); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const transactions = await wallet.client.call( + const transactions = await walletClient.call( WalletApiOperation.GetTransactions, {}, ); diff --git a/packages/taler-harness/src/integrationtests/test-paywall-flow.ts b/packages/taler-harness/src/integrationtests/test-paywall-flow.ts index a9601c625..b0477a049 100644 --- a/packages/taler-harness/src/integrationtests/test-paywall-flow.ts +++ b/packages/taler-harness/src/integrationtests/test-paywall-flow.ts @@ -18,19 +18,23 @@ * Imports. */ import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { - createSimpleTestkudosEnvironment, - withdrawViaBank, -} from "../harness/helpers.js"; import { PreparePayResultType, codecForMerchantOrderStatusUnpaid, ConfirmPayResultType, URL, } from "@gnu-taler/taler-util"; -import axiosImp from "axios"; -const axios = axiosImp.default; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2, +} from "../harness/helpers.js"; +import { createPlatformHttpLib } from "@gnu-taler/taler-util/http"; + +const httpLib = createPlatformHttpLib({ + allowHttp: true, + enableThrottling: false, +}); /** * Run test for basic, bank-integrated withdrawal. @@ -38,12 +42,17 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; export async function runPaywallFlowTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); /** * ========================================================================= @@ -77,9 +86,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) { t.assertTrue(orderStatus.already_paid_order_id === undefined); let publicOrderStatusUrl = new URL(orderStatus.order_status_url); - let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { - validateStatus: () => true, - }); + let publicOrderStatusResp = await httpLib.fetch(publicOrderStatusUrl.href); if (publicOrderStatusResp.status != 402) { throw Error( @@ -88,12 +95,12 @@ export async function runPaywallFlowTest(t: GlobalTestState) { } let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( - publicOrderStatusResp.data, + publicOrderStatusResp.json(), ); console.log(pubUnpaidStatus); - let preparePayResp = await wallet.client.call( + let preparePayResp = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri: pubUnpaidStatus.taler_pay_uri, @@ -105,10 +112,8 @@ export async function runPaywallFlowTest(t: GlobalTestState) { const proposalId = preparePayResp.proposalId; console.log("requesting", publicOrderStatusUrl.href); - publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { - validateStatus: () => true, - }); - console.log("response body", publicOrderStatusResp.data); + publicOrderStatusResp = await httpLib.fetch(publicOrderStatusUrl.href); + console.log("response body", publicOrderStatusResp.json()); if (publicOrderStatusResp.status != 402) { throw Error( `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`, @@ -116,26 +121,20 @@ export async function runPaywallFlowTest(t: GlobalTestState) { } pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( - publicOrderStatusResp.data, + publicOrderStatusResp.json(), ); - const confirmPayRes = await wallet.client.call( - WalletApiOperation.ConfirmPay, - { - proposalId: proposalId, - }, - ); - - t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done); - - publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { - validateStatus: () => true, + const confirmPayRes = await walletClient.call(WalletApiOperation.ConfirmPay, { + proposalId: proposalId, }); - console.log(publicOrderStatusResp.data); + t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done); + publicOrderStatusResp = await httpLib.fetch(publicOrderStatusUrl.href); + + console.log(publicOrderStatusResp.json()); if (publicOrderStatusResp.status != 200) { - console.log(publicOrderStatusResp.data); + console.log(publicOrderStatusResp.json()); throw Error( `expected status 200 (after paying), but got ${publicOrderStatusResp.status}`, ); @@ -158,7 +157,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) { // Pay with new taler://pay URI, which should // have the new session ID! // Wallet should now automatically re-play payment. - preparePayResp = await wallet.client.call( + preparePayResp = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri: talerPayUriOne, @@ -199,7 +198,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) { // Here the re-purchase detection should kick in, // and the wallet should re-pay for the old order // under the new session ID (mysession-three). - preparePayResp = await wallet.client.call( + preparePayResp = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri: orderStatus.taler_pay_uri, @@ -232,19 +231,17 @@ export async function runPaywallFlowTest(t: GlobalTestState) { console.log("requesting public status", publicOrderStatusUrl); // Ask the order status of the claimed-but-unpaid order - publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { - validateStatus: () => true, - }); + publicOrderStatusResp = await httpLib.fetch(publicOrderStatusUrl.href); if (publicOrderStatusResp.status != 402) { throw Error(`expected status 402, but got ${publicOrderStatusResp.status}`); } pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( - publicOrderStatusResp.data, + publicOrderStatusResp.json(), ); - console.log(publicOrderStatusResp.data); + console.log(publicOrderStatusResp.json()); t.assertTrue(pubUnpaidStatus.already_paid_order_id === firstOrderId); } diff --git a/packages/taler-harness/src/integrationtests/test-refund-auto.ts b/packages/taler-harness/src/integrationtests/test-refund-auto.ts index 4c2a2f94a..607080e68 100644 --- a/packages/taler-harness/src/integrationtests/test-refund-auto.ts +++ b/packages/taler-harness/src/integrationtests/test-refund-auto.ts @@ -17,13 +17,13 @@ /** * Imports. */ -import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { - createSimpleTestkudosEnvironment, - withdrawViaBank, -} from "../harness/helpers.js"; import { Duration, durationFromSpec } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; +import { + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2, +} from "../harness/helpers.js"; /** * Run test for basic, bank-integrated withdrawal. @@ -31,12 +31,17 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; export async function runRefundAutoTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); // Set up order. const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { @@ -61,13 +66,12 @@ export async function runRefundAutoTest(t: GlobalTestState) { // Make wallet pay for the order - const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, { + const r1 = await walletClient.call(WalletApiOperation.PreparePayForUri, { talerPayUri: orderStatus.taler_pay_uri, }); - await wallet.client.call(WalletApiOperation.ConfirmPay, { - // FIXME: should be validated, don't cast! - proposalId: r1.proposalId, + await walletClient.call(WalletApiOperation.ConfirmPay, { + transactionId: r1.transactionId, }); // Check if payment was successful. @@ -88,9 +92,9 @@ export async function runRefundAutoTest(t: GlobalTestState) { console.log(ref); // The wallet should now automatically pick up the refund. - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const transactions = await wallet.client.call( + const transactions = await walletClient.call( WalletApiOperation.GetTransactions, {}, ); diff --git a/packages/taler-harness/src/integrationtests/test-refund-incremental.ts b/packages/taler-harness/src/integrationtests/test-refund-incremental.ts index c8fcfc831..8e7e38b71 100644 --- a/packages/taler-harness/src/integrationtests/test-refund-incremental.ts +++ b/packages/taler-harness/src/integrationtests/test-refund-incremental.ts @@ -18,21 +18,21 @@ * Imports. */ import { - GlobalTestState, - delayMs, - MerchantPrivateApi, -} from "../harness/harness.js"; -import { - createSimpleTestkudosEnvironment, - withdrawViaBank, -} from "../harness/helpers.js"; -import { - TransactionType, Amounts, - durationFromSpec, Duration, + TransactionType, + durationFromSpec, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { + GlobalTestState, + MerchantPrivateApi, + delayMs, +} from "../harness/harness.js"; +import { + createSimpleTestkudosEnvironmentV2, + withdrawViaBankV2, +} from "../harness/helpers.js"; /** * Run test for basic, bank-integrated withdrawal. @@ -40,12 +40,17 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; export async function runRefundIncrementalTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant } = + await createSimpleTestkudosEnvironmentV2(t); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); // Set up order. @@ -68,11 +73,11 @@ export async function runRefundIncrementalTest(t: GlobalTestState) { // Make wallet pay for the order - const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, { + const r1 = await walletClient.call(WalletApiOperation.PreparePayForUri, { talerPayUri: orderStatus.taler_pay_uri, }); - await wallet.client.call(WalletApiOperation.ConfirmPay, { + await walletClient.call(WalletApiOperation.ConfirmPay, { proposalId: r1.proposalId, }); @@ -94,15 +99,15 @@ export async function runRefundIncrementalTest(t: GlobalTestState) { console.log("first refund increase response", ref); { - let wr = await wallet.client.call(WalletApiOperation.StartRefundQuery, { + let wr = await walletClient.call(WalletApiOperation.StartRefundQuery, { transactionId: r1.transactionId, }); - await wallet.runUntilDone(); - console.log(wr); - const txs = await wallet.client.call( - WalletApiOperation.GetTransactions, + await walletClient.call( + WalletApiOperation.TestingWaitTransactionsFinal, {}, ); + console.log(wr); + const txs = await walletClient.call(WalletApiOperation.GetTransactions, {}); console.log( "transactions after applying first refund:", JSON.stringify(txs, undefined, 2), @@ -136,10 +141,13 @@ export async function runRefundIncrementalTest(t: GlobalTestState) { console.log("third refund increase response", ref); { - let wr = await wallet.client.call(WalletApiOperation.StartRefundQuery, { + let wr = await walletClient.call(WalletApiOperation.StartRefundQuery, { transactionId: r1.transactionId, }); - await wallet.runUntilDone(); + await walletClient.call( + WalletApiOperation.TestingWaitTransactionsFinal, + {}, + ); console.log(wr); } @@ -153,25 +161,17 @@ export async function runRefundIncrementalTest(t: GlobalTestState) { console.log(JSON.stringify(orderStatus, undefined, 2)); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const bal = await wallet.client.call(WalletApiOperation.GetBalances, {}); + const bal = await walletClient.call(WalletApiOperation.GetBalances, {}); console.log(JSON.stringify(bal, undefined, 2)); { - const txs = await wallet.client.call( - WalletApiOperation.GetTransactions, - {}, - ); + const txs = await walletClient.call(WalletApiOperation.GetTransactions, {}); console.log(JSON.stringify(txs, undefined, 2)); const txTypes = txs.transactions.map((x) => x.type); - t.assertDeepEqual(txTypes, [ - "withdrawal", - "payment", - "refund", - "refund", - ]); + t.assertDeepEqual(txTypes, ["withdrawal", "payment", "refund", "refund"]); for (const tx of txs.transactions) { if (tx.type !== TransactionType.Refund) { diff --git a/packages/taler-harness/src/integrationtests/test-revocation.ts b/packages/taler-harness/src/integrationtests/test-revocation.ts index 0fbb4960e..04707e51a 100644 --- a/packages/taler-harness/src/integrationtests/test-revocation.ts +++ b/packages/taler-harness/src/integrationtests/test-revocation.ts @@ -213,3 +213,4 @@ export async function runRevocationTest(t: GlobalTestState) { runRevocationTest.timeoutMs = 120000; runRevocationTest.suites = ["wallet"]; +runRevocationTest.excludeByDefault = true; \ No newline at end of file diff --git a/packages/taler-harness/src/integrationtests/test-simple-payment.ts b/packages/taler-harness/src/integrationtests/test-simple-payment.ts index 82fa5f21d..58ab61435 100644 --- a/packages/taler-harness/src/integrationtests/test-simple-payment.ts +++ b/packages/taler-harness/src/integrationtests/test-simple-payment.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2020 Taler Systems S.A. + (C) 2023 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 @@ -20,11 +20,10 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironmentV2, withdrawViaBankV2, makeTestPaymentV2, + useSharedTestkudosEnvironment, } from "../harness/helpers.js"; -import { j2s } from "@gnu-taler/taler-util"; /** * Run test for basic, bank-integrated withdrawal and payment. @@ -33,7 +32,7 @@ export async function runSimplePaymentTest(t: GlobalTestState) { // Set up test environment const { walletClient, bank, exchange, merchant } = - await createSimpleTestkudosEnvironmentV2(t); + await useSharedTestkudosEnvironment(t); // Withdraw digital cash into the wallet. diff --git a/packages/taler-harness/src/integrationtests/test-tipping.ts b/packages/taler-harness/src/integrationtests/test-tipping.ts index 332f702d7..53d7f08c8 100644 --- a/packages/taler-harness/src/integrationtests/test-tipping.ts +++ b/packages/taler-harness/src/integrationtests/test-tipping.ts @@ -18,8 +18,8 @@ * Imports. */ import { + BankAccessApiClient, WalletApiOperation, - BankApi, WireGatewayApiClient, } from "@gnu-taler/taler-wallet-core"; import { @@ -28,7 +28,7 @@ import { MerchantPrivateApi, getWireMethodForTest, } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; +import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js"; import { TransactionMajorState } from "@gnu-taler/taler-util"; /** @@ -37,10 +37,14 @@ import { TransactionMajorState } from "@gnu-taler/taler-util"; export async function runTippingTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant, exchangeBankAccount } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, merchant, exchangeBankAccount } = + await createSimpleTestkudosEnvironmentV2(t); - const mbu = await BankApi.createRandomBankUser(bank); + const bankAccessApiClient = new BankAccessApiClient({ + allowHttp: true, + baseUrl: bank.bankAccessApiBaseUrl, + }); + const mbu = await bankAccessApiClient.createRandomBankUser(); const merchantClient = new MerchantApiClient( merchant.makeInstanceBaseUrl("default"), @@ -99,7 +103,7 @@ export async function runTippingTest(t: GlobalTestState) { console.log("created tip", tip); const doTip = async (): Promise => { - const ptr = await wallet.client.call(WalletApiOperation.PrepareReward, { + const ptr = await walletClient.call(WalletApiOperation.PrepareReward, { talerRewardUri: tip.taler_reward_uri, }); @@ -108,19 +112,22 @@ export async function runTippingTest(t: GlobalTestState) { t.assertAmountEquals(ptr.rewardAmountRaw, "TESTKUDOS:5"); t.assertAmountEquals(ptr.rewardAmountEffective, "TESTKUDOS:4.85"); - await wallet.client.call(WalletApiOperation.AcceptReward, { + await walletClient.call(WalletApiOperation.AcceptReward, { walletRewardId: ptr.walletRewardId, }); - await wallet.runUntilDone(); + await walletClient.call( + WalletApiOperation.TestingWaitTransactionsFinal, + {}, + ); - const bal = await wallet.client.call(WalletApiOperation.GetBalances, {}); + const bal = await walletClient.call(WalletApiOperation.GetBalances, {}); console.log(bal); t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:4.85"); - const txns = await wallet.client.call( + const txns = await walletClient.call( WalletApiOperation.GetTransactions, {}, ); @@ -128,7 +135,10 @@ export async function runTippingTest(t: GlobalTestState) { console.log("Transactions:", JSON.stringify(txns, undefined, 2)); t.assertDeepEqual(txns.transactions[0].type, "reward"); - t.assertDeepEqual(txns.transactions[0].txState.major, TransactionMajorState.Done); + t.assertDeepEqual( + txns.transactions[0].txState.major, + TransactionMajorState.Done, + ); t.assertAmountEquals( txns.transactions[0].amountEffective, "TESTKUDOS:4.85", diff --git a/packages/taler-harness/src/integrationtests/test-tos-format.ts b/packages/taler-harness/src/integrationtests/test-tos-format.ts new file mode 100644 index 000000000..e6087af9d --- /dev/null +++ b/packages/taler-harness/src/integrationtests/test-tos-format.ts @@ -0,0 +1,101 @@ +/* + 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 { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { GlobalTestState } from "../harness/harness.js"; +import { + createSimpleTestkudosEnvironmentV2, +} from "../harness/helpers.js"; +import * as fs from "fs"; + +/** + * Run test for basic, bank-integrated withdrawal and payment. + */ +export async function runTermOfServiceFormatTest(t: GlobalTestState) { + // Set up test environment + const tosDir = t.testDir + `/tos/`; + const langs = ["es", "en", "de"] + + langs.forEach(l => { + const langDir = tosDir + l + "/" + fs.mkdirSync(langDir, { recursive: true }); + fs.writeFileSync(langDir + "v1.txt", "text content"); + fs.writeFileSync(langDir + "v1.md", "markdown content"); + fs.writeFileSync(langDir + "v1.html", "html content"); + }); + + const { walletClient, exchange, } = + await createSimpleTestkudosEnvironmentV2(t, undefined, { + additionalExchangeConfig: (ex) => { + ex.changeConfig((cfg) => { + cfg.setString("exchange", "terms_etag", "v1") + cfg.setString("exchange", "terms_dir", tosDir) + }) + } + }); + + + { + const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, { + exchangeBaseUrl: exchange.baseUrl, + }) + + t.assertDeepEqual(tos.content, "text content"); + } + + { + const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, { + exchangeBaseUrl: exchange.baseUrl, + acceptedFormat: ["text/html"] + }) + + t.assertDeepEqual(tos.content, "html content"); + } + + { + const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, { + exchangeBaseUrl: exchange.baseUrl, + acceptedFormat: ["text/markdown"] + }) + + t.assertDeepEqual(tos.content, "markdown content"); + } + + { + const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, { + exchangeBaseUrl: exchange.baseUrl, + acceptedFormat: ["text/markdown", "text/html"] + }) + + // prefer markdown since its the first one in the list + t.assertDeepEqual(tos.content, "markdown content"); + } + + { + const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, { + exchangeBaseUrl: exchange.baseUrl, + acceptedFormat: ["text/pdf", "text/html"] + }) + + // there is no pdf so fallback in html + t.assertDeepEqual(tos.content, "html content"); + } +} + +runTermOfServiceFormatTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts b/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts index f36e2e4f1..5321cf5c7 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts @@ -22,7 +22,9 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState, WalletCli } from "../harness/harness.js"; import { createSimpleTestkudosEnvironment, + createSimpleTestkudosEnvironmentV2, withdrawViaBank, + withdrawViaBankV2, } from "../harness/helpers.js"; import { SyncService } from "../harness/sync.js"; @@ -32,8 +34,8 @@ import { SyncService } from "../harness/sync.js"; export async function runWalletBackupBasicTest(t: GlobalTestState) { // Set up test environment - const { commonDb, merchant, wallet, bank, exchange } = - await createSimpleTestkudosEnvironment(t); + const { commonDb, merchant, walletClient, bank, exchange } = + await createSimpleTestkudosEnvironmentV2(t); const sync = await SyncService.create(t, { currency: "TESTKUDOS", @@ -49,32 +51,32 @@ export async function runWalletBackupBasicTest(t: GlobalTestState) { await sync.start(); await sync.pingUntilAvailable(); - await wallet.client.call(WalletApiOperation.AddBackupProvider, { + await walletClient.call(WalletApiOperation.AddBackupProvider, { backupProviderBaseUrl: sync.baseUrl, activate: false, name: sync.baseUrl, }); { - const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); + const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {}); t.assertDeepEqual(bi.providers[0].active, false); } - await wallet.client.call(WalletApiOperation.AddBackupProvider, { + await walletClient.call(WalletApiOperation.AddBackupProvider, { backupProviderBaseUrl: sync.baseUrl, activate: true, name: sync.baseUrl, }); { - const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); + const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {}); t.assertDeepEqual(bi.providers[0].active, true); } - await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); + await walletClient.call(WalletApiOperation.RunBackupCycle, {}); { - const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); + const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {}); console.log(bi); t.assertDeepEqual( bi.providers[0].paymentStatus.type, @@ -82,32 +84,32 @@ export async function runWalletBackupBasicTest(t: GlobalTestState) { ); } - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" }); + await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:10" }); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); + await walletClient.call(WalletApiOperation.RunBackupCycle, {}); { - const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); + const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {}); console.log(bi); } - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:5" }); + await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:5" }); - await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); + await walletClient.call(WalletApiOperation.RunBackupCycle, {}); { - const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); + const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {}); console.log(bi); } - const backupRecovery = await wallet.client.call( + const backupRecovery = await walletClient.call( WalletApiOperation.ExportBackupRecovery, {}, ); - const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {}); + const txs = await walletClient.call(WalletApiOperation.GetTransactions, {}); console.log(`backed up transactions ${j2s(txs)}`); const wallet2 = new WalletCli(t, "wallet2"); diff --git a/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts b/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts index 2aceab86e..e3e18d5a4 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts @@ -21,21 +21,22 @@ import { PreparePayResultType } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState, - WalletCli, MerchantPrivateApi, + WalletCli, } from "../harness/harness.js"; import { - createSimpleTestkudosEnvironment, - makeTestPayment, + createSimpleTestkudosEnvironmentV2, + makeTestPaymentV2, withdrawViaBank, + withdrawViaBankV2 } from "../harness/helpers.js"; import { SyncService } from "../harness/sync.js"; export async function runWalletBackupDoublespendTest(t: GlobalTestState) { // Set up test environment - const { commonDb, merchant, wallet, bank, exchange } = - await createSimpleTestkudosEnvironment(t); + const { commonDb, merchant, walletClient, bank, exchange } = + await createSimpleTestkudosEnvironmentV2(t); const sync = await SyncService.create(t, { currency: "TESTKUDOS", @@ -51,21 +52,21 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) { await sync.start(); await sync.pingUntilAvailable(); - await wallet.client.call(WalletApiOperation.AddBackupProvider, { + await walletClient.call(WalletApiOperation.AddBackupProvider, { backupProviderBaseUrl: sync.baseUrl, activate: true, name: sync.baseUrl, }); - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" }); + await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:10" }); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); - await wallet.runUntilDone(); - await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); + await walletClient.call(WalletApiOperation.RunBackupCycle, {}); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); + await walletClient.call(WalletApiOperation.RunBackupCycle, {}); - const backupRecovery = await wallet.client.call( + const backupRecovery = await walletClient.call( WalletApiOperation.ExportBackupRecovery, {}, ); @@ -80,23 +81,23 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) { console.log( "wallet1 balance before spend:", - await wallet.client.call(WalletApiOperation.GetBalances, {}), + await walletClient.call(WalletApiOperation.GetBalances, {}), ); - await makeTestPayment(t, { + await makeTestPaymentV2(t, { merchant, - wallet, + walletClient, order: { summary: "foo", amount: "TESTKUDOS:7", }, }); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); console.log( "wallet1 balance after spend:", - await wallet.client.call(WalletApiOperation.GetBalances, {}), + await walletClient.call(WalletApiOperation.GetBalances, {}), ); { diff --git a/packages/taler-harness/src/integrationtests/test-wallet-balance.ts b/packages/taler-harness/src/integrationtests/test-wallet-balance.ts index b5b1917a6..0f75bd96e 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-balance.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-balance.ts @@ -30,7 +30,11 @@ import { setupDb, WalletCli, } from "../harness/harness.js"; -import { withdrawViaBank } from "../harness/helpers.js"; +import { + createSimpleTestkudosEnvironmentV2, + withdrawViaBank, + withdrawViaBankV2, +} from "../harness/helpers.js"; /** * Test for wallet balance error messages / different types of insufficient balance. @@ -41,72 +45,17 @@ import { withdrawViaBank } from "../harness/helpers.js"; export async function runWalletBalanceTest(t: GlobalTestState) { // Set up test environment - const db = await setupDb(t); - - const bank = await FakebankService.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", - ); - exchange.addBankAccount("1", exchangeBankAccount); - - bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - - await bank.start(); - - await bank.pingUntilAvailable(); - - const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")); - exchange.addCoinConfigList(coinConfig); - - await exchange.start(); - await exchange.pingUntilAvailable(); - - merchant.addExchange(exchange); - - await merchant.start(); - await merchant.pingUntilAvailable(); - - // Fakebank uses x-taler-bank, but merchant is configured to only accept sepa! - const label = "mymerchant"; - await merchant.addInstance({ - id: "default", - name: "Default Instance", - paytoUris: [ - `payto://iban/SANDBOXX/${getRandomIban(label)}?receiver-name=${label}`, - ], - defaultWireTransferDelay: Duration.toTalerProtocolDuration( - Duration.fromSpec({ minutes: 1 }), - ), - }); - - console.log("setup done!"); - - const wallet = new WalletCli(t); + const { merchant, walletClient, exchange, bank } = + await createSimpleTestkudosEnvironmentV2(t); // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBankV2(t, { + walletClient, + bank, + exchange, + amount: "TESTKUDOS:20", + }); const order = { summary: "Buy me!", @@ -126,7 +75,7 @@ export async function runWalletBalanceTest(t: GlobalTestState) { // Make wallet pay for the order - const preparePayResult = await wallet.client.call( + const preparePayResult = await walletClient.call( WalletApiOperation.PreparePayForUri, { talerPayUri: orderStatus.taler_pay_uri, @@ -152,7 +101,7 @@ export async function runWalletBalanceTest(t: GlobalTestState) { Amounts.isZero(preparePayResult.balanceDetails.balanceMerchantDepositable), ); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); } runWalletBalanceTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts b/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts index c384a6e74..58f564f34 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts @@ -37,7 +37,7 @@ import { withdrawCoin, } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; +import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js"; /** * Run test for basic, bank-integrated withdrawal and payment. @@ -45,7 +45,7 @@ import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; export async function runWalletDblessTest(t: GlobalTestState) { // Set up test environment - const { bank, exchange } = await createSimpleTestkudosEnvironment(t); + const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t); const http = createPlatformHttpLib({ allowHttp: true, diff --git a/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts b/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts index 810250f53..c70fd51d4 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts @@ -18,27 +18,24 @@ * Imports. */ import { - Amounts, Duration, NotificationType, - PreparePayResultType, TransactionMajorState, } from "@gnu-taler/taler-util"; import { - BankAccessApi, - BankApi, + BankAccessApiClient, WalletApiOperation, } from "@gnu-taler/taler-wallet-core"; import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; import { ExchangeService, FakebankService, - getRandomIban, GlobalTestState, MerchantService, - setupDb, WalletClient, WalletService, + getRandomIban, + setupDb, } from "../harness/harness.js"; /** @@ -126,10 +123,14 @@ export async function runWalletNotificationsTest(t: GlobalTestState) { skipDefaults: true, }); - const user = await BankApi.createRandomBankUser(bank); - const wop = await BankAccessApi.createWithdrawalOperation( - bank, - user, + const bankAccessApiClient = new BankAccessApiClient({ + allowHttp: true, + baseUrl: bank.bankAccessApiBaseUrl, + }); + const user = await bankAccessApiClient.createRandomBankUser(); + bankAccessApiClient.setAuth(user); + const wop = await bankAccessApiClient.createWithdrawalOperation( + user.username, "TESTKUDOS:20", ); @@ -163,7 +164,7 @@ export async function runWalletNotificationsTest(t: GlobalTestState) { // Confirm it - await BankApi.confirmWithdrawalOperation(bank, user, wop); + await bankAccessApiClient.confirmWithdrawalOperation(user.username, wop); await withdrawalFinishedReceivedPromise; } diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts index bf2dc0133..c3069c317 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts @@ -20,8 +20,7 @@ import { TalerErrorCode } from "@gnu-taler/taler-util"; import { WalletApiOperation, - BankApi, - BankAccessApi, + BankAccessApiClient, } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; @@ -36,10 +35,14 @@ export async function runWithdrawalAbortBankTest(t: GlobalTestState) { // Create a withdrawal operation - const user = await BankApi.createRandomBankUser(bank); - const wop = await BankAccessApi.createWithdrawalOperation( - bank, - user, + const bankAccessApiClient = new BankAccessApiClient({ + allowHttp: true, + baseUrl: bank.bankAccessApiBaseUrl, + }); + const user = await bankAccessApiClient.createRandomBankUser(); + bankAccessApiClient.setAuth(user); + const wop = await bankAccessApiClient.createWithdrawalOperation( + user.username, "TESTKUDOS:10", ); @@ -53,8 +56,7 @@ export async function runWithdrawalAbortBankTest(t: GlobalTestState) { // Abort it - await BankApi.abortWithdrawalOperation(bank, user, wop); - //await BankApi.confirmWithdrawalOperation(bank, user, wop); + await bankAccessApiClient.abortWithdrawalOperation(user.username, wop); // Withdraw diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts index 396f0f03f..61687ec02 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts @@ -19,11 +19,7 @@ */ import { GlobalTestState } from "../harness/harness.js"; import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js"; -import { - WalletApiOperation, - BankApi, - BankAccessApi, -} from "@gnu-taler/taler-wallet-core"; +import { BankAccessApiClient, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { j2s, NotificationType, @@ -44,10 +40,14 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) { // Create a withdrawal operation - const user = await BankApi.createRandomBankUser(bank); - const wop = await BankAccessApi.createWithdrawalOperation( - bank, - user, + const bankAccessApiClient = new BankAccessApiClient({ + allowHttp: true, + baseUrl: bank.bankAccessApiBaseUrl, + }); + const user = await bankAccessApiClient.createRandomBankUser(); + bankAccessApiClient.setAuth(user); + const wop = await bankAccessApiClient.createWithdrawalOperation( + user.username, "TESTKUDOS:10", ); @@ -129,7 +129,7 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) { // Confirm it - await BankApi.confirmWithdrawalOperation(bank, user, wop); + await bankAccessApiClient.confirmWithdrawalOperation(user.username, wop); await withdrawalBankConfirmedCond; diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts index 065b134c4..06355b964 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts @@ -26,6 +26,7 @@ import { } from "../harness/harness.js"; import { BankAccessApi, + BankAccessApiClient, BankApi, WalletApiOperation, } from "@gnu-taler/taler-wallet-core"; @@ -112,8 +113,13 @@ export async function runWithdrawalFeesTest(t: GlobalTestState) { const amount = "TESTKUDOS:7.5"; - const user = await BankApi.createRandomBankUser(bank); - const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount); + const bankAccessApiClient = new BankAccessApiClient({ + allowHttp: true, + baseUrl: bank.bankAccessApiBaseUrl, + }); + const user = await bankAccessApiClient.createRandomBankUser(); + bankAccessApiClient.setAuth(user); + const wop = await bankAccessApiClient.createWithdrawalOperation(user.username, amount); // Hand it to the wallet @@ -150,10 +156,7 @@ export async function runWithdrawalFeesTest(t: GlobalTestState) { // Confirm it - await BankApi.confirmWithdrawalOperation(bank, user, wop); - - await exchange.runWirewatchOnce(); - + await bankAccessApiClient.confirmWithdrawalOperation(user.username, wop); await wallet.runUntilDone(); // Check balance diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts index f56e4d24d..a9d544ee0 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts @@ -27,7 +27,11 @@ import { } from "../harness/harness.js"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; -import { NotificationType, TransactionMajorState, URL } from "@gnu-taler/taler-util"; +import { + NotificationType, + TransactionMajorState, + URL, +} from "@gnu-taler/taler-util"; /** * Withdraw a high amount. Mostly intended as a perf test. @@ -84,7 +88,8 @@ export async function runWithdrawalHugeTest(t: GlobalTestState) { await wallet.connect(); const withdrawalFinishedCond = wallet.waitForNotificationCond( - (wn) => wn.type === NotificationType.TransactionStateTransition && + (wn) => + wn.type === NotificationType.TransactionStateTransition && wn.transactionId.startsWith("txn:withdrawal:") && wn.newTxState.major === TransactionMajorState.Done, ); diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts index a356a5c1a..d49235f89 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts @@ -17,14 +17,14 @@ /** * Imports. */ -import { GlobalTestState } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; +import { AbsoluteTime, Logger, j2s } from "@gnu-taler/taler-util"; import { + BankAccessApiClient, WalletApiOperation, - BankApi, WireGatewayApiClient, } from "@gnu-taler/taler-wallet-core"; -import { AbsoluteTime, j2s, Logger } from "@gnu-taler/taler-util"; +import { GlobalTestState } from "../harness/harness.js"; +import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js"; const logger = new Logger("test-withdrawal-manual.ts"); @@ -34,14 +34,19 @@ const logger = new Logger("test-withdrawal-manual.ts"); export async function runWithdrawalManualTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, exchangeBankAccount } = - await createSimpleTestkudosEnvironment(t); + const { walletClient, bank, exchange, exchangeBankAccount } = + await createSimpleTestkudosEnvironmentV2(t); // Create a withdrawal operation - const user = await BankApi.createRandomBankUser(bank); + const bankAccessApiClient = new BankAccessApiClient({ + baseUrl: bank.bankAccessApiBaseUrl, + allowHttp: true, + }); - await wallet.client.call(WalletApiOperation.AddExchange, { + const user = await bankAccessApiClient.createRandomBankUser(); + + await walletClient.call(WalletApiOperation.AddExchange, { exchangeBaseUrl: exchange.baseUrl, }); @@ -50,7 +55,7 @@ export async function runWithdrawalManualTest(t: GlobalTestState) { logger.info("starting AcceptManualWithdrawal request"); // We expect this to return immediately. - const wres = await wallet.client.call( + const wres = await walletClient.call( WalletApiOperation.AcceptManualWithdrawal, { exchangeBaseUrl: exchange.baseUrl, @@ -84,11 +89,11 @@ export async function runWithdrawalManualTest(t: GlobalTestState) { await exchange.runWirewatchOnce(); - await wallet.runUntilDone(); + await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); // Check balance - const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {}); + const balResp = await walletClient.call(WalletApiOperation.GetBalances, {}); t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available); await t.shutdown(); diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts index 58e2a20f7..cbdca04b9 100644 --- a/packages/taler-harness/src/integrationtests/testrunner.ts +++ b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { CancellationToken, Logger, minimatch } from "@gnu-taler/taler-util"; +import { CancellationToken, Logger, minimatch, setGlobalLogLevelFromString } from "@gnu-taler/taler-util"; import * as child_process from "child_process"; import * as fs from "fs"; import * as os from "os"; @@ -104,6 +104,7 @@ import { runExchangeDepositTest } from "./test-exchange-deposit.js"; import { runPeerRepairTest } from "./test-peer-repair.js"; import { runPaymentShareTest } from "./test-payment-share.js"; import { runSimplePaymentTest } from "./test-simple-payment.js"; +import { runTermOfServiceFormatTest } from "./test-tos-format.js"; /** * Test runner. @@ -200,6 +201,7 @@ const allTests: TestMainFunction[] = [ runWithdrawalFakebankTest, runWithdrawalFeesTest, runWithdrawalHugeTest, + runTermOfServiceFormatTest, ]; export interface TestRunSpec { diff --git a/packages/taler-harness/src/sandcastle-config.ts b/packages/taler-harness/src/sandcastle-config.ts new file mode 100644 index 000000000..a7f7233ac --- /dev/null +++ b/packages/taler-harness/src/sandcastle-config.ts @@ -0,0 +1,10 @@ +// Work in progress. +// TS-based schema for the sandcastle configuration. + +export interface SandcastleConfig { + currency: string; + merchant: { + apiKey: string; + baseUrl: string; + }; +} diff --git a/packages/taler-util/src/logging.ts b/packages/taler-util/src/logging.ts index c4b2a3da0..79fc49cdd 100644 --- a/packages/taler-util/src/logging.ts +++ b/packages/taler-util/src/logging.ts @@ -32,13 +32,17 @@ export enum LogLevel { None = "none", } -export let globalLogLevel = LogLevel.Info; +let globalLogLevel = LogLevel.Info; +const byTagLogLevel: Record = {}; + +export function getGlobalLogLevel(): string { + return globalLogLevel; +} export function setGlobalLogLevelFromString(logLevelStr: string): void { globalLogLevel = getLevelForString(logLevelStr); } -export const byTagLogLevel: Record = {}; export function setLogLevelFromString(tag: string, logLevelStr: string): void { byTagLogLevel[tag] = getLevelForString(logLevelStr); } @@ -98,7 +102,7 @@ function writeNodeLog( * and uses the corresponding console.* method to log in the browser. */ export class Logger { - constructor(private tag: string) {} + constructor(private tag: string) { } shouldLogTrace(): boolean { const level = byTagLogLevel[this.tag] ?? globalLogLevel; diff --git a/packages/taler-util/src/talerconfig.ts b/packages/taler-util/src/talerconfig.ts index 59c789cae..d86c58678 100644 --- a/packages/taler-util/src/talerconfig.ts +++ b/packages/taler-util/src/talerconfig.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2020 Taler Systems S.A. + (C) 2020-2023 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 @@ -39,10 +39,30 @@ export class ConfigError extends Error { } } +enum EntryOrigin { + /** + * From a default file. + */ + DefaultFile = 1, + /** + * From a system/installation specific default value. + */ + DefaultSystem = 2, + /** + * Loaded from file or string + */ + Loaded = 3, + /** + * Changed after loading + */ + Changed = 4, +} + interface Entry { value: string; sourceLine: number; sourceFile: string; + origin: EntryOrigin; } interface Section { @@ -159,12 +179,12 @@ export function pathsub( const r = lookup(inner, depth + 1); if (r !== undefined) { - s = s.substr(0, start) + r + s.substr(p + 1); + s = s.substring(0, start) + r + s.substring(p + 1); l = start + r.length; continue; } else if (defaultValue !== undefined) { const resolvedDefault = pathsub(defaultValue, lookup, depth + 1); - s = s.substr(0, start) + resolvedDefault + s.substr(p + 1); + s = s.substring(0, start) + resolvedDefault + s.substring(p + 1); l = start + resolvedDefault.length; continue; } @@ -176,7 +196,7 @@ export function pathsub( if (m && m[0]) { const r = lookup(m[0], depth + 1); if (r !== undefined) { - s = s.substr(0, l) + r + s.substr(l + 1 + m[0].length); + s = s.substring(0, l) + r + s.substring(l + 1 + m[0].length); l = l + r.length; continue; } @@ -195,6 +215,7 @@ export interface LoadOptions { export interface StringifyOptions { diagnostics?: boolean; + excludeDefaults?: boolean; } export interface LoadedFile { @@ -282,7 +303,11 @@ export class Configuration { private nestLevel = 0; - private loadFromFilename(filename: string, opts: LoadOptions = {}): void { + private loadFromFilename( + filename: string, + isDefaultSource: boolean, + opts: LoadOptions = {}, + ): void { filename = expandPath(filename); const checkCycle = () => { @@ -309,7 +334,7 @@ export class Configuration { const oldNestLevel = this.nestLevel; this.nestLevel += 1; try { - this.loadFromString(s, { + this.internalLoadFromString(s, isDefaultSource, { ...opts, filename: filename, }); @@ -318,7 +343,11 @@ export class Configuration { } } - private loadGlob(parentFilename: string, fileglob: string): void { + private loadGlob( + parentFilename: string, + isDefaultSource: boolean, + fileglob: string, + ): void { const resolvedParent = nodejs_fs.realpathSync(parentFilename); const parentDir = nodejs_path.dirname(resolvedParent); @@ -339,12 +368,16 @@ export class Configuration { for (const f of files) { if (globMatch(tail, f)) { const fullPath = nodejs_path.join(head, f); - this.loadFromFilename(fullPath); + this.loadFromFilename(fullPath, isDefaultSource); } } } - private loadSecret(sectionName: string, filename: string): void { + private loadSecret( + sectionName: string, + filename: string, + isDefaultSource: boolean, + ): void { const sec = this.provideSection(sectionName); sec.secretFilename = filename; const otherCfg = new Configuration(); @@ -354,7 +387,7 @@ export class Configuration { sec.inaccessible = true; return; } - otherCfg.loadFromFilename(filename, { + otherCfg.loadFromFilename(filename, isDefaultSource, { banDirectives: true, }); const otherSec = otherCfg.provideSection(sectionName); @@ -363,7 +396,11 @@ export class Configuration { } } - loadFromString(s: string, opts: LoadOptions = {}): void { + private internalLoadFromString( + s: string, + isDefaultSource: boolean, + opts: LoadOptions = {}, + ): void { let lineNo = 0; const fn = opts.filename ?? ""; const reComment = /^\s*#.*$/; @@ -399,7 +436,10 @@ export class Configuration { ); } const arg = directiveMatch[2].trim(); - this.loadFromFilename(normalizeInlineFilename(opts.filename, arg)); + this.loadFromFilename( + normalizeInlineFilename(opts.filename, arg), + isDefaultSource, + ); break; } case "inline-secret": { @@ -419,7 +459,7 @@ export class Configuration { opts.filename, sp[1], ); - this.loadSecret(sp[0], secretFilename); + this.loadSecret(sp[0], secretFilename, isDefaultSource); break; } case "inline-matching": { @@ -429,7 +469,7 @@ export class Configuration { `invalid configuration, @inline-matching@ directive in ${fn}:${lineNo} can only be used from a file`, ); } - this.loadGlob(opts.filename, arg); + this.loadGlob(opts.filename, isDefaultSource, arg); break; } default: @@ -462,6 +502,9 @@ export class Configuration { value: val, sourceFile: opts.filename ?? "", sourceLine: lineNo, + origin: isDefaultSource + ? EntryOrigin.DefaultFile + : EntryOrigin.Loaded, }; continue; } @@ -471,6 +514,10 @@ export class Configuration { } } + loadFromString(s: string, opts: LoadOptions = {}): void { + return this.internalLoadFromString(s, false, opts); + } + private provideSection(section: string): Section { const secNorm = section.toUpperCase(); if (this.sectionMap[secNorm]) { @@ -496,6 +543,24 @@ export class Configuration { value, sourceLine: 0, sourceFile: "", + origin: EntryOrigin.Changed, + }; + } + + /** + * Set a string value to a value from default locations. + */ + private setStringSystemDefault( + section: string, + option: string, + value: string, + ): void { + const sec = this.provideSection(section); + sec.entries[option.toUpperCase()] = { + value, + sourceLine: 0, + sourceFile: "", + origin: EntryOrigin.DefaultSystem, }; } @@ -559,7 +624,7 @@ export class Configuration { lookupVariable(x: string, depth: number = 0): string | undefined { // We loop up options in PATHS in upper case, as option names // are case insensitive - const val = this.findEntry("PATHS", x)?.value; + let val = this.findEntry("PATHS", x)?.value; if (val !== undefined) { return pathsub(val, (v, d) => this.lookupVariable(v, d), depth); } @@ -578,30 +643,63 @@ export class Configuration { ); } - loadFrom(dirname: string): void { + loadDefaultsFromDir(dirname: string): void { const files = nodejs_fs.readdirSync(dirname); for (const f of files) { const fn = nodejs_path.join(dirname, f); - this.loadFromFilename(fn); + this.loadFromFilename(fn, true); } } private loadDefaults(): void { - let bc = process.env["TALER_BASE_CONFIG"]; - if (!bc) { + let baseConfigDir = process.env["TALER_BASE_CONFIG"]; + if (!baseConfigDir) { /* Try to locate the configuration based on the location * of the taler-config binary. */ const path = which("taler-config"); if (path) { - bc = nodejs_fs.realpathSync( + baseConfigDir = nodejs_fs.realpathSync( nodejs_path.dirname(path) + "/../share/taler/config.d", ); } } - if (!bc) { - bc = "/usr/share/taler/config.d"; + if (!baseConfigDir) { + baseConfigDir = "/usr/share/taler/config.d"; } - this.loadFrom(bc); + + let installPrefix = process.env["TALER_PREFIX"]; + if (!installPrefix) { + /* Try to locate install path based on the location + * of the taler-config binary. */ + const path = which("taler-config"); + if (path) { + installPrefix = nodejs_fs.realpathSync( + nodejs_path.dirname(path) + "/..", + ); + } + } + if (!installPrefix) { + installPrefix = "/usr"; + } + + this.setStringSystemDefault( + "PATHS", + "LIBEXECDIR", + `${installPrefix}/taler/libexec/`, + ); + this.setStringSystemDefault( + "PATHS", + "DOCDIR", + `${installPrefix}/share/doc/taler/`, + ); + this.setStringSystemDefault("PATHS", "ICONDIR", `${installPrefix}/share/icons/`); + this.setStringSystemDefault("PATHS", "LOCALEDIR", `${installPrefix}/share/locale/`); + this.setStringSystemDefault("PATHS", "PREFIX", `${installPrefix}/`); + this.setStringSystemDefault("PATHS", "BINDIR", `${installPrefix}/bin`); + this.setStringSystemDefault("PATHS", "LIBDIR", `${installPrefix}/lib/taler/`); + this.setStringSystemDefault("PATHS", "DATADIR", `${installPrefix}/share/taler/`); + + this.loadDefaultsFromDir(baseConfigDir); } getDefaultConfigFilename(): string | undefined { @@ -631,11 +729,13 @@ export class Configuration { const cfg = new Configuration(); cfg.loadDefaults(); if (filename) { - cfg.loadFromFilename(filename); + cfg.loadFromFilename(filename, false); } else { const fn = cfg.getDefaultConfigFilename(); if (fn) { - cfg.loadFromFilename(fn); + // It's the default filename for the main config file, + // but we don't consider the values default values. + cfg.loadFromFilename(fn, false); } } cfg.hintEntrypoint = filename; @@ -657,26 +757,51 @@ export class Configuration { } for (const sectionName of Object.keys(this.sectionMap)) { const sec = this.sectionMap[sectionName]; - if (opts.diagnostics && sec.secretFilename) { - s += `# Secret section from ${sec.secretFilename}\n`; - s += `# Secret accessible: ${!sec.inaccessible}\n`; - } - s += `[${sectionName}]\n`; + let headerWritten = false; for (const optionName of Object.keys(sec.entries)) { const entry = this.sectionMap[sectionName].entries[optionName]; + if ( + opts.excludeDefaults && + (entry.origin === EntryOrigin.DefaultSystem || + entry.origin === EntryOrigin.DefaultFile) + ) { + continue; + } + if (!headerWritten) { + if (opts.diagnostics && sec.secretFilename) { + s += `# Secret section from ${sec.secretFilename}\n`; + s += `# Secret accessible: ${!sec.inaccessible}\n`; + } + s += `[${sectionName}]\n`; + headerWritten = true; + } if (entry !== undefined) { if (opts.diagnostics) { - s += `# ${entry.sourceFile}:${entry.sourceLine}\n`; + switch (entry.origin) { + case EntryOrigin.DefaultFile: + case EntryOrigin.Changed: + case EntryOrigin.Loaded: + s += `# ${entry.sourceFile}:${entry.sourceLine}\n`; + break; + case EntryOrigin.DefaultSystem: + s += `# (system/installation default)\n`; + break; + } } s += `${optionName} = ${entry.value}\n`; } } - s += "\n"; + if (headerWritten) { + s += "\n"; + } } return s; } - write(filename: string): void { - nodejs_fs.writeFileSync(filename, this.stringify()); + write(filename: string, opts: { excludeDefaults?: boolean } = {}): void { + nodejs_fs.writeFileSync( + filename, + this.stringify({ excludeDefaults: opts.excludeDefaults }), + ); } } diff --git a/packages/taler-wallet-core/src/bank-api-client.ts b/packages/taler-wallet-core/src/bank-api-client.ts index a7484b0b2..8e351cb48 100644 --- a/packages/taler-wallet-core/src/bank-api-client.ts +++ b/packages/taler-wallet-core/src/bank-api-client.ts @@ -267,8 +267,7 @@ export namespace BankAccessApi { export interface BankAccessApiClientArgs { baseUrl: string; - username: string; - password: string; + auth?: { username: string; password: string }; enableThrottling?: boolean; allowHttp?: boolean; } @@ -330,7 +329,7 @@ export class WireGatewayApiClient { * but it will be nice to have in utils to be used by others */ export class BankAccessApiClient { - httpLib; + httpLib: HttpRequestLibrary; constructor(private args: BankAccessApiClientArgs) { this.httpLib = createPlatformHttpLib({ @@ -339,19 +338,33 @@ export class BankAccessApiClient { }); } - async getTransactions(): Promise { - const reqUrl = new URL( - `accounts/${this.args.username}/transactions`, - this.args.baseUrl, - ); + setAuth(auth: { username: string; password: string }) { + this.args.auth = auth; + } + + private makeAuthHeader(): Record { + if (!this.args.auth) { + return {}; + } const authHeaderValue = makeBasicAuthHeader( - this.args.username, - this.args.password, + this.args.auth.username, + this.args.auth.password, + ); + return { + Authorization: authHeaderValue, + }; + } + + async getTransactions(username: string): Promise { + const auth = this.args.auth; + const reqUrl = new URL( + `accounts/${username}/transactions`, + this.args.baseUrl, ); const resp = await this.httpLib.fetch(reqUrl.href, { method: "GET", headers: { - Authorization: authHeaderValue, + ...this.makeAuthHeader(), }, }); @@ -360,24 +373,128 @@ export class BankAccessApiClient { } async createTransaction( + username: string, req: BankAccessApiCreateTransactionRequest, ): Promise { const reqUrl = new URL( - `accounts/${this.args.username}/transactions`, + `accounts/${username}/transactions`, this.args.baseUrl, ); - const authHeaderValue = makeBasicAuthHeader( - this.args.username, - this.args.password, - ); + const resp = await this.httpLib.fetch(reqUrl.href, { method: "POST", body: req, - headers: { - Authorization: authHeaderValue, - }, + headers: this.makeAuthHeader(), }); return await readSuccessResponseJsonOrThrow(resp, codecForAny()); } + + async registerAccount( + username: string, + password: string, + options: { + iban?: string; + }, + ): Promise { + const url = new URL("testing/register", this.args.baseUrl); + const resp = await this.httpLib.fetch(url.href, { + method: "POST", + body: { + username, + password, + iban: options?.iban, + }, + }); + let paytoUri = `payto://x-taler-bank/localhost/${username}`; + if (resp.status !== 200 && resp.status !== 202 && resp.status !== 204) { + logger.error(`${j2s(await resp.json())}`); + throw TalerError.fromDetail( + TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR, + { + httpStatusCode: resp.status, + }, + ); + } + try { + // Pybank has no body, thus this might throw. + const respJson = await resp.json(); + // LibEuFin demobank returns payto URI in response + if (respJson.paytoUri) { + paytoUri = respJson.paytoUri; + } + } catch (e) { + // Do nothing + } + return { + password, + username, + accountPaytoUri: paytoUri, + }; + } + + async createRandomBankUser(): Promise { + const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase(); + const password = "pw-" + encodeCrock(getRandomBytes(10)).toLowerCase(); + // FIXME: This is just a temporary workaround, because demobank is running out of short IBANs + const iban = generateIban("DE", 15); + return await this.registerAccount(username, password, { + iban, + }); + } + + async createWithdrawalOperation( + user: string, + amount: string, + ): Promise { + const url = new URL(`accounts/${user}/withdrawals`, this.args.baseUrl); + const resp = await this.httpLib.fetch(url.href, { + method: "POST", + body: { + amount, + }, + headers: this.makeAuthHeader(), + }); + return readSuccessResponseJsonOrThrow( + resp, + codecForWithdrawalOperationInfo(), + ); + } + + async confirmWithdrawalOperation( + username: string, + wopi: WithdrawalOperationInfo, + ): Promise { + const url = new URL( + `accounts/${username}/withdrawals/${wopi.withdrawal_id}/confirm`, + this.args.baseUrl, + ); + logger.info(`confirming withdrawal operation via ${url.href}`); + const resp = await this.httpLib.fetch(url.href, { + method: "POST", + body: {}, + headers: this.makeAuthHeader(), + }); + + logger.info(`response status ${resp.status}`); + const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny()); + + // FIXME: We don't check the status here! + } + + async abortWithdrawalOperation( + accountName: string, + wopi: WithdrawalOperationInfo, + ): Promise { + const url = new URL( + `accounts/${accountName}/withdrawals/${wopi.withdrawal_id}/abort`, + this.args.baseUrl, + ); + const resp = await this.httpLib.fetch(url.href, { + method: "POST", + body: {}, + headers: this.makeAuthHeader(), + }); + await readSuccessResponseJsonOrThrow(resp, codecForAny()); + } }