diff options
Diffstat (limited to 'packages/taler-wallet-cli')
92 files changed, 9 insertions, 17288 deletions
diff --git a/packages/taler-wallet-cli/assets/.gitkeep b/packages/taler-wallet-cli/assets/.gitkeep deleted file mode 100644 index e69de29bb..000000000 --- a/packages/taler-wallet-cli/assets/.gitkeep +++ /dev/null diff --git a/packages/taler-wallet-cli/build.mjs b/packages/taler-wallet-cli/build.mjs index b6b3f4a16..14b626815 100755 --- a/packages/taler-wallet-cli/build.mjs +++ b/packages/taler-wallet-cli/build.mjs @@ -53,17 +53,15 @@ function git_hash() {  export const buildConfig = {    entryPoints: ["src/index.ts"], -  outfile: "dist/taler-wallet-cli.cjs", +  outfile: "dist/taler-wallet-cli.mjs",    bundle: true,    minify: false,    target: [ -    'es6' +    'es2020'    ], -  format: 'cjs', -  platform: 'node', +  format: 'esm', +  platform: 'neutral',    sourcemap: true, -  jsxFactory: 'h', -  jsxFragment: 'Fragment',    define: {      '__VERSION__': `"${_package.version}"`,      '__GIT_HASH__': `"${GIT_HASH}"`, @@ -76,4 +74,3 @@ esbuild      console.log(e)      process.exit(1)    }); - diff --git a/packages/taler-wallet-cli/package.json b/packages/taler-wallet-cli/package.json index 455c5d0cb..8069cc5dd 100644 --- a/packages/taler-wallet-cli/package.json +++ b/packages/taler-wallet-cli/package.json @@ -35,7 +35,7 @@      "@rollup/plugin-json": "^4.1.0",      "@rollup/plugin-node-resolve": "^13.3.0",      "@rollup/plugin-replace": "^4.0.0", -    "@types/node": "^18.8.5", +    "@types/node": "^18.11.17",      "prettier": "^2.5.1",      "rimraf": "^3.0.2",      "rollup": "^2.79.0", diff --git a/packages/taler-wallet-cli/src/assets.ts b/packages/taler-wallet-cli/src/assets.ts deleted file mode 100644 index 8e9306780..000000000 --- a/packages/taler-wallet-cli/src/assets.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import path from "path"; -import fs from "fs"; - -const assetFileUrl = import.meta.url; - -/** - * Resolve an asset name into an absolute filename. - * - * The asset file should be placed in the "assets" directory - * at the top level of the package (i.e. next to package.json). - */ -export function resolveAsset(name: string): string { -  const n = path.basename(assetFileUrl); -  const d = path.dirname(assetFileUrl); -  let assetPath: string; -  // Currently both asset paths are the same. -  // This might change if the file that contains "resolveAsset" -  // ever moves.  Thus, we're keeping the case distinction. -  if (n.endsWith("assets.js")) { -    // We're not bundled.  Path is relative to the current file. -    assetPath = path.resolve(path.join(d, "..", "assets", name)); -  } else if (n.endsWith("taler-wallet-cli.js")) { -    // We're bundled.  Currently, this path is the same -    // FIXME:  Take into account some ASSETS environment variable? -    assetPath = path.resolve(path.join(d, "..", "assets", name)); -  } else { -    throw Error("Can't resolve asset (unknown)"); -  } -  if (!fs.existsSync(assetPath)) { -    throw Error(`Asset '${name} not found'`); -  } -  return assetPath; -} diff --git a/packages/taler-wallet-cli/src/bench1.ts b/packages/taler-wallet-cli/src/bench1.ts deleted file mode 100644 index 84786d25a..000000000 --- a/packages/taler-wallet-cli/src/bench1.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  buildCodecForObject, -  codecForNumber, -  codecForString, -  codecForBoolean, -  codecOptional, -  j2s, -  Logger, -} from "@gnu-taler/taler-util"; -import { -  getDefaultNodeWallet2, -  NodeHttpLib, -  WalletApiOperation, -  Wallet, -  AccessStats, -} from "@gnu-taler/taler-wallet-core"; - -/** - * Entry point for the benchmark. - * - * The benchmark runs against an existing Taler deployment and does not - * set up its own services. - */ -export async function runBench1(configJson: any): Promise<void> { -  const logger = new Logger("Bench1"); - -  // Validate the configuration file for this benchmark. -  const b1conf = codecForBench1Config().decode(configJson); - -  const myHttpLib = new NodeHttpLib(); -  myHttpLib.setThrottling(false); - -  const numIter = b1conf.iterations ?? 1; -  const numDeposits = b1conf.deposits ?? 5; -  const restartWallet = b1conf.restartAfter ?? 20; - -  const withdrawOnly = b1conf.withdrawOnly ?? false; -  const withdrawAmount = (numDeposits + 1) * 10; - -  logger.info( -    `Starting Benchmark iterations=${numIter} deposits=${numDeposits}`, -  ); - -  const trustExchange = !!process.env["TALER_WALLET_INSECURE_TRUST_EXCHANGE"]; -  if (trustExchange) { -    logger.info("trusting exchange (not validating signatures)"); -  } else { -    logger.info("not trusting exchange (validating signatures)"); -  } -  const batchWithdrawal = !!process.env["TALER_WALLET_BATCH_WITHDRAWAL"]; - -  let wallet = {} as Wallet; -  let getDbStats: () => AccessStats; - -  for (let i = 0; i < numIter; i++) { -    // Create a new wallet in each iteration -    // otherwise the TPS go down -    // my assumption is that the in-memory db file gets too large -    if (i % restartWallet == 0) { -      if (Object.keys(wallet).length !== 0) { -        wallet.stop(); -        console.log("wallet DB stats", j2s(getDbStats!())); -      } - -      const res = await getDefaultNodeWallet2({ -        // No persistent DB storage. -        persistentStoragePath: undefined, -        httpLib: myHttpLib, -      }); -      wallet = res.wallet; -      getDbStats = res.getDbStats; -      if (trustExchange) { -        wallet.setInsecureTrustExchange(); -      } -      wallet.setBatchWithdrawal(batchWithdrawal); -      await wallet.client.call(WalletApiOperation.InitWallet, {}); -    } - -    logger.trace(`Starting withdrawal amount=${withdrawAmount}`); -    let start = Date.now(); - -    await wallet.client.call(WalletApiOperation.WithdrawFakebank, { -      amount: b1conf.currency + ":" + withdrawAmount, -      bank: b1conf.bank, -      exchange: b1conf.exchange, -    }); - -    await wallet.runTaskLoop({ -      stopWhenDone: true, -    }); - -    logger.info( -      `Finished withdrawal amount=${withdrawAmount} time=${Date.now() - start}`, -    ); - -    if (!withdrawOnly) { -      for (let i = 0; i < numDeposits; i++) { -        logger.trace(`Starting deposit amount=10`); -        start = Date.now(); - -        await wallet.client.call(WalletApiOperation.CreateDepositGroup, { -          amount: b1conf.currency + ":10", -          depositPaytoUri: b1conf.payto, -        }); - -        await wallet.runTaskLoop({ -          stopWhenDone: true, -        }); - -        logger.info(`Finished deposit amount=10 time=${Date.now() - start}`); -      } -    } -  } - -  wallet.stop(); -  console.log("wallet DB stats", j2s(getDbStats!())); -} - -/** - * Format of the configuration file passed to the benchmark - */ -interface Bench1Config { -  /** -   * Base URL of the bank. -   */ -  bank: string; - -  /** -   * Payto url for deposits. -   */ -  payto: string; - -  /** -   * Base URL of the exchange. -   */ -  exchange: string; - -  /** -   * How many withdraw/deposit iterations should be made? -   * Defaults to 1. -   */ -  iterations?: number; - -  currency: string; - -  deposits?: number; - -  /** -   * How any iterations run until the wallet db gets purged -   * Defaults to 20. -   */ -  restartAfter?: number; - -  withdrawOnly?: boolean; -} - -/** - * Schema validation codec for Bench1Config. - */ -const codecForBench1Config = () => -  buildCodecForObject<Bench1Config>() -    .property("bank", codecForString()) -    .property("payto", codecForString()) -    .property("exchange", codecForString()) -    .property("iterations", codecOptional(codecForNumber())) -    .property("deposits", codecOptional(codecForNumber())) -    .property("currency", codecForString()) -    .property("restartAfter", codecOptional(codecForNumber())) -    .property("withdrawOnly", codecOptional(codecForBoolean())) -    .build("Bench1Config"); diff --git a/packages/taler-wallet-cli/src/bench2.ts b/packages/taler-wallet-cli/src/bench2.ts deleted file mode 100644 index 196737436..000000000 --- a/packages/taler-wallet-cli/src/bench2.ts +++ /dev/null @@ -1,170 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  buildCodecForObject, -  codecForNumber, -  codecForString, -  codecOptional, -  Logger, -} from "@gnu-taler/taler-util"; -import { -  checkReserve, -  createFakebankReserve, -  CryptoDispatcher, -  depositCoin, -  downloadExchangeInfo, -  findDenomOrThrow, -  NodeHttpLib, -  refreshCoin, -  SynchronousCryptoWorkerFactoryNode, -  withdrawCoin, -} from "@gnu-taler/taler-wallet-core"; - -/** - * Entry point for the benchmark. - * - * The benchmark runs against an existing Taler deployment and does not - * set up its own services. - */ -export async function runBench2(configJson: any): Promise<void> { -  const logger = new Logger("Bench2"); - -  // Validate the configuration file for this benchmark. -  const benchConf = codecForBench2Config().decode(configJson); -  const curr = benchConf.currency; -  const cryptoDisp = new CryptoDispatcher(new SynchronousCryptoWorkerFactoryNode()); -  const cryptoApi = cryptoDisp.cryptoApi; - -  const http = new NodeHttpLib(); -  http.setThrottling(false); - -  const numIter = benchConf.iterations ?? 1; -  const numDeposits = benchConf.deposits ?? 5; - -  const reserveAmount = (numDeposits + 1) * 10; - -  for (let i = 0; i < numIter; i++) { -    const exchangeInfo = await downloadExchangeInfo(benchConf.exchange, http); - -    const reserveKeyPair = await cryptoApi.createEddsaKeypair({}); - -    console.log("creating fakebank reserve"); - -    await createFakebankReserve({ -      amount: `${curr}:${reserveAmount}`, -      exchangeInfo, -      fakebankBaseUrl: benchConf.bank, -      http, -      reservePub: reserveKeyPair.pub, -    }); - -    console.log("waiting for reserve"); - -    await checkReserve(http, benchConf.exchange, reserveKeyPair.pub); - -    console.log("reserve found"); - -    const d1 = findDenomOrThrow(exchangeInfo, `${curr}:8`); - -    for (let j = 0; j < numDeposits; j++) { -      console.log("withdrawing coin"); -      const coin = await withdrawCoin({ -        http, -        cryptoApi, -        reserveKeyPair: { -          reservePriv: reserveKeyPair.priv, -          reservePub: reserveKeyPair.pub, -        }, -        denom: d1, -        exchangeBaseUrl: benchConf.exchange, -      }); - -      console.log("depositing coin"); - -      await depositCoin({ -        amount: `${curr}:4`, -        coin: coin, -        cryptoApi, -        exchangeBaseUrl: benchConf.exchange, -        http, -        depositPayto: benchConf.payto, -      }); - -      const refreshDenoms = [ -        findDenomOrThrow(exchangeInfo, `${curr}:1`), -        findDenomOrThrow(exchangeInfo, `${curr}:1`), -      ]; - -      console.log("refreshing coin"); - -      await refreshCoin({ -        oldCoin: coin, -        cryptoApi, -        http, -        newDenoms: refreshDenoms, -      }); - -      console.log("refresh done"); -    } -  } -} - -/** - * Format of the configuration file passed to the benchmark - */ -interface Bench2Config { -  /** -   * Base URL of the bank. -   */ -  bank: string; - -  /** -   * Payto url for deposits. -   */ -  payto: string; - -  /** -   * Base URL of the exchange. -   */ -  exchange: string; - -  /** -   * How many withdraw/deposit iterations should be made? -   * Defaults to 1. -   */ -  iterations?: number; - -  currency: string; - -  deposits?: number; -} - -/** - * Schema validation codec for Bench1Config. - */ -const codecForBench2Config = () => -  buildCodecForObject<Bench2Config>() -    .property("bank", codecForString()) -    .property("payto", codecForString()) -    .property("exchange", codecForString()) -    .property("iterations", codecOptional(codecForNumber())) -    .property("deposits", codecOptional(codecForNumber())) -    .property("currency", codecForString()) -    .build("Bench2Config"); diff --git a/packages/taler-wallet-cli/src/bench3.ts b/packages/taler-wallet-cli/src/bench3.ts deleted file mode 100644 index 6041c525c..000000000 --- a/packages/taler-wallet-cli/src/bench3.ts +++ /dev/null @@ -1,205 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  buildCodecForObject, -  codecForNumber, -  codecForString, -  codecOptional, -  j2s, -  Logger, -} from "@gnu-taler/taler-util"; -import { -  getDefaultNodeWallet2, -  NodeHttpLib, -  WalletApiOperation, -  Wallet, -  AccessStats, -} from "@gnu-taler/taler-wallet-core"; -import benchMerchantIDGenerator from "./benchMerchantIDGenerator.js"; - -/** - * Entry point for the benchmark. - * - * The benchmark runs against an existing Taler deployment and does not - * set up its own services. - */ -export async function runBench3(configJson: any): Promise<void> { -  const logger = new Logger("Bench3"); - -  // Validate the configuration file for this benchmark. -  const b3conf = codecForBench3Config().decode(configJson); - -  if (!b3conf.paytoTemplate.includes("${id")) { -    throw new Error("Payto template url must contain '${id}' placeholder"); -  } - -  const myHttpLib = new NodeHttpLib(); -  myHttpLib.setThrottling(false); - -  const numIter = b3conf.iterations ?? 1; -  const numDeposits = b3conf.deposits ?? 5; -  const restartWallet = b3conf.restartAfter ?? 20; - -  const withdrawAmount = (numDeposits + 1) * 10; - -  const IDGenerator = benchMerchantIDGenerator(b3conf.randomAlg, b3conf.numMerchants ?? 100); - -  logger.info( -    `Starting Benchmark iterations=${numIter} deposits=${numDeposits} with ${b3conf.randomAlg} merchant selection`, -  ); - -  const trustExchange = !!process.env["TALER_WALLET_INSECURE_TRUST_EXCHANGE"]; -  if (trustExchange) { -    logger.info("trusting exchange (not validating signatures)"); -  } else { -    logger.info("not trusting exchange (validating signatures)"); -  } -  const batchWithdrawal = !!process.env["TALER_WALLET_BATCH_WITHDRAWAL"]; - -  let wallet = {} as Wallet; -  let getDbStats: () => AccessStats; - -  for (let i = 0; i < numIter; i++) { -    // Create a new wallet in each iteration -    // otherwise the TPS go down -    // my assumption is that the in-memory db file gets too large -    if (i % restartWallet == 0) { -      if (Object.keys(wallet).length !== 0) { -        wallet.stop(); -        console.log("wallet DB stats", j2s(getDbStats!())); -      } - -      const res = await getDefaultNodeWallet2({ -        // No persistent DB storage. -        persistentStoragePath: undefined, -        httpLib: myHttpLib, -      }); -      wallet = res.wallet; -      getDbStats = res.getDbStats; -      if (trustExchange) { -        wallet.setInsecureTrustExchange(); -      } -      wallet.setBatchWithdrawal(batchWithdrawal); -      await wallet.client.call(WalletApiOperation.InitWallet, {}); -    } - -    logger.trace(`Starting withdrawal amount=${withdrawAmount}`); -    let start = Date.now(); - -    await wallet.client.call(WalletApiOperation.WithdrawFakebank, { -      amount: b3conf.currency + ":" + withdrawAmount, -      bank: b3conf.bank, -      exchange: b3conf.exchange, -    }); - -    await wallet.runTaskLoop({ -      stopWhenDone: true, -    }); - -    logger.info( -      `Finished withdrawal amount=${withdrawAmount} time=${Date.now() - start}`, -    ); - -    for (let i = 0; i < numDeposits; i++) { -      logger.trace(`Starting deposit amount=10`); -      start = Date.now(); - -      let merchID = IDGenerator.getRandomMerchantID(); -      let payto = b3conf.paytoTemplate.replace("${id}", merchID.toString()); - -      await wallet.client.call(WalletApiOperation.CreateDepositGroup, { -        amount: b3conf.currency + ":10", -        depositPaytoUri: payto, -      }); - -      await wallet.runTaskLoop({ -        stopWhenDone: true, -      }); - -      logger.info(`Finished deposit amount=10 time=${Date.now() - start}`); -    } -  } - -  wallet.stop(); -  console.log("wallet DB stats", j2s(getDbStats!())); -} - -/** - * Format of the configuration file passed to the benchmark - */ -interface Bench3Config { -  /** -   * Base URL of the bank. -   */ -  bank: string; - -  /** -   * Payto url template for deposits, must contain '${id}' for replacements. -   */ -  paytoTemplate: string; - -  /** -   * Base URL of the exchange. -   */ -  exchange: string; - -  /** -   * How many withdraw/deposit iterations should be made? -   * Defaults to 1. -   */ -  iterations?: number; - -  currency: string; - -  deposits?: number; - -  /** -   * How any iterations run until the wallet db gets purged -   * Defaults to 20. -   */ -  restartAfter?: number; - -  /** -   * Number of merchants to select from randomly -   */ -  numMerchants?: number; - -  /** -   * Which random generator to use. -   * Possible values: 'zipf', 'rand' -   */ -  randomAlg: string; -} - -/** - * Schema validation codec for Bench1Config. - */ -const codecForBench3Config = () => -  buildCodecForObject<Bench3Config>() -    .property("bank", codecForString()) -    .property("paytoTemplate", codecForString()) -    .property("numMerchants", codecOptional(codecForNumber())) -    .property("randomAlg", codecForString()) -    .property("exchange", codecForString()) -    .property("iterations", codecOptional(codecForNumber())) -    .property("deposits", codecOptional(codecForNumber())) -    .property("currency", codecForString()) -    .property("restartAfter", codecOptional(codecForNumber())) -    .build("Bench1Config"); diff --git a/packages/taler-wallet-cli/src/benchMerchantIDGenerator.ts b/packages/taler-wallet-cli/src/benchMerchantIDGenerator.ts deleted file mode 100644 index b83c12bb8..000000000 --- a/packages/taler-wallet-cli/src/benchMerchantIDGenerator.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> -  - @author: Boss Marco - */ - -const getRandomInt = function(max: number) { -  return Math.floor(Math.random() * max); -} - -abstract class BenchMerchantIDGenerator { -  abstract getRandomMerchantID(): number -} - -class ZipfGenerator extends BenchMerchantIDGenerator { - -  weights: number[]; -  total_weight: number; - -  constructor(numMerchants: number) { -    super(); -    this.weights = new Array<number>(numMerchants); -    for (var i = 0; i < this.weights.length; i++) { -      /* we use integers (floor), make sure we have big enough values  -       * by multiplying with -       * numMerchants again */ -      this.weights[i] = Math.floor((numMerchants/(i+1)) * numMerchants); -    } -    this.total_weight = this.weights.reduce((p, n) => p + n); -  } - -  getRandomMerchantID(): number { -    let random = getRandomInt(this.total_weight); -    let current = 0; - -    for (var i = 0; i < this.weights.length; i++) { -      current += this.weights[i]; -      if (random <= current) { -          return i+1; -      } -    } - -    /* should never come here */ -    return getRandomInt(this.weights.length); -  } -} - -class RandomGenerator extends BenchMerchantIDGenerator { - -  max: number - -  constructor(numMerchants: number) { -    super(); -    this.max = numMerchants -  } - -  getRandomMerchantID() { -    return getRandomInt(this.max); -  } -} - -export default function(type: string, maxID: number): BenchMerchantIDGenerator { -  switch (type) { -    case "zipf": -      return new ZipfGenerator(maxID); -    case "rand": -      return new RandomGenerator(maxID); -    default: -      throw new Error("Valid types are 'zipf' and 'rand'"); -  } -} diff --git a/packages/taler-wallet-cli/src/env-full.ts b/packages/taler-wallet-cli/src/env-full.ts deleted file mode 100644 index 3a684db0b..000000000 --- a/packages/taler-wallet-cli/src/env-full.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { Duration, j2s, URL } from "@gnu-taler/taler-util"; -import { CoinConfig, defaultCoinConfig } from "./harness/denomStructures.js"; -import { -  GlobalTestState, -  setupDb, -  ExchangeService, -  FakebankService, -  MerchantService, -  getPayto, -} from "./harness/harness.js"; - -/** - * Entry point for the full Taler test environment. - */ -export async function runEnvFull(t: GlobalTestState): Promise<void> { -  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", -  ); -  console.log("exchange bank account", j2s(exchangeBankAccount)); -  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(); - -  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 }), -    ), -  }); - -  console.log("setup done!"); -} diff --git a/packages/taler-wallet-cli/src/env1.ts b/packages/taler-wallet-cli/src/env1.ts deleted file mode 100644 index aec0b7b8f..000000000 --- a/packages/taler-wallet-cli/src/env1.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { URL } from "@gnu-taler/taler-util"; -import { CoinConfig, defaultCoinConfig } from "./harness/denomStructures.js"; -import { -  GlobalTestState, -  setupDb, -  ExchangeService, -  FakebankService, -} from "./harness/harness.js"; - -/** - * Entry point for the benchmark. - * - * The benchmark runs against an existing Taler deployment and does not - * set up its own services. - */ -export async function runEnv1(t: GlobalTestState): Promise<void> { -  const db = await setupDb(t); - -  const bank = await FakebankService.create(t, { -    currency: "TESTKUDOS", -    httpPort: 8082, -    allowRegistrations: true, -    database: db.connStr, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  exchange.addBankAccount("1", { -    accountName: "exchange", -    accountPassword: "x", -    wireGatewayApiBaseUrl: new URL("/exchange/", bank.baseUrl).href, -    accountPaytoUri: "payto://x-taler-bank/localhost/exchange", -  }); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")); -  exchange.addCoinConfigList(coinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  console.log("setup done!"); -} diff --git a/packages/taler-wallet-cli/src/harness/denomStructures.ts b/packages/taler-wallet-cli/src/harness/denomStructures.ts deleted file mode 100644 index b12857c7e..000000000 --- a/packages/taler-wallet-cli/src/harness/denomStructures.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -export interface CoinCoinfigCommon { -  name: string; -  value: string; -  durationWithdraw: string; -  durationSpend: string; -  durationLegal: string; -  feeWithdraw: string; -  feeDeposit: string; -  feeRefresh: string; -  feeRefund: string; -  ageRestricted?: boolean; -} - -export interface CoinConfigRsa extends CoinCoinfigCommon { -  cipher: "RSA"; -  rsaKeySize: number; -} - -/** - * Clause Schnorr coin config. - */ -export interface CoinConfigCs extends CoinCoinfigCommon { -  cipher: "CS"; -} - -export type CoinConfig = CoinConfigRsa | CoinConfigCs; - -const coinRsaCommon = { -  cipher: "RSA" as const, -  durationLegal: "3 years", -  durationSpend: "2 years", -  durationWithdraw: "7 days", -  rsaKeySize: 1024, -}; - -export const coin_ct1 = (curr: string): CoinConfig => ({ -  ...coinRsaCommon, -  name: `${curr}_ct1`, -  value: `${curr}:0.01`, -  feeDeposit: `${curr}:0.00`, -  feeRefresh: `${curr}:0.01`, -  feeRefund: `${curr}:0.00`, -  feeWithdraw: `${curr}:0.01`, -}); - -export const coin_ct10 = (curr: string): CoinConfig => ({ -  ...coinRsaCommon, -  name: `${curr}_ct10`, -  value: `${curr}:0.10`, -  feeDeposit: `${curr}:0.01`, -  feeRefresh: `${curr}:0.01`, -  feeRefund: `${curr}:0.00`, -  feeWithdraw: `${curr}:0.01`, -}); - -export const coin_u1 = (curr: string): CoinConfig => ({ -  ...coinRsaCommon, -  name: `${curr}_u1`, -  value: `${curr}:1`, -  feeDeposit: `${curr}:0.02`, -  feeRefresh: `${curr}:0.02`, -  feeRefund: `${curr}:0.02`, -  feeWithdraw: `${curr}:0.02`, -}); - -export const coin_u2 = (curr: string): CoinConfig => ({ -  ...coinRsaCommon, -  name: `${curr}_u2`, -  value: `${curr}:2`, -  feeDeposit: `${curr}:0.02`, -  feeRefresh: `${curr}:0.02`, -  feeRefund: `${curr}:0.02`, -  feeWithdraw: `${curr}:0.02`, -}); - -export const coin_u4 = (curr: string): CoinConfig => ({ -  ...coinRsaCommon, -  name: `${curr}_u4`, -  value: `${curr}:4`, -  feeDeposit: `${curr}:0.02`, -  feeRefresh: `${curr}:0.02`, -  feeRefund: `${curr}:0.02`, -  feeWithdraw: `${curr}:0.02`, -}); - -export const coin_u8 = (curr: string): CoinConfig => ({ -  ...coinRsaCommon, -  name: `${curr}_u8`, -  value: `${curr}:8`, -  feeDeposit: `${curr}:0.16`, -  feeRefresh: `${curr}:0.16`, -  feeRefund: `${curr}:0.16`, -  feeWithdraw: `${curr}:0.16`, -}); - -const coin_u10 = (curr: string): CoinConfig => ({ -  ...coinRsaCommon, -  name: `${curr}_u10`, -  value: `${curr}:10`, -  feeDeposit: `${curr}:0.2`, -  feeRefresh: `${curr}:0.2`, -  feeRefund: `${curr}:0.2`, -  feeWithdraw: `${curr}:0.2`, -}); - -export const defaultCoinConfig = [ -  coin_ct1, -  coin_ct10, -  coin_u1, -  coin_u2, -  coin_u4, -  coin_u8, -  coin_u10, -]; - -export function makeNoFeeCoinConfig(curr: string): CoinConfig[] { -  const cc: CoinConfig[] = []; - -  for (let i = 0; i < 16; i++) { -    const ct = 2 ** i; - -    const unit = Math.floor(ct / 100); -    const cent = ct % 100; - -    cc.push({ -      cipher: "RSA", -      durationLegal: "3 years", -      durationSpend: "2 years", -      durationWithdraw: "7 days", -      rsaKeySize: 1024, -      name: `${curr}-u${i}`, -      feeDeposit: `${curr}:0`, -      feeRefresh: `${curr}:0`, -      feeRefund: `${curr}:0`, -      feeWithdraw: `${curr}:0`, -      value: `${curr}:${unit}.${cent}`, -    }); -  } - -  return cc; -} diff --git a/packages/taler-wallet-cli/src/harness/faultInjection.ts b/packages/taler-wallet-cli/src/harness/faultInjection.ts deleted file mode 100644 index 4c3d0c123..000000000 --- a/packages/taler-wallet-cli/src/harness/faultInjection.ts +++ /dev/null @@ -1,256 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Fault injection proxy. - * - * @author Florian Dold <dold@taler.net> - */ - -/** - * Imports - */ -import * as http from "http"; -import { URL } from "url"; -import { -  GlobalTestState, -  ExchangeService, -  ExchangeServiceInterface, -  MerchantServiceInterface, -  MerchantService, -} from "../harness/harness.js"; - -export interface FaultProxyConfig { -  inboundPort: number; -  targetPort: number; -} - -/** - * Fault injection context.  Modified by fault injection functions. - */ -export interface FaultInjectionRequestContext { -  requestUrl: string; -  method: string; -  requestHeaders: Record<string, string | string[] | undefined>; -  requestBody?: Buffer; -  dropRequest: boolean; -} - -export interface FaultInjectionResponseContext { -  request: FaultInjectionRequestContext; -  statusCode: number; -  responseHeaders: Record<string, string | string[] | undefined>; -  responseBody: Buffer | undefined; -  dropResponse: boolean; -} - -export interface FaultSpec { -  modifyRequest?: (ctx: FaultInjectionRequestContext) => Promise<void>; -  modifyResponse?: (ctx: FaultInjectionResponseContext) => Promise<void>; -} - -export class FaultProxy { -  constructor( -    private globalTestState: GlobalTestState, -    private faultProxyConfig: FaultProxyConfig, -  ) {} - -  private currentFaultSpecs: FaultSpec[] = []; - -  start() { -    const server = http.createServer((req, res) => { -      const requestChunks: Buffer[] = []; -      const requestUrl = `http://localhost:${this.faultProxyConfig.inboundPort}${req.url}`; -      console.log("request for", new URL(requestUrl)); -      req.on("data", (chunk) => { -        requestChunks.push(chunk); -      }); -      req.on("end", async () => { -        console.log("end of data"); -        let requestBuffer: Buffer | undefined; -        if (requestChunks.length > 0) { -          requestBuffer = Buffer.concat(requestChunks); -        } -        console.log("full request body", requestBuffer); - -        const faultReqContext: FaultInjectionRequestContext = { -          dropRequest: false, -          method: req.method!!, -          requestHeaders: req.headers, -          requestUrl, -          requestBody: requestBuffer, -        }; - -        for (const faultSpec of this.currentFaultSpecs) { -          if (faultSpec.modifyRequest) { -            await faultSpec.modifyRequest(faultReqContext); -          } -        } - -        if (faultReqContext.dropRequest) { -          res.destroy(); -          return; -        } - -        const faultedUrl = new URL(faultReqContext.requestUrl); - -        const proxyRequest = http.request({ -          method: faultReqContext.method, -          host: "localhost", -          port: this.faultProxyConfig.targetPort, -          path: faultedUrl.pathname + faultedUrl.search, -          headers: faultReqContext.requestHeaders, -        }); - -        console.log( -          `proxying request to target path '${ -            faultedUrl.pathname + faultedUrl.search -          }'`, -        ); - -        if (faultReqContext.requestBody) { -          proxyRequest.write(faultReqContext.requestBody); -        } -        proxyRequest.end(); -        proxyRequest.on("response", (proxyResp) => { -          console.log("gotten response from target", proxyResp.statusCode); -          const respChunks: Buffer[] = []; -          proxyResp.on("data", (proxyRespData) => { -            respChunks.push(proxyRespData); -          }); -          proxyResp.on("end", async () => { -            console.log("end of target response"); -            let responseBuffer: Buffer | undefined; -            if (respChunks.length > 0) { -              responseBuffer = Buffer.concat(respChunks); -            } -            const faultRespContext: FaultInjectionResponseContext = { -              request: faultReqContext, -              dropResponse: false, -              responseBody: responseBuffer, -              responseHeaders: proxyResp.headers, -              statusCode: proxyResp.statusCode!!, -            }; -            for (const faultSpec of this.currentFaultSpecs) { -              const modResponse = faultSpec.modifyResponse; -              if (modResponse) { -                await modResponse(faultRespContext); -              } -            } -            if (faultRespContext.dropResponse) { -              req.destroy(); -              return; -            } -            if (faultRespContext.responseBody) { -              // We must accommodate for potentially changed content length -              faultRespContext.responseHeaders[ -                "content-length" -              ] = `${faultRespContext.responseBody.byteLength}`; -            } -            console.log("writing response head"); -            res.writeHead( -              faultRespContext.statusCode, -              http.STATUS_CODES[faultRespContext.statusCode], -              faultRespContext.responseHeaders, -            ); -            if (faultRespContext.responseBody) { -              res.write(faultRespContext.responseBody); -            } -            res.end(); -          }); -        }); -      }); -    }); - -    server.listen(this.faultProxyConfig.inboundPort); -    this.globalTestState.servers.push(server); -  } - -  addFault(f: FaultSpec) { -    this.currentFaultSpecs.push(f); -  } - -  clearAllFaults() { -    this.currentFaultSpecs = []; -  } -} - -export class FaultInjectedExchangeService implements ExchangeServiceInterface { -  baseUrl: string; -  port: number; -  faultProxy: FaultProxy; - -  get name(): string { -    return this.innerExchange.name; -  } - -  get masterPub(): string { -    return this.innerExchange.masterPub; -  } - -  private innerExchange: ExchangeService; - -  constructor( -    t: GlobalTestState, -    e: ExchangeService, -    proxyInboundPort: number, -  ) { -    this.innerExchange = e; -    this.faultProxy = new FaultProxy(t, { -      inboundPort: proxyInboundPort, -      targetPort: e.port, -    }); -    this.faultProxy.start(); - -    const exchangeUrl = new URL(e.baseUrl); -    exchangeUrl.port = `${proxyInboundPort}`; -    this.baseUrl = exchangeUrl.href; -    this.port = proxyInboundPort; -  } -} - -export class FaultInjectedMerchantService implements MerchantServiceInterface { -  baseUrl: string; -  port: number; -  faultProxy: FaultProxy; - -  get name(): string { -    return this.innerMerchant.name; -  } - -  private innerMerchant: MerchantService; -  private inboundPort: number; - -  constructor( -    t: GlobalTestState, -    m: MerchantService, -    proxyInboundPort: number, -  ) { -    this.innerMerchant = m; -    this.faultProxy = new FaultProxy(t, { -      inboundPort: proxyInboundPort, -      targetPort: m.port, -    }); -    this.faultProxy.start(); -    this.inboundPort = proxyInboundPort; -  } - -  makeInstanceBaseUrl(instanceName?: string | undefined): string { -    const url = new URL(this.innerMerchant.makeInstanceBaseUrl(instanceName)); -    url.port = `${this.inboundPort}`; -    return url.href; -  } -} diff --git a/packages/taler-wallet-cli/src/harness/harness.ts b/packages/taler-wallet-cli/src/harness/harness.ts deleted file mode 100644 index 6f722dc8d..000000000 --- a/packages/taler-wallet-cli/src/harness/harness.ts +++ /dev/null @@ -1,2024 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Test harness for various GNU Taler components. - * Also provides a fault-injection proxy. - * - * @author Florian Dold <dold@taler.net> - */ - -const logger = new Logger("harness.ts"); - -/** - * Imports - */ -import { -  AmountJson, -  Amounts, -  AmountString, -  Configuration, -  CoreApiResponse, -  createEddsaKeyPair, -  Duration, -  eddsaGetPublic, -  EddsaKeyPair, -  encodeCrock, -  hash, -  j2s, -  Logger, -  parsePaytoUri, -  stringToBytes, -  TalerProtocolDuration, -} from "@gnu-taler/taler-util"; -import { -  BankAccessApi, -  BankApi, -  BankServiceHandle, -  HarnessExchangeBankAccount, -  NodeHttpLib, -  openPromise, -  TalerError, -  WalletCoreApiClient, -} from "@gnu-taler/taler-wallet-core"; -import { deepStrictEqual } from "assert"; -import axiosImp, { AxiosError } from "axios"; -import { ChildProcess, spawn } from "child_process"; -import * as child_process from "child_process"; -import * as fs from "fs"; -import * as http from "http"; -import * as path from "path"; -import * as readline from "readline"; -import { URL } from "url"; -import * as util from "util"; -import { CoinConfig } from "./denomStructures.js"; -import { LibeufinNexusApi, LibeufinSandboxApi } from "./libeufin-apis.js"; -import { -  codecForMerchantOrderPrivateStatusResponse, -  codecForPostOrderResponse, -  MerchantInstancesResponse, -  MerchantOrderPrivateStatusResponse, -  PostOrderRequest, -  PostOrderResponse, -  TipCreateConfirmation, -  TipCreateRequest, -  TippingReserveStatus, -} from "./merchantApiTypes.js"; - -const exec = util.promisify(child_process.exec); - -const axios = axiosImp.default; - -export async function delayMs(ms: number): Promise<void> { -  return new Promise((resolve, reject) => { -    setTimeout(() => resolve(), ms); -  }); -} - -export interface WithAuthorization { -  Authorization?: string; -} - -interface WaitResult { -  code: number | null; -  signal: NodeJS.Signals | null; -} - -/** - * Run a shell command, return stdout. - */ -export async function sh( -  t: GlobalTestState, -  logName: string, -  command: string, -  env: { [index: string]: string | undefined } = process.env, -): Promise<string> { -  logger.info(`running command ${command}`); -  return new Promise((resolve, reject) => { -    const stdoutChunks: Buffer[] = []; -    const proc = spawn(command, { -      stdio: ["inherit", "pipe", "pipe"], -      shell: true, -      env: env, -    }); -    proc.stdout.on("data", (x) => { -      if (x instanceof Buffer) { -        stdoutChunks.push(x); -      } else { -        throw Error("unexpected data chunk type"); -      } -    }); -    const stderrLogFileName = path.join(t.testDir, `${logName}-stderr.log`); -    const stderrLog = fs.createWriteStream(stderrLogFileName, { -      flags: "a", -    }); -    proc.stderr.pipe(stderrLog); -    proc.on("exit", (code, signal) => { -      logger.info(`child process exited (${code} / ${signal})`); -      if (code != 0) { -        reject(Error(`Unexpected exit code ${code} for '${command}'`)); -        return; -      } -      const b = Buffer.concat(stdoutChunks).toString("utf-8"); -      resolve(b); -    }); -    proc.on("error", () => { -      reject(Error("Child process had error")); -    }); -  }); -} - -function shellescape(args: string[]) { -  const ret = args.map((s) => { -    if (/[^A-Za-z0-9_\/:=-]/.test(s)) { -      s = "'" + s.replace(/'/g, "'\\''") + "'"; -      s = s.replace(/^(?:'')+/g, "").replace(/\\'''/g, "\\'"); -    } -    return s; -  }); -  return ret.join(" "); -} - -/** - * Run a shell command, return stdout. - * - * Log stderr to a log file. - */ -export async function runCommand( -  t: GlobalTestState, -  logName: string, -  command: string, -  args: string[], -  env: { [index: string]: string | undefined } = process.env, -): Promise<string> { -  logger.info(`running command ${shellescape([command, ...args])}`); -  return new Promise((resolve, reject) => { -    const stdoutChunks: Buffer[] = []; -    const proc = spawn(command, args, { -      stdio: ["inherit", "pipe", "pipe"], -      shell: false, -      env: env, -    }); -    proc.stdout.on("data", (x) => { -      if (x instanceof Buffer) { -        stdoutChunks.push(x); -      } else { -        throw Error("unexpected data chunk type"); -      } -    }); -    const stderrLogFileName = path.join(t.testDir, `${logName}-stderr.log`); -    const stderrLog = fs.createWriteStream(stderrLogFileName, { -      flags: "a", -    }); -    proc.stderr.pipe(stderrLog); -    proc.on("exit", (code, signal) => { -      logger.info(`child process exited (${code} / ${signal})`); -      if (code != 0) { -        reject(Error(`Unexpected exit code ${code} for '${command}'`)); -        return; -      } -      const b = Buffer.concat(stdoutChunks).toString("utf-8"); -      resolve(b); -    }); -    proc.on("error", () => { -      reject(Error("Child process had error")); -    }); -  }); -} - -export class ProcessWrapper { -  private waitPromise: Promise<WaitResult>; -  constructor(public proc: ChildProcess) { -    this.waitPromise = new Promise((resolve, reject) => { -      proc.on("exit", (code, signal) => { -        resolve({ code, signal }); -      }); -      proc.on("error", (err) => { -        reject(err); -      }); -    }); -  } - -  wait(): Promise<WaitResult> { -    return this.waitPromise; -  } -} - -export class GlobalTestParams { -  testDir: string; -} - -export class GlobalTestState { -  testDir: string; -  procs: ProcessWrapper[]; -  servers: http.Server[]; -  inShutdown: boolean = false; -  constructor(params: GlobalTestParams) { -    this.testDir = params.testDir; -    this.procs = []; -    this.servers = []; -  } - -  async assertThrowsTalerErrorAsync( -    block: () => Promise<void>, -  ): Promise<TalerError> { -    try { -      await block(); -    } catch (e) { -      if (e instanceof TalerError) { -        return e; -      } -      throw Error(`expected TalerError to be thrown, but got ${e}`); -    } -    throw Error( -      `expected TalerError to be thrown, but block finished without throwing`, -    ); -  } - -  async assertThrowsAsync(block: () => Promise<void>): Promise<any> { -    try { -      await block(); -    } catch (e) { -      return e; -    } -    throw Error( -      `expected exception to be thrown, but block finished without throwing`, -    ); -  } - -  assertAxiosError(e: any): asserts e is AxiosError { -    if (!e.isAxiosError) { -      throw Error("expected axios error"); -    } -  } - -  assertTrue(b: boolean): asserts b { -    if (!b) { -      throw Error("test assertion failed"); -    } -  } - -  assertDeepEqual<T>(actual: any, expected: T): asserts actual is T { -    deepStrictEqual(actual, expected); -  } - -  assertAmountEquals( -    amtActual: string | AmountJson, -    amtExpected: string | AmountJson, -  ): void { -    if (Amounts.cmp(amtActual, amtExpected) != 0) { -      throw Error( -        `test assertion failed: expected ${Amounts.stringify( -          amtExpected, -        )} but got ${Amounts.stringify(amtActual)}`, -      ); -    } -  } - -  assertAmountLeq(a: string | AmountJson, b: string | AmountJson): void { -    if (Amounts.cmp(a, b) > 0) { -      throw Error( -        `test assertion failed: expected ${Amounts.stringify( -          a, -        )} to be less or equal (leq) than ${Amounts.stringify(b)}`, -      ); -    } -  } - -  shutdownSync(): void { -    for (const s of this.servers) { -      s.close(); -      s.removeAllListeners(); -    } -    for (const p of this.procs) { -      if (p.proc.exitCode == null) { -        p.proc.kill("SIGTERM"); -      } -    } -  } - -  spawnService( -    command: string, -    args: string[], -    logName: string, -    env: { [index: string]: string | undefined } = process.env, -  ): ProcessWrapper { -    logger.info( -      `spawning process (${logName}): ${shellescape([command, ...args])}`, -    ); -    const proc = spawn(command, args, { -      stdio: ["inherit", "pipe", "pipe"], -      env: env, -    }); -    logger.info(`spawned process (${logName}) with pid ${proc.pid}`); -    proc.on("error", (err) => { -      logger.warn(`could not start process (${command})`, err); -    }); -    proc.on("exit", (code, signal) => { -      logger.warn(`process ${logName} exited ${j2s({ code, signal })}`); -    }); -    const stderrLogFileName = this.testDir + `/${logName}-stderr.log`; -    const stderrLog = fs.createWriteStream(stderrLogFileName, { -      flags: "a", -    }); -    proc.stderr.pipe(stderrLog); -    const stdoutLogFileName = this.testDir + `/${logName}-stdout.log`; -    const stdoutLog = fs.createWriteStream(stdoutLogFileName, { -      flags: "a", -    }); -    proc.stdout.pipe(stdoutLog); -    const procWrap = new ProcessWrapper(proc); -    this.procs.push(procWrap); -    return procWrap; -  } - -  async shutdown(): Promise<void> { -    if (this.inShutdown) { -      return; -    } -    if (shouldLingerInTest()) { -      logger.info("refusing to shut down, lingering was requested"); -      return; -    } -    this.inShutdown = true; -    logger.info("shutting down"); -    for (const s of this.servers) { -      s.close(); -      s.removeAllListeners(); -    } -    for (const p of this.procs) { -      if (p.proc.exitCode == null) { -        logger.info(`killing process ${p.proc.pid}`); -        p.proc.kill("SIGTERM"); -        await p.wait(); -      } -    } -  } -} - -export function shouldLingerInTest(): boolean { -  return !!process.env["TALER_TEST_LINGER"]; -} - -export interface TalerConfigSection { -  options: Record<string, string | undefined>; -} - -export interface TalerConfig { -  sections: Record<string, TalerConfigSection>; -} - -export interface DbInfo { -  /** -   * Postgres connection string. -   */ -  connStr: string; - -  dbname: string; -} - -export async function setupDb(gc: GlobalTestState): Promise<DbInfo> { -  const dbname = "taler-integrationtest"; -  await exec(`dropdb "${dbname}" || true`); -  await exec(`createdb "${dbname}"`); -  return { -    connStr: `postgres:///${dbname}`, -    dbname, -  }; -} - -export interface BankConfig { -  currency: string; -  httpPort: number; -  database: string; -  allowRegistrations: boolean; -  maxDebt?: string; -} - -export interface FakeBankConfig { -  currency: string; -  httpPort: number; -} - -function setTalerPaths(config: Configuration, home: string) { -  config.setString("paths", "taler_home", home); -  // We need to make sure that the path of taler_runtime_dir isn't too long, -  // as it contains unix domain sockets (108 character limit). -  const runDir = fs.mkdtempSync("/tmp/taler-test-"); -  config.setString("paths", "taler_runtime_dir", runDir); -  config.setString( -    "paths", -    "taler_data_home", -    "$TALER_HOME/.local/share/taler/", -  ); -  config.setString("paths", "taler_config_home", "$TALER_HOME/.config/taler/"); -  config.setString("paths", "taler_cache_home", "$TALER_HOME/.config/taler/"); -} - -function setCoin(config: Configuration, c: CoinConfig) { -  const s = `coin_${c.name}`; -  config.setString(s, "value", c.value); -  config.setString(s, "duration_withdraw", c.durationWithdraw); -  config.setString(s, "duration_spend", c.durationSpend); -  config.setString(s, "duration_legal", c.durationLegal); -  config.setString(s, "fee_deposit", c.feeDeposit); -  config.setString(s, "fee_withdraw", c.feeWithdraw); -  config.setString(s, "fee_refresh", c.feeRefresh); -  config.setString(s, "fee_refund", c.feeRefund); -  if (c.ageRestricted) { -    config.setString(s, "age_restricted", "yes"); -  } -  if (c.cipher === "RSA") { -    config.setString(s, "rsa_keysize", `${c.rsaKeySize}`); -    config.setString(s, "cipher", "RSA"); -  } else if (c.cipher === "CS") { -    config.setString(s, "cipher", "CS"); -  } else { -    throw new Error(); -  } -} - -/** - * Send an HTTP request until it succeeds or the process dies. - */ -export async function pingProc( -  proc: ProcessWrapper | undefined, -  url: string, -  serviceName: string, -): Promise<void> { -  if (!proc || proc.proc.exitCode !== null) { -    throw Error(`service process ${serviceName} not started, can't ping`); -  } -  while (true) { -    try { -      logger.info(`pinging ${serviceName} at ${url}`); -      const resp = await axios.get(url); -      logger.info(`service ${serviceName} available`); -      return; -    } catch (e: any) { -      logger.info(`service ${serviceName} not ready:`, e.toString()); -      //console.log(e); -      await delayMs(1000); -    } -    if (!proc || proc.proc.exitCode != null || proc.proc.signalCode != null) { -      throw Error(`service process ${serviceName} stopped unexpectedly`); -    } -  } -} - -class BankServiceBase { -  proc: ProcessWrapper | undefined; - -  protected constructor( -    protected globalTestState: GlobalTestState, -    protected bankConfig: BankConfig, -    protected configFile: string, -  ) {} -} - -/** - * Work in progress.  The key point is that both Sandbox and Nexus - * will be configured and started by this class. - */ -class LibEuFinBankService extends BankServiceBase implements BankServiceHandle { -  sandboxProc: ProcessWrapper | undefined; -  nexusProc: ProcessWrapper | undefined; - -  http = new NodeHttpLib(); - -  static async create( -    gc: GlobalTestState, -    bc: BankConfig, -  ): Promise<LibEuFinBankService> { -    return new LibEuFinBankService(gc, bc, "foo"); -  } - -  get port() { -    return this.bankConfig.httpPort; -  } -  get nexusPort() { -    return this.bankConfig.httpPort + 1000; -  } - -  get nexusDbConn(): string { -    return `jdbc:sqlite:${this.globalTestState.testDir}/libeufin-nexus.sqlite3`; -  } - -  get sandboxDbConn(): string { -    return `jdbc:sqlite:${this.globalTestState.testDir}/libeufin-sandbox.sqlite3`; -  } - -  get nexusBaseUrl(): string { -    return `http://localhost:${this.nexusPort}`; -  } - -  get baseUrlDemobank(): string { -    let url = new URL("demobanks/default/", this.baseUrlNetloc); -    return url.href; -  } - -  // FIXME: Duplicate?  Where is this needed? -  get baseUrlAccessApi(): string { -    let url = new URL("access-api/", this.baseUrlDemobank); -    return url.href; -  } - -  get bankAccessApiBaseUrl(): string { -    let url = new URL("access-api/", this.baseUrlDemobank); -    return url.href; -  } - -  get baseUrlNetloc(): string { -    return `http://localhost:${this.bankConfig.httpPort}/`; -  } - -  get baseUrl(): string { -    return this.baseUrlAccessApi; -  } - -  async setSuggestedExchange( -    e: ExchangeServiceInterface, -    exchangePayto: string, -  ) { -    await sh( -      this.globalTestState, -      "libeufin-sandbox-set-default-exchange", -      `libeufin-sandbox default-exchange ${e.baseUrl} ${exchangePayto}`, -      { -        ...process.env, -        LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxDbConn, -      }, -    ); -  } - -  // Create one at both sides: Sandbox and Nexus. -  async createExchangeAccount( -    accountName: string, -    password: string, -  ): Promise<HarnessExchangeBankAccount> { -    logger.info("Create Exchange account(s)!"); -    /** -     * Many test cases try to create a Exchange account before -     * starting the bank;  that's because the Pybank did it entirely -     * via the configuration file. -     */ -    await this.start(); -    await this.pingUntilAvailable(); -    await LibeufinSandboxApi.createDemobankAccount(accountName, password, { -      baseUrl: this.baseUrlAccessApi, -    }); -    let bankAccountLabel = accountName; -    await LibeufinSandboxApi.createDemobankEbicsSubscriber( -      { -        hostID: "talertestEbicsHost", -        userID: "exchangeEbicsUser", -        partnerID: "exchangeEbicsPartner", -      }, -      bankAccountLabel, -      { baseUrl: this.baseUrlDemobank }, -    ); - -    await LibeufinNexusApi.createUser( -      { baseUrl: this.nexusBaseUrl }, -      { -        username: accountName, -        password: password, -      }, -    ); -    await LibeufinNexusApi.createEbicsBankConnection( -      { baseUrl: this.nexusBaseUrl }, -      { -        name: "ebics-connection", // connection name. -        ebicsURL: new URL("ebicsweb", this.baseUrlNetloc).href, -        hostID: "talertestEbicsHost", -        userID: "exchangeEbicsUser", -        partnerID: "exchangeEbicsPartner", -      }, -    ); -    await LibeufinNexusApi.connectBankConnection( -      { baseUrl: this.nexusBaseUrl }, -      "ebics-connection", -    ); -    await LibeufinNexusApi.fetchAccounts( -      { baseUrl: this.nexusBaseUrl }, -      "ebics-connection", -    ); -    await LibeufinNexusApi.importConnectionAccount( -      { baseUrl: this.nexusBaseUrl }, -      "ebics-connection", // connection name -      accountName, // offered account label -      `${accountName}-nexus-label`, // bank account label at Nexus -    ); -    await LibeufinNexusApi.createTwgFacade( -      { baseUrl: this.nexusBaseUrl }, -      { -        name: "exchange-facade", -        connectionName: "ebics-connection", -        accountName: `${accountName}-nexus-label`, -        currency: "EUR", -        reserveTransferLevel: "report", -      }, -    ); -    await LibeufinNexusApi.postPermission( -      { baseUrl: this.nexusBaseUrl }, -      { -        action: "grant", -        permission: { -          subjectId: accountName, -          subjectType: "user", -          resourceType: "facade", -          resourceId: "exchange-facade", // facade name -          permissionName: "facade.talerWireGateway.transfer", -        }, -      }, -    ); -    await LibeufinNexusApi.postPermission( -      { baseUrl: this.nexusBaseUrl }, -      { -        action: "grant", -        permission: { -          subjectId: accountName, -          subjectType: "user", -          resourceType: "facade", -          resourceId: "exchange-facade", // facade name -          permissionName: "facade.talerWireGateway.history", -        }, -      }, -    ); -    // Set fetch task. -    await LibeufinNexusApi.postTask( -      { baseUrl: this.nexusBaseUrl }, -      `${accountName}-nexus-label`, -      { -        name: "wirewatch-task", -        cronspec: "* * *", -        type: "fetch", -        params: { -          level: "all", -          rangeType: "all", -        }, -      }, -    ); -    await LibeufinNexusApi.postTask( -      { baseUrl: this.nexusBaseUrl }, -      `${accountName}-nexus-label`, -      { -        name: "aggregator-task", -        cronspec: "* * *", -        type: "submit", -        params: {}, -      }, -    ); -    let facadesResp = await LibeufinNexusApi.getAllFacades({ -      baseUrl: this.nexusBaseUrl, -    }); -    let accountInfoResp = await LibeufinSandboxApi.demobankAccountInfo( -      "admin", -      "secret", -      { baseUrl: this.baseUrlAccessApi }, -      accountName, // bank account label. -    ); -    return { -      accountName: accountName, -      accountPassword: password, -      accountPaytoUri: accountInfoResp.data.paytoUri, -      wireGatewayApiBaseUrl: facadesResp.data.facades[0].baseUrl, -    }; -  } - -  async start(): Promise<void> { -    /** -     * Because many test cases try to create a Exchange bank -     * account _before_ starting the bank (Pybank did it only via -     * the config), it is possible that at this point Sandbox and -     * Nexus are already running.  Hence, this method only launches -     * them if they weren't launched earlier. -     */ - -    // Only go ahead if BOTH aren't running. -    if (this.sandboxProc || this.nexusProc) { -      logger.info("Nexus or Sandbox already running, not taking any action."); -      return; -    } -    await sh( -      this.globalTestState, -      "libeufin-sandbox-config-demobank", -      `libeufin-sandbox config --currency=${this.bankConfig.currency} default`, -      { -        ...process.env, -        LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxDbConn, -        LIBEUFIN_SANDBOX_ADMIN_PASSWORD: "secret", -      }, -    ); -    this.sandboxProc = this.globalTestState.spawnService( -      "libeufin-sandbox", -      ["serve", "--port", `${this.port}`], -      "libeufin-sandbox", -      { -        ...process.env, -        LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxDbConn, -        LIBEUFIN_SANDBOX_ADMIN_PASSWORD: "secret", -      }, -    ); -    await runCommand( -      this.globalTestState, -      "libeufin-nexus-superuser", -      "libeufin-nexus", -      ["superuser", "admin", "--password", "test"], -      { -        ...process.env, -        LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusDbConn, -      }, -    ); -    this.nexusProc = this.globalTestState.spawnService( -      "libeufin-nexus", -      ["serve", "--port", `${this.nexusPort}`], -      "libeufin-nexus", -      { -        ...process.env, -        LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusDbConn, -      }, -    ); -    // need to wait here, because at this point -    // a Ebics host needs to be created (RESTfully) -    await this.pingUntilAvailable(); -    LibeufinSandboxApi.createEbicsHost( -      { baseUrl: this.baseUrlNetloc }, -      "talertestEbicsHost", -    ); -  } - -  async pingUntilAvailable(): Promise<void> { -    await pingProc( -      this.sandboxProc, -      `http://localhost:${this.bankConfig.httpPort}`, -      "libeufin-sandbox", -    ); -    await pingProc( -      this.nexusProc, -      `${this.nexusBaseUrl}/config`, -      "libeufin-nexus", -    ); -  } -} - -/** - * Implementation of the bank service using the "taler-fakebank-run" tool. - */ -export class FakebankService -  extends BankServiceBase -  implements BankServiceHandle -{ -  proc: ProcessWrapper | undefined; - -  http = new NodeHttpLib(); - -  // We store "created" accounts during setup and -  // register them after startup. -  private accounts: { -    accountName: string; -    accountPassword: string; -  }[] = []; - -  static async create( -    gc: GlobalTestState, -    bc: BankConfig, -  ): Promise<FakebankService> { -    const config = new Configuration(); -    setTalerPaths(config, gc.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); - -    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); -  } - -  get baseUrl(): string { -    return `http://localhost:${this.bankConfig.httpPort}/`; -  } - -  get bankAccessApiBaseUrl(): string { -    let url = new URL("taler-bank-access/", this.baseUrl); -    return url.href; -  } - -  async createExchangeAccount( -    accountName: string, -    password: string, -  ): Promise<HarnessExchangeBankAccount> { -    this.accounts.push({ -      accountName, -      accountPassword: password, -    }); -    return { -      accountName: accountName, -      accountPassword: password, -      accountPaytoUri: getPayto(accountName), -      wireGatewayApiBaseUrl: `http://localhost:${this.bankConfig.httpPort}/taler-wire-gateway/${accountName}/`, -    }; -  } - -  get port() { -    return this.bankConfig.httpPort; -  } - -  async start(): Promise<void> { -    logger.info("starting fakebank"); -    if (this.proc) { -      logger.info("fakebank already running, not starting again"); -      return; -    } -    this.proc = this.globalTestState.spawnService( -      "taler-fakebank-run", -      [ -        "-c", -        this.configFile, -        "--signup-bonus", -        `${this.bankConfig.currency}:100`, -      ], -      "bank", -    ); -    await this.pingUntilAvailable(); -    for (const acc of this.accounts) { -      await BankApi.registerAccount(this, acc.accountName, acc.accountPassword); -    } -  } - -  async pingUntilAvailable(): Promise<void> { -    const url = `http://localhost:${this.bankConfig.httpPort}/taler-bank-integration/config`; -    await pingProc(this.proc, url, "bank"); -  } -} - -// Use libeufin bank instead of pybank. -const useLibeufinBank = false; - -export type BankService = BankServiceHandle; -export const BankService = FakebankService; - -export interface ExchangeConfig { -  name: string; -  currency: string; -  roundUnit?: string; -  httpPort: number; -  database: string; -} - -export interface ExchangeServiceInterface { -  readonly baseUrl: string; -  readonly port: number; -  readonly name: string; -  readonly masterPub: string; -} - -export class ExchangeService implements ExchangeServiceInterface { -  static fromExistingConfig(gc: GlobalTestState, exchangeName: string) { -    const cfgFilename = gc.testDir + `/exchange-${exchangeName}.conf`; -    const config = Configuration.load(cfgFilename); -    const ec: ExchangeConfig = { -      currency: config.getString("taler", "currency").required(), -      database: config.getString("exchangedb-postgres", "config").required(), -      httpPort: config.getNumber("exchange", "port").required(), -      name: exchangeName, -      roundUnit: config.getString("taler", "currency_round_unit").required(), -    }; -    const privFile = config.getPath("exchange", "master_priv_file").required(); -    const eddsaPriv = fs.readFileSync(privFile); -    const keyPair: EddsaKeyPair = { -      eddsaPriv, -      eddsaPub: eddsaGetPublic(eddsaPriv), -    }; -    return new ExchangeService(gc, ec, cfgFilename, keyPair); -  } - -  private currentTimetravel: Duration | undefined; - -  setTimetravel(t: Duration | undefined): void { -    if (this.isRunning()) { -      throw Error("can't set time travel while the exchange is running"); -    } -    this.currentTimetravel = t; -  } - -  private get timetravelArg(): string | undefined { -    if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") { -      // Convert to microseconds -      return `--timetravel=+${this.currentTimetravel.d_ms * 1000}`; -    } -    return undefined; -  } - -  /** -   * Return an empty array if no time travel is set, -   * and an array with the time travel command line argument -   * otherwise. -   */ -  private get timetravelArgArr(): string[] { -    const tta = this.timetravelArg; -    if (tta) { -      return [tta]; -    } -    return []; -  } - -  async runWirewatchOnce() { -    if (useLibeufinBank) { -      // Not even 2 secods showed to be enough! -      await waitMs(4000); -    } -    await runCommand( -      this.globalState, -      `exchange-${this.name}-wirewatch-once`, -      "taler-exchange-wirewatch", -      [...this.timetravelArgArr, "-c", this.configFilename, "-t"], -    ); -  } - -  async runAggregatorOnce() { -    try { -      await runCommand( -        this.globalState, -        `exchange-${this.name}-aggregator-once`, -        "taler-exchange-aggregator", -        [...this.timetravelArgArr, "-c", this.configFilename, "-t", "-y"], -      ); -    } catch (e) { -      logger.info( -        "running aggregator with KYC off didn't work, might be old version, running again", -      ); -      await runCommand( -        this.globalState, -        `exchange-${this.name}-aggregator-once`, -        "taler-exchange-aggregator", -        [...this.timetravelArgArr, "-c", this.configFilename, "-t"], -      ); -    } -  } - -  async runTransferOnce() { -    await runCommand( -      this.globalState, -      `exchange-${this.name}-transfer-once`, -      "taler-exchange-transfer", -      [...this.timetravelArgArr, "-c", this.configFilename, "-t"], -    ); -  } - -  changeConfig(f: (config: Configuration) => void) { -    const config = Configuration.load(this.configFilename); -    f(config); -    config.write(this.configFilename); -  } - -  static create(gc: GlobalTestState, e: ExchangeConfig) { -    const config = new Configuration(); -    config.setString("taler", "currency", e.currency); -    config.setString( -      "taler", -      "currency_round_unit", -      e.roundUnit ?? `${e.currency}:0.01`, -    ); -    setTalerPaths(config, gc.testDir + "/talerhome"); -    config.setString( -      "exchange", -      "revocation_dir", -      "${TALER_DATA_HOME}/exchange/revocations", -    ); -    config.setString("exchange", "max_keys_caching", "forever"); -    config.setString("exchange", "db", "postgres"); -    config.setString( -      "exchange-offline", -      "master_priv_file", -      "${TALER_DATA_HOME}/exchange/offline-keys/master.priv", -    ); -    config.setString("exchange", "serve", "tcp"); -    config.setString("exchange", "port", `${e.httpPort}`); - -    config.setString("exchangedb-postgres", "config", e.database); - -    config.setString("taler-exchange-secmod-eddsa", "lookahead_sign", "20 s"); -    config.setString("taler-exchange-secmod-rsa", "lookahead_sign", "20 s"); - -    const exchangeMasterKey = createEddsaKeyPair(); - -    config.setString( -      "exchange", -      "master_public_key", -      encodeCrock(exchangeMasterKey.eddsaPub), -    ); - -    const masterPrivFile = config -      .getPath("exchange-offline", "master_priv_file") -      .required(); - -    fs.mkdirSync(path.dirname(masterPrivFile), { recursive: true }); - -    fs.writeFileSync(masterPrivFile, Buffer.from(exchangeMasterKey.eddsaPriv)); - -    const cfgFilename = gc.testDir + `/exchange-${e.name}.conf`; -    config.write(cfgFilename); -    return new ExchangeService(gc, e, cfgFilename, exchangeMasterKey); -  } - -  addOfferedCoins(offeredCoins: ((curr: string) => CoinConfig)[]) { -    const config = Configuration.load(this.configFilename); -    offeredCoins.forEach((cc) => -      setCoin(config, cc(this.exchangeConfig.currency)), -    ); -    config.write(this.configFilename); -  } - -  addCoinConfigList(ccs: CoinConfig[]) { -    const config = Configuration.load(this.configFilename); -    ccs.forEach((cc) => setCoin(config, cc)); -    config.write(this.configFilename); -  } - -  enableAgeRestrictions(maskStr: string) { -    const config = Configuration.load(this.configFilename); -    config.setString("exchange-extension-age_restriction", "enabled", "yes"); -    config.setString( -      "exchange-extension-age_restriction", -      "age_groups", -      maskStr, -    ); -    config.write(this.configFilename); -  } - -  get masterPub() { -    return encodeCrock(this.keyPair.eddsaPub); -  } - -  get port() { -    return this.exchangeConfig.httpPort; -  } - -  async addBankAccount( -    localName: string, -    exchangeBankAccount: HarnessExchangeBankAccount, -  ): Promise<void> { -    const config = Configuration.load(this.configFilename); -    config.setString( -      `exchange-account-${localName}`, -      "wire_response", -      `\${TALER_DATA_HOME}/exchange/account-${localName}.json`, -    ); -    config.setString( -      `exchange-account-${localName}`, -      "payto_uri", -      exchangeBankAccount.accountPaytoUri, -    ); -    config.setString(`exchange-account-${localName}`, "enable_credit", "yes"); -    config.setString(`exchange-account-${localName}`, "enable_debit", "yes"); -    config.setString( -      `exchange-accountcredentials-${localName}`, -      "wire_gateway_url", -      exchangeBankAccount.wireGatewayApiBaseUrl, -    ); -    config.setString( -      `exchange-accountcredentials-${localName}`, -      "wire_gateway_auth_method", -      "basic", -    ); -    config.setString( -      `exchange-accountcredentials-${localName}`, -      "username", -      exchangeBankAccount.accountName, -    ); -    config.setString( -      `exchange-accountcredentials-${localName}`, -      "password", -      exchangeBankAccount.accountPassword, -    ); -    config.write(this.configFilename); -  } - -  exchangeHttpProc: ProcessWrapper | undefined; -  exchangeWirewatchProc: ProcessWrapper | undefined; - -  helperCryptoRsaProc: ProcessWrapper | undefined; -  helperCryptoEddsaProc: ProcessWrapper | undefined; -  helperCryptoCsProc: ProcessWrapper | undefined; - -  constructor( -    private globalState: GlobalTestState, -    private exchangeConfig: ExchangeConfig, -    private configFilename: string, -    private keyPair: EddsaKeyPair, -  ) {} - -  get name() { -    return this.exchangeConfig.name; -  } - -  get baseUrl() { -    return `http://localhost:${this.exchangeConfig.httpPort}/`; -  } - -  isRunning(): boolean { -    return !!this.exchangeWirewatchProc || !!this.exchangeHttpProc; -  } - -  async stop(): Promise<void> { -    const wirewatch = this.exchangeWirewatchProc; -    if (wirewatch) { -      wirewatch.proc.kill("SIGTERM"); -      await wirewatch.wait(); -      this.exchangeWirewatchProc = undefined; -    } -    const httpd = this.exchangeHttpProc; -    if (httpd) { -      httpd.proc.kill("SIGTERM"); -      await httpd.wait(); -      this.exchangeHttpProc = undefined; -    } -    const cryptoRsa = this.helperCryptoRsaProc; -    if (cryptoRsa) { -      cryptoRsa.proc.kill("SIGTERM"); -      await cryptoRsa.wait(); -      this.helperCryptoRsaProc = undefined; -    } -    const cryptoEddsa = this.helperCryptoEddsaProc; -    if (cryptoEddsa) { -      cryptoEddsa.proc.kill("SIGTERM"); -      await cryptoEddsa.wait(); -      this.helperCryptoRsaProc = undefined; -    } -    const cryptoCs = this.helperCryptoCsProc; -    if (cryptoCs) { -      cryptoCs.proc.kill("SIGTERM"); -      await cryptoCs.wait(); -      this.helperCryptoCsProc = undefined; -    } -  } - -  /** -   * Update keys signing the keys generated by the security module -   * with the offline signing key. -   */ -  async keyup(): Promise<void> { -    await runCommand( -      this.globalState, -      "exchange-offline", -      "taler-exchange-offline", -      ["-c", this.configFilename, "download", "sign", "upload"], -    ); - -    const accounts: string[] = []; -    const accountTargetTypes: Set<string> = new Set(); - -    const config = Configuration.load(this.configFilename); -    for (const sectionName of config.getSectionNames()) { -      if (sectionName.startsWith("EXCHANGE-ACCOUNT-")) { -        const paytoUri = config.getString(sectionName, "payto_uri").required(); -        const p = parsePaytoUri(paytoUri); -        if (!p) { -          throw Error(`invalid payto uri in exchange config: ${paytoUri}`); -        } -        accountTargetTypes.add(p?.targetType); -        accounts.push(paytoUri); -      } -    } - -    logger.info("configuring bank accounts", accounts); - -    for (const acc of accounts) { -      await runCommand( -        this.globalState, -        "exchange-offline", -        "taler-exchange-offline", -        ["-c", this.configFilename, "enable-account", acc, "upload"], -      ); -    } - -    const year = new Date().getFullYear(); -    for (const accTargetType of accountTargetTypes.values()) { -      for (let i = year; i < year + 5; i++) { -        await runCommand( -          this.globalState, -          "exchange-offline", -          "taler-exchange-offline", -          [ -            "-c", -            this.configFilename, -            "wire-fee", -            // Year -            `${i}`, -            // Wire method -            accTargetType, -            // Wire fee -            `${this.exchangeConfig.currency}:0.01`, -            // Closing fee -            `${this.exchangeConfig.currency}:0.01`,  -            "upload", -          ], -        ); -      } -    } - -    await runCommand( -      this.globalState, -      "exchange-offline", -      "taler-exchange-offline", -      [ -        "-c", -        this.configFilename, -        "global-fee", -        // year -        "now", -        // history fee -        `${this.exchangeConfig.currency}:0.01`, -        // account fee -        `${this.exchangeConfig.currency}:0.01`, -        // purse fee -        `${this.exchangeConfig.currency}:0.00`, -        // purse timeout -        "1h", -        // history expiration -        "1year", -        // free purses per account -        "5", -        "upload", -      ], -    ); -  } - -  async revokeDenomination(denomPubHash: string) { -    if (!this.isRunning()) { -      throw Error("exchange must be running when revoking denominations"); -    } -    await runCommand( -      this.globalState, -      "exchange-offline", -      "taler-exchange-offline", -      [ -        "-c", -        this.configFilename, -        "revoke-denomination", -        denomPubHash, -        "upload", -      ], -    ); -  } - -  async purgeSecmodKeys(): Promise<void> { -    const cfg = Configuration.load(this.configFilename); -    const rsaKeydir = cfg -      .getPath("taler-exchange-secmod-rsa", "KEY_DIR") -      .required(); -    const eddsaKeydir = cfg -      .getPath("taler-exchange-secmod-eddsa", "KEY_DIR") -      .required(); -    // Be *VERY* careful when changing this, or you will accidentally delete user data. -    await sh(this.globalState, "rm-secmod-keys", `rm -rf ${rsaKeydir}/COIN_*`); -    await sh(this.globalState, "rm-secmod-keys", `rm ${eddsaKeydir}/*`); -  } - -  async purgeDatabase(): Promise<void> { -    await sh( -      this.globalState, -      "exchange-dbinit", -      `taler-exchange-dbinit -r -c "${this.configFilename}"`, -    ); -  } - -  async start(): Promise<void> { -    if (this.isRunning()) { -      throw Error("exchange is already running"); -    } -    await sh( -      this.globalState, -      "exchange-dbinit", -      `taler-exchange-dbinit -c "${this.configFilename}"`, -    ); - -    this.helperCryptoEddsaProc = this.globalState.spawnService( -      "taler-exchange-secmod-eddsa", -      ["-c", this.configFilename, "-LDEBUG", ...this.timetravelArgArr], -      `exchange-crypto-eddsa-${this.name}`, -    ); - -    this.helperCryptoCsProc = this.globalState.spawnService( -      "taler-exchange-secmod-cs", -      ["-c", this.configFilename, "-LDEBUG", ...this.timetravelArgArr], -      `exchange-crypto-cs-${this.name}`, -    ); - -    this.helperCryptoRsaProc = this.globalState.spawnService( -      "taler-exchange-secmod-rsa", -      ["-c", this.configFilename, "-LDEBUG", ...this.timetravelArgArr], -      `exchange-crypto-rsa-${this.name}`, -    ); - -    this.exchangeWirewatchProc = this.globalState.spawnService( -      "taler-exchange-wirewatch", -      ["-c", this.configFilename, ...this.timetravelArgArr], -      `exchange-wirewatch-${this.name}`, -    ); - -    this.exchangeHttpProc = this.globalState.spawnService( -      "taler-exchange-httpd", -      ["-LINFO", "-c", this.configFilename, ...this.timetravelArgArr], -      `exchange-httpd-${this.name}`, -    ); - -    await this.pingUntilAvailable(); -    await this.keyup(); -  } - -  async pingUntilAvailable(): Promise<void> { -    // We request /management/keys, since /keys can block -    // when we didn't do the key setup yet. -    const url = `http://localhost:${this.exchangeConfig.httpPort}/management/keys`; -    await pingProc(this.exchangeHttpProc, url, `exchange (${this.name})`); -  } -} - -export interface MerchantConfig { -  name: string; -  currency: string; -  httpPort: number; -  database: string; -} - -export interface PrivateOrderStatusQuery { -  instance?: string; -  orderId: string; -  sessionId?: string; -} - -export interface MerchantServiceInterface { -  makeInstanceBaseUrl(instanceName?: string): string; -  readonly port: number; -  readonly name: string; -} - -export class MerchantApiClient { -  constructor( -    private baseUrl: string, -    public readonly auth: MerchantAuthConfiguration, -  ) {} - -  async changeAuth(auth: MerchantAuthConfiguration): Promise<void> { -    const url = new URL("private/auth", this.baseUrl); -    await axios.post(url.href, auth, { -      headers: this.makeAuthHeader(), -    }); -  } - -  async deleteInstance(instanceId: string) { -    const url = new URL(`management/instances/${instanceId}`, this.baseUrl); -    await axios.delete(url.href, { -      headers: this.makeAuthHeader(), -    }); -  } - -  async createInstance(req: MerchantInstanceConfig): Promise<void> { -    const url = new URL("management/instances", this.baseUrl); -    await axios.post(url.href, req, { -      headers: this.makeAuthHeader(), -    }); -  } - -  async getInstances(): Promise<MerchantInstancesResponse> { -    const url = new URL("management/instances", this.baseUrl); -    const resp = await axios.get(url.href, { -      headers: this.makeAuthHeader(), -    }); -    return resp.data; -  } - -  async getInstanceFullDetails(instanceId: string): Promise<any> { -    const url = new URL(`management/instances/${instanceId}`, this.baseUrl); -    try { -      const resp = await axios.get(url.href, { -        headers: this.makeAuthHeader(), -      }); -      return resp.data; -    } catch (e) { -      throw e; -    } -  } - -  makeAuthHeader(): Record<string, string> { -    switch (this.auth.method) { -      case "external": -        return {}; -      case "token": -        return { -          Authorization: `Bearer ${this.auth.token}`, -        }; -    } -  } -} - -/** - * FIXME:  This should be deprecated in favor of MerchantApiClient - */ -export namespace MerchantPrivateApi { -  export async function createOrder( -    merchantService: MerchantServiceInterface, -    instanceName: string, -    req: PostOrderRequest, -    withAuthorization: WithAuthorization = {}, -  ): Promise<PostOrderResponse> { -    const baseUrl = merchantService.makeInstanceBaseUrl(instanceName); -    let url = new URL("private/orders", baseUrl); -    const resp = await axios.post(url.href, req, { -      headers: withAuthorization as Record<string, string>, -    }); -    return codecForPostOrderResponse().decode(resp.data); -  } - -  export async function queryPrivateOrderStatus( -    merchantService: MerchantServiceInterface, -    query: PrivateOrderStatusQuery, -    withAuthorization: WithAuthorization = {}, -  ): Promise<MerchantOrderPrivateStatusResponse> { -    const reqUrl = new URL( -      `private/orders/${query.orderId}`, -      merchantService.makeInstanceBaseUrl(query.instance), -    ); -    if (query.sessionId) { -      reqUrl.searchParams.set("session_id", query.sessionId); -    } -    const resp = await axios.get(reqUrl.href, { -      headers: withAuthorization as Record<string, string>, -    }); -    return codecForMerchantOrderPrivateStatusResponse().decode(resp.data); -  } - -  export async function giveRefund( -    merchantService: MerchantServiceInterface, -    r: { -      instance: string; -      orderId: string; -      amount: string; -      justification: string; -    }, -  ): Promise<{ talerRefundUri: string }> { -    const reqUrl = new URL( -      `private/orders/${r.orderId}/refund`, -      merchantService.makeInstanceBaseUrl(r.instance), -    ); -    const resp = await axios.post(reqUrl.href, { -      refund: r.amount, -      reason: r.justification, -    }); -    return { -      talerRefundUri: resp.data.taler_refund_uri, -    }; -  } - -  export async function createTippingReserve( -    merchantService: MerchantServiceInterface, -    instance: string, -    req: CreateMerchantTippingReserveRequest, -  ): Promise<CreateMerchantTippingReserveConfirmation> { -    const reqUrl = new URL( -      `private/reserves`, -      merchantService.makeInstanceBaseUrl(instance), -    ); -    const resp = await axios.post(reqUrl.href, req); -    // FIXME: validate -    return resp.data; -  } - -  export async function queryTippingReserves( -    merchantService: MerchantServiceInterface, -    instance: string, -  ): Promise<TippingReserveStatus> { -    const reqUrl = new URL( -      `private/reserves`, -      merchantService.makeInstanceBaseUrl(instance), -    ); -    const resp = await axios.get(reqUrl.href); -    // FIXME: validate -    return resp.data; -  } - -  export async function giveTip( -    merchantService: MerchantServiceInterface, -    instance: string, -    req: TipCreateRequest, -  ): Promise<TipCreateConfirmation> { -    const reqUrl = new URL( -      `private/tips`, -      merchantService.makeInstanceBaseUrl(instance), -    ); -    const resp = await axios.post(reqUrl.href, req); -    // FIXME: validate -    return resp.data; -  } -} - -export interface CreateMerchantTippingReserveRequest { -  // Amount that the merchant promises to put into the reserve -  initial_balance: AmountString; - -  // Exchange the merchant intends to use for tipping -  exchange_url: string; - -  // Desired wire method, for example "iban" or "x-taler-bank" -  wire_method: string; -} - -export interface CreateMerchantTippingReserveConfirmation { -  // Public key identifying the reserve -  reserve_pub: string; - -  // Wire account of the exchange where to transfer the funds -  payto_uri: string; -} - -export class MerchantService implements MerchantServiceInterface { -  static fromExistingConfig(gc: GlobalTestState, name: string) { -    const cfgFilename = gc.testDir + `/merchant-${name}.conf`; -    const config = Configuration.load(cfgFilename); -    const mc: MerchantConfig = { -      currency: config.getString("taler", "currency").required(), -      database: config.getString("merchantdb-postgres", "config").required(), -      httpPort: config.getNumber("merchant", "port").required(), -      name, -    }; -    return new MerchantService(gc, mc, cfgFilename); -  } - -  proc: ProcessWrapper | undefined; - -  constructor( -    private globalState: GlobalTestState, -    private merchantConfig: MerchantConfig, -    private configFilename: string, -  ) {} - -  private currentTimetravel: Duration | undefined; - -  private isRunning(): boolean { -    return !!this.proc; -  } - -  setTimetravel(t: Duration | undefined): void { -    if (this.isRunning()) { -      throw Error("can't set time travel while the exchange is running"); -    } -    this.currentTimetravel = t; -  } - -  private get timetravelArg(): string | undefined { -    if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") { -      // Convert to microseconds -      return `--timetravel=+${this.currentTimetravel.d_ms * 1000}`; -    } -    return undefined; -  } - -  /** -   * Return an empty array if no time travel is set, -   * and an array with the time travel command line argument -   * otherwise. -   */ -  private get timetravelArgArr(): string[] { -    const tta = this.timetravelArg; -    if (tta) { -      return [tta]; -    } -    return []; -  } - -  get port(): number { -    return this.merchantConfig.httpPort; -  } - -  get name(): string { -    return this.merchantConfig.name; -  } - -  async stop(): Promise<void> { -    const httpd = this.proc; -    if (httpd) { -      httpd.proc.kill("SIGTERM"); -      await httpd.wait(); -      this.proc = undefined; -    } -  } - -  async start(): Promise<void> { -    await exec(`taler-merchant-dbinit -c "${this.configFilename}"`); - -    this.proc = this.globalState.spawnService( -      "taler-merchant-httpd", -      [ -        "taler-merchant-httpd", -        "-LDEBUG", -        "-c", -        this.configFilename, -        ...this.timetravelArgArr, -      ], -      `merchant-${this.merchantConfig.name}`, -    ); -  } - -  static async create( -    gc: GlobalTestState, -    mc: MerchantConfig, -  ): Promise<MerchantService> { -    const config = new Configuration(); -    config.setString("taler", "currency", mc.currency); - -    const cfgFilename = gc.testDir + `/merchant-${mc.name}.conf`; -    setTalerPaths(config, gc.testDir + "/talerhome"); -    config.setString("merchant", "serve", "tcp"); -    config.setString("merchant", "port", `${mc.httpPort}`); -    config.setString( -      "merchant", -      "keyfile", -      "${TALER_DATA_HOME}/merchant/merchant.priv", -    ); -    config.setString("merchantdb-postgres", "config", mc.database); -    config.write(cfgFilename); - -    return new MerchantService(gc, mc, cfgFilename); -  } - -  addExchange(e: ExchangeServiceInterface): void { -    const config = Configuration.load(this.configFilename); -    config.setString( -      `merchant-exchange-${e.name}`, -      "exchange_base_url", -      e.baseUrl, -    ); -    config.setString( -      `merchant-exchange-${e.name}`, -      "currency", -      this.merchantConfig.currency, -    ); -    config.setString(`merchant-exchange-${e.name}`, "master_key", e.masterPub); -    config.write(this.configFilename); -  } - -  async addDefaultInstance(): Promise<void> { -    return await this.addInstance({ -      id: "default", -      name: "Default Instance", -      paytoUris: [getPayto("merchant-default")], -      auth: { -        method: "external", -      }, -    }); -  } - -  async addInstance( -    instanceConfig: PartialMerchantInstanceConfig, -  ): Promise<void> { -    if (!this.proc) { -      throw Error("merchant must be running to add instance"); -    } -    logger.info("adding instance"); -    const url = `http://localhost:${this.merchantConfig.httpPort}/management/instances`; -    const auth = instanceConfig.auth ?? { method: "external" }; - -    const body: MerchantInstanceConfig = { -      auth, -      payto_uris: instanceConfig.paytoUris, -      id: instanceConfig.id, -      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`, -      default_wire_transfer_delay: -        instanceConfig.defaultWireTransferDelay ?? -        Duration.toTalerProtocolDuration( -          Duration.fromSpec({ -            days: 1, -          }), -        ), -      default_pay_delay: -        instanceConfig.defaultPayDelay ?? -        Duration.toTalerProtocolDuration(Duration.getForever()), -    }; -    await axios.post(url, body); -  } - -  makeInstanceBaseUrl(instanceName?: string): string { -    if (instanceName === undefined || instanceName === "default") { -      return `http://localhost:${this.merchantConfig.httpPort}/`; -    } else { -      return `http://localhost:${this.merchantConfig.httpPort}/instances/${instanceName}/`; -    } -  } - -  async pingUntilAvailable(): Promise<void> { -    const url = `http://localhost:${this.merchantConfig.httpPort}/config`; -    await pingProc(this.proc, url, `merchant (${this.merchantConfig.name})`); -  } -} - -export interface MerchantAuthConfiguration { -  method: "external" | "token"; -  token?: string; -} - -export interface PartialMerchantInstanceConfig { -  auth?: MerchantAuthConfiguration; -  id: string; -  name: string; -  paytoUris: string[]; -  address?: unknown; -  jurisdiction?: unknown; -  defaultMaxWireFee?: string; -  defaultMaxDepositFee?: string; -  defaultWireFeeAmortization?: number; -  defaultWireTransferDelay?: TalerProtocolDuration; -  defaultPayDelay?: TalerProtocolDuration; -} - -export interface MerchantInstanceConfig { -  auth: MerchantAuthConfiguration; -  id: string; -  name: string; -  payto_uris: string[]; -  address: unknown; -  jurisdiction: unknown; -  default_max_wire_fee: string; -  default_max_deposit_fee: string; -  default_wire_fee_amortization: number; -  default_wire_transfer_delay: TalerProtocolDuration; -  default_pay_delay: TalerProtocolDuration; -} - -type TestStatus = "pass" | "fail" | "skip"; - -export interface TestRunResult { -  /** -   * Name of the test. -   */ -  name: string; - -  /** -   * How long did the test run? -   */ -  timeSec: number; - -  status: TestStatus; - -  reason?: string; -} - -export async function runTestWithState( -  gc: GlobalTestState, -  testMain: (t: GlobalTestState) => Promise<void>, -  testName: string, -  linger: boolean = false, -): Promise<TestRunResult> { -  const startMs = new Date().getTime(); - -  const p = openPromise(); -  let status: TestStatus; - -  const handleSignal = (s: string) => { -    logger.warn( -      `**** received fatal process event, terminating test ${testName}`, -    ); -    gc.shutdownSync(); -    process.exit(1); -  }; - -  process.on("SIGINT", handleSignal); -  process.on("SIGTERM", handleSignal); -  process.on("unhandledRejection", handleSignal); -  process.on("uncaughtException", handleSignal); - -  try { -    logger.info("running test in directory", gc.testDir); -    await Promise.race([testMain(gc), p.promise]); -    status = "pass"; -    if (linger) { -      const rl = readline.createInterface({ -        input: process.stdin, -        output: process.stdout, -        terminal: true, -      }); -      await new Promise<void>((resolve, reject) => { -        rl.question("Press enter to shut down test.", () => { -          logger.error("Requested shutdown"); -          resolve(); -        }); -      }); -      rl.close(); -    } -  } catch (e) { -    console.error("FATAL: test failed with exception", e); -    if (e instanceof TalerError) { -      console.error(`error detail: ${j2s(e.errorDetail)}`); -    } -    status = "fail"; -  } finally { -    await gc.shutdown(); -  } -  const afterMs = new Date().getTime(); -  return { -    name: testName, -    timeSec: (afterMs - startMs) / 1000, -    status, -  }; -} - -function shellWrap(s: string) { -  return "'" + s.replace("\\", "\\\\").replace("'", "\\'") + "'"; -} - -export interface WalletCliOpts { -  cryptoWorkerType?: "sync" | "node-worker-thread"; -} - -export class WalletCli { -  private currentTimetravel: Duration | undefined; -  private _client: WalletCoreApiClient; - -  setTimetravel(d: Duration | undefined) { -    this.currentTimetravel = d; -  } - -  private get timetravelArg(): string | undefined { -    if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") { -      // Convert to microseconds -      return `--timetravel=${this.currentTimetravel.d_ms * 1000}`; -    } -    return undefined; -  } - -  constructor( -    private globalTestState: GlobalTestState, -    private name: string = "default", -    cliOpts: WalletCliOpts = {}, -  ) { -    const self = this; -    this._client = { -      async call(op: any, payload: any): Promise<any> { -        logger.info( -          `calling wallet with timetravel arg ${j2s(self.timetravelArg)}`, -        ); -        const cryptoWorkerArg = cliOpts.cryptoWorkerType -          ? `--crypto-worker=${cliOpts.cryptoWorkerType}` -          : ""; -        const resp = await sh( -          self.globalTestState, -          `wallet-${self.name}`, -          `taler-wallet-cli ${ -            self.timetravelArg ?? "" -          } ${cryptoWorkerArg} --no-throttle -LTRACE --skip-defaults --wallet-db '${ -            self.dbfile -          }' api '${op}' ${shellWrap(JSON.stringify(payload))}`, -        ); -        logger.info("--- wallet core response ---"); -        logger.info(resp); -        logger.info("--- end of response ---"); -        let ar: any; -        try { -          ar = JSON.parse(resp) as CoreApiResponse; -        } catch (e) { -          throw new Error("wallet CLI did not return a proper JSON response"); -        } -        if (ar.type === "error") { -          throw TalerError.fromUncheckedDetail(ar.error); -        } -        return ar.result; -      }, -    }; -  } - -  get dbfile(): string { -    return this.globalTestState.testDir + `/walletdb-${this.name}.json`; -  } - -  deleteDatabase() { -    fs.unlinkSync(this.dbfile); -  } - -  private get timetravelArgArr(): string[] { -    const tta = this.timetravelArg; -    if (tta) { -      return [tta]; -    } -    return []; -  } - -  get client(): WalletCoreApiClient { -    return this._client; -  } - -  async runUntilDone(args: { maxRetries?: number } = {}): Promise<void> { -    await runCommand( -      this.globalTestState, -      `wallet-${this.name}`, -      "taler-wallet-cli", -      [ -        "--no-throttle", -        ...this.timetravelArgArr, -        "-LTRACE", -        "--skip-defaults", -        "--wallet-db", -        this.dbfile, -        "run-until-done", -        ...(args.maxRetries ? ["--max-retries", `${args.maxRetries}`] : []), -      ], -    ); -  } - -  async runPending(): Promise<void> { -    await runCommand( -      this.globalTestState, -      `wallet-${this.name}`, -      "taler-wallet-cli", -      [ -        "--no-throttle", -        "--skip-defaults", -        "-LTRACE", -        ...this.timetravelArgArr, -        "--wallet-db", -        this.dbfile, -        "advanced", -        "run-pending", -      ], -    ); -  } -} - -export function getRandomIban(salt: string | null = null): string { -  function getBban(salt: string | null): string { -    if (!salt) return Math.random().toString().substring(2, 6); -    let hashed = hash(stringToBytes(salt)); -    let ret = ""; -    for (let i = 0; i < hashed.length; i++) { -      ret += hashed[i].toString(); -    } -    return ret.substring(0, 4); -  } - -  let cc_no_check = "131400"; // == DE00 -  let bban = getBban(salt); -  let check_digits = ( -    98 - -    (Number.parseInt(`${bban}${cc_no_check}`) % 97) -  ).toString(); -  if (check_digits.length == 1) { -    check_digits = `0${check_digits}`; -  } -  return `DE${check_digits}${bban}`; -} - -export function getWireMethodForTest(): string { -  if (useLibeufinBank) return "iban"; -  return "x-taler-bank"; -} - -/** - * Generate a payto address, whose authority depends - * on whether the banking is served by euFin or Pybank. - */ -export function getPayto(label: string): string { -  if (useLibeufinBank) -    return `payto://iban/SANDBOXX/${getRandomIban( -      label, -    )}?receiver-name=${label}`; -  return `payto://x-taler-bank/localhost/${label}`; -} - -function waitMs(ms: number): Promise<void> { -  return new Promise((resolve) => setTimeout(resolve, ms)); -} diff --git a/packages/taler-wallet-cli/src/harness/helpers.ts b/packages/taler-wallet-cli/src/harness/helpers.ts deleted file mode 100644 index affaccd61..000000000 --- a/packages/taler-wallet-cli/src/harness/helpers.ts +++ /dev/null @@ -1,444 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Helpers to create typical test environments. - * - * @author Florian Dold <dold@taler.net> - */ - -/** - * Imports - */ -import { -  AmountString, -  ConfirmPayResultType, -  MerchantContractTerms, -  Duration, -  PreparePayResultType, -} from "@gnu-taler/taler-util"; -import { -  BankAccessApi, -  BankApi, -  HarnessExchangeBankAccount, -  WalletApiOperation, -} from "@gnu-taler/taler-wallet-core"; -import { CoinConfig, defaultCoinConfig } from "./denomStructures.js"; -import { -  FaultInjectedExchangeService, -  FaultInjectedMerchantService, -} from "./faultInjection.js"; -import { -  BankService, -  DbInfo, -  ExchangeService, -  ExchangeServiceInterface, -  getPayto, -  GlobalTestState, -  MerchantPrivateApi, -  MerchantService, -  MerchantServiceInterface, -  setupDb, -  WalletCli, -  WithAuthorization, -} from "./harness.js"; - -export interface SimpleTestEnvironment { -  commonDb: DbInfo; -  bank: BankService; -  exchange: ExchangeService; -  exchangeBankAccount: HarnessExchangeBankAccount; -  merchant: MerchantService; -  wallet: WalletCli; -} - -export interface EnvOptions { -  /** -   * If provided, enable age restrictions with the specified age mask string. -   */ -  ageMaskSpec?: string; - -  mixedAgeRestriction?: boolean; -} - -/** - * Run a test case with a simple TESTKUDOS Taler environment, consisting - * of one exchange, one bank and one merchant. - */ -export async function createSimpleTestkudosEnvironment( -  t: GlobalTestState, -  coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")), -  opts: EnvOptions = {}, -): Promise<SimpleTestEnvironment> { -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); -  exchange.addBankAccount("1", exchangeBankAccount); - -  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  const ageMaskSpec = opts.ageMaskSpec; - -  if (ageMaskSpec) { -    exchange.enableAgeRestrictions(ageMaskSpec); -    // Enable age restriction for all coins. -    exchange.addCoinConfigList( -      coinConfig.map((x) => ({ -        ...x, -        name: `${x.name}-age`, -        ageRestricted: true, -      })), -    ); -    // For mixed age restrictions, we also offer coins without age restrictions -    if (opts.mixedAgeRestriction) { -      exchange.addCoinConfigList( -        coinConfig.map((x) => ({ ...x, ageRestricted: false })), -      ); -    } -  } else { -    exchange.addCoinConfigList(coinConfig); -  } - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  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 }), -    ), -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  return { -    commonDb: db, -    exchange, -    merchant, -    wallet, -    bank, -    exchangeBankAccount, -  }; -} - -export interface FaultyMerchantTestEnvironment { -  commonDb: DbInfo; -  bank: BankService; -  exchange: ExchangeService; -  faultyExchange: FaultInjectedExchangeService; -  exchangeBankAccount: HarnessExchangeBankAccount; -  merchant: MerchantService; -  faultyMerchant: FaultInjectedMerchantService; -  wallet: WalletCli; -} - -/** - * Run a test case with a simple TESTKUDOS Taler environment, consisting - * of one exchange, one bank and one merchant. - */ -export async function createFaultInjectedMerchantTestkudosEnvironment( -  t: GlobalTestState, -): Promise<FaultyMerchantTestEnvironment> { -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const faultyMerchant = new FaultInjectedMerchantService(t, merchant, 9083); -  const faultyExchange = new FaultInjectedExchangeService(t, exchange, 9081); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); -  exchange.addBankAccount("1", exchangeBankAccount); - -  bank.setSuggestedExchange( -    faultyExchange, -    exchangeBankAccount.accountPaytoUri, -  ); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  exchange.addOfferedCoins(defaultCoinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(faultyExchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  await merchant.addInstance({ -    id: "minst1", -    name: "minst1", -    paytoUris: [getPayto("minst1")], -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  return { -    commonDb: db, -    exchange, -    merchant, -    wallet, -    bank, -    exchangeBankAccount, -    faultyMerchant, -    faultyExchange, -  }; -} - -/** - * Start withdrawing into the wallet. - * - * Only starts the operation, does not wait for it to finish. - */ -export async function startWithdrawViaBank( -  t: GlobalTestState, -  p: { -    wallet: WalletCli; -    bank: BankService; -    exchange: ExchangeServiceInterface; -    amount: AmountString; -    restrictAge?: number; -  }, -): Promise<void> { -  const { wallet, bank, exchange, amount } = p; - -  const user = await BankApi.createRandomBankUser(bank); -  const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount); - -  // Hand it to the wallet - -  await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, { -    talerWithdrawUri: wop.taler_withdraw_uri, -    restrictAge: p.restrictAge, -  }); - -  await wallet.runPending(); - -  // Withdraw (AKA select) - -  await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, { -    exchangeBaseUrl: exchange.baseUrl, -    talerWithdrawUri: wop.taler_withdraw_uri, -    restrictAge: p.restrictAge, -  }); - -  // Confirm it - -  await BankApi.confirmWithdrawalOperation(bank, user, wop); - -  // We do *not* call runPending / runUntilDone on the wallet here. -  // Some tests rely on the final withdraw failing. -} - -/** - * Withdraw balance. - */ -export async function withdrawViaBank( -  t: GlobalTestState, -  p: { -    wallet: WalletCli; -    bank: BankService; -    exchange: ExchangeServiceInterface; -    amount: AmountString; -    restrictAge?: number; -  }, -): Promise<void> { -  const { wallet } = p; - -  await startWithdrawViaBank(t, p); - -  await wallet.runUntilDone(); - -  // Check balance - -  await wallet.client.call(WalletApiOperation.GetBalances, {}); -} - -export async function applyTimeTravel( -  timetravelDuration: Duration, -  s: { -    exchange?: ExchangeService; -    merchant?: MerchantService; -    wallet?: WalletCli; -  }, -): Promise<void> { -  if (s.exchange) { -    await s.exchange.stop(); -    s.exchange.setTimetravel(timetravelDuration); -    await s.exchange.start(); -    await s.exchange.pingUntilAvailable(); -  } - -  if (s.merchant) { -    await s.merchant.stop(); -    s.merchant.setTimetravel(timetravelDuration); -    await s.merchant.start(); -    await s.merchant.pingUntilAvailable(); -  } - -  if (s.wallet) { -    s.wallet.setTimetravel(timetravelDuration); -  } -} - -/** - * Make a simple payment and check that it succeeded. - */ -export async function makeTestPayment( -  t: GlobalTestState, -  args: { -    merchant: MerchantServiceInterface; -    wallet: WalletCli; -    order: Partial<MerchantContractTerms>; -    instance?: string; -  }, -  auth: WithAuthorization = {}, -): Promise<void> { -  // Set up order. - -  const { wallet, merchant } = args; -  const instance = args.instance ?? "default"; - -  const orderResp = await MerchantPrivateApi.createOrder( -    merchant, -    instance, -    { -      order: args.order, -    }, -    auth, -  ); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( -    merchant, -    { -      orderId: orderResp.order_id, -    }, -    auth, -  ); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  // Make wallet pay for the order - -  const preparePayResult = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: orderStatus.taler_pay_uri, -    }, -  ); - -  t.assertTrue( -    preparePayResult.status === PreparePayResultType.PaymentPossible, -  ); - -  const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, { -    proposalId: preparePayResult.proposalId, -  }); - -  t.assertTrue(r2.type === ConfirmPayResultType.Done); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( -    merchant, -    { -      orderId: orderResp.order_id, -      instance, -    }, -    auth, -  ); - -  t.assertTrue(orderStatus.order_status === "paid"); -} diff --git a/packages/taler-wallet-cli/src/harness/libeufin-apis.ts b/packages/taler-wallet-cli/src/harness/libeufin-apis.ts deleted file mode 100644 index f55275927..000000000 --- a/packages/taler-wallet-cli/src/harness/libeufin-apis.ts +++ /dev/null @@ -1,872 +0,0 @@ -/** - * This file defines most of the API calls offered - * by Nexus and Sandbox.  They don't have state, - * therefore got moved away from libeufin.ts where - * the services get actually started and managed. - */ - -import axiosImp from "axios"; -const axios = axiosImp.default; -import { Logger, URL } from "@gnu-taler/taler-util"; - -export interface LibeufinSandboxServiceInterface { -  baseUrl: string; -} - -export interface LibeufinNexusServiceInterface { -  baseUrl: string; -} - -export interface CreateEbicsSubscriberRequest { -  hostID: string; -  userID: string; -  partnerID: string; -  systemID?: string; -} - -export interface BankAccountInfo { -  iban: string; -  bic: string; -  name: string; -  label: string; -} - -export interface CreateEbicsBankConnectionRequest { -  name: string; // connection name. -  ebicsURL: string; -  hostID: string; -  userID: string; -  partnerID: string; -  systemID?: string; -} - -export interface UpdateNexusUserRequest { -  newPassword: string; -} - -export interface NexusAuth { -  auth: { -    username: string; -    password: string; -  }; -} - -export interface PostNexusTaskRequest { -  name: string; -  cronspec: string; -  type: string; // fetch | submit -  params: -    | { -        level: string; // report | statement | all -        rangeType: string; // all | since-last | previous-days | latest -      } -    | {}; -} - -export interface CreateNexusUserRequest { -  username: string; -  password: string; -} - -export interface PostNexusPermissionRequest { -  action: "revoke" | "grant"; -  permission: { -    subjectType: string; -    subjectId: string; -    resourceType: string; -    resourceId: string; -    permissionName: string; -  }; -} - -export interface CreateAnastasisFacadeRequest { -  name: string; -  connectionName: string; -  accountName: string; -  currency: string; -  reserveTransferLevel: "report" | "statement" | "notification"; -} - -export interface CreateTalerWireGatewayFacadeRequest { -  name: string; -  connectionName: string; -  accountName: string; -  currency: string; -  reserveTransferLevel: "report" | "statement" | "notification"; -} - -export interface SandboxAccountTransactions { -  payments: { -    accountLabel: string; -    creditorIban: string; -    creditorBic?: string; -    creditorName: string; -    debtorIban: string; -    debtorBic: string; -    debtorName: string; -    amount: string; -    currency: string; -    subject: string; -    date: string; -    creditDebitIndicator: "debit" | "credit"; -    accountServicerReference: string; -  }[]; -} - -export interface DeleteBankConnectionRequest { -  bankConnectionId: string; -} - -export interface SimulateIncomingTransactionRequest { -  debtorIban: string; -  debtorBic: string; -  debtorName: string; - -  /** -   * Subject / unstructured remittance info. -   */ -  subject: string; - -  /** -   * Decimal amount without currency. -   */ -  amount: string; -} - -export interface CreateEbicsBankAccountRequest { -  subscriber: { -    hostID: string; -    partnerID: string; -    userID: string; -    systemID?: string; -  }; -  // IBAN -  iban: string; -  // BIC -  bic: string; -  // human name -  name: string; -  label: string; -} - -export interface LibeufinSandboxAddIncomingRequest { -  creditorIban: string; -  creditorBic: string; -  creditorName: string; -  debtorIban: string; -  debtorBic: string; -  debtorName: string; -  subject: string; -  amount: string; -  currency: string; -  uid: string; -  direction: string; -} - -function getRandomString(): string { -  return Math.random().toString(36).substring(2); -} - -/** - * APIs spread accross Legacy and Access, it is therefore - * the "base URL" relative to which API every call addresses. - */ -export namespace LibeufinSandboxApi { -  // Need Access API base URL. -  export async function demobankAccountInfo( -    username: string, -    password: string, -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    accountLabel: string, -  ) { -    let url = new URL( -      `accounts/${accountLabel}`, -      libeufinSandboxService.baseUrl, -    ); -    return await axios.get(url.href, { -      auth: { -        username: username, -        password: password, -      }, -    }); -  } - -  // Creates one bank account via the Access API. -  // Need the /demobanks/$id/access-api as the base URL -  export async function createDemobankAccount( -    username: string, -    password: string, -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    iban: string|null = null, -  ) { -    let url = new URL( -      "testing/register", -      libeufinSandboxService.baseUrl -    ); -    await axios.post(url.href, { -      username: username, -      password: password, -      iban: iban -    }); -  } -  // Need /demobanks/$id as the base URL -  export async function createDemobankEbicsSubscriber( -    req: CreateEbicsSubscriberRequest, -    demobankAccountLabel: string, -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    username: string = "admin", -    password: string = "secret", -  ) { -    // baseUrl should already be pointed to one demobank. -    let url = new URL( -      "ebics/subscribers", -       libeufinSandboxService.baseUrl -       ); -    await axios.post( -      url.href, -      { -        userID: req.userID, -        hostID: req.hostID, -        partnerID: req.partnerID, -        demobankAccountLabel: demobankAccountLabel, -      }, -      { -        auth: { -          username: "admin", -          password: "secret", -        }, -      }, -    ); -  } - -  export async function rotateKeys( -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    hostID: string, -  ) { -    const baseUrl = libeufinSandboxService.baseUrl; -    let url = new URL(`admin/ebics/hosts/${hostID}/rotate-keys`, baseUrl); -    await axios.post( -      url.href, -      {}, -      { -        auth: { -          username: "admin", -          password: "secret", -        }, -      }, -    ); -  } -  export async function createEbicsHost( -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    hostID: string, -  ) { -    const baseUrl = libeufinSandboxService.baseUrl; -    let url = new URL("admin/ebics/hosts", baseUrl); -    await axios.post( -      url.href, -      { -        hostID, -        ebicsVersion: "2.5", -      }, -      { -        auth: { -          username: "admin", -          password: "secret", -        }, -      }, -    ); -  } - -  export async function createBankAccount( -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    req: BankAccountInfo, -  ) { -    const baseUrl = libeufinSandboxService.baseUrl; -    let url = new URL(`admin/bank-accounts/${req.label}`, baseUrl); -    await axios.post(url.href, req, { -      auth: { -        username: "admin", -        password: "secret", -      }, -    }); -  } - -  /** -   * This function is useless.  It creates a Ebics subscriber -   * but never gives it a bank account.  To be removed -   */ -  export async function createEbicsSubscriber( -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    req: CreateEbicsSubscriberRequest, -  ) { -    const baseUrl = libeufinSandboxService.baseUrl; -    let url = new URL("admin/ebics/subscribers", baseUrl); -    await axios.post(url.href, req, { -      auth: { -        username: "admin", -        password: "secret", -      }, -    }); -  } - -  /** -   * Create a new bank account and associate it to -   * a existing EBICS subscriber. -   */ -  export async function createEbicsBankAccount( -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    req: CreateEbicsBankAccountRequest, -  ) { -    const baseUrl = libeufinSandboxService.baseUrl; -    let url = new URL("admin/ebics/bank-accounts", baseUrl); -    await axios.post(url.href, req, { -      auth: { -        username: "admin", -        password: "secret", -      }, -    }); -  } - -  export async function simulateIncomingTransaction( -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    accountLabel: string, -    req: SimulateIncomingTransactionRequest, -  ) { -    const baseUrl = libeufinSandboxService.baseUrl; -    let url = new URL( -      `admin/bank-accounts/${accountLabel}/simulate-incoming-transaction`, -      baseUrl, -    ); -    await axios.post(url.href, req, { -      auth: { -        username: "admin", -        password: "secret", -      }, -    }); -  } - -  export async function getAccountTransactions( -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    accountLabel: string, -  ): Promise<SandboxAccountTransactions> { -    const baseUrl = libeufinSandboxService.baseUrl; -    let url = new URL( -      `admin/bank-accounts/${accountLabel}/transactions`, -      baseUrl, -    ); -    const res = await axios.get(url.href, { -      auth: { -        username: "admin", -        password: "secret", -      }, -    }); -    return res.data as SandboxAccountTransactions; -  } - -  export async function getCamt053( -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    accountLabel: string, -  ): Promise<any> { -    const baseUrl = libeufinSandboxService.baseUrl; -    let url = new URL("admin/payments/camt", baseUrl); -    return await axios.post( -      url.href, -      { -        bankaccount: accountLabel, -        type: 53, -      }, -      { -        auth: { -          username: "admin", -          password: "secret", -        }, -      }, -    ); -  } - -  export async function getAccountInfoWithBalance( -    libeufinSandboxService: LibeufinSandboxServiceInterface, -    accountLabel: string, -  ): Promise<any> { -    const baseUrl = libeufinSandboxService.baseUrl; -    let url = new URL(`admin/bank-accounts/${accountLabel}`, baseUrl); -    return await axios.get(url.href, { -      auth: { -        username: "admin", -        password: "secret", -      }, -    }); -  } -} - -export namespace LibeufinNexusApi { -  export async function getAllConnections( -    nexus: LibeufinNexusServiceInterface, -  ): Promise<any> { -    let url = new URL("bank-connections", nexus.baseUrl); -    const res = await axios.get(url.href, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -    return res; -  } - -  export async function deleteBankConnection( -    libeufinNexusService: LibeufinNexusServiceInterface, -    req: DeleteBankConnectionRequest, -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL("bank-connections/delete-connection", baseUrl); -    return await axios.post(url.href, req, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function createEbicsBankConnection( -    libeufinNexusService: LibeufinNexusServiceInterface, -    req: CreateEbicsBankConnectionRequest, -  ): Promise<void> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL("bank-connections", baseUrl); -    await axios.post( -      url.href, -      { -        source: "new", -        type: "ebics", -        name: req.name, -        data: { -          ebicsURL: req.ebicsURL, -          hostID: req.hostID, -          userID: req.userID, -          partnerID: req.partnerID, -          systemID: req.systemID, -        }, -      }, -      { -        auth: { -          username: "admin", -          password: "test", -        }, -      }, -    ); -  } - -  export async function getBankAccount( -    libeufinNexusService: LibeufinNexusServiceInterface, -    accountName: string, -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`bank-accounts/${accountName}`, baseUrl); -    return await axios.get(url.href, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function submitInitiatedPayment( -    libeufinNexusService: LibeufinNexusServiceInterface, -    accountName: string, -    paymentId: string, -  ): Promise<void> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL( -      `bank-accounts/${accountName}/payment-initiations/${paymentId}/submit`, -      baseUrl, -    ); -    await axios.post( -      url.href, -      {}, -      { -        auth: { -          username: "admin", -          password: "test", -        }, -      }, -    ); -  } - -  export async function fetchAccounts( -    libeufinNexusService: LibeufinNexusServiceInterface, -    connectionName: string, -  ): Promise<void> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL( -      `bank-connections/${connectionName}/fetch-accounts`, -      baseUrl, -    ); -    await axios.post( -      url.href, -      {}, -      { -        auth: { -          username: "admin", -          password: "test", -        }, -      }, -    ); -  } - -  export async function importConnectionAccount( -    libeufinNexusService: LibeufinNexusServiceInterface, -    connectionName: string, -    offeredAccountId: string, -    nexusBankAccountId: string, -  ): Promise<void> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL( -      `bank-connections/${connectionName}/import-account`, -      baseUrl, -    ); -    await axios.post( -      url.href, -      { -        offeredAccountId, -        nexusBankAccountId, -      }, -      { -        auth: { -          username: "admin", -          password: "test", -        }, -      }, -    ); -  } - -  export async function connectBankConnection( -    libeufinNexusService: LibeufinNexusServiceInterface, -    connectionName: string, -  ) { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`bank-connections/${connectionName}/connect`, baseUrl); -    await axios.post( -      url.href, -      {}, -      { -        auth: { -          username: "admin", -          password: "test", -        }, -      }, -    ); -  } - -  export async function getPaymentInitiations( -    libeufinNexusService: LibeufinNexusServiceInterface, -    accountName: string, -    username: string = "admin", -    password: string = "test", -  ): Promise<void> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL( -      `/bank-accounts/${accountName}/payment-initiations`, -      baseUrl, -    ); -    let response = await axios.get(url.href, { -      auth: { -        username: username, -        password: password, -      }, -    }); -    console.log( -      `Payment initiations of: ${accountName}`, -      JSON.stringify(response.data, null, 2), -    ); -  } - -  export async function getConfig( -    libeufinNexusService: LibeufinNexusServiceInterface, -  ): Promise<void> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`/config`, baseUrl); -    let response = await axios.get(url.href); -  } - -  // Uses the Anastasis API to get a list of transactions. -  export async function getAnastasisTransactions( -    libeufinNexusService: LibeufinNexusServiceInterface, -    anastasisBaseUrl: string, -    params: {}, // of the request: {delta: 5, ..} -    username: string = "admin", -    password: string = "test", -  ): Promise<any> { -    let url = new URL("history/incoming", anastasisBaseUrl); -    let response = await axios.get(url.href, { -      params: params, -      auth: { -        username: username, -        password: password, -      }, -    }); -    return response; -  } - -  // FIXME: this function should return some structured -  // object that represents a history. -  export async function getAccountTransactions( -    libeufinNexusService: LibeufinNexusServiceInterface, -    accountName: string, -    username: string = "admin", -    password: string = "test", -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`/bank-accounts/${accountName}/transactions`, baseUrl); -    let response = await axios.get(url.href, { -      auth: { -        username: username, -        password: password, -      }, -    }); -    return response; -  } - -  export async function fetchTransactions( -    libeufinNexusService: LibeufinNexusServiceInterface, -    accountName: string, -    rangeType: string = "all", -    level: string = "report", -    username: string = "admin", -    password: string = "test", -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL( -      `/bank-accounts/${accountName}/fetch-transactions`, -      baseUrl, -    ); -    return await axios.post( -      url.href, -      { -        rangeType: rangeType, -        level: level, -      }, -      { -        auth: { -          username: username, -          password: password, -        }, -      }, -    ); -  } - -  export async function changePassword( -    libeufinNexusService: LibeufinNexusServiceInterface, -    username: string, -    req: UpdateNexusUserRequest, -    auth: NexusAuth, -  ) { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`/users/${username}/password`, baseUrl); -    await axios.post(url.href, req, auth); -  } - -  export async function getUser( -    libeufinNexusService: LibeufinNexusServiceInterface, -    auth: NexusAuth, -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`/user`, baseUrl); -    return await axios.get(url.href, auth); -  } - -  export async function createUser( -    libeufinNexusService: LibeufinNexusServiceInterface, -    req: CreateNexusUserRequest, -  ) { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`/users`, baseUrl); -    await axios.post(url.href, req, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function getAllPermissions( -    libeufinNexusService: LibeufinNexusServiceInterface, -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`/permissions`, baseUrl); -    return await axios.get(url.href, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function postPermission( -    libeufinNexusService: LibeufinNexusServiceInterface, -    req: PostNexusPermissionRequest, -  ) { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`/permissions`, baseUrl); -    await axios.post(url.href, req, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function getTasks( -    libeufinNexusService: LibeufinNexusServiceInterface, -    bankAccountName: string, -    // When void, the request returns the list of all the -    // tasks under this bank account. -    taskName: string | void, -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl); -    if (taskName) url = new URL(taskName, `${url.href}/`); - -    // It's caller's responsibility to interpret the response. -    return await axios.get(url.href, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function deleteTask( -    libeufinNexusService: LibeufinNexusServiceInterface, -    bankAccountName: string, -    taskName: string, -  ) { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL( -      `/bank-accounts/${bankAccountName}/schedule/${taskName}`, -      baseUrl, -    ); -    await axios.delete(url.href, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function postTask( -    libeufinNexusService: LibeufinNexusServiceInterface, -    bankAccountName: string, -    req: PostNexusTaskRequest, -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl); -    return await axios.post(url.href, req, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function deleteFacade( -    libeufinNexusService: LibeufinNexusServiceInterface, -    facadeName: string, -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL(`facades/${facadeName}`, baseUrl); -    return await axios.delete(url.href, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function getAllFacades( -    libeufinNexusService: LibeufinNexusServiceInterface, -  ): Promise<any> { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL("facades", baseUrl); -    return await axios.get(url.href, { -      auth: { -        username: "admin", -        password: "test", -      }, -    }); -  } - -  export async function createAnastasisFacade( -    libeufinNexusService: LibeufinNexusServiceInterface, -    req: CreateAnastasisFacadeRequest, -  ) { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL("facades", baseUrl); -    await axios.post( -      url.href, -      { -        name: req.name, -        type: "anastasis", -        config: { -          bankAccount: req.accountName, -          bankConnection: req.connectionName, -          currency: req.currency, -          reserveTransferLevel: req.reserveTransferLevel, -        }, -      }, -      { -        auth: { -          username: "admin", -          password: "test", -        }, -      }, -    ); -  } - -  export async function createTwgFacade( -    libeufinNexusService: LibeufinNexusServiceInterface, -    req: CreateTalerWireGatewayFacadeRequest, -  ) { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL("facades", baseUrl); -    await axios.post( -      url.href, -      { -        name: req.name, -        type: "taler-wire-gateway", -        config: { -          bankAccount: req.accountName, -          bankConnection: req.connectionName, -          currency: req.currency, -          reserveTransferLevel: req.reserveTransferLevel, -        }, -      }, -      { -        auth: { -          username: "admin", -          password: "test", -        }, -      }, -    ); -  } - -  export async function submitAllPaymentInitiations( -    libeufinNexusService: LibeufinNexusServiceInterface, -    accountId: string, -  ) { -    const baseUrl = libeufinNexusService.baseUrl; -    let url = new URL( -      `/bank-accounts/${accountId}/submit-all-payment-initiations`, -      baseUrl, -    ); -    await axios.post( -      url.href, -      {}, -      { -        auth: { -          username: "admin", -          password: "test", -        }, -      }, -    ); -  } -} diff --git a/packages/taler-wallet-cli/src/harness/libeufin.ts b/packages/taler-wallet-cli/src/harness/libeufin.ts deleted file mode 100644 index 638c8ed90..000000000 --- a/packages/taler-wallet-cli/src/harness/libeufin.ts +++ /dev/null @@ -1,910 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * This file defines euFin test logic that needs state - * and that depends on the main harness.ts.  The other - * definitions - mainly helper functions to call RESTful - * APIs - moved to libeufin-apis.ts.  That enables harness.ts - * to depend on such API calls, in contrast to the previous - * situation where harness.ts had to include this file causing - * a circular dependency.  */ - -/** - * Imports. - */ -import axios from "axios"; -import { URL, Logger } from "@gnu-taler/taler-util"; -import { -  GlobalTestState, -  DbInfo, -  pingProc, -  ProcessWrapper, -  runCommand, -  setupDb, -  sh, -  getRandomIban, -} from "../harness/harness.js"; -import { -  LibeufinSandboxApi, -  LibeufinNexusApi, -  CreateEbicsBankAccountRequest, -  LibeufinSandboxServiceInterface, -  CreateTalerWireGatewayFacadeRequest, -  SimulateIncomingTransactionRequest, -  SandboxAccountTransactions, -  DeleteBankConnectionRequest, -  CreateEbicsBankConnectionRequest, -  UpdateNexusUserRequest, -  NexusAuth, -  CreateAnastasisFacadeRequest, -  PostNexusTaskRequest, -  PostNexusPermissionRequest, -  CreateNexusUserRequest, -} from "../harness/libeufin-apis.js"; - - -const logger = new Logger("libeufin.ts"); - -export { LibeufinSandboxApi, LibeufinNexusApi }; - -export interface LibeufinServices { -  libeufinSandbox: LibeufinSandboxService; -  libeufinNexus: LibeufinNexusService; -  commonDb: DbInfo; -} - -export interface LibeufinSandboxConfig { -  httpPort: number; -  databaseJdbcUri: string; -} - -export interface LibeufinNexusConfig { -  httpPort: number; -  databaseJdbcUri: string; -} - -interface LibeufinNexusMoneyMovement { -  amount: string; -  creditDebitIndicator: string; -  details: { -    debtor: { -      name: string; -    }; -    debtorAccount: { -      iban: string; -    }; -    debtorAgent: { -      bic: string; -    }; -    creditor: { -      name: string; -    }; -    creditorAccount: { -      iban: string; -    }; -    creditorAgent: { -      bic: string; -    }; -    endToEndId: string; -    unstructuredRemittanceInformation: string; -  }; -} - -interface LibeufinNexusBatches { -  batchTransactions: Array<LibeufinNexusMoneyMovement>; -} - -interface LibeufinNexusTransaction { -  amount: string; -  creditDebitIndicator: string; -  status: string; -  bankTransactionCode: string; -  valueDate: string; -  bookingDate: string; -  accountServicerRef: string; -  batches: Array<LibeufinNexusBatches>; -} - -interface LibeufinNexusTransactions { -  transactions: Array<LibeufinNexusTransaction>; -} - -export interface LibeufinCliDetails { -  nexusUrl: string; -  sandboxUrl: string; -  nexusDatabaseUri: string; -  sandboxDatabaseUri: string; -  nexusUser: LibeufinNexusUser; -} - -export interface LibeufinEbicsSubscriberDetails { -  hostId: string; -  partnerId: string; -  userId: string; -} - -export interface LibeufinEbicsConnectionDetails { -  subscriberDetails: LibeufinEbicsSubscriberDetails; -  ebicsUrl: string; -  connectionName: string; -} - -export interface LibeufinBankAccountDetails { -  currency: string; -  iban: string; -  bic: string; -  personName: string; -  accountName: string; -} - -export interface LibeufinNexusUser { -  username: string; -  password: string; -} - -export interface LibeufinBackupFileDetails { -  passphrase: string; -  outputFile: string; -  connectionName: string; -} - -export interface LibeufinKeyLetterDetails { -  outputFile: string; -  connectionName: string; -} - -export interface LibeufinBankAccountImportDetails { -  offeredBankAccountName: string; -  nexusBankAccountName: string; -  connectionName: string; -} - -export interface LibeufinPreparedPaymentDetails { -  creditorIban: string; -  creditorBic: string; -  creditorName: string; -  subject: string; -  amount: string; -  currency: string; -  nexusBankAccountName: string; -} - -export class LibeufinSandboxService implements LibeufinSandboxServiceInterface { -  static async create( -    gc: GlobalTestState, -    sandboxConfig: LibeufinSandboxConfig, -  ): Promise<LibeufinSandboxService> { -    return new LibeufinSandboxService(gc, sandboxConfig); -  } - -  sandboxProc: ProcessWrapper | undefined; -  globalTestState: GlobalTestState; - -  constructor( -    gc: GlobalTestState, -    private sandboxConfig: LibeufinSandboxConfig, -  ) { -    this.globalTestState = gc; -  } - -  get baseUrl(): string { -    return `http://localhost:${this.sandboxConfig.httpPort}/`; -  } - -  async start(): Promise<void> { -    await sh( -      this.globalTestState, -      "libeufin-sandbox-config", -      "libeufin-sandbox config default", -      { -        ...process.env, -        LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri, -      }, -    ); - -    this.sandboxProc = this.globalTestState.spawnService( -      "libeufin-sandbox", -      ["serve", "--port", `${this.sandboxConfig.httpPort}`], -      "libeufin-sandbox", -      { -        ...process.env, -        LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri, -        LIBEUFIN_SANDBOX_ADMIN_PASSWORD: "secret", -      }, -    ); -  } - -  async c53tick(): Promise<string> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-sandbox-c53tick", -      "libeufin-sandbox camt053tick", -      { -        ...process.env, -        LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri, -      }, -    ); -    return stdout; -  } - -  async makeTransaction( -    debit: string, -    credit: string, -    amount: string, // $currency:x.y -    subject: string, -  ): Promise<string> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-sandbox-maketransfer", -      `libeufin-sandbox make-transaction --debit-account=${debit} --credit-account=${credit} ${amount} "${subject}"`, -      { -        ...process.env, -        LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri, -      }, -    ); -    return stdout; -  } - -  async pingUntilAvailable(): Promise<void> { -    const url = this.baseUrl; -    await pingProc(this.sandboxProc, url, "libeufin-sandbox"); -  } -} - -export class LibeufinNexusService { -  static async create( -    gc: GlobalTestState, -    nexusConfig: LibeufinNexusConfig, -  ): Promise<LibeufinNexusService> { -    return new LibeufinNexusService(gc, nexusConfig); -  } - -  nexusProc: ProcessWrapper | undefined; -  globalTestState: GlobalTestState; - -  constructor(gc: GlobalTestState, private nexusConfig: LibeufinNexusConfig) { -    this.globalTestState = gc; -  } - -  get baseUrl(): string { -    return `http://localhost:${this.nexusConfig.httpPort}/`; -  } - -  async start(): Promise<void> { -    await runCommand( -      this.globalTestState, -      "libeufin-nexus-superuser", -      "libeufin-nexus", -      ["superuser", "admin", "--password", "test"], -      { -        ...process.env, -        LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusConfig.databaseJdbcUri, -      }, -    ); - -    this.nexusProc = this.globalTestState.spawnService( -      "libeufin-nexus", -      ["serve", "--port", `${this.nexusConfig.httpPort}`], -      "libeufin-nexus", -      { -        ...process.env, -        LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusConfig.databaseJdbcUri, -      }, -    ); -  } - -  async pingUntilAvailable(): Promise<void> { -    const url = `${this.baseUrl}config`; -    await pingProc(this.nexusProc, url, "libeufin-nexus"); -  } - -  async createNexusSuperuser(details: LibeufinNexusUser): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-nexus", -      `libeufin-nexus superuser ${details.username} --password=${details.password}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusConfig.databaseJdbcUri, -      }, -    ); -    console.log(stdout); -  } -} - -export interface TwgAddIncomingRequest { -  amount: string; -  reserve_pub: string; -  debit_account: string; -} - -/** - * The bundle aims at minimizing the amount of input - * data that is required to initialize a new user + Ebics - * connection. - */ -export class NexusUserBundle { -  userReq: CreateNexusUserRequest; -  connReq: CreateEbicsBankConnectionRequest; -  anastasisReq: CreateAnastasisFacadeRequest; -  twgReq: CreateTalerWireGatewayFacadeRequest; -  twgTransferPermission: PostNexusPermissionRequest; -  twgHistoryPermission: PostNexusPermissionRequest; -  twgAddIncomingPermission: PostNexusPermissionRequest; -  localAccountName: string; -  remoteAccountName: string; - -  constructor(salt: string, ebicsURL: string) { -    this.userReq = { -      username: `username-${salt}`, -      password: `password-${salt}`, -    }; - -    this.connReq = { -      name: `connection-${salt}`, -      ebicsURL: ebicsURL, -      hostID: `ebicshost,${salt}`, -      partnerID: `ebicspartner,${salt}`, -      userID: `ebicsuser,${salt}`, -    }; - -    this.twgReq = { -      currency: "EUR", -      name: `twg-${salt}`, -      reserveTransferLevel: "report", -      accountName: `local-account-${salt}`, -      connectionName: `connection-${salt}`, -    }; -    this.anastasisReq = { -      currency: "EUR", -      name: `anastasis-${salt}`, -      reserveTransferLevel: "report", -      accountName: `local-account-${salt}`, -      connectionName: `connection-${salt}`, -    }; -    this.remoteAccountName = `remote-account-${salt}`; -    this.localAccountName = `local-account-${salt}`; -    this.twgTransferPermission = { -      action: "grant", -      permission: { -        subjectId: `username-${salt}`, -        subjectType: "user", -        resourceType: "facade", -        resourceId: `twg-${salt}`, -        permissionName: "facade.talerWireGateway.transfer", -      }, -    }; -    this.twgHistoryPermission = { -      action: "grant", -      permission: { -        subjectId: `username-${salt}`, -        subjectType: "user", -        resourceType: "facade", -        resourceId: `twg-${salt}`, -        permissionName: "facade.talerWireGateway.history", -      }, -    }; -  } -} - -/** - * The bundle aims at minimizing the amount of input - * data that is required to initialize a new Sandbox - * customer, associating their bank account with a Ebics - * subscriber. - */ -export class SandboxUserBundle { -  ebicsBankAccount: CreateEbicsBankAccountRequest; -  constructor(salt: string) { -    this.ebicsBankAccount = { -      bic: "BELADEBEXXX", -      iban: getRandomIban(), -      label: `remote-account-${salt}`, -      name: `Taler Exchange: ${salt}`, -      subscriber: { -        hostID: `ebicshost,${salt}`, -        partnerID: `ebicspartner,${salt}`, -        userID: `ebicsuser,${salt}`, -      }, -    }; -  } -} - -export class LibeufinCli { -  cliDetails: LibeufinCliDetails; -  globalTestState: GlobalTestState; - -  constructor(gc: GlobalTestState, cd: LibeufinCliDetails) { -    this.globalTestState = gc; -    this.cliDetails = cd; -  } - -  env(): any { -    return { -      ...process.env, -      LIBEUFIN_SANDBOX_URL: this.cliDetails.sandboxUrl, -      LIBEUFIN_SANDBOX_USERNAME: "admin", -      LIBEUFIN_SANDBOX_PASSWORD: "secret", -    }; -  } - -  async checkSandbox(): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-checksandbox", -      "libeufin-cli sandbox check", -      this.env(), -    ); -  } - -  async registerBankCustomer(username: string, password: string): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-registercustomer", -      "libeufin-cli sandbox demobank register --name='Test Customer'", -      { -        ...process.env, -        LIBEUFIN_SANDBOX_URL: this.cliDetails.sandboxUrl + "/demobanks/default", -        LIBEUFIN_SANDBOX_USERNAME: username, -        LIBEUFIN_SANDBOX_PASSWORD: password, -      }, -    ); -    console.log(stdout); -  } - -  async createEbicsHost(hostId: string): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-createebicshost", -      `libeufin-cli sandbox ebicshost create --host-id=${hostId}`, -      this.env(), -    ); -    console.log(stdout); -  } - -  async createEbicsSubscriber( -    details: LibeufinEbicsSubscriberDetails, -  ): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-createebicssubscriber", -      "libeufin-cli sandbox ebicssubscriber create" + -        ` --host-id=${details.hostId}` + -        ` --partner-id=${details.partnerId}` + -        ` --user-id=${details.userId}`, -      this.env(), -    ); -    console.log(stdout); -  } - -  async createEbicsBankAccount( -    sd: LibeufinEbicsSubscriberDetails, -    bankAccountDetails: LibeufinBankAccountDetails, -  ): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-createebicsbankaccount", -      "libeufin-cli sandbox ebicsbankaccount create" + -        ` --iban=${bankAccountDetails.iban}` + -        ` --bic=${bankAccountDetails.bic}` + -        ` --person-name='${bankAccountDetails.personName}'` + -        ` --account-name=${bankAccountDetails.accountName}` + -        ` --ebics-host-id=${sd.hostId}` + -        ` --ebics-partner-id=${sd.partnerId}` + -        ` --ebics-user-id=${sd.userId}`, -      this.env(), -    ); -    console.log(stdout); -  } - -  async generateTransactions(accountName: string): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-generatetransactions", -      `libeufin-cli sandbox bankaccount generate-transactions ${accountName}`, -      this.env(), -    ); -    console.log(stdout); -  } - -  async showSandboxTransactions(accountName: string): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-showsandboxtransactions", -      `libeufin-cli sandbox bankaccount transactions ${accountName}`, -      this.env(), -    ); -    console.log(stdout); -  } - -  async createEbicsConnection( -    connectionDetails: LibeufinEbicsConnectionDetails, -  ): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-createebicsconnection", -      `libeufin-cli connections new-ebics-connection` + -        ` --ebics-url=${connectionDetails.ebicsUrl}` + -        ` --host-id=${connectionDetails.subscriberDetails.hostId}` + -        ` --partner-id=${connectionDetails.subscriberDetails.partnerId}` + -        ` --ebics-user-id=${connectionDetails.subscriberDetails.userId}` + -        ` ${connectionDetails.connectionName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async createBackupFile(details: LibeufinBackupFileDetails): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-createbackupfile", -      `libeufin-cli connections export-backup` + -        ` --passphrase=${details.passphrase}` + -        ` --output-file=${details.outputFile}` + -        ` ${details.connectionName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async createKeyLetter(details: LibeufinKeyLetterDetails): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-createkeyletter", -      `libeufin-cli connections get-key-letter` + -        ` ${details.connectionName} ${details.outputFile}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async connect(connectionName: string): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-connect", -      `libeufin-cli connections connect ${connectionName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async downloadBankAccounts(connectionName: string): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-downloadbankaccounts", -      `libeufin-cli connections download-bank-accounts ${connectionName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async listOfferedBankAccounts(connectionName: string): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-listofferedbankaccounts", -      `libeufin-cli connections list-offered-bank-accounts ${connectionName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async importBankAccount( -    importDetails: LibeufinBankAccountImportDetails, -  ): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-importbankaccount", -      "libeufin-cli connections import-bank-account" + -        ` --offered-account-id=${importDetails.offeredBankAccountName}` + -        ` --nexus-bank-account-id=${importDetails.nexusBankAccountName}` + -        ` ${importDetails.connectionName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async fetchTransactions(bankAccountName: string): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-fetchtransactions", -      `libeufin-cli accounts fetch-transactions ${bankAccountName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async transactions(bankAccountName: string): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-transactions", -      `libeufin-cli accounts transactions ${bankAccountName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async preparePayment(details: LibeufinPreparedPaymentDetails): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-preparepayment", -      `libeufin-cli accounts prepare-payment` + -        ` --creditor-iban=${details.creditorIban}` + -        ` --creditor-bic=${details.creditorBic}` + -        ` --creditor-name='${details.creditorName}'` + -        ` --payment-subject='${details.subject}'` + -        ` --payment-amount=${details.currency}:${details.amount}` + -        ` ${details.nexusBankAccountName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async submitPayment( -    details: LibeufinPreparedPaymentDetails, -    paymentUuid: string, -  ): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-submitpayments", -      `libeufin-cli accounts submit-payments` + -        ` --payment-uuid=${paymentUuid}` + -        ` ${details.nexusBankAccountName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async newAnastasisFacade(req: NewAnastasisFacadeReq): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-new-anastasis-facade", -      `libeufin-cli facades new-anastasis-facade` + -        ` --currency ${req.currency}` + -        ` --facade-name ${req.facadeName}` + -        ` ${req.connectionName} ${req.accountName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async newTalerWireGatewayFacade(req: NewTalerWireGatewayReq): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-new-taler-wire-gateway-facade", -      `libeufin-cli facades new-taler-wire-gateway-facade` + -        ` --currency ${req.currency}` + -        ` --facade-name ${req.facadeName}` + -        ` ${req.connectionName} ${req.accountName}`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } - -  async listFacades(): Promise<void> { -    const stdout = await sh( -      this.globalTestState, -      "libeufin-cli-facades-list", -      `libeufin-cli facades list`, -      { -        ...process.env, -        LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, -        LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, -        LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, -      }, -    ); -    console.log(stdout); -  } -} - -interface NewAnastasisFacadeReq { -  facadeName: string; -  connectionName: string; -  accountName: string; -  currency: string; -} - -interface NewTalerWireGatewayReq { -  facadeName: string; -  connectionName: string; -  accountName: string; -  currency: string; -} - -/** - * Launch Nexus and Sandbox AND creates users / facades / bank accounts / - * .. all that's required to start making bank traffic. - */ -export async function launchLibeufinServices( -  t: GlobalTestState, -  nexusUserBundle: NexusUserBundle[], -  sandboxUserBundle: SandboxUserBundle[] = [], -  withFacades: string[] = [], // takes only "twg" and/or "anastasis" -): Promise<LibeufinServices> { -  const db = await setupDb(t); - -  const libeufinSandbox = await LibeufinSandboxService.create(t, { -    httpPort: 5010, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, -  }); - -  await libeufinSandbox.start(); -  await libeufinSandbox.pingUntilAvailable(); - -  const libeufinNexus = await LibeufinNexusService.create(t, { -    httpPort: 5011, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -  }); - -  await libeufinNexus.start(); -  await libeufinNexus.pingUntilAvailable(); -  console.log("Libeufin services launched!"); -   -  for (let sb of sandboxUserBundle) { -    await LibeufinSandboxApi.createEbicsHost( -      libeufinSandbox, -      sb.ebicsBankAccount.subscriber.hostID, -    ); -    await LibeufinSandboxApi.createEbicsSubscriber( -      libeufinSandbox, -      sb.ebicsBankAccount.subscriber, -    ); -    await LibeufinSandboxApi.createDemobankAccount( -      sb.ebicsBankAccount.label, -      "password-unused", -      { baseUrl: libeufinSandbox.baseUrl + "/demobanks/default/access-api/" } -    ); -    await LibeufinSandboxApi.createDemobankEbicsSubscriber( -      sb.ebicsBankAccount.subscriber, -      sb.ebicsBankAccount.label, -      { baseUrl: libeufinSandbox.baseUrl + "/demobanks/default/" } -    ); -  } -  console.log("Sandbox user(s) / account(s) / subscriber(s): created"); - -  for (let nb of nexusUserBundle) { -    await LibeufinNexusApi.createEbicsBankConnection(libeufinNexus, nb.connReq); -    await LibeufinNexusApi.connectBankConnection( -      libeufinNexus, -      nb.connReq.name, -    ); -    await LibeufinNexusApi.fetchAccounts(libeufinNexus, nb.connReq.name); -    await LibeufinNexusApi.importConnectionAccount( -      libeufinNexus, -      nb.connReq.name, -      nb.remoteAccountName, -      nb.localAccountName, -    ); -    await LibeufinNexusApi.createUser(libeufinNexus, nb.userReq); -    for (let facade of withFacades) { -      switch (facade) { -        case "twg": -          await LibeufinNexusApi.createTwgFacade(libeufinNexus, nb.twgReq); -          await LibeufinNexusApi.postPermission( -            libeufinNexus, -            nb.twgTransferPermission, -          ); -          await LibeufinNexusApi.postPermission( -            libeufinNexus, -            nb.twgHistoryPermission, -          ); -          break; -        case "anastasis": -          await LibeufinNexusApi.createAnastasisFacade( -            libeufinNexus, -            nb.anastasisReq, -          ); -      } -    } -  } -  console.log( -    "Nexus user(s) / connection(s) / facade(s) / permission(s): created", -  ); - -  return { -    commonDb: db, -    libeufinNexus: libeufinNexus, -    libeufinSandbox: libeufinSandbox, -  }; -} - -/** - * Helper function that searches a payment among - * a list, as returned by Nexus.  The key is just - * the payment subject. - */ -export function findNexusPayment( -  key: string, -  payments: LibeufinNexusTransactions, -): LibeufinNexusMoneyMovement | void { -  let transactions = payments["transactions"]; -  for (let i = 0; i < transactions.length; i++) { -    let batches = transactions[i]["batches"]; -    for (let y = 0; y < batches.length; y++) { -      let movements = batches[y]["batchTransactions"]; -      for (let z = 0; z < movements.length; z++) { -        let movement = movements[z]; -        if (movement["details"]["unstructuredRemittanceInformation"] == key) -          return movement; -      } -    } -  } -} diff --git a/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts b/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts deleted file mode 100644 index 2a59b0160..000000000 --- a/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts +++ /dev/null @@ -1,337 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Test harness for various GNU Taler components. - * Also provides a fault-injection proxy. - * - * @author Florian Dold <dold@taler.net> - */ - -/** - * Imports. - */ -import { -  MerchantContractTerms, -  Duration, -  Codec, -  buildCodecForObject, -  codecForString, -  codecOptional, -  codecForConstString, -  codecForBoolean, -  codecForNumber, -  codecForMerchantContractTerms, -  codecForAny, -  buildCodecForUnion, -  AmountString, -  AbsoluteTime, -  CoinPublicKeyString, -  EddsaPublicKeyString, -  codecForAmountString, -  TalerProtocolDuration, -  codecForTimestamp, -  TalerProtocolTimestamp, -} from "@gnu-taler/taler-util"; - -export interface PostOrderRequest { -  // The order must at least contain the minimal -  // order detail, but can override all -  order: Partial<MerchantContractTerms>; - -  // if set, the backend will then set the refund deadline to the current -  // time plus the specified delay. -  refund_delay?: TalerProtocolDuration; - -  // specifies the payment target preferred by the client. Can be used -  // to select among the various (active) wire methods supported by the instance. -  payment_target?: string; - -  // FIXME: some fields are missing - -  // Should a token for claiming the order be generated? -  // False can make sense if the ORDER_ID is sufficiently -  // high entropy to prevent adversarial claims (like it is -  // if the backend auto-generates one). Default is 'true'. -  create_token?: boolean; -} - -export type ClaimToken = string; - -export interface PostOrderResponse { -  order_id: string; -  token?: ClaimToken; -} - -export const codecForPostOrderResponse = (): Codec<PostOrderResponse> => -  buildCodecForObject<PostOrderResponse>() -    .property("order_id", codecForString()) -    .property("token", codecOptional(codecForString())) -    .build("PostOrderResponse"); - - -export const codecForRefundDetails = (): Codec<RefundDetails> => -  buildCodecForObject<RefundDetails>() -    .property("reason", codecForString()) -    .property("pending", codecForBoolean()) -    .property("amount", codecForString()) -    .property("timestamp", codecForTimestamp) -    .build("PostOrderResponse"); - -export const codecForCheckPaymentPaidResponse = -  (): Codec<CheckPaymentPaidResponse> => -    buildCodecForObject<CheckPaymentPaidResponse>() -      .property("order_status_url", codecForString()) -      .property("order_status", codecForConstString("paid")) -      .property("refunded", codecForBoolean()) -      .property("wired", codecForBoolean()) -      .property("deposit_total", codecForAmountString()) -      .property("exchange_ec", codecForNumber()) -      .property("exchange_hc", codecForNumber()) -      .property("refund_amount", codecForAmountString()) -      .property("contract_terms", codecForMerchantContractTerms()) -      // FIXME: specify -      .property("wire_details", codecForAny()) -      .property("wire_reports", codecForAny()) -      .property("refund_details", codecForAny()) -      .build("CheckPaymentPaidResponse"); - -export const codecForCheckPaymentUnpaidResponse = -  (): Codec<CheckPaymentUnpaidResponse> => -    buildCodecForObject<CheckPaymentUnpaidResponse>() -      .property("order_status", codecForConstString("unpaid")) -      .property("taler_pay_uri", codecForString()) -      .property("order_status_url", codecForString()) -      .property("already_paid_order_id", codecOptional(codecForString())) -      .build("CheckPaymentPaidResponse"); - -export const codecForCheckPaymentClaimedResponse = -  (): Codec<CheckPaymentClaimedResponse> => -    buildCodecForObject<CheckPaymentClaimedResponse>() -      .property("order_status", codecForConstString("claimed")) -      .property("contract_terms", codecForMerchantContractTerms()) -      .build("CheckPaymentClaimedResponse"); - -export const codecForMerchantOrderPrivateStatusResponse = -  (): Codec<MerchantOrderPrivateStatusResponse> => -    buildCodecForUnion<MerchantOrderPrivateStatusResponse>() -      .discriminateOn("order_status") -      .alternative("paid", codecForCheckPaymentPaidResponse()) -      .alternative("unpaid", codecForCheckPaymentUnpaidResponse()) -      .alternative("claimed", codecForCheckPaymentClaimedResponse()) -      .build("MerchantOrderPrivateStatusResponse"); - -export type MerchantOrderPrivateStatusResponse = -  | CheckPaymentPaidResponse -  | CheckPaymentUnpaidResponse -  | CheckPaymentClaimedResponse; - -export interface CheckPaymentClaimedResponse { -  // Wallet claimed the order, but didn't pay yet. -  order_status: "claimed"; - -  contract_terms: MerchantContractTerms; -} - -export interface CheckPaymentPaidResponse { -  // did the customer pay for this contract -  order_status: "paid"; - -  // Was the payment refunded (even partially) -  refunded: boolean; - -  // Did the exchange wire us the funds -  wired: boolean; - -  // Total amount the exchange deposited into our bank account -  // for this contract, excluding fees. -  deposit_total: AmountString; - -  // Numeric error code indicating errors the exchange -  // encountered tracking the wire transfer for this purchase (before -  // we even got to specific coin issues). -  // 0 if there were no issues. -  exchange_ec: number; - -  // HTTP status code returned by the exchange when we asked for -  // information to track the wire transfer for this purchase. -  // 0 if there were no issues. -  exchange_hc: number; - -  // Total amount that was refunded, 0 if refunded is false. -  refund_amount: AmountString; - -  // Contract terms -  contract_terms: MerchantContractTerms; - -  // Ihe wire transfer status from the exchange for this order if available, otherwise empty array -  wire_details: TransactionWireTransfer[]; - -  // Reports about trouble obtaining wire transfer details, empty array if no trouble were encountered. -  wire_reports: TransactionWireReport[]; - -  // The refund details for this order.  One entry per -  // refunded coin; empty array if there are no refunds. -  refund_details: RefundDetails[]; - -  order_status_url: string; -} - -export interface CheckPaymentUnpaidResponse { -  order_status: "unpaid"; - -  // URI that the wallet must process to complete the payment. -  taler_pay_uri: string; - -  order_status_url: string; - -  // Alternative order ID which was paid for already in the same session. -  // Only given if the same product was purchased before in the same session. -  already_paid_order_id?: string; - -  // We do we NOT return the contract terms here because they may not -  // exist in case the wallet did not yet claim them. -} - -export interface RefundDetails { -  // Reason given for the refund -  reason: string; - -  // when was the refund approved -  timestamp: TalerProtocolTimestamp; - -  // has not been taken yet -  pending: boolean; - -  // Total amount that was refunded (minus a refund fee). -  amount: AmountString; -} - -export interface TransactionWireTransfer { -  // Responsible exchange -  exchange_url: string; - -  // 32-byte wire transfer identifier -  wtid: string; - -  // execution time of the wire transfer -  execution_time: AbsoluteTime; - -  // Total amount that has been wire transferred -  // to the merchant -  amount: AmountString; - -  // Was this transfer confirmed by the merchant via the -  // POST /transfers API, or is it merely claimed by the exchange? -  confirmed: boolean; -} - -export interface TransactionWireReport { -  // Numerical error code -  code: number; - -  // Human-readable error description -  hint: string; - -  // Numerical error code from the exchange. -  exchange_ec: number; - -  // HTTP status code received from the exchange. -  exchange_hc: number; - -  // Public key of the coin for which we got the exchange error. -  coin_pub: CoinPublicKeyString; -} - -export interface TippingReserveStatus { -  // Array of all known reserves (possibly empty!) -  reserves: ReserveStatusEntry[]; -} - -export interface ReserveStatusEntry { -  // Public key of the reserve -  reserve_pub: string; - -  // Timestamp when it was established -  creation_time: AbsoluteTime; - -  // Timestamp when it expires -  expiration_time: AbsoluteTime; - -  // Initial amount as per reserve creation call -  merchant_initial_amount: AmountString; - -  // Initial amount as per exchange, 0 if exchange did -  // not confirm reserve creation yet. -  exchange_initial_amount: AmountString; - -  // Amount picked up so far. -  pickup_amount: AmountString; - -  // Amount approved for tips that exceeds the pickup_amount. -  committed_amount: AmountString; - -  // Is this reserve active (false if it was deleted but not purged) -  active: boolean; -} - -export interface TipCreateConfirmation { -  // Unique tip identifier for the tip that was created. -  tip_id: string; - -  // taler://tip URI for the tip -  taler_tip_uri: string; - -  // URL that will directly trigger processing -  // the tip when the browser is redirected to it -  tip_status_url: string; - -  // when does the tip expire -  tip_expiration: AbsoluteTime; -} - -export interface TipCreateRequest { -  // Amount that the customer should be tipped -  amount: AmountString; - -  // Justification for giving the tip -  justification: string; - -  // URL that the user should be directed to after tipping, -  // will be included in the tip_token. -  next_url: string; -} - -export interface MerchantInstancesResponse { -  // List of instances that are present in the backend (see Instance) -  instances: MerchantInstanceDetail[]; -} - -export interface MerchantInstanceDetail { -  // Merchant name corresponding to this instance. -  name: string; - -  // Merchant instance this response is about ($INSTANCE) -  id: string; - -  // Public key of the merchant/instance, in Crockford Base32 encoding. -  merchant_pub: EddsaPublicKeyString; - -  // List of the payment targets supported by this instance. Clients can -  // specify the desired payment target in /order requests.  Note that -  // front-ends do not have to support wallets selecting payment targets. -  payment_targets: string[]; -} diff --git a/packages/taler-wallet-cli/src/harness/sync.ts b/packages/taler-wallet-cli/src/harness/sync.ts deleted file mode 100644 index a9e8de412..000000000 --- a/packages/taler-wallet-cli/src/harness/sync.ts +++ /dev/null @@ -1,119 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { URL } from "@gnu-taler/taler-util"; -import * as fs from "fs"; -import * as util from "util"; -import { -  GlobalTestState, -  pingProc, -  ProcessWrapper, -} from "../harness/harness.js"; -import { Configuration } from "@gnu-taler/taler-util"; -import * as child_process from "child_process"; - -const exec = util.promisify(child_process.exec); - -export interface SyncConfig { -  /** -   * Human-readable name used in the test harness logs. -   */ -  name: string; - -  httpPort: number; - -  /** -   * Database connection string (only postgres is supported). -   */ -  database: string; - -  annualFee: string; - -  currency: string; - -  uploadLimitMb: number; - -  /** -   * Fulfillment URL used for contract terms related to -   * sync. -   */ -  fulfillmentUrl: string; - -  paymentBackendUrl: string; -} - -function setSyncPaths(config: Configuration, home: string) { -  config.setString("paths", "sync_home", home); -  // We need to make sure that the path of taler_runtime_dir isn't too long, -  // as it contains unix domain sockets (108 character limit). -  const runDir = fs.mkdtempSync("/tmp/taler-test-"); -  config.setString("paths", "sync_runtime_dir", runDir); -  config.setString("paths", "sync_data_home", "$SYNC_HOME/.local/share/sync/"); -  config.setString("paths", "sync_config_home", "$SYNC_HOME/.config/sync/"); -  config.setString("paths", "sync_cache_home", "$SYNC_HOME/.config/sync/"); -} - -export class SyncService { -  static async create( -    gc: GlobalTestState, -    sc: SyncConfig, -  ): Promise<SyncService> { -    const config = new Configuration(); - -    const cfgFilename = gc.testDir + `/sync-${sc.name}.conf`; -    setSyncPaths(config, gc.testDir + "/synchome"); -    config.setString("taler", "currency", sc.currency); -    config.setString("sync", "serve", "tcp"); -    config.setString("sync", "port", `${sc.httpPort}`); -    config.setString("sync", "db", "postgres"); -    config.setString("syncdb-postgres", "config", sc.database); -    config.setString("sync", "payment_backend_url", sc.paymentBackendUrl); -    config.setString("sync", "upload_limit_mb", `${sc.uploadLimitMb}`); -    config.write(cfgFilename); - -    return new SyncService(gc, sc, cfgFilename); -  } - -  proc: ProcessWrapper | undefined; - -  get baseUrl(): string { -    return `http://localhost:${this.syncConfig.httpPort}/`; -  } - -  async start(): Promise<void> { -    await exec(`sync-dbinit -c "${this.configFilename}"`); - -    this.proc = this.globalState.spawnService( -      "sync-httpd", -      ["-LDEBUG", "-c", this.configFilename], -      `sync-${this.syncConfig.name}`, -    ); -  } - -  async pingUntilAvailable(): Promise<void> { -    const url = new URL("config", this.baseUrl).href; -    await pingProc(this.proc, url, "sync"); -  } - -  constructor( -    private globalState: GlobalTestState, -    private syncConfig: SyncConfig, -    private configFilename: string, -  ) { } -} diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts index 8fad3bdbf..90dc2fdd1 100644 --- a/packages/taler-wallet-cli/src/index.ts +++ b/packages/taler-wallet-cli/src/index.ts @@ -17,20 +17,13 @@  /**   * Imports.   */ -import { deepStrictEqual } from "assert"; -import fs from "fs"; -import os from "os"; -import path from "path"; -// Polyfill for encoding which isn't present globally in older nodejs versions  import {    addPaytoQueryParams,    AgeRestriction, -  Amounts,    classifyTalerUri,    clk,    codecForList,    codecForString, -  Configuration,    decodeCrock,    encodeCrock,    getRandomBytes, @@ -39,12 +32,11 @@ import {    parsePaytoUri,    PreparePayResultType,    RecoveryMergeStrategy, -  rsaBlind,    setDangerousTimetravel,    setGlobalLogLevelFromString,    TalerUriType, -  parseDevExperimentUri,  } from "@gnu-taler/taler-util"; +import type { TalerCryptoInterface } from "@gnu-taler/taler-wallet-core";  import {    CryptoDispatcher,    getDefaultNodeWallet, @@ -60,20 +52,8 @@ import {    WalletCoreApiClient,    walletCoreDebugFlags,  } from "@gnu-taler/taler-wallet-core"; -import type { TalerCryptoInterface } from "@gnu-taler/taler-wallet-core"; -import { TextDecoder, TextEncoder } from "util"; -import { runBench1 } from "./bench1.js"; -import { runBench2 } from "./bench2.js"; -import { runBench3 } from "./bench3.js"; -import { runEnv1 } from "./env1.js"; -import { GlobalTestState, runTestWithState } from "./harness/harness.js"; -import { getTestInfo, runTests } from "./integrationtests/testrunner.js"; -import { lintExchangeDeployment } from "./lint.js"; -import { runEnvFull } from "./env-full.js"; -// @ts-ignore -global.TextEncoder = TextEncoder; -// @ts-ignore -global.TextDecoder = TextDecoder; +import fs from "fs"; +import os from "os";  // This module also serves as the entry point for the crypto  // thread worker, and thus must expose these two handlers. @@ -895,75 +875,6 @@ advancedCli    });  advancedCli -  .subcommand("bench1", "bench1", { -    help: "Run the 'bench1' benchmark", -  }) -  .requiredOption("configJson", ["--config-json"], clk.STRING) -  .action(async (args) => { -    let config: any; -    try { -      config = JSON.parse(args.bench1.configJson); -    } catch (e) { -      console.log("Could not parse config JSON"); -    } -    await runBench1(config); -  }); - -advancedCli -  .subcommand("bench2", "bench2", { -    help: "Run the 'bench2' benchmark", -  }) -  .requiredOption("configJson", ["--config-json"], clk.STRING) -  .action(async (args) => { -    let config: any; -    try { -      config = JSON.parse(args.bench2.configJson); -    } catch (e) { -      console.log("Could not parse config JSON"); -    } -    await runBench2(config); -  }); - -advancedCli -  .subcommand("bench3", "bench3", { -    help: "Run the 'bench3' benchmark", -  }) -  .requiredOption("configJson", ["--config-json"], clk.STRING) -  .action(async (args) => { -    let config: any; -    try { -      config = JSON.parse(args.bench3.configJson); -    } catch (e) { -      console.log("Could not parse config JSON"); -    } -    await runBench3(config); -  }); - -advancedCli -  .subcommand("envFull", "env-full", { -    help: "Run a test environment for bench1", -  }) -  .action(async (args) => { -    const testDir = fs.mkdtempSync(path.join(os.tmpdir(), "taler-env-full-")); -    const testState = new GlobalTestState({ -      testDir, -    }); -    await runTestWithState(testState, runEnvFull, "env-full", true); -  }); - -advancedCli -  .subcommand("env1", "env1", { -    help: "Run a test environment for bench1", -  }) -  .action(async (args) => { -    const testDir = fs.mkdtempSync(path.join(os.tmpdir(), "taler-env1-")); -    const testState = new GlobalTestState({ -      testDir, -    }); -    await runTestWithState(testState, runEnv1, "env1", true); -  }); - -advancedCli    .subcommand("withdrawFakebank", "withdraw-fakebank", {      help: "Withdraw via a fakebank.",    }) @@ -992,7 +903,7 @@ advancedCli    })    .action((args) => {      const enc = fs.readFileSync(0, "utf8"); -    fs.writeFileSync(1, decodeCrock(enc.trim())); +    console.log(decodeCrock(enc.trim()));    });  advancedCli @@ -1207,92 +1118,6 @@ advancedCli      });    }); -const deploymentCli = walletCli.subcommand("deploymentArgs", "deployment", { -  help: "Subcommands for handling GNU Taler deployments.", -}); - -deploymentCli -  .subcommand("lintExchange", "lint-exchange", { -    help: "Run checks on the exchange deployment.", -  }) -  .flag("cont", ["--continue"], { -    help: "Continue after errors if possible", -  }) -  .flag("debug", ["--debug"], { -    help: "Output extra debug info", -  }) -  .action(async (args) => { -    await lintExchangeDeployment( -      args.lintExchange.debug, -      args.lintExchange.cont, -    ); -  }); - -deploymentCli -  .subcommand("coincfg", "gen-coin-config", { -    help: "Generate a coin/denomination configuration for the exchange.", -  }) -  .requiredOption("minAmount", ["--min-amount"], clk.STRING, { -    help: "Smallest denomination", -  }) -  .requiredOption("maxAmount", ["--max-amount"], clk.STRING, { -    help: "Largest denomination", -  }) -  .action(async (args) => { -    let out = ""; - -    const stamp = Math.floor(new Date().getTime() / 1000); - -    const min = Amounts.parseOrThrow(args.coincfg.minAmount); -    const max = Amounts.parseOrThrow(args.coincfg.maxAmount); -    if (min.currency != max.currency) { -      console.error("currency mismatch"); -      process.exit(1); -    } -    const currency = min.currency; -    let x = min; -    let n = 1; - -    out += "# Coin configuration for the exchange.\n"; -    out += '# Should be placed in "/etc/taler/conf.d/exchange-coins.conf".\n'; -    out += "\n"; - -    while (Amounts.cmp(x, max) < 0) { -      out += `[COIN-${currency}-n${n}-t${stamp}]\n`; -      out += `VALUE = ${Amounts.stringify(x)}\n`; -      out += `DURATION_WITHDRAW = 7 days\n`; -      out += `DURATION_SPEND = 2 years\n`; -      out += `DURATION_LEGAL = 6 years\n`; -      out += `FEE_WITHDRAW = ${currency}:0\n`; -      out += `FEE_DEPOSIT = ${Amounts.stringify(min)}\n`; -      out += `FEE_REFRESH = ${currency}:0\n`; -      out += `FEE_REFUND = ${currency}:0\n`; -      out += `RSA_KEYSIZE = 2048\n`; -      out += "\n"; -      x = Amounts.add(x, x).amount; -      n++; -    } - -    console.log(out); -  }); - -const deploymentConfigCli = deploymentCli.subcommand("configArgs", "config", { -  help: "Subcommands the Taler configuration.", -}); - -deploymentConfigCli -  .subcommand("show", "show") -  .flag("diagnostics", ["-d", "--diagnostics"]) -  .maybeArgument("cfgfile", clk.STRING, {}) -  .action(async (args) => { -    const cfg = Configuration.load(args.show.cfgfile); -    console.log( -      cfg.stringify({ -        diagnostics: args.show.diagnostics, -      }), -    ); -  }); -  const testCli = walletCli.subcommand("testingArgs", "testing", {    help: "Subcommands for testing.",  }); @@ -1426,102 +1251,12 @@ testCli.subcommand("logtest", "logtest").action(async (args) => {    logger.error("This is an error message.");  }); -testCli -  .subcommand("listIntegrationtests", "list-integrationtests") -  .action(async (args) => { -    for (const t of getTestInfo()) { -      let s = t.name; -      if (t.suites.length > 0) { -        s += ` (suites: ${t.suites.join(",")})`; -      } -      if (t.excludeByDefault) { -        s += ` [excluded by default]`; -      } -      console.log(s); -    } -  }); - -testCli -  .subcommand("runIntegrationtests", "run-integrationtests") -  .maybeArgument("pattern", clk.STRING, { -    help: "Glob pattern to select which tests to run", -  }) -  .maybeOption("suites", ["--suites"], clk.STRING, { -    help: "Only run selected suites (comma-separated list)", -  }) -  .flag("dryRun", ["--dry"], { -    help: "Only print tests that will be selected to run.", -  }) -  .flag("quiet", ["--quiet"], { -    help: "Produce less output.", -  }) -  .action(async (args) => { -    await runTests({ -      includePattern: args.runIntegrationtests.pattern, -      suiteSpec: args.runIntegrationtests.suites, -      dryRun: args.runIntegrationtests.dryRun, -      verbosity: args.runIntegrationtests.quiet ? 0 : 1, -    }); -  }); -  async function read(stream: NodeJS.ReadStream) {    const chunks = [];    for await (const chunk of stream) chunks.push(chunk);    return Buffer.concat(chunks).toString("utf8");  } -testCli.subcommand("tvgcheck", "tvgcheck").action(async (args) => { -  const data = await read(process.stdin); - -  const lines = data.match(/[^\r\n]+/g); - -  if (!lines) { -    throw Error("can't split lines"); -  } - -  const vals: Record<string, string> = {}; - -  let inBlindSigningSection = false; - -  for (const line of lines) { -    if (line === "blind signing:") { -      inBlindSigningSection = true; -      continue; -    } -    if (line[0] !== " ") { -      inBlindSigningSection = false; -      continue; -    } -    if (inBlindSigningSection) { -      const m = line.match(/  (\w+) (\w+)/); -      if (!m) { -        console.log("bad format"); -        process.exit(2); -      } -      vals[m[1]] = m[2]; -    } -  } - -  console.log(vals); - -  const req = (k: string) => { -    if (!vals[k]) { -      throw Error(`no value for ${k}`); -    } -    return decodeCrock(vals[k]); -  }; - -  const myBm = rsaBlind( -    req("message_hash"), -    req("blinding_key_secret"), -    req("rsa_public_key"), -  ); - -  deepStrictEqual(req("blinded_message"), myBm); - -  console.log("check passed!"); -}); -  testCli    .subcommand("cryptoworker", "cryptoworker")    .maybeOption("impl", ["--impl"], clk.STRING) diff --git a/packages/taler-wallet-cli/src/integrationtests/scenario-prompt-payment.ts b/packages/taler-wallet-cli/src/integrationtests/scenario-prompt-payment.ts deleted file mode 100644 index ea05de8e9..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/scenario-prompt-payment.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runPromptPaymentScenario(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  // Set up order. - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -    }, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  console.log(orderStatus); - -  // Wait "forever" -  await new Promise(() => {}); -} diff --git a/packages/taler-wallet-cli/src/integrationtests/test-age-restrictions-merchant.ts b/packages/taler-wallet-cli/src/integrationtests/test-age-restrictions-merchant.ts deleted file mode 100644 index ff589dd79..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-age-restrictions-merchant.ts +++ /dev/null @@ -1,201 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { BankApi, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { defaultCoinConfig } from "../harness/denomStructures.js"; -import { -  getWireMethodForTest, -  GlobalTestState, -  MerchantPrivateApi, -  WalletCli, -} from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -  makeTestPayment, -} from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet: walletOne, -    bank, -    exchange, -    merchant, -    exchangeBankAccount, -  } = await createSimpleTestkudosEnvironment( -    t, -    defaultCoinConfig.map((x) => x("TESTKUDOS")), -    { -      ageMaskSpec: "8:10:12:14:16:18:21", -    }, -  ); - -  const walletTwo = new WalletCli(t, "walletTwo"); -  const walletThree = new WalletCli(t, "walletThree"); - -  { -    const walletZero = new WalletCli(t, "walletZero"); - -    await withdrawViaBank(t, { -      wallet: walletZero, -      bank, -      exchange, -      amount: "TESTKUDOS:20", -      restrictAge: 13, -    }); - -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -      minimum_age: 9, -    }; - -    await makeTestPayment(t, { wallet: walletZero, merchant, order }); -    await walletZero.runUntilDone(); -  } - -  { -    const wallet = walletOne; - -    await withdrawViaBank(t, { -      wallet, -      bank, -      exchange, -      amount: "TESTKUDOS:20", -      restrictAge: 13, -    }); - -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -      minimum_age: 9, -    }; - -    await makeTestPayment(t, { wallet, merchant, order }); -    await wallet.runUntilDone(); -  } - -  { -    const wallet = walletTwo; - -    await withdrawViaBank(t, { -      wallet, -      bank, -      exchange, -      amount: "TESTKUDOS:20", -      restrictAge: 13, -    }); - -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -    }; - -    await makeTestPayment(t, { wallet, merchant, order }); -    await wallet.runUntilDone(); -  } - -  { -    const wallet = walletThree; - -    await withdrawViaBank(t, { -      wallet, -      bank, -      exchange, -      amount: "TESTKUDOS:20", -    }); - -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -      minimum_age: 9, -    }; - -    await makeTestPayment(t, { wallet, merchant, order }); -    await wallet.runUntilDone(); -  } - -  // Pay with coin from tipping -  { -    const mbu = await BankApi.createRandomBankUser(bank); -    const tipReserveResp = await MerchantPrivateApi.createTippingReserve( -      merchant, -      "default", -      { -        exchange_url: exchange.baseUrl, -        initial_balance: "TESTKUDOS:10", -        wire_method: getWireMethodForTest(), -      }, -    ); - -    t.assertDeepEqual( -      tipReserveResp.payto_uri, -      exchangeBankAccount.accountPaytoUri, -    ); - -    await BankApi.adminAddIncoming(bank, { -      amount: "TESTKUDOS:10", -      debitAccountPayto: mbu.accountPaytoUri, -      exchangeBankAccount, -      reservePub: tipReserveResp.reserve_pub, -    }); - -    await exchange.runWirewatchOnce(); - -    const tip = await MerchantPrivateApi.giveTip(merchant, "default", { -      amount: "TESTKUDOS:5", -      justification: "why not?", -      next_url: "https://example.com/after-tip", -    }); - -    const walletTipping = new WalletCli(t, "age-tipping"); - -    const ptr = await walletTipping.client.call(WalletApiOperation.PrepareTip, { -      talerTipUri: tip.taler_tip_uri, -    }); - -    await walletTipping.client.call(WalletApiOperation.AcceptTip, { -      walletTipId: ptr.walletTipId, -    }); - -    await walletTipping.runUntilDone(); - -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:4", -      fulfillment_url: "taler://fulfillment-success/thx", -      minimum_age: 9, -    }; - -    await makeTestPayment(t, { wallet: walletTipping, merchant, order }); -    await walletTipping.runUntilDone(); -  } -} - -runAgeRestrictionsMerchantTest.suites = ["wallet"]; -runAgeRestrictionsMerchantTest.timeoutMs = 120 * 1000; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-age-restrictions-mixed-merchant.ts b/packages/taler-wallet-cli/src/integrationtests/test-age-restrictions-mixed-merchant.ts deleted file mode 100644 index 8bf71b63d..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-age-restrictions-mixed-merchant.ts +++ /dev/null @@ -1,116 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { defaultCoinConfig } from "../harness/denomStructures.js"; -import { GlobalTestState, WalletCli } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -  makeTestPayment, -} from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet: walletOne, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment( -    t, -    defaultCoinConfig.map((x) => x("TESTKUDOS")), -    { -      ageMaskSpec: "8:10:12:14:16:18:21", -      mixedAgeRestriction: true, -    }, -  ); - -  const walletTwo = new WalletCli(t, "walletTwo"); -  const walletThree = new WalletCli(t, "walletThree"); - -  { -    const wallet = walletOne; - -    await withdrawViaBank(t, { -      wallet, -      bank, -      exchange, -      amount: "TESTKUDOS:20", -      restrictAge: 13, -    }); - -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -      minimum_age: 9, -    }; - -    await makeTestPayment(t, { wallet, merchant, order }); -    await wallet.runUntilDone(); -  } - -  { -    const wallet = walletTwo; - -    await withdrawViaBank(t, { -      wallet, -      bank, -      exchange, -      amount: "TESTKUDOS:20", -      restrictAge: 13, -    }); - -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -    }; - -    await makeTestPayment(t, { wallet, merchant, order }); -    await wallet.runUntilDone(); -  } - -  { -    const wallet = walletThree; - -    await withdrawViaBank(t, { -      wallet, -      bank, -      exchange, -      amount: "TESTKUDOS:20", -    }); - -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -      minimum_age: 9, -    }; - -    await makeTestPayment(t, { wallet, merchant, order }); -    await wallet.runUntilDone(); -  } -} - -runAgeRestrictionsMixedMerchantTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-age-restrictions-peer.ts b/packages/taler-wallet-cli/src/integrationtests/test-age-restrictions-peer.ts deleted file mode 100644 index af5b4df52..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-age-restrictions-peer.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { AbsoluteTime, Duration } from "@gnu-taler/taler-util"; -import { getDefaultNodeWallet2, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { defaultCoinConfig } from "../harness/denomStructures.js"; -import { GlobalTestState, WalletCli } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -  makeTestPayment, -} from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runAgeRestrictionsPeerTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet: walletOne, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment( -    t, -    defaultCoinConfig.map((x) => x("TESTKUDOS")), -    { -      ageMaskSpec: "8:10:12:14:16:18:21", -    }, -  ); - -  const walletTwo = new WalletCli(t, "walletTwo"); -  const walletThree = new WalletCli(t, "walletThree"); - -  { -    const wallet = walletOne; - -    await withdrawViaBank(t, { -      wallet, -      bank, -      exchange, -      amount: "TESTKUDOS:20", -      restrictAge: 13, -    }); - -    const purse_expiration = AbsoluteTime.toTimestamp( -      AbsoluteTime.addDuration( -        AbsoluteTime.now(), -        Duration.fromSpec({ days: 2 }), -      ), -    ); - -    const initResp = await wallet.client.call(WalletApiOperation.InitiatePeerPushPayment, { -      partialContractTerms: { -        summary: "Hello, World", -        amount: "TESTKUDOS:1", -        purse_expiration, -      }, -    }); - -    await wallet.runUntilDone(); - -    const checkResp = await walletTwo.client.call(WalletApiOperation.CheckPeerPushPayment, { -      talerUri: initResp.talerUri, -    }); - -    await walletTwo.client.call(WalletApiOperation.AcceptPeerPushPayment, { -      peerPushPaymentIncomingId: checkResp.peerPushPaymentIncomingId, -    }); - -    await walletTwo.runUntilDone(); -  } -} - -runAgeRestrictionsPeerTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts b/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts deleted file mode 100644 index c7a23d3ce..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts +++ /dev/null @@ -1,136 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  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, -  CreditDebitIndicator, -} from "@gnu-taler/taler-wallet-core"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runBankApiTest(t: GlobalTestState) { -  // Set up test environment - -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    currency: "TESTKUDOS", -    httpPort: 8082, -    database: db.connStr, -    allowRegistrations: true, -  }); - -  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(); - -  exchange.addOfferedCoins(defaultCoinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); -  await merchant.addDefaultInstance(); -  await merchant.addInstance({ -    id: "minst1", -    name: "minst1", -    paytoUris: [getPayto("minst1")], -  }); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  console.log("setup done!"); - -  const bankUser = await BankApi.registerAccount(bank, "user1", "pw1"); - -  // Make sure that registering twice results in a 409 Conflict -  { -    const e = await t.assertThrowsTalerErrorAsync(async () => { -      await BankApi.registerAccount(bank, "user1", "pw2"); -    }); -    t.assertTrue(e.errorDetail.httpStatusCode === 409); -  } - -  let balResp = await BankAccessApi.getAccountBalance(bank, bankUser); - -  console.log(balResp); - -  // Check that we got the sign-up bonus. -  t.assertAmountEquals(balResp.balance.amount, "TESTKUDOS:100"); -  t.assertTrue( -    balResp.balance.credit_debit_indicator === CreditDebitIndicator.Credit, -  ); - -  const res = createEddsaKeyPair(); - -  await BankApi.adminAddIncoming(bank, { -    amount: "TESTKUDOS:115", -    debitAccountPayto: bankUser.accountPaytoUri, -    exchangeBankAccount: exchangeBankAccount, -    reservePub: encodeCrock(res.eddsaPub), -  }); - -  balResp = await BankAccessApi.getAccountBalance(bank, bankUser); -  t.assertAmountEquals(balResp.balance.amount, "TESTKUDOS:15"); -  t.assertTrue( -    balResp.balance.credit_debit_indicator === CreditDebitIndicator.Debit, -  ); -} diff --git a/packages/taler-wallet-cli/src/integrationtests/test-claim-loop.ts b/packages/taler-wallet-cli/src/integrationtests/test-claim-loop.ts deleted file mode 100644 index a509e3b19..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-claim-loop.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js"; -import { URL } from "url"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; - -/** - * Run test for the merchant's order lifecycle. - * - * FIXME: Is this test still necessary?  We initially wrote if to confirm/document - * assumptions about how the merchant should work. - */ -export async function runClaimLoopTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment(t); - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  // Set up order. -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -    }, -  }); - -  // Query private order status before claiming it. -  let orderStatusBefore = await MerchantPrivateApi.queryPrivateOrderStatus( -    merchant, -    { -      orderId: orderResp.order_id, -    }, -  ); -  t.assertTrue(orderStatusBefore.order_status === "unpaid"); -  let statusUrlBefore = new URL(orderStatusBefore.order_status_url); - -  // Make wallet claim the unpaid order. -  t.assertTrue(orderStatusBefore.order_status === "unpaid"); -  const talerPayUri = orderStatusBefore.taler_pay_uri; -  await wallet.client.call(WalletApiOperation.PreparePayForUri, { -    talerPayUri, -  }); - -  // Query private order status after claiming it. -  let orderStatusAfter = await MerchantPrivateApi.queryPrivateOrderStatus( -    merchant, -    { -      orderId: orderResp.order_id, -    }, -  ); -  t.assertTrue(orderStatusAfter.order_status === "claimed"); - -  await t.shutdown(); -} diff --git a/packages/taler-wallet-cli/src/integrationtests/test-clause-schnorr.ts b/packages/taler-wallet-cli/src/integrationtests/test-clause-schnorr.ts deleted file mode 100644 index bf42dc4c6..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-clause-schnorr.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; -import { GlobalTestState } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -  makeTestPayment, -} from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runClauseSchnorrTest(t: GlobalTestState) { -  // Set up test environment - -  const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => { -    return { -      ...x("TESTKUDOS"), -      cipher: "CS", -    }; -  }); - -  // We need to have at least one RSA denom configured -  coinConfig.push({ -    cipher: "RSA", -    rsaKeySize: 1024, -    durationLegal: "3 years", -    durationSpend: "2 years", -    durationWithdraw: "7 days", -    feeDeposit: "TESTKUDOS:42", -    value: "TESTKUDOS:0.0001", -    feeWithdraw: "TESTKUDOS:42", -    feeRefresh: "TESTKUDOS:42", -    feeRefund: "TESTKUDOS:42", -    name: "rsa_dummy", -  }); - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t, coinConfig); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  const order = { -    summary: "Buy me!", -    amount: "TESTKUDOS:5", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  await makeTestPayment(t, { wallet, merchant, order }); -  await wallet.runUntilDone(); - -  // Test JSON normalization of contract terms: Does the wallet -  // agree with the merchant? -  const order2 = { -    summary: "Testing “unicode” characters", -    amount: "TESTKUDOS:5", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  await makeTestPayment(t, { wallet, merchant, order: order2 }); -  await wallet.runUntilDone(); - -  // Test JSON normalization of contract terms: Does the wallet -  // agree with the merchant? -  const order3 = { -    summary: "Testing\nNewlines\rAnd\tStuff\nHere\b", -    amount: "TESTKUDOS:5", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  await makeTestPayment(t, { wallet, merchant, order: order3 }); - -  await wallet.runUntilDone(); -} - -runClauseSchnorrTest.suites = ["experimental-wallet"]; -runClauseSchnorrTest.excludeByDefault = true; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts b/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts deleted file mode 100644 index b5ecbee4a..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -} from "../harness/helpers.js"; - -export async function runDenomUnofferedTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  // Make the exchange forget the denomination. -  // Effectively we completely reset the exchange, -  // but keep the exchange master public key. - -  await exchange.stop(); -  await exchange.purgeDatabase(); -  await exchange.purgeSecmodKeys(); -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  await merchant.stop(); -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  const order = { -    summary: "Buy me!", -    amount: "TESTKUDOS:5", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  { -    const orderResp = await MerchantPrivateApi.createOrder( -      merchant, -      "default", -      { -        order: order, -      }, -    ); - -    let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( -      merchant, -      { -        orderId: orderResp.order_id, -      }, -    ); - -    t.assertTrue(orderStatus.order_status === "unpaid"); - -    // Make wallet pay for the order - -    const preparePayResult = await wallet.client.call( -      WalletApiOperation.PreparePayForUri, -      { -        talerPayUri: orderStatus.taler_pay_uri, -      }, -    ); - -    t.assertTrue( -      preparePayResult.status === PreparePayResultType.PaymentPossible, -    ); - -    const exc = await t.assertThrowsTalerErrorAsync(async () => { -      await wallet.client.call(WalletApiOperation.ConfirmPay, { -        proposalId: preparePayResult.proposalId, -      }); -    }); - -    t.assertTrue( -      exc.hasErrorCode(TalerErrorCode.WALLET_PENDING_OPERATION_FAILED), -    ); - -    // FIXME: We might want a more specific error code here! -    t.assertDeepEqual( -      exc.errorDetail.innerError.code, -      TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, -    ); -    const merchantErrorCode = (exc.errorDetail.innerError.errorResponse as any) -      .code; -    t.assertDeepEqual( -      merchantErrorCode, -      TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND, -    ); -  } - -  await wallet.client.call(WalletApiOperation.AddExchange, { -    exchangeBaseUrl: exchange.baseUrl, -    forceUpdate: true, -  }); - -  // Now withdrawal should work again. -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  await wallet.runUntilDone(); - -  const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {}); -  console.log(JSON.stringify(txs, undefined, 2)); -} - -runDenomUnofferedTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-deposit.ts b/packages/taler-wallet-cli/src/integrationtests/test-deposit.ts deleted file mode 100644 index 07382c43e..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-deposit.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState, getPayto } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runDepositTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  await wallet.runUntilDone(); - -  const { depositGroupId } = await wallet.client.call( -    WalletApiOperation.CreateDepositGroup, -    { -      amount: "TESTKUDOS:10", -      depositPaytoUri: getPayto("foo"), -    }, -  ); - -  await wallet.runUntilDone(); - -  const transactions = await wallet.client.call( -    WalletApiOperation.GetTransactions, -    {}, -  ); -  console.log("transactions", JSON.stringify(transactions, undefined, 2)); -  t.assertDeepEqual(transactions.transactions[0].type, "withdrawal"); -  t.assertTrue(!transactions.transactions[0].pending); -  t.assertDeepEqual(transactions.transactions[1].type, "deposit"); -  t.assertTrue(!transactions.transactions[1].pending); -  // The raw amount is what ends up on the bank account, which includes -  // deposit and wire fees. -  t.assertDeepEqual(transactions.transactions[1].amountRaw, "TESTKUDOS:9.79"); - -  const trackResult = wallet.client.call(WalletApiOperation.TrackDepositGroup, { -    depositGroupId, -  }); - -  console.log(JSON.stringify(trackResult, undefined, 2)); -} diff --git a/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts b/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts deleted file mode 100644 index 6b63c3741..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts +++ /dev/null @@ -1,285 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  GlobalTestState, -  WalletCli, -  setupDb, -  BankService, -  ExchangeService, -  MerchantService, -  getPayto, -} from "../harness/harness.js"; -import { -  WalletApiOperation, -  BankApi, -  BankAccessApi, -} from "@gnu-taler/taler-wallet-core"; -import { -  ExchangesListResponse, -  URL, -  TalerErrorCode, -  j2s, -} from "@gnu-taler/taler-util"; -import { -  FaultInjectedExchangeService, -  FaultInjectionResponseContext, -} from "../harness/faultInjection.js"; -import { defaultCoinConfig } from "../harness/denomStructures.js"; - -/** - * Test if the wallet handles outdated exchange versions correct.y - */ -export async function runExchangeManagementTest( -  t: GlobalTestState, -): Promise<void> { -  // Set up test environment - -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); -  exchange.addBankAccount("1", exchangeBankAccount); - -  const faultyExchange = new FaultInjectedExchangeService(t, exchange, 8091); - -  bank.setSuggestedExchange( -    faultyExchange, -    exchangeBankAccount.accountPaytoUri, -  ); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  exchange.addOfferedCoins(defaultCoinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  await merchant.addInstance({ -    id: "minst1", -    name: "minst1", -    paytoUris: [getPayto("minst1")], -  }); - -  console.log("setup done!"); - -  /* -   * ========================================================================= -   * Check that the exchange can be added to the wallet -   * (without any faults active). -   * ========================================================================= -   */ - -  const wallet = new WalletCli(t); - -  let exchangesList: ExchangesListResponse; - -  exchangesList = await wallet.client.call( -    WalletApiOperation.ListExchanges, -    {}, -  ); -  console.log("exchanges list:", j2s(exchangesList)); -  t.assertTrue(exchangesList.exchanges.length === 0); - -  // Try before fault is injected -  await wallet.client.call(WalletApiOperation.AddExchange, { -    exchangeBaseUrl: faultyExchange.baseUrl, -  }); - -  exchangesList = await wallet.client.call( -    WalletApiOperation.ListExchanges, -    {}, -  ); -  t.assertTrue(exchangesList.exchanges.length === 1); - -  await wallet.client.call(WalletApiOperation.ListExchanges, {}); - -  console.log("listing exchanges"); - -  exchangesList = await wallet.client.call( -    WalletApiOperation.ListExchanges, -    {}, -  ); -  t.assertTrue(exchangesList.exchanges.length === 1); - -  console.log("got list", exchangesList); - -  /* -   * ========================================================================= -   * Check what happens if the exchange returns something totally -   * bogus for /keys. -   * ========================================================================= -   */ - -  wallet.deleteDatabase(); - -  exchangesList = await wallet.client.call( -    WalletApiOperation.ListExchanges, -    {}, -  ); -  t.assertTrue(exchangesList.exchanges.length === 0); - -  faultyExchange.faultProxy.addFault({ -    async modifyResponse(ctx: FaultInjectionResponseContext) { -      const url = new URL(ctx.request.requestUrl); -      if (url.pathname === "/keys") { -        const body = { -          version: "whaaat", -        }; -        ctx.responseBody = Buffer.from(JSON.stringify(body), "utf-8"); -      } -    }, -  }); - -  const err1 = await t.assertThrowsTalerErrorAsync(async () => { -    await wallet.client.call(WalletApiOperation.AddExchange, { -      exchangeBaseUrl: faultyExchange.baseUrl, -    }); -  }); - -  // Response is malformed, since it didn't even contain a version code -  // in a format the wallet can understand. -  t.assertTrue( -    err1.errorDetail.code === TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, -  ); -  exchangesList = await wallet.client.call( -    WalletApiOperation.ListExchanges, -    {}, -  ); -  console.log("exchanges list", j2s(exchangesList)); -  t.assertTrue(exchangesList.exchanges.length === 1); -  t.assertTrue( -    exchangesList.exchanges[0].lastUpdateErrorInfo?.error.code === -      TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, -  ); - -  /* -   * ========================================================================= -   * Check what happens if the exchange returns an old, unsupported -   * version for /keys -   * ========================================================================= -   */ - -  wallet.deleteDatabase(); -  faultyExchange.faultProxy.clearAllFaults(); - -  faultyExchange.faultProxy.addFault({ -    async modifyResponse(ctx: FaultInjectionResponseContext) { -      const url = new URL(ctx.request.requestUrl); -      if (url.pathname === "/keys") { -        const keys = ctx.responseBody?.toString("utf-8"); -        t.assertTrue(keys != null); -        const keysJson = JSON.parse(keys); -        keysJson["version"] = "2:0:0"; -        ctx.responseBody = Buffer.from(JSON.stringify(keysJson), "utf-8"); -      } -    }, -  }); - -  const err2 = await t.assertThrowsTalerErrorAsync(async () => { -    await wallet.client.call(WalletApiOperation.AddExchange, { -      exchangeBaseUrl: faultyExchange.baseUrl, -    }); -  }); - -  t.assertTrue( -    err2.hasErrorCode( -      TalerErrorCode.WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE, -    ), -  ); - -  exchangesList = await wallet.client.call( -    WalletApiOperation.ListExchanges, -    {}, -  ); -  t.assertTrue(exchangesList.exchanges.length === 1); -  t.assertTrue( -    exchangesList.exchanges[0].lastUpdateErrorInfo?.error.code === -      TalerErrorCode.WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE, -  ); - -  /* -   * ========================================================================= -   * Check that the exchange version is also checked when -   * the exchange is implicitly added via the suggested -   * exchange of a bank-integrated withdrawal. -   * ========================================================================= -   */ - -  // Fault from above is still active! - -  // Create withdrawal operation - -  const user = await BankApi.createRandomBankUser(bank); -  const wop = await BankAccessApi.createWithdrawalOperation( -    bank, -    user, -    "TESTKUDOS:10", -  ); - -  // Hand it to the wallet - -  const wd = await wallet.client.call( -    WalletApiOperation.GetWithdrawalDetailsForUri, -    { -      talerWithdrawUri: wop.taler_withdraw_uri, -    }, -  ); - -  // Make sure the faulty exchange isn't used for the suggestion. -  t.assertTrue(wd.possibleExchanges.length === 0); -} - -runExchangeManagementTest.suites = ["wallet", "exchange"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts b/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts deleted file mode 100644 index 074126e9f..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts +++ /dev/null @@ -1,240 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  AbsoluteTime, -  codecForExchangeKeysJson, -  DenominationPubKey, -  Duration, -  durationFromSpec, -} from "@gnu-taler/taler-util"; -import { -  NodeHttpLib, -  readSuccessResponseJsonOrThrow, -} from "@gnu-taler/taler-wallet-core"; -import { makeNoFeeCoinConfig } from "../harness/denomStructures.js"; -import { -  BankService, -  ExchangeService, -  GlobalTestState, -  MerchantPrivateApi, -  MerchantService, -  setupDb, -  WalletCli, -  getPayto, -} from "../harness/harness.js"; -import { startWithdrawViaBank, withdrawViaBank } from "../harness/helpers.js"; - -async function applyTimeTravel( -  timetravelDuration: Duration, -  s: { -    exchange?: ExchangeService; -    merchant?: MerchantService; -    wallet?: WalletCli; -  }, -): Promise<void> { -  if (s.exchange) { -    await s.exchange.stop(); -    s.exchange.setTimetravel(timetravelDuration); -    await s.exchange.start(); -    await s.exchange.pingUntilAvailable(); -  } - -  if (s.merchant) { -    await s.merchant.stop(); -    s.merchant.setTimetravel(timetravelDuration); -    await s.merchant.start(); -    await s.merchant.pingUntilAvailable(); -  } - -  if (s.wallet) { -    console.log("setting wallet time travel to", timetravelDuration); -    s.wallet.setTimetravel(timetravelDuration); -  } -} - -const http = new NodeHttpLib(); - -/** - * Basic time travel test. - */ -export async function runExchangeTimetravelTest(t: GlobalTestState) { -  // Set up test environment - -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); -  exchange.addBankAccount("1", exchangeBankAccount); - -  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  exchange.addCoinConfigList(makeNoFeeCoinConfig("TESTKUDOS")); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  await merchant.addInstance({ -    id: "minst1", -    name: "minst1", -    paytoUris: [getPayto("minst1")], -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" }); - -  const keysResp1 = await http.get(exchange.baseUrl + "keys"); -  const keys1 = await readSuccessResponseJsonOrThrow( -    keysResp1, -    codecForExchangeKeysJson(), -  ); -  console.log( -    "keys 1 (before time travel):", -    JSON.stringify(keys1, undefined, 2), -  ); - -  // Travel into the future, the deposit expiration is two years -  // into the future. -  console.log("applying first time travel"); -  await applyTimeTravel(durationFromSpec({ days: 400 }), { -    wallet, -    exchange, -    merchant, -  }); - -  const keysResp2 = await http.get(exchange.baseUrl + "keys"); -  const keys2 = await readSuccessResponseJsonOrThrow( -    keysResp2, -    codecForExchangeKeysJson(), -  ); -  console.log( -    "keys 2 (after time travel):", -    JSON.stringify(keys2, undefined, 2), -  ); - -  const denomPubs1 = keys1.denoms.map((x) => { -    return { -      denomPub: x.denom_pub, -      expireDeposit: AbsoluteTime.stringify( -        AbsoluteTime.fromTimestamp(x.stamp_expire_deposit), -      ), -    }; -  }); - -  const denomPubs2 = keys2.denoms.map((x) => { -    return { -      denomPub: x.denom_pub, -      expireDeposit: AbsoluteTime.stringify( -        AbsoluteTime.fromTimestamp(x.stamp_expire_deposit), -      ), -    }; -  }); -  const dps2 = new Set(denomPubs2.map((x) => x.denomPub)); - -  console.log("=== KEYS RESPONSE 1 ==="); - -  console.log( -    "list issue date", -    AbsoluteTime.stringify(AbsoluteTime.fromTimestamp(keys1.list_issue_date)), -  ); -  console.log("num denoms", keys1.denoms.length); -  console.log("denoms", JSON.stringify(denomPubs1, undefined, 2)); - -  console.log("=== KEYS RESPONSE 2 ==="); - -  console.log( -    "list issue date", -    AbsoluteTime.stringify(AbsoluteTime.fromTimestamp(keys2.list_issue_date)), -  ); -  console.log("num denoms", keys2.denoms.length); -  console.log("denoms", JSON.stringify(denomPubs2, undefined, 2)); - -  for (const da of denomPubs1) { -    let found = false; -    for (const db of denomPubs2) { -      const d1 = da.denomPub; -      const d2 = db.denomPub; -      if (DenominationPubKey.cmp(d1, d2) === 0) { -        found = true; -        break; -      } -    } -    if (!found) { -      console.log("=== ERROR ==="); -      console.log( -        `denomination with public key ${da.denomPub} is not present in new /keys response`, -      ); -      console.log( -        `the new /keys response was issued ${AbsoluteTime.stringify( -          AbsoluteTime.fromTimestamp(keys2.list_issue_date), -        )}`, -      ); -      console.log( -        `however, the missing denomination has stamp_expire_deposit ${da.expireDeposit}`, -      ); -      console.log("see above for the verbatim /keys responses"); -      t.assertTrue(false); -    } -  } -} - -runExchangeTimetravelTest.suites = ["exchange"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-fee-regression.ts b/packages/taler-wallet-cli/src/integrationtests/test-fee-regression.ts deleted file mode 100644 index 8c5a5bea4..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-fee-regression.ts +++ /dev/null @@ -1,200 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { -  GlobalTestState, -  BankService, -  ExchangeService, -  MerchantService, -  setupDb, -  WalletCli, -  getPayto, -} from "../harness/harness.js"; -import { -  withdrawViaBank, -  makeTestPayment, -  SimpleTestEnvironment, -} from "../harness/helpers.js"; - -/** - * Run a test case with a simple TESTKUDOS Taler environment, consisting - * of one exchange, one bank and one merchant. - */ -export async function createMyTestkudosEnvironment( -  t: GlobalTestState, -): Promise<SimpleTestEnvironment> { -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); -  exchange.addBankAccount("1", exchangeBankAccount); - -  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  const coinCommon = { -    cipher: "RSA" as const, -    durationLegal: "3 years", -    durationSpend: "2 years", -    durationWithdraw: "7 days", -    rsaKeySize: 1024, -    feeDeposit: "TESTKUDOS:0.0025", -    feeWithdraw: "TESTKUDOS:0", -    feeRefresh: "TESTKUDOS:0", -    feeRefund: "TESTKUDOS:0", -  }; - -  exchange.addCoinConfigList([ -    { -      ...coinCommon, -      name: "c1", -      value: "TESTKUDOS:1.28", -    }, -    { -      ...coinCommon, -      name: "c2", -      value: "TESTKUDOS:0.64", -    }, -    { -      ...coinCommon, -      name: "c3", -      value: "TESTKUDOS:0.32", -    }, -    { -      ...coinCommon, -      name: "c4", -      value: "TESTKUDOS:0.16", -    }, -    { -      ...coinCommon, -      name: "c5", -      value: "TESTKUDOS:0.08", -    }, -    { -      ...coinCommon, -      name: "c5", -      value: "TESTKUDOS:0.04", -    }, -    { -      ...coinCommon, -      name: "c6", -      value: "TESTKUDOS:0.02", -    }, -    { -      ...coinCommon, -      name: "c7", -      value: "TESTKUDOS:0.01", -    }, -  ]); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addDefaultInstance(); -  await merchant.addInstance({ -    id: "minst1", -    name: "minst1", -    paytoUris: [getPayto("minst1")], -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  return { -    commonDb: db, -    exchange, -    merchant, -    wallet, -    bank, -    exchangeBankAccount, -  }; -} - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runFeeRegressionTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createMyTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { -    wallet, -    bank, -    exchange, -    amount: "TESTKUDOS:1.92", -  }); - -  const coins = await wallet.client.call(WalletApiOperation.DumpCoins, {}); - -  // Make sure we really withdraw one 0.64 and one 1.28 coin. -  t.assertTrue(coins.coins.length === 2); - -  const order = { -    summary: "Buy me!", -    amount: "TESTKUDOS:1.30", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  await makeTestPayment(t, { wallet, merchant, order }); - -  await wallet.runUntilDone(); - -  const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {}); -  t.assertAmountEquals(txs.transactions[1].amountEffective, "TESTKUDOS:1.30"); -  console.log(txs); -} - -runFeeRegressionTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-forced-selection.ts b/packages/taler-wallet-cli/src/integrationtests/test-forced-selection.ts deleted file mode 100644 index 91be11a82..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-forced-selection.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { 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"; - -/** - * Run test for forced denom/coin selection. - */ -export async function runForcedSelectionTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  await wallet.client.call(WalletApiOperation.AddExchange, { -    exchangeBaseUrl: exchange.baseUrl, -  }); - -  await wallet.client.call(WalletApiOperation.WithdrawTestBalance, { -    exchangeBaseUrl: exchange.baseUrl, -    amount: "TESTKUDOS:10", -    bankBaseUrl: bank.baseUrl, -    bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl, -    forcedDenomSel: { -      denoms: [ -        { -          value: "TESTKUDOS:2", -          count: 3, -        }, -      ], -    }, -  }); - -  await wallet.runUntilDone(); - -  const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {}); -  console.log(coinDump); -  t.assertDeepEqual(coinDump.coins.length, 3); - -  const payResp = await wallet.client.call(WalletApiOperation.TestPay, { -    amount: "TESTKUDOS:3", -    merchantBaseUrl: merchant.makeInstanceBaseUrl(), -    summary: "bla", -    forcedCoinSel: { -      coins: [ -        { -          value: "TESTKUDOS:2", -          contribution: "TESTKUDOS:1", -        }, -        { -          value: "TESTKUDOS:2", -          contribution: "TESTKUDOS:1", -        }, -        { -          value: "TESTKUDOS:2", -          contribution: "TESTKUDOS:1", -        }, -      ], -    }, -  }); - -  console.log(j2s(payResp)); - -  // Without forced selection, we would only use 2 coins. -  t.assertDeepEqual(payResp.payCoinSelection.coinContributions.length, 3); -} - -runForcedSelectionTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankaccount.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankaccount.ts deleted file mode 100644 index c3cbc0608..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankaccount.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  NexusUserBundle, -  LibeufinNexusApi, -  LibeufinNexusService, -  LibeufinSandboxService, -  LibeufinSandboxApi, -  findNexusPayment, -} from "../harness/libeufin.js"; - -/** - * Run basic test with LibEuFin. - */ -export async function runLibeufinApiBankaccountTest(t: GlobalTestState) { -  const nexus = await LibeufinNexusService.create(t, { -    httpPort: 5011, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -  }); -  await nexus.start(); -  await nexus.pingUntilAvailable(); - -  await LibeufinNexusApi.createUser(nexus, { -    username: "one", -    password: "testing-the-bankaccount-api", -  }); -  const sandbox = await LibeufinSandboxService.create(t, { -    httpPort: 5012, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, -  }); -  await sandbox.start(); -  await sandbox.pingUntilAvailable(); -  await LibeufinSandboxApi.createEbicsHost(sandbox, "mock"); -  await LibeufinSandboxApi.createDemobankAccount( -    "mock", -    "password-unused", -    { baseUrl: sandbox.baseUrl + "/demobanks/default/access-api/" }, -    "DE71500105179674997361" -  ); -  await LibeufinSandboxApi.createDemobankEbicsSubscriber( -    { -      hostID: "mock", -      partnerID: "mock", -      userID: "mock", -    }, -    "mock", -    { baseUrl: sandbox.baseUrl + "/demobanks/default/" } -  ); -  await LibeufinNexusApi.createEbicsBankConnection(nexus, { -    name: "bankaccount-api-test-connection", -    ebicsURL: "http://localhost:5012/ebicsweb", -    hostID: "mock", -    userID: "mock", -    partnerID: "mock", -  }); -  await LibeufinNexusApi.connectBankConnection( -    nexus, -    "bankaccount-api-test-connection", -  ); -  await LibeufinNexusApi.fetchAccounts( -    nexus, -    "bankaccount-api-test-connection", -  ); - -  await LibeufinNexusApi.importConnectionAccount( -    nexus, -    "bankaccount-api-test-connection", -    "mock", -    "local-mock", -  ); -  await LibeufinSandboxApi.simulateIncomingTransaction( -    sandbox, -    "mock", // creditor bankaccount label -    { -      debtorIban: "DE84500105176881385584", -      debtorBic: "BELADEBEXXX", -      debtorName: "mock2", -      amount: "1", -      subject: "mock subject", -    }, -  ); -  await LibeufinNexusApi.fetchTransactions(nexus, "local-mock"); -  let transactions = await LibeufinNexusApi.getAccountTransactions( -    nexus, -    "local-mock", -  ); -  let el = findNexusPayment("mock subject", transactions.data); -  t.assertTrue(el instanceof Object); -} - -runLibeufinApiBankaccountTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankconnection.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankconnection.ts deleted file mode 100644 index 912b7b2ac..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankconnection.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { LibeufinNexusApi, LibeufinNexusService } from "../harness/libeufin.js"; - -/** - * Run basic test with LibEuFin. - */ -export async function runLibeufinApiBankconnectionTest(t: GlobalTestState) { -  const nexus = await LibeufinNexusService.create(t, { -    httpPort: 5011, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -  }); -  await nexus.start(); -  await nexus.pingUntilAvailable(); - -  await LibeufinNexusApi.createUser(nexus, { -    username: "one", -    password: "testing-the-bankconnection-api", -  }); - -  await LibeufinNexusApi.createEbicsBankConnection(nexus, { -    name: "bankconnection-api-test-connection", -    ebicsURL: "http://localhost:5012/ebicsweb", -    hostID: "mock", -    userID: "mock", -    partnerID: "mock", -  }); - -  let connections = await LibeufinNexusApi.getAllConnections(nexus); -  t.assertTrue(connections.data["bankConnections"].length == 1); - -  await LibeufinNexusApi.deleteBankConnection(nexus, { -    bankConnectionId: "bankconnection-api-test-connection", -  }); -  connections = await LibeufinNexusApi.getAllConnections(nexus); -  t.assertTrue(connections.data["bankConnections"].length == 0); -} -runLibeufinApiBankconnectionTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade-bad-request.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade-bad-request.ts deleted file mode 100644 index a1da9e0da..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade-bad-request.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { URL } from "@gnu-taler/taler-util"; -import axiosImp from "axios"; -import { GlobalTestState } from "../harness/harness.js"; -import { -  launchLibeufinServices, -  NexusUserBundle, -  SandboxUserBundle, -} from "../harness/libeufin.js"; - -const axios = axiosImp.default; - -export async function runLibeufinApiFacadeBadRequestTest(t: GlobalTestState) { -  /** -   * User saltetd "01" -   */ -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); -  const user01sandbox = new SandboxUserBundle("01"); - -  /** -   * Launch Sandbox and Nexus. -   */ -  const libeufinServices = await launchLibeufinServices( -    t, -    [user01nexus], -    [user01sandbox], -    ["twg"], -  ); -  console.log("malformed facade"); -  const baseUrl = libeufinServices.libeufinNexus.baseUrl; -  let url = new URL("facades", baseUrl); -  let resp = await axios.post( -    url.href, -    { -      name: "malformed-facade", -      type: "taler-wire-gateway", -      config: {}, // malformation here. -    }, -    { -      auth: { -        username: "admin", -        password: "test", -      }, -      validateStatus: () => true, -    }, -  ); -  t.assertTrue(resp.status == 400); -} - -runLibeufinApiFacadeBadRequestTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade.ts deleted file mode 100644 index 946c565d4..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  SandboxUserBundle, -  NexusUserBundle, -  launchLibeufinServices, -  LibeufinNexusApi, -} from "../harness/libeufin.js"; - -/** - * Run basic test with LibEuFin. - */ -export async function runLibeufinApiFacadeTest(t: GlobalTestState) { -  /** -   * User saltetd "01" -   */ -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); -  const user01sandbox = new SandboxUserBundle("01"); - -  /** -   * Launch Sandbox and Nexus. -   */ -  const libeufinServices = await launchLibeufinServices( -    t, -    [user01nexus], -    [user01sandbox], -    ["twg"], -  ); -  let resp = await LibeufinNexusApi.getAllFacades( -    libeufinServices.libeufinNexus, -  ); -  // check that original facade shows up. -  t.assertTrue(resp.data["facades"][0]["name"] == user01nexus.twgReq["name"]); - -  const twgBaseUrl: string = resp.data["facades"][0]["baseUrl"]; -  t.assertTrue(typeof twgBaseUrl === "string"); -  t.assertTrue(twgBaseUrl.startsWith("http://")); -  t.assertTrue(twgBaseUrl.endsWith("/")); - -  // delete it. -  resp = await LibeufinNexusApi.deleteFacade( -    libeufinServices.libeufinNexus, -    user01nexus.twgReq["name"], -  ); -  // check that no facades show up. -  t.assertTrue(!resp.data.hasOwnProperty("facades")); -} - -runLibeufinApiFacadeTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-permissions.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-permissions.ts deleted file mode 100644 index f8f2d7d80..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-permissions.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  NexusUserBundle, -  LibeufinNexusApi, -  LibeufinNexusService, -} from "../harness/libeufin.js"; - -/** - * Run basic test with LibEuFin. - */ -export async function runLibeufinApiPermissionsTest(t: GlobalTestState) { -  const nexus = await LibeufinNexusService.create(t, { -    httpPort: 5011, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -  }); -  await nexus.start(); -  await nexus.pingUntilAvailable(); - -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); - -  await LibeufinNexusApi.createUser(nexus, user01nexus.userReq); -  await LibeufinNexusApi.postPermission( -    nexus, -    user01nexus.twgTransferPermission, -  ); -  let transferPermission = await LibeufinNexusApi.getAllPermissions(nexus); -  let element = transferPermission.data["permissions"].pop(); -  t.assertTrue( -    element["permissionName"] == "facade.talerwiregateway.transfer" && -      element["subjectId"] == "username-01", -  ); -  let denyTransfer = user01nexus.twgTransferPermission; - -  // Now revoke permission. -  denyTransfer["action"] = "revoke"; -  await LibeufinNexusApi.postPermission(nexus, denyTransfer); - -  transferPermission = await LibeufinNexusApi.getAllPermissions(nexus); -  t.assertTrue(transferPermission.data["permissions"].length == 0); -} - -runLibeufinApiPermissionsTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-camt.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-camt.ts deleted file mode 100644 index cb85c1ffc..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-camt.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  LibeufinSandboxApi, -  LibeufinSandboxService, -} from "../harness/libeufin.js"; - -// This test only checks that LibEuFin doesn't fail when -// it generates Camt statements - no assertions take place. -// Furthermore, it prints the Camt.053 being generated. -export async function runLibeufinApiSandboxCamtTest(t: GlobalTestState) { -  const sandbox = await LibeufinSandboxService.create(t, { -    httpPort: 5012, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, -  }); -  await sandbox.start(); -  await sandbox.pingUntilAvailable(); -   -  await LibeufinSandboxApi.createDemobankAccount( -    "mock-account-0", -    "password-unused", -    { baseUrl: sandbox.baseUrl + "/demobanks/default/access-api/" } -  ); -  await LibeufinSandboxApi.createDemobankAccount( -    "mock-account-1", -    "password-unused", -    { baseUrl: sandbox.baseUrl + "/demobanks/default/access-api/" } -  ); -  await sandbox.makeTransaction( -    "mock-account-0", -    "mock-account-1", -    "EUR:1", -    "+1", -  ); -  await sandbox.makeTransaction( -    "mock-account-0", -    "mock-account-1", -    "EUR:1", -    "+1", -  ); -  await sandbox.makeTransaction( -    "mock-account-0", -    "mock-account-1", -    "EUR:1", -    "+1", -  ); -  await sandbox.makeTransaction( -    "mock-account-1", -    "mock-account-0", -    "EUR:5", -    "minus 5", -  ); -  await sandbox.c53tick(); -  let ret = await LibeufinSandboxApi.getCamt053(sandbox, "mock-account-1"); -  console.log(ret); -} -runLibeufinApiSandboxCamtTest.excludeByDefault = true; -runLibeufinApiSandboxCamtTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-transactions.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-transactions.ts deleted file mode 100644 index 24fd9d3ef..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-transactions.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  LibeufinSandboxApi, -  LibeufinSandboxService, -} from "../harness/libeufin.js"; - -export async function runLibeufinApiSandboxTransactionsTest( -  t: GlobalTestState, -) { -  const sandbox = await LibeufinSandboxService.create(t, { -    httpPort: 5012, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, -  }); -  await sandbox.start(); -  await sandbox.pingUntilAvailable(); -  await LibeufinSandboxApi.createDemobankAccount( -    "mock-account", -    "password-unused", -    { baseUrl: sandbox.baseUrl + "/demobanks/default/access-api/" }, -    "DE71500105179674997361" -  ); -  await LibeufinSandboxApi.simulateIncomingTransaction( -    sandbox, -    "mock-account", -    { -      debtorIban: "DE84500105176881385584", -      debtorBic: "BELADEBEXXX", -      debtorName: "mock2", -      subject: "mock subject", -      amount: "1", // EUR is default. -    }, -  ); -  await LibeufinSandboxApi.simulateIncomingTransaction( -    sandbox, -    "mock-account", -    { -      debtorIban: "DE84500105176881385584", -      debtorBic: "BELADEBEXXX", -      debtorName: "mock2", -      subject: "mock subject 2", -      amount: "1.1", // EUR is default. -    }, -  ); -  let ret = await LibeufinSandboxApi.getAccountInfoWithBalance( -    sandbox, -    "mock-account", -  ); -  t.assertAmountEquals(ret.data.balance, "EUR:2.1"); -} -runLibeufinApiSandboxTransactionsTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-scheduling.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-scheduling.ts deleted file mode 100644 index 95f4bfaa0..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-scheduling.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  launchLibeufinServices, -  LibeufinNexusApi, -  LibeufinNexusService, -  NexusUserBundle, -  SandboxUserBundle, -} from "../harness/libeufin.js"; - -/** - * Test Nexus scheduling API.  It creates a task, check whether it shows - * up, then deletes it, and check if it's gone.  Ideally, a check over the - * _liveliness_ of a scheduled task should happen. - */ -export async function runLibeufinApiSchedulingTest(t: GlobalTestState) { -  const nexus = await LibeufinNexusService.create(t, { -    httpPort: 5011, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -  }); -  await nexus.start(); -  await nexus.pingUntilAvailable(); - -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); -  const user01sandbox = new SandboxUserBundle("01"); -  await launchLibeufinServices(t, [user01nexus], [user01sandbox]); -  await LibeufinNexusApi.postTask(nexus, user01nexus.localAccountName, { -    name: "test-task", -    cronspec: "* * *", -    type: "fetch", -    params: { -      level: "all", -      rangeType: "all", -    }, -  }); -  let resp = await LibeufinNexusApi.getTasks( -    nexus, -    user01nexus.localAccountName, -    "test-task", -  ); -  t.assertTrue(resp.data["taskName"] == "test-task"); -  await LibeufinNexusApi.deleteTask( -    nexus, -    user01nexus.localAccountName, -    "test-task", -  ); -  try { -    await LibeufinNexusApi.getTasks( -      nexus, -      user01nexus.localAccountName, -      "test-task", -    ); -  } catch (err: any) { -    t.assertTrue(err.response.status == 404); -  } - -  // Same with submit task. -  await LibeufinNexusApi.postTask(nexus, user01nexus.localAccountName, { -    name: "test-task", -    cronspec: "* * *", -    type: "submit", -    params: {}, -  }); -  resp = await LibeufinNexusApi.getTasks( -    nexus, -    user01nexus.localAccountName, -    "test-task", -  ); -  t.assertTrue(resp.data["taskName"] == "test-task"); -  await LibeufinNexusApi.deleteTask( -    nexus, -    user01nexus.localAccountName, -    "test-task", -  ); -  try { -    await LibeufinNexusApi.getTasks( -      nexus, -      user01nexus.localAccountName, -      "test-task", -    ); -  } catch (err: any) { -    t.assertTrue(err.response.status == 404); -  } -} -runLibeufinApiSchedulingTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-users.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-users.ts deleted file mode 100644 index bc3103c7e..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-users.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { LibeufinNexusApi, LibeufinNexusService } from "../harness/libeufin.js"; - -/** - * Run basic test with LibEuFin. - */ -export async function runLibeufinApiUsersTest(t: GlobalTestState) { -  const nexus = await LibeufinNexusService.create(t, { -    httpPort: 5011, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -  }); -  await nexus.start(); -  await nexus.pingUntilAvailable(); - -  await LibeufinNexusApi.createUser(nexus, { -    username: "one", -    password: "will-be-changed", -  }); - -  await LibeufinNexusApi.changePassword( -    nexus, -    "one", -    { -      newPassword: "got-changed", -    }, -    { -      auth: { -        username: "admin", -        password: "test", -      }, -    }, -  ); - -  let resp = await LibeufinNexusApi.getUser(nexus, { -    auth: { -      username: "one", -      password: "got-changed", -    }, -  }); -  console.log(resp.data); -  t.assertTrue(resp.data["username"] == "one" && !resp.data["superuser"]); -} - -runLibeufinApiUsersTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-bad-gateway.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-bad-gateway.ts deleted file mode 100644 index 53aacca84..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-bad-gateway.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState, delayMs } from "../harness/harness.js"; -import { -  NexusUserBundle, -  LibeufinNexusApi, -  LibeufinNexusService, -  LibeufinSandboxService, -} from "../harness/libeufin.js"; - -/** - * Testing how Nexus reacts when the Sandbox is unreachable. - * Typically, because the user specified a wrong EBICS endpoint. - */ -export async function runLibeufinBadGatewayTest(t: GlobalTestState) { -  /** -   * User saltetd "01" -   */ -  const user01nexus = new NexusUserBundle( -    "01", "http://localhost:5010/not-found", // the EBICS endpoint at Sandbox -  ); - -  // Start Nexus -  const libeufinNexus = await LibeufinNexusService.create(t, { -    httpPort: 5011, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -  }); -  await libeufinNexus.start(); -  await libeufinNexus.pingUntilAvailable(); - -  // Start Sandbox -  const libeufinSandbox = await LibeufinSandboxService.create(t, { -    httpPort: 5010, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, -  }); -  await libeufinSandbox.start(); -  await libeufinSandbox.pingUntilAvailable(); -   -  // Connecting to a non-existent Sandbox endpoint. -  await LibeufinNexusApi.createEbicsBankConnection( -    libeufinNexus, -    user01nexus.connReq -  ); - -  // 502 Bad Gateway expected. -  try { -    await LibeufinNexusApi.connectBankConnection( -      libeufinNexus, -      user01nexus.connReq.name, -    ); -  } catch(e: any) { -    t.assertTrue(e.response.status == 502); -    return; -  } -  t.assertTrue(false); -} -runLibeufinBadGatewayTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts deleted file mode 100644 index 94fd76683..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts +++ /dev/null @@ -1,308 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { AbsoluteTime, MerchantContractTerms, Duration } from "@gnu-taler/taler-util"; -import { -  WalletApiOperation, -  HarnessExchangeBankAccount, -} from "@gnu-taler/taler-wallet-core"; -import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; -import { -  DbInfo, -  ExchangeService, -  GlobalTestState, -  MerchantService, -  setupDb, -  WalletCli, -} from "../harness/harness.js"; -import { makeTestPayment } from "../harness/helpers.js"; -import { -  LibeufinNexusApi, -  LibeufinNexusService, -  LibeufinSandboxApi, -  LibeufinSandboxService, -} from "../harness/libeufin.js"; - -const exchangeIban = "DE71500105179674997361"; -const customerIban = "DE84500105176881385584"; -const customerBic = "BELADEBEXXX"; -const merchantIban = "DE42500105171245624648"; - -export interface LibeufinTestEnvironment { -  commonDb: DbInfo; -  exchange: ExchangeService; -  exchangeBankAccount: HarnessExchangeBankAccount; -  merchant: MerchantService; -  wallet: WalletCli; -  libeufinSandbox: LibeufinSandboxService; -  libeufinNexus: LibeufinNexusService; -} - -/** - * Create a Taler environment with LibEuFin and an EBICS account. - */ -export async function createLibeufinTestEnvironment( -  t: GlobalTestState, -  coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("EUR")), -): Promise<LibeufinTestEnvironment> { -  const db = await setupDb(t); - -  const libeufinSandbox = await LibeufinSandboxService.create(t, { -    httpPort: 5010, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, -  }); - -  await libeufinSandbox.start(); -  await libeufinSandbox.pingUntilAvailable(); - -  const libeufinNexus = await LibeufinNexusService.create(t, { -    httpPort: 5011, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -  }); - -  await libeufinNexus.start(); -  await libeufinNexus.pingUntilAvailable(); - -  await LibeufinSandboxApi.createEbicsHost(libeufinSandbox, "host01"); -  // Subscriber and bank Account for the exchange -  await LibeufinSandboxApi.createDemobankAccount( -    "exchangeacct", -    "password-unused", -    { baseUrl: libeufinSandbox.baseUrl + "/demobanks/default/access-api/" }, -    exchangeIban -  ); -  await LibeufinSandboxApi.createDemobankEbicsSubscriber( -    { -      hostID: "host01", -      partnerID: "partner01", -      userID: "user01", -    }, -    "exchangeacct", -    { baseUrl: libeufinSandbox.baseUrl + "/demobanks/default/" } -  ); - -  await LibeufinSandboxApi.createDemobankAccount( -    "merchantacct", -    "password-unused", -    { baseUrl: libeufinSandbox.baseUrl + "/demobanks/default/access-api/" }, -    merchantIban -  ); -  await LibeufinSandboxApi.createDemobankEbicsSubscriber( -    { -      hostID: "host01", -      partnerID: "partner02", -      userID: "user02", -    }, -    "merchantacct", -    { baseUrl: libeufinSandbox.baseUrl + "/demobanks/default/" }, -  ); - -  await LibeufinNexusApi.createEbicsBankConnection(libeufinNexus, { -    name: "myconn", -    ebicsURL: "http://localhost:5010/ebicsweb", -    hostID: "host01", -    partnerID: "partner01", -    userID: "user01", -  }); -  await LibeufinNexusApi.connectBankConnection(libeufinNexus, "myconn"); -  await LibeufinNexusApi.fetchAccounts(libeufinNexus, "myconn"); -  await LibeufinNexusApi.importConnectionAccount( -    libeufinNexus, -    "myconn", -    "exchangeacct", -    "myacct", -  ); - -  await LibeufinNexusApi.createTwgFacade(libeufinNexus, { -    name: "twg1", -    accountName: "myacct", -    connectionName: "myconn", -    currency: "EUR", -    reserveTransferLevel: "report", -  }); - -  await LibeufinNexusApi.createUser(libeufinNexus, { -    username: "twguser", -    password: "twgpw", -  }); - -  await LibeufinNexusApi.postPermission(libeufinNexus, { -    action: "grant", -    permission: { -      subjectType: "user", -      subjectId: "twguser", -      resourceType: "facade", -      resourceId: "twg1", -      permissionName: "facade.talerWireGateway.history", -    }, -  }); - -  await LibeufinNexusApi.postPermission(libeufinNexus, { -    action: "grant", -    permission: { -      subjectType: "user", -      subjectId: "twguser", -      resourceType: "facade", -      resourceId: "twg1", -      permissionName: "facade.talerWireGateway.transfer", -    }, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "EUR", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "EUR", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const exchangeBankAccount: HarnessExchangeBankAccount = { -    accountName: "twguser", -    accountPassword: "twgpw", -    accountPaytoUri: `payto://iban/${exchangeIban}?receiver-name=Exchange`, -    wireGatewayApiBaseUrl: -      "http://localhost:5011/facades/twg1/taler-wire-gateway/", -  }; - -  exchange.addBankAccount("1", exchangeBankAccount); - -  exchange.addCoinConfigList(coinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [`payto://iban/${merchantIban}?receiver-name=Merchant`], -    defaultWireTransferDelay: Duration.toTalerProtocolDuration( -      Duration.getZero(), -    ), -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  return { -    commonDb: db, -    exchange, -    merchant, -    wallet, -    exchangeBankAccount, -    libeufinNexus, -    libeufinSandbox, -  }; -} - -/** - * Run basic test with LibEuFin. - */ -export async function runLibeufinBasicTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, exchange, merchant, libeufinSandbox, libeufinNexus } = -    await createLibeufinTestEnvironment(t); - -  await wallet.client.call(WalletApiOperation.AddExchange, { -    exchangeBaseUrl: exchange.baseUrl, -  }); - -  const wr = await wallet.client.call( -    WalletApiOperation.AcceptManualWithdrawal, -    { -      exchangeBaseUrl: exchange.baseUrl, -      amount: "EUR:15", -    }, -  ); - -  const reservePub: string = wr.reservePub; - -  await LibeufinSandboxApi.simulateIncomingTransaction( -    libeufinSandbox, -    "exchangeacct", -    { -      amount: "15.00", -      debtorBic: customerBic, -      debtorIban: customerIban, -      debtorName: "Jane Customer", -      subject: `Taler Top-up ${reservePub}`, -    }, -  ); - -  await LibeufinNexusApi.fetchTransactions(libeufinNexus, "myacct"); - -  await exchange.runWirewatchOnce(); - -  await wallet.runUntilDone(); - -  const bal = await wallet.client.call(WalletApiOperation.GetBalances, {}); -  console.log("balances", JSON.stringify(bal, undefined, 2)); -  t.assertAmountEquals(bal.balances[0].available, "EUR:14.7"); - -  const order: Partial<MerchantContractTerms> = { -    summary: "Buy me!", -    amount: "EUR:5", -    fulfillment_url: "taler://fulfillment-success/thx", -    wire_transfer_deadline: AbsoluteTime.toTimestamp(AbsoluteTime.now()), -  }; - -  await makeTestPayment(t, { wallet, merchant, order }); - -  await exchange.runAggregatorOnce(); -  await exchange.runTransferOnce(); - -  await LibeufinNexusApi.submitAllPaymentInitiations(libeufinNexus, "myacct"); - -  const exchangeTransactions = await LibeufinSandboxApi.getAccountTransactions( -    libeufinSandbox, -    "exchangeacct", -  ); - -  console.log( -    "exchange transactions:", -    JSON.stringify(exchangeTransactions, undefined, 2), -  ); - -  t.assertDeepEqual( -    exchangeTransactions.payments[0].creditDebitIndicator, -    "credit", -  ); -  t.assertDeepEqual( -    exchangeTransactions.payments[1].creditDebitIndicator, -    "debit", -  ); -  t.assertDeepEqual(exchangeTransactions.payments[1].debtorIban, exchangeIban); -  t.assertDeepEqual( -    exchangeTransactions.payments[1].creditorIban, -    merchantIban, -  ); -} -runLibeufinBasicTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-c5x.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-c5x.ts deleted file mode 100644 index 2ba29656a..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-c5x.ts +++ /dev/null @@ -1,147 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  launchLibeufinServices, -  LibeufinNexusApi, -  NexusUserBundle, -  SandboxUserBundle, -} from "../harness/libeufin.js"; - -/** - * This test checks how the C52 and C53 coordinate.  It'll test - * whether fresh transactions stop showing as C52 after they get - * included in a bank statement. - */ -export async function runLibeufinC5xTest(t: GlobalTestState) { -  /** -   * User saltetd "01" -   */ -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); -  const user01sandbox = new SandboxUserBundle("01"); - -  /** -   * User saltetd "02". -   */ -  const user02nexus = new NexusUserBundle( -    "02", -    "http://localhost:5010/ebicsweb", -  ); -  const user02sandbox = new SandboxUserBundle("02"); - -  /** -   * Launch Sandbox and Nexus. -   */ -  const libeufinServices = await launchLibeufinServices( -    t, -    [user01nexus, user02nexus], -    [user01sandbox, user02sandbox], -    ["twg"], -  ); - -  // Check that C52 and C53 have zero entries. - -  // C52 -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    "all", // range -    "report", // level -  ); -  // C53 -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    "all", // range -    "statement", // level -  ); -  const nexusTxs = await LibeufinNexusApi.getAccountTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); -  t.assertTrue(nexusTxs.data["transactions"].length == 0); - -  // Addressing one payment to user 01 -  await libeufinServices.libeufinSandbox.makeTransaction( -    user02sandbox.ebicsBankAccount.label, // debit -    user01sandbox.ebicsBankAccount.label, // credit -    "EUR:10", -    "first payment", -  ); - -  let expectOne = await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    "all", // range -    "report", // C52 -  ); -  t.assertTrue(expectOne.data.newTransactions == 1); -  t.assertTrue(expectOne.data.downloadedTransactions == 1); - -  /* Expect zero payments being downloaded because the -   * previous request consumed already the one pending -   * payment. -   */ -  let expectZero = await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    "all", // range -    "report", // C52 -  ); -  t.assertTrue(expectZero.data.newTransactions == 0); -  t.assertTrue(expectZero.data.downloadedTransactions == 0); - -  /** -   * A statement should still account zero payments because -   * so far the payment made before is still pending.  -   */ -  expectZero = await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    "all", // range -    "statement", // C53 -  ); -  t.assertTrue(expectZero.data.newTransactions == 0); -  t.assertTrue(expectZero.data.downloadedTransactions == 0); - -  /** -   * Ticking now.  That books any pending transaction. -   */ -  await libeufinServices.libeufinSandbox.c53tick(); - -  /** -   * A statement is now expected to download the transaction, -   * although that got already ingested along the report -   * earlier.  Thus the transaction counts as downloaded but -   * not as new. -   */ -  expectOne = await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    "all", // range -    "statement", // C53 -  ); -  t.assertTrue(expectOne.data.downloadedTransactions == 1); -  t.assertTrue(expectOne.data.newTransactions == 0); -} -runLibeufinC5xTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-facade-anastasis.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-facade-anastasis.ts deleted file mode 100644 index 1ed258c3a..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-facade-anastasis.ts +++ /dev/null @@ -1,169 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  SandboxUserBundle, -  NexusUserBundle, -  launchLibeufinServices, -  LibeufinNexusApi, -  LibeufinSandboxApi, -} from "../harness/libeufin.js"; - -/** - * Testing the Anastasis API, offered by the Anastasis facade. - */ -export async function runLibeufinAnastasisFacadeTest(t: GlobalTestState) { -  /** -   * User saltetd "01" -   */ -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); -  const user01sandbox = new SandboxUserBundle("01"); - -  /** -   * Launch Sandbox and Nexus. -   */ -  const libeufinServices = await launchLibeufinServices( -    t, -    [user01nexus], -    [user01sandbox], -    ["anastasis"], // create only one Anastasis facade. -  ); -  let resp = await LibeufinNexusApi.getAllFacades( -    libeufinServices.libeufinNexus, -  ); -  // check that original facade shows up. -  t.assertTrue(resp.data["facades"][0]["name"] == user01nexus.anastasisReq["name"]); -  const anastasisBaseUrl: string = resp.data["facades"][0]["baseUrl"]; -  t.assertTrue(typeof anastasisBaseUrl === "string"); -  t.assertTrue(anastasisBaseUrl.startsWith("http://")); -  t.assertTrue(anastasisBaseUrl.endsWith("/")); - -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); - -  await LibeufinNexusApi.postPermission( -    libeufinServices.libeufinNexus, { -      action: "grant", -      permission: { -        subjectId: user01nexus.userReq.username, -        subjectType: "user", -        resourceType: "facade", -        resourceId: user01nexus.anastasisReq.name, -        permissionName: "facade.anastasis.history", -      }, -  } -  ); - -  // check if empty. -  let txsEmpty = await LibeufinNexusApi.getAnastasisTransactions( -    libeufinServices.libeufinNexus, -    anastasisBaseUrl, {delta: 5}) - -  t.assertTrue(txsEmpty.data.incoming_transactions.length == 0); - -  LibeufinSandboxApi.simulateIncomingTransaction( -    libeufinServices.libeufinSandbox, -    user01sandbox.ebicsBankAccount.label, -    { -      debtorIban: "ES3314655813489414469157", -      debtorBic: "BCMAESM1XXX", -      debtorName: "Mock Donor", -      subject: "Anastasis donation", -      amount: "3", // Sandbox takes currency from its 'config' -    }, -  ) - -  LibeufinSandboxApi.simulateIncomingTransaction( -    libeufinServices.libeufinSandbox, -    user01sandbox.ebicsBankAccount.label, -    { -      debtorIban: "ES3314655813489414469157", -      debtorBic: "BCMAESM1XXX", -      debtorName: "Mock Donor", -      subject: "another Anastasis donation", -      amount: "1", // Sandbox takes currency from its "config" -    }, -  ) - -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); - -  let txs = await LibeufinNexusApi.getAnastasisTransactions( -    libeufinServices.libeufinNexus, -    anastasisBaseUrl, -    {delta: 5}, -    user01nexus.userReq.username, -    user01nexus.userReq.password, -  ); - -  // check the two payments show up -  let txsList = txs.data.incoming_transactions -  t.assertTrue(txsList.length == 2); -  t.assertTrue([txsList[0].subject, txsList[1].subject].includes("Anastasis donation")); -  t.assertTrue([txsList[0].subject, txsList[1].subject].includes("another Anastasis donation")); -  t.assertTrue(txsList[0].row_id == 1) -  t.assertTrue(txsList[1].row_id == 2) - -  LibeufinSandboxApi.simulateIncomingTransaction( -    libeufinServices.libeufinSandbox, -    user01sandbox.ebicsBankAccount.label, -    { -      debtorIban: "ES3314655813489414469157", -      debtorBic: "BCMAESM1XXX", -      debtorName: "Mock Donor", -      subject: "last Anastasis donation", -      amount: "10.10", // Sandbox takes currency from its "config" -    }, -  ) - -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); - -  let txsLast = await LibeufinNexusApi.getAnastasisTransactions( -    libeufinServices.libeufinNexus, -    anastasisBaseUrl, -    {delta: 5, start: 2}, -    user01nexus.userReq.username, -    user01nexus.userReq.password, -  ); -  console.log(txsLast.data.incoming_transactions[0].subject == "last Anastasis donation"); - -  let txsReverse = await LibeufinNexusApi.getAnastasisTransactions( -    libeufinServices.libeufinNexus, -    anastasisBaseUrl, -    {delta: -5, start: 4}, -    user01nexus.userReq.username, -    user01nexus.userReq.password, -  ); -  t.assertTrue(txsReverse.data.incoming_transactions[0].row_id == 3); -  t.assertTrue(txsReverse.data.incoming_transactions[1].row_id == 2); -  t.assertTrue(txsReverse.data.incoming_transactions[2].row_id == 1); -} - -runLibeufinAnastasisFacadeTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-keyrotation.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-keyrotation.ts deleted file mode 100644 index 21bf07de2..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-keyrotation.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  SandboxUserBundle, -  NexusUserBundle, -  launchLibeufinServices, -  LibeufinSandboxApi, -  LibeufinNexusApi, -} from "../harness/libeufin.js"; - -/** - * Run basic test with LibEuFin. - */ -export async function runLibeufinKeyrotationTest(t: GlobalTestState) { -  /** -   * User saltetd "01" -   */ -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); -  const user01sandbox = new SandboxUserBundle("01"); - -  /** -   * Launch Sandbox and Nexus. -   */ -  const libeufinServices = await launchLibeufinServices( -    t, [user01nexus], [user01sandbox], -  ); - -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); - -  /* Rotate the Sandbox keys, and fetch the transactions again */ -  await LibeufinSandboxApi.rotateKeys( -    libeufinServices.libeufinSandbox, -    user01sandbox.ebicsBankAccount.subscriber.hostID, -  ); - -  try { -    await LibeufinNexusApi.fetchTransactions( -      libeufinServices.libeufinNexus, -      user01nexus.localAccountName, -    ); -  } catch (e: any) { -    /** -     * Asserting that Nexus responded with a 500 Internal server -     * error, because the bank signed the last response with a new -     * key pair that was never downloaded by Nexus. -     * -     * NOTE: the bank accepted the request addressed to the old -     * public key.  Should it in this case reject the request even -     * before trying to verify it? -     */ -    t.assertTrue(e.response.status == 500); -    t.assertTrue(e.response.data.code == 9000); -  } -} -runLibeufinKeyrotationTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts deleted file mode 100644 index 850b0f1d9..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts +++ /dev/null @@ -1,118 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  SandboxUserBundle, -  NexusUserBundle, -  launchLibeufinServices, -  LibeufinNexusApi, -  LibeufinCli -} from "../harness/libeufin.js"; - -/** - * This test checks how the C52 and C53 coordinate.  It'll test - * whether fresh transactions stop showing as C52 after they get - * included in a bank statement. - */ -export async function runLibeufinNexusBalanceTest(t: GlobalTestState) { -  /** -   * User saltetd "01" -   */ -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); -  const user01sandbox = new SandboxUserBundle("01"); - -  /** -   * User saltetd "02". -   */ -  const user02nexus = new NexusUserBundle( -    "02", -    "http://localhost:5010/ebicsweb", -  ); -  const user02sandbox = new SandboxUserBundle("02"); - -  /** -   * Launch Sandbox and Nexus. -   */ -  const libeufinServices = await launchLibeufinServices( -    t, -    [user01nexus, user02nexus], -    [user01sandbox, user02sandbox], -    ["twg"], -  ); - -  // user 01 gets 10 -  await libeufinServices.libeufinSandbox.makeTransaction( -    user02sandbox.ebicsBankAccount.label, // debit -    user01sandbox.ebicsBankAccount.label, // credit -    "EUR:10", -    "first payment", -  ); -  // user 01 gets another 10 -  await libeufinServices.libeufinSandbox.makeTransaction( -    user02sandbox.ebicsBankAccount.label, // debit -    user01sandbox.ebicsBankAccount.label, // credit -    "EUR:10", -    "second payment", -  ); - -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    "all", // range -    "report", // level -  ); - -  // Check that user 01 has 20, via Nexus. -  let accountInfo = await LibeufinNexusApi.getBankAccount( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); -  t.assertAmountEquals(accountInfo.data.lastSeenBalance, "EUR:20"); - -  // Booking the first two transactions. -  await libeufinServices.libeufinSandbox.c53tick(); - -  // user 01 gives 30 -  await libeufinServices.libeufinSandbox.makeTransaction( -    user01sandbox.ebicsBankAccount.label, -    user02sandbox.ebicsBankAccount.label, -    "EUR:30", -    "third payment", -  ); - -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    "all", // range -    "report", // level -  ); - -  let accountInfoDebit = await LibeufinNexusApi.getBankAccount( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); -  t.assertDeepEqual(accountInfoDebit.data.lastSeenBalance, "-EUR:10"); -} - -runLibeufinNexusBalanceTest.suites = ["libeufin"]; -runLibeufinNexusBalanceTest.excludeByDefault = true; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund-multiple-users.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund-multiple-users.ts deleted file mode 100644 index 245f34331..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund-multiple-users.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState, delayMs } from "../harness/harness.js"; -import { -  SandboxUserBundle, -  NexusUserBundle, -  launchLibeufinServices, -  LibeufinSandboxApi, -  LibeufinNexusApi, -} from "../harness/libeufin.js"; - -/** - * User 01 expects a refund from user 02, and expectedly user 03 - * should not be involved in the process. - */ -export async function runLibeufinRefundMultipleUsersTest(t: GlobalTestState) { -  /** -   * User saltetd "01" -   */ -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); -  const user01sandbox = new SandboxUserBundle("01"); - -  /** -   * User saltetd "02" -   */ -  const user02nexus = new NexusUserBundle( -    "02", -    "http://localhost:5010/ebicsweb", -  ); -  const user02sandbox = new SandboxUserBundle("02"); - -  /** -   * User saltetd "03" -   */ -  const user03nexus = new NexusUserBundle( -    "03", -    "http://localhost:5010/ebicsweb", -  ); -  const user03sandbox = new SandboxUserBundle("03"); - -  /** -   * Launch Sandbox and Nexus. -   */ -  const libeufinServices = await launchLibeufinServices( -    t, -    [user01nexus, user02nexus], -    [user01sandbox, user02sandbox], -    ["twg"], -  ); - -  // user 01 gets the payment -  await libeufinServices.libeufinSandbox.makeTransaction( -    user02sandbox.ebicsBankAccount.label, // debit -    user01sandbox.ebicsBankAccount.label, // credit -    "EUR:1", -    "not a public key", -  ); - -  // user 01 fetches the payments -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); - -  // user 01 tries to submit the reimbursement, as -  // the payment didn't have a valid public key in -  // the subject. -  await LibeufinNexusApi.submitInitiatedPayment( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    "1", // so far the only one that can exist. -  ); - -  // user 02 checks whether a reimbursement arrived. -  let history = await LibeufinSandboxApi.getAccountTransactions( -    libeufinServices.libeufinSandbox, -    user02sandbox.ebicsBankAccount["label"], -  ); -  // reimbursement arrived IFF the total payments are 2: -  // 1 the original (faulty) transaction + 1 the reimbursement. -  t.assertTrue(history["payments"].length == 2); -} - -runLibeufinRefundMultipleUsersTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund.ts deleted file mode 100644 index 9d90121a0..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState, delayMs } from "../harness/harness.js"; -import { -  SandboxUserBundle, -  NexusUserBundle, -  launchLibeufinServices, -  LibeufinSandboxApi, -  LibeufinNexusApi, -} from "../harness/libeufin.js"; - -/** - * Run basic test with LibEuFin. - */ -export async function runLibeufinRefundTest(t: GlobalTestState) { -  /** -   * User saltetd "01" -   */ -  const user01nexus = new NexusUserBundle( -    "01", -    "http://localhost:5010/ebicsweb", -  ); -  const user01sandbox = new SandboxUserBundle("01"); - -  /** -   * User saltetd "02" -   */ -  const user02nexus = new NexusUserBundle( -    "02", -    "http://localhost:5010/ebicsweb", -  ); -  const user02sandbox = new SandboxUserBundle("02"); - -  /** -   * Launch Sandbox and Nexus. -   */ -  const libeufinServices = await launchLibeufinServices( -    t, -    [user01nexus, user02nexus], -    [user01sandbox, user02sandbox], -    ["twg"], -  ); - -  // user 02 pays user 01 with a faulty (non Taler) subject. -  await libeufinServices.libeufinSandbox.makeTransaction( -    user02sandbox.ebicsBankAccount.label, // debit -    user01sandbox.ebicsBankAccount.label, // credit -    "EUR:1", -    "not a public key", -  ); - -  // The bad payment should be now ingested and prepared as -  // a reimbursement. -  await LibeufinNexusApi.fetchTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); -  // Check that the payment arrived at the Nexus. -  const nexusTxs = await LibeufinNexusApi.getAccountTransactions( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -  ); -  t.assertTrue(nexusTxs.data["transactions"].length == 1); - -  // Submit the reimbursement -  await LibeufinNexusApi.submitInitiatedPayment( -    libeufinServices.libeufinNexus, -    user01nexus.localAccountName, -    // The initiated payment (= the reimbursement) ID below -    // got set by the Taler facade; at this point only one must -    // exist.  If "1" is not found, a 404 will make this test fail. -    "1", -  ); - -  // user 02 checks whether the reimbursement arrived. -  let history = await LibeufinSandboxApi.getAccountTransactions( -    libeufinServices.libeufinSandbox, -    user02sandbox.ebicsBankAccount["label"], -  ); -  // 2 payments must exist: 1 the original (faulty) payment + -  // 1 the reimbursement. -  t.assertTrue(history["payments"].length == 2); -} -runLibeufinRefundTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-sandbox-wire-transfer-cli.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-sandbox-wire-transfer-cli.ts deleted file mode 100644 index e56cb3d68..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-sandbox-wire-transfer-cli.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  LibeufinSandboxApi, -  LibeufinSandboxService, -} from "../harness/libeufin.js"; - -export async function runLibeufinSandboxWireTransferCliTest( -  t: GlobalTestState, -) { -  const sandbox = await LibeufinSandboxService.create(t, { -    httpPort: 5012, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, -  }); -  await sandbox.start(); -  await sandbox.pingUntilAvailable(); -  await LibeufinSandboxApi.createDemobankAccount( -    "mock-account", -    "password-unused", -    { baseUrl: sandbox.baseUrl + "/demobanks/default/access-api/" }, -    "DE71500105179674997361" -  ); -  await LibeufinSandboxApi.createDemobankAccount( -    "mock-account-2", -    "password-unused", -    { baseUrl: sandbox.baseUrl + "/demobanks/default/access-api/" }, -    "DE71500105179674997364" -  ); - -  await sandbox.makeTransaction( -    "mock-account", -    "mock-account-2", -    "EUR:1", -    "one!", -  ); -  await sandbox.makeTransaction( -    "mock-account", -    "mock-account-2", -    "EUR:1", -    "two!", -  ); -  await sandbox.makeTransaction( -    "mock-account", -    "mock-account-2", -    "EUR:1", -    "three!", -  ); -  await sandbox.makeTransaction( -    "mock-account-2", -    "mock-account", -    "EUR:1", -    "Give one back.", -  ); -  await sandbox.makeTransaction( -    "mock-account-2", -    "mock-account", -    "EUR:0.11", -    "Give fraction back.", -  ); -  let ret = await LibeufinSandboxApi.getAccountInfoWithBalance( -    sandbox, -    "mock-account-2", -  ); -  console.log(ret.data.balance); -  t.assertTrue(ret.data.balance == "EUR:1.89"); -} -runLibeufinSandboxWireTransferCliTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-tutorial.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-tutorial.ts deleted file mode 100644 index 7bc067cfe..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-tutorial.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  LibeufinNexusService, -  LibeufinSandboxService, -  LibeufinCli, -} from "../harness/libeufin.js"; - -/** - * Run basic test with LibEuFin. - */ -export async function runLibeufinTutorialTest(t: GlobalTestState) { -  // Set up test environment - -  const libeufinSandbox = await LibeufinSandboxService.create(t, { -    httpPort: 5010, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, -  }); - -  await libeufinSandbox.start(); -  await libeufinSandbox.pingUntilAvailable(); - -  const libeufinNexus = await LibeufinNexusService.create(t, { -    httpPort: 5011, -    databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -  }); - -  const nexusUser = { username: "foo", password: "secret" }; -  const libeufinCli = new LibeufinCli(t, { -    sandboxUrl: libeufinSandbox.baseUrl, -    nexusUrl: libeufinNexus.baseUrl, -    sandboxDatabaseUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, -    nexusDatabaseUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, -    nexusUser: nexusUser, -  }); - -  const ebicsDetails = { -    hostId: "testhost", -    partnerId: "partner01", -    userId: "user01", -  }; -  const bankAccountDetails = { -    currency: "EUR", -    iban: "DE18500105172929531888", -    bic: "INGDDEFFXXX", -    personName: "Jane Normal", -    accountName: "testacct01", -  }; - -  await libeufinCli.checkSandbox(); -  await libeufinCli.createEbicsHost("testhost"); -  await libeufinCli.createEbicsSubscriber(ebicsDetails); -  await libeufinCli.createEbicsBankAccount(ebicsDetails, bankAccountDetails); -  await libeufinCli.generateTransactions(bankAccountDetails.accountName); - -  await libeufinNexus.start(); -  await libeufinNexus.pingUntilAvailable(); - -  await libeufinNexus.createNexusSuperuser(nexusUser); -  const connectionDetails = { -    subscriberDetails: ebicsDetails, -    ebicsUrl: `${libeufinSandbox.baseUrl}ebicsweb`, // FIXME: need appropriate URL concatenation -    connectionName: "my-ebics-conn", -  }; -  await libeufinCli.createEbicsConnection(connectionDetails); -  await libeufinCli.createBackupFile({ -    passphrase: "secret", -    outputFile: `${t.testDir}/connection-backup.json`, -    connectionName: connectionDetails.connectionName, -  }); -  await libeufinCli.createKeyLetter({ -    outputFile: `${t.testDir}/letter.pdf`, -    connectionName: connectionDetails.connectionName, -  }); -  await libeufinCli.connect(connectionDetails.connectionName); -  await libeufinCli.downloadBankAccounts(connectionDetails.connectionName); -  await libeufinCli.listOfferedBankAccounts(connectionDetails.connectionName); - -  const bankAccountImportDetails = { -    offeredBankAccountName: bankAccountDetails.accountName, -    nexusBankAccountName: "at-nexus-testacct01", -    connectionName: connectionDetails.connectionName, -  }; - -  await libeufinCli.importBankAccount(bankAccountImportDetails); -  await libeufinSandbox.c53tick() -  await libeufinCli.fetchTransactions(bankAccountImportDetails.nexusBankAccountName); -  await libeufinCli.transactions(bankAccountImportDetails.nexusBankAccountName); - -  const paymentDetails = { -    creditorIban: "DE42500105171245624648", -    creditorBic: "BELADEBEXXX", -    creditorName: "Mina Musterfrau", -    subject: "Purchase 01234", -    amount: "1.0", -    currency: "EUR", -    nexusBankAccountName: bankAccountImportDetails.nexusBankAccountName, -  }; -  await libeufinCli.preparePayment(paymentDetails); -  await libeufinCli.submitPayment(paymentDetails, "1"); - -  await libeufinCli.newTalerWireGatewayFacade({ -    accountName: bankAccountImportDetails.nexusBankAccountName, -    connectionName: "my-ebics-conn", -    currency: "EUR", -    facadeName: "my-twg", -  }); -  await libeufinCli.listFacades(); -} -runLibeufinTutorialTest.suites = ["libeufin"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-exchange-confusion.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-exchange-confusion.ts deleted file mode 100644 index 30ab1cd4b..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-exchange-confusion.ts +++ /dev/null @@ -1,243 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  codecForMerchantOrderStatusUnpaid, -  ConfirmPayResultType, -  PreparePayResultType, -} from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import axiosImp from "axios"; -const axios = axiosImp.default; -import { URL } from "url"; -import { defaultCoinConfig } from "../harness/denomStructures.js"; -import { -  FaultInjectedExchangeService, -  FaultInjectedMerchantService, -} from "../harness/faultInjection.js"; -import { -  BankService, -  ExchangeService, -  getPayto, -  GlobalTestState, -  MerchantPrivateApi, -  MerchantService, -  setupDb, -  WalletCli, -} from "../harness/harness.js"; -import { -  FaultyMerchantTestEnvironment, -  withdrawViaBank, -} from "../harness/helpers.js"; - -/** - * Run a test case with a simple TESTKUDOS Taler environment, consisting - * of one exchange, one bank and one merchant. - */ -export async function createConfusedMerchantTestkudosEnvironment( -  t: GlobalTestState, -): Promise<FaultyMerchantTestEnvironment> { -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const faultyMerchant = new FaultInjectedMerchantService(t, merchant, 9083); -  const faultyExchange = new FaultInjectedExchangeService(t, exchange, 9081); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); -  exchange.addBankAccount("1", exchangeBankAccount); - -  bank.setSuggestedExchange( -    faultyExchange, -    exchangeBankAccount.accountPaytoUri, -  ); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  exchange.addOfferedCoins(defaultCoinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  // Confuse the merchant by adding the non-proxied exchange. -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  await merchant.addInstance({ -    id: "minst1", -    name: "minst1", -    paytoUris: [getPayto("minst1")], -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  return { -    commonDb: db, -    exchange, -    merchant, -    wallet, -    bank, -    exchangeBankAccount, -    faultyMerchant, -    faultyExchange, -  }; -} - -/** - * Confuse the merchant by having one URL for the same exchange in the config, - * but sending coins from the same exchange with a different URL. - */ -export async function runMerchantExchangeConfusionTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, faultyExchange, faultyMerchant } = -    await createConfusedMerchantTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { -    wallet, -    bank, -    exchange: faultyExchange, -    amount: "TESTKUDOS:20", -  }); - -  /** -   * ========================================================================= -   * Create an order and let the wallet pay under a session ID -   * -   * We check along the way that the JSON response to /orders/{order_id} -   * returns the right thing. -   * ========================================================================= -   */ - -  const merchant = faultyMerchant; - -  let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "https://example.com/article42", -    }, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -    sessionId: "mysession-one", -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  t.assertTrue(orderStatus.already_paid_order_id === undefined); -  let publicOrderStatusUrl = orderStatus.order_status_url; - -  let publicOrderStatusResp = await axios.get(publicOrderStatusUrl, { -    validateStatus: () => true, -  }); - -  if (publicOrderStatusResp.status != 402) { -    throw Error( -      `expected status 402 (before claiming), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  console.log(pubUnpaidStatus); - -  let preparePayResp = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: pubUnpaidStatus.taler_pay_uri, -    }, -  ); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible); - -  const proposalId = preparePayResp.proposalId; - -  const orderUrlWithHash = new URL(publicOrderStatusUrl); -  orderUrlWithHash.searchParams.set( -    "h_contract", -    preparePayResp.contractTermsHash, -  ); - -  console.log("requesting", orderUrlWithHash.href); - -  publicOrderStatusResp = await axios.get(orderUrlWithHash.href, { -    validateStatus: () => true, -  }); - -  if (publicOrderStatusResp.status != 402) { -    throw Error( -      `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  const confirmPayRes = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId: proposalId, -    }, -  ); - -  t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done); -} - -runMerchantExchangeConfusionTest.suites = ["merchant"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts deleted file mode 100644 index 09231cdd8..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { URL } from "@gnu-taler/taler-util"; -import axiosImp from "axios"; -const axios = axiosImp.default; -import { -  ExchangeService, -  GlobalTestState, -  MerchantApiClient, -  MerchantService, -  setupDb, -  getPayto, -} from "../harness/harness.js"; - -/** - * Test instance deletion and authentication for it - */ -export async function runMerchantInstancesDeleteTest(t: GlobalTestState) { -  // Set up test environment - -  const db = await setupDb(t); - -  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, -  }); - -  // We add the exchange to the config, but note that the exchange won't be started. -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  // Base URL for the default instance. -  const baseUrl = merchant.makeInstanceBaseUrl(); - -  { -    const r = await axios.get(new URL("config", baseUrl).href); -    console.log(r.data); -    t.assertDeepEqual(r.data.currency, "TESTKUDOS"); -  } - -  // Instances should initially be empty -  { -    const r = await axios.get(new URL("management/instances", baseUrl).href); -    t.assertDeepEqual(r.data.instances, []); -  } - -  // Add an instance, no auth! -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -    auth: { -      method: "external", -    }, -  }); - -  // Add an instance, no auth! -  await merchant.addInstance({ -    id: "myinst", -    name: "Second Instance", -    paytoUris: [getPayto("merchant-default")], -    auth: { -      method: "external", -    }, -  }); - -  let merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), { -    method: "external", -  }); - -  await merchantClient.changeAuth({ -    method: "token", -    token: "secret-token:foobar", -  }); - -  merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), { -    method: "token", -    token: "secret-token:foobar", -  }); - -  // Check that deleting an instance checks the auth -  // of the default instance. -  { -    const unauthMerchantClient = new MerchantApiClient( -      merchant.makeInstanceBaseUrl(), -      { -        method: "token", -        token: "secret-token:invalid", -      }, -    ); - -    const exc = await t.assertThrowsAsync(async () => { -      await unauthMerchantClient.deleteInstance("myinst"); -    }); -    console.log("Got expected exception", exc); -    t.assertAxiosError(exc); -    t.assertDeepEqual(exc.response?.status, 401); -  } -} - -runMerchantInstancesDeleteTest.suites = ["merchant"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-urls.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-urls.ts deleted file mode 100644 index a4e44c7f3..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-urls.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { Duration } from "@gnu-taler/taler-util"; -import axiosImp from "axios"; -const axios = axiosImp.default; -import { -  ExchangeService, -  GlobalTestState, -  MerchantApiClient, -  MerchantService, -  setupDb, -  getPayto, -} from "../harness/harness.js"; - -/** - * Do basic checks on instance management and authentication. - */ -export async function runMerchantInstancesUrlsTest(t: GlobalTestState) { -  // Set up test environment - -  const db = await setupDb(t); - -  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, -  }); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  const clientForDefault = new MerchantApiClient( -    merchant.makeInstanceBaseUrl(), -    { -      method: "token", -      token: "secret-token:i-am-default", -    }, -  ); - -  await clientForDefault.createInstance({ -    id: "default", -    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, -    default_wire_transfer_delay: Duration.toTalerProtocolDuration( -      Duration.fromSpec({ seconds: 60 }), -    ), -    jurisdiction: {}, -    name: "My Default Instance", -    payto_uris: [getPayto("bar")], -    auth: { -      method: "token", -      token: "secret-token:i-am-default", -    }, -  }); - -  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, -    default_wire_transfer_delay: Duration.toTalerProtocolDuration( -      Duration.fromSpec({ seconds: 60 }), -    ), -    jurisdiction: {}, -    name: "My Second Instance", -    payto_uris: [getPayto("bar")], -    auth: { -      method: "token", -      token: "secret-token:i-am-myinst", -    }, -  }); - -  async function check(url: string, token: string, expectedStatus: number) { -    const resp = await axios.get(url, { -      headers: { -        Authorization: `Bearer ${token}`, -      }, -      validateStatus: () => true, -    }); -    console.log( -      `checking ${url}, expected ${expectedStatus}, got ${resp.status}`, -    ); -    t.assertDeepEqual(resp.status, expectedStatus); -  } - -  const tokDefault = "secret-token:i-am-default"; - -  const defaultBaseUrl = merchant.makeInstanceBaseUrl(); - -  await check( -    `${defaultBaseUrl}private/instances/default/instances/default/config`, -    tokDefault, -    404, -  ); - -  // Instance management is only available when accessing the default instance -  // directly. -  await check( -    `${defaultBaseUrl}instances/default/private/instances`, -    "foo", -    404, -  ); - -  // Non-default instances don't allow instance management. -  await check(`${defaultBaseUrl}instances/foo/private/instances`, "foo", 404); -  await check( -    `${defaultBaseUrl}instances/myinst/private/instances`, -    "foo", -    404, -  ); - -  await check(`${defaultBaseUrl}config`, "foo", 200); -  await check(`${defaultBaseUrl}instances/default/config`, "foo", 200); -  await check(`${defaultBaseUrl}instances/myinst/config`, "foo", 200); -  await check(`${defaultBaseUrl}instances/foo/config`, "foo", 404); -  await check( -    `${defaultBaseUrl}instances/default/instances/config`, -    "foo", -    404, -  ); - -  await check( -    `${defaultBaseUrl}private/instances/myinst/config`, -    tokDefault, -    404, -  ); - -  await check( -    `${defaultBaseUrl}instances/myinst/private/orders`, -    tokDefault, -    401, -  ); - -  await check( -    `${defaultBaseUrl}instances/myinst/private/orders`, -    tokDefault, -    401, -  ); - -  await check( -    `${defaultBaseUrl}instances/myinst/private/orders`, -    "secret-token:i-am-myinst", -    200, -  ); - -  await check( -    `${defaultBaseUrl}private/instances/myinst/orders`, -    tokDefault, -    404, -  ); -} - -runMerchantInstancesUrlsTest.suites = ["merchant"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances.ts deleted file mode 100644 index 3efe83241..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances.ts +++ /dev/null @@ -1,184 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { URL } from "@gnu-taler/taler-util"; -import axiosImp from "axios"; -const axios = axiosImp.default; -import { -  ExchangeService, -  GlobalTestState, -  MerchantApiClient, -  MerchantService, -  setupDb, -  getPayto -} from "../harness/harness.js"; - -/** - * Do basic checks on instance management and authentication. - */ -export async function runMerchantInstancesTest(t: GlobalTestState) { -  // Set up test environment - -  const db = await setupDb(t); - -  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, -  }); - -  // We add the exchange to the config, but note that the exchange won't be started. -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  // Base URL for the default instance. -  const baseUrl = merchant.makeInstanceBaseUrl(); - -  { -    const r = await axios.get(new URL("config", baseUrl).href); -    console.log(r.data); -    t.assertDeepEqual(r.data.currency, "TESTKUDOS"); -  } - -  // Instances should initially be empty -  { -    const r = await axios.get(new URL("management/instances", baseUrl).href); -    t.assertDeepEqual(r.data.instances, []); -  } - -  // Add an instance, no auth! -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -    auth: { -      method: "external", -    }, -  }); - -  // Add an instance, no auth! -  await merchant.addInstance({ -    id: "myinst", -    name: "Second Instance", -    paytoUris: [getPayto("merchant-default")], -    auth: { -      method: "external", -    }, -  }); - -  let merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), { -    method: "external", -  }); - -  { -    const r = await merchantClient.getInstances(); -    t.assertDeepEqual(r.instances.length, 2); -  } - -  // Check that a "malformed" bearer Authorization header gets ignored -  { -    const url = merchant.makeInstanceBaseUrl(); -    const resp = await axios.get(new URL("management/instances", url).href, { -      headers: { -        Authorization: "foo bar-baz", -      }, -    }); -    t.assertDeepEqual(resp.status, 200); -  } - -  { -    const fullDetails = await merchantClient.getInstanceFullDetails("default"); -    t.assertDeepEqual(fullDetails.auth.method, "external"); -  } - -  await merchantClient.changeAuth({ -    method: "token", -    token: "secret-token:foobar", -  }); - -  // Now this should fail, as we didn't change the auth of the client yet. -  const exc = await t.assertThrowsAsync(async () => { -    console.log("requesting instances with auth", merchantClient.auth); -    const resp = await merchantClient.getInstances(); -    console.log("instances result:", resp); -  }); - -  console.log(exc); - -  t.assertAxiosError(exc); -  t.assertTrue(exc.response?.status === 401); - -  merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), { -    method: "token", -    token: "secret-token:foobar", -  }); - -  // With the new client auth settings, request should work again. -  await merchantClient.getInstances(); - -  // Now, try some variations. -  { -    const url = merchant.makeInstanceBaseUrl(); -    const resp = await axios.get(new URL("management/instances", url).href, { -      headers: { -        // Note the spaces -        Authorization: "Bearer     secret-token:foobar", -      }, -    }); -    t.assertDeepEqual(resp.status, 200); -  } - -  // Check that auth is reported properly -  { -    const fullDetails = await merchantClient.getInstanceFullDetails("default"); -    t.assertDeepEqual(fullDetails.auth.method, "token"); -    // Token should *not* be reported back. -    t.assertDeepEqual(fullDetails.auth.token, undefined); -  } - -  // Check that deleting an instance checks the auth -  // of the default instance. -  { -    const unauthMerchantClient = new MerchantApiClient( -      merchant.makeInstanceBaseUrl(), -      { -        method: "external", -      }, -    ); - -    const exc = await t.assertThrowsAsync(async () => { -      await unauthMerchantClient.deleteInstance("myinst"); -    }); -    console.log(exc); -    t.assertAxiosError(exc); -    t.assertDeepEqual(exc.response?.status, 401); -  } -} - -runMerchantInstancesTest.suites = ["merchant"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-longpolling.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-longpolling.ts deleted file mode 100644 index 4b9f53f05..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-longpolling.ts +++ /dev/null @@ -1,162 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { 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"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runMerchantLongpollingTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  /** -   * ========================================================================= -   * Create an order and let the wallet pay under a session ID -   * -   * We check along the way that the JSON response to /orders/{order_id} -   * returns the right thing. -   * ========================================================================= -   */ - -  let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "https://example.com/article42", -    }, -    create_token: false, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -    sessionId: "mysession-one", -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  t.assertTrue(orderStatus.already_paid_order_id === undefined); -  let publicOrderStatusUrl = new URL(orderStatus.order_status_url); - -  // First, request order status without longpolling -  { -    console.log("requesting", publicOrderStatusUrl.href); -    let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { -      validateStatus: () => true, -    }); - -    if (publicOrderStatusResp.status != 402) { -      throw Error( -        `expected status 402 (before claiming, no long polling), but got ${publicOrderStatusResp.status}`, -      ); -    } -  } - -  // Now do long-polling for half a second! -  publicOrderStatusUrl.searchParams.set("timeout_ms", "500"); - -  console.log("requesting", publicOrderStatusUrl.href); -  let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { -    validateStatus: () => true, -  }); - -  if (publicOrderStatusResp.status != 402) { -    throw Error( -      `expected status 402 (before claiming, with long-polling), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  console.log(pubUnpaidStatus); - -  /** -   * ========================================================================= -   * Now actually pay, but WHILE a long poll is active! -   * ========================================================================= -   */ - -  let preparePayResp = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: pubUnpaidStatus.taler_pay_uri, -    }, -  ); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible); - -  publicOrderStatusUrl.searchParams.set("timeout_ms", "5000"); -  publicOrderStatusUrl.searchParams.set( -    "h_contract", -    preparePayResp.contractTermsHash, -  ); - -  let publicOrderStatusPromise = axios.get(publicOrderStatusUrl.href, { -    validateStatus: () => true, -  }); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible); - -  const proposalId = preparePayResp.proposalId; - -  publicOrderStatusResp = await publicOrderStatusPromise; - -  if (publicOrderStatusResp.status != 402) { -    throw Error( -      `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  const confirmPayRes = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId: proposalId, -    }, -  ); - -  t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done); -} - -runMerchantLongpollingTest.suites = ["merchant"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts deleted file mode 100644 index 5d9b23fa7..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts +++ /dev/null @@ -1,303 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  GlobalTestState, -  MerchantPrivateApi, -  MerchantServiceInterface, -  WalletCli, -  ExchangeServiceInterface, -} from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -} from "../harness/helpers.js"; -import { -  URL, -  durationFromSpec, -  PreparePayResultType, -  Duration, -} from "@gnu-taler/taler-util"; -import axiosImp from "axios"; -const axios = axiosImp.default; -import { -  WalletApiOperation, -  BankServiceHandle, -} from "@gnu-taler/taler-wallet-core"; - -async function testRefundApiWithFulfillmentUrl( -  t: GlobalTestState, -  env: { -    merchant: MerchantServiceInterface; -    bank: BankServiceHandle; -    wallet: WalletCli; -    exchange: ExchangeServiceInterface; -  }, -): Promise<void> { -  const { wallet, bank, exchange, merchant } = env; - -  // Set up order. -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "https://example.com/fulfillment", -    }, -    refund_delay: Duration.toTalerProtocolDuration( -      durationFromSpec({ minutes: 5 }), -    ), -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  const talerPayUri = orderStatus.taler_pay_uri; -  const orderId = orderResp.order_id; - -  // Make wallet pay for the order - -  let preparePayResult = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri, -    }, -  ); - -  t.assertTrue( -    preparePayResult.status === PreparePayResultType.PaymentPossible, -  ); - -  await wallet.client.call(WalletApiOperation.ConfirmPay, { -    proposalId: preparePayResult.proposalId, -  }); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  preparePayResult = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri, -    }, -  ); - -  t.assertTrue( -    preparePayResult.status === PreparePayResultType.AlreadyConfirmed, -  ); - -  await MerchantPrivateApi.giveRefund(merchant, { -    amount: "TESTKUDOS:5", -    instance: "default", -    justification: "foo", -    orderId: orderResp.order_id, -  }); - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:5"); - -  // Now test what the merchant gives as a response for various requests to the -  // public order status URL! - -  let publicOrderStatusUrl = new URL( -    `orders/${orderId}`, -    merchant.makeInstanceBaseUrl(), -  ); -  publicOrderStatusUrl.searchParams.set( -    "h_contract", -    preparePayResult.contractTermsHash, -  ); - -  let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { -    validateStatus: () => true, -  }); -  console.log(publicOrderStatusResp.data); -  t.assertTrue(publicOrderStatusResp.status === 200); -  t.assertAmountEquals(publicOrderStatusResp.data.refund_amount, "TESTKUDOS:5"); - -  publicOrderStatusUrl = new URL( -    `orders/${orderId}`, -    merchant.makeInstanceBaseUrl(), -  ); -  console.log(`requesting order status via '${publicOrderStatusUrl.href}'`); -  publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { -    validateStatus: () => true, -  }); -  console.log(publicOrderStatusResp.status); -  console.log(publicOrderStatusResp.data); -  // We didn't give any authentication, so we should get a fulfillment URL back -  t.assertTrue(publicOrderStatusResp.status === 403); -} - -async function testRefundApiWithFulfillmentMessage( -  t: GlobalTestState, -  env: { -    merchant: MerchantServiceInterface; -    bank: BankServiceHandle; -    wallet: WalletCli; -    exchange: ExchangeServiceInterface; -  }, -): Promise<void> { -  const { wallet, bank, exchange, merchant } = env; - -  // Set up order. -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_message: "Thank you for buying foobar", -    }, -    refund_delay: Duration.toTalerProtocolDuration( -      durationFromSpec({ minutes: 5 }), -    ), -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  const talerPayUri = orderStatus.taler_pay_uri; -  const orderId = orderResp.order_id; - -  // Make wallet pay for the order - -  let preparePayResult = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri, -    }, -  ); - -  t.assertTrue( -    preparePayResult.status === PreparePayResultType.PaymentPossible, -  ); - -  await wallet.client.call(WalletApiOperation.ConfirmPay, { -    proposalId: preparePayResult.proposalId, -  }); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  preparePayResult = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri, -    }, -  ); - -  t.assertTrue( -    preparePayResult.status === PreparePayResultType.AlreadyConfirmed, -  ); - -  await MerchantPrivateApi.giveRefund(merchant, { -    amount: "TESTKUDOS:5", -    instance: "default", -    justification: "foo", -    orderId: orderResp.order_id, -  }); - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:5"); - -  // Now test what the merchant gives as a response for various requests to the -  // public order status URL! - -  let publicOrderStatusUrl = new URL( -    `orders/${orderId}`, -    merchant.makeInstanceBaseUrl(), -  ); -  publicOrderStatusUrl.searchParams.set( -    "h_contract", -    preparePayResult.contractTermsHash, -  ); - -  let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { -    validateStatus: () => true, -  }); -  console.log(publicOrderStatusResp.data); -  t.assertTrue(publicOrderStatusResp.status === 200); -  t.assertAmountEquals(publicOrderStatusResp.data.refund_amount, "TESTKUDOS:5"); - -  publicOrderStatusUrl = new URL( -    `orders/${orderId}`, -    merchant.makeInstanceBaseUrl(), -  ); - -  publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { -    validateStatus: () => true, -  }); -  console.log(publicOrderStatusResp.data); -  // We didn't give any authentication, so we should get a fulfillment URL back -  t.assertTrue(publicOrderStatusResp.status === 403); -} - -/** - * Test case for the refund API of the merchant backend. - */ -export async function runMerchantRefundApiTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  await testRefundApiWithFulfillmentUrl(t, { -    wallet, -    bank, -    exchange, -    merchant, -  }); - -  await testRefundApiWithFulfillmentMessage(t, { -    wallet, -    bank, -    exchange, -    merchant, -  }); -} - -runMerchantRefundApiTest.suites = ["merchant"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts deleted file mode 100644 index 70edaaf0c..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts +++ /dev/null @@ -1,620 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  ConfirmPayResultType, -  PreparePayResultType, -  URL, -  encodeCrock, -  getRandomBytes, -} from "@gnu-taler/taler-util"; -import { NodeHttpLib, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { -  BankService, -  ExchangeService, -  GlobalTestState, -  MerchantPrivateApi, -  MerchantService, -  WalletCli, -} from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -} from "../harness/helpers.js"; - -const httpLib = new NodeHttpLib(); - -interface Context { -  merchant: MerchantService; -  merchantBaseUrl: string; -  bank: BankService; -  exchange: ExchangeService; -} - -async function testWithClaimToken( -  t: GlobalTestState, -  c: Context, -): Promise<void> { -  const wallet = new WalletCli(t, "withclaimtoken"); -  const { bank, exchange } = c; -  const { merchant, merchantBaseUrl } = c; -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); -  const sessionId = "mysession"; -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "https://example.com/article42", -      public_reorder_url: "https://example.com/article42-share", -    }, -  }); - -  const claimToken = orderResp.token; -  const orderId = orderResp.order_id; -  t.assertTrue(!!claimToken); -  let talerPayUri: string; - -  { -    const httpResp = await httpLib.get( -      new URL(`orders/${orderId}`, merchantBaseUrl).href, -    ); -    const r = await httpResp.json(); -    t.assertDeepEqual(httpResp.status, 202); -    console.log(r); -  } - -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    t.assertDeepEqual(httpResp.status, 402); -    console.log(r); -    talerPayUri = r.taler_pay_uri; -    t.assertTrue(!!talerPayUri); -  } - -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken); -    const httpResp = await httpLib.get(url.href, { -      headers: { -        Accept: "text/html", -      }, -    }); -    const r = await httpResp.text(); -    t.assertDeepEqual(httpResp.status, 402); -    console.log(r); -  } - -  const preparePayResp = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri, -    }, -  ); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible); -  const contractTermsHash = preparePayResp.contractTermsHash; -  const proposalId = preparePayResp.proposalId; - -  // claimed, unpaid, access with wrong h_contract -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const hcWrong = encodeCrock(getRandomBytes(64)); -    url.searchParams.set("h_contract", hcWrong); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 403); -  } - -  // claimed, unpaid, access with wrong claim token -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const ctWrong = encodeCrock(getRandomBytes(16)); -    url.searchParams.set("token", ctWrong); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 403); -  } - -  // claimed, unpaid, access with correct claim token -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 402); -  } - -  // claimed, unpaid, access with correct contract terms hash -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("h_contract", contractTermsHash); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 402); -  } - -  // claimed, unpaid, access without credentials -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 202); -  } - -  const confirmPayRes = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId: proposalId, -    }, -  ); - -  t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done); - -  // paid, access without credentials -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 202); -  } - -  // paid, access with wrong h_contract -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const hcWrong = encodeCrock(getRandomBytes(64)); -    url.searchParams.set("h_contract", hcWrong); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 403); -  } - -  // paid, access with wrong claim token -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const ctWrong = encodeCrock(getRandomBytes(16)); -    url.searchParams.set("token", ctWrong); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 403); -  } - -  // paid, access with correct h_contract -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("h_contract", contractTermsHash); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 200); -  } - -  // paid, access with correct claim token, JSON -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 200); -    const respFulfillmentUrl = r.fulfillment_url; -    t.assertDeepEqual(respFulfillmentUrl, "https://example.com/article42"); -  } - -  // paid, access with correct claim token, HTML -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("token", claimToken); -    const httpResp = await httpLib.get(url.href, { -      headers: { Accept: "text/html" }, -    }); -    t.assertDeepEqual(httpResp.status, 200); -  } - -  const confirmPayRes2 = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId: proposalId, -      sessionId: sessionId, -    }, -  ); - -  t.assertTrue(confirmPayRes2.type === ConfirmPayResultType.Done); - -  // Create another order with identical fulfillment URL to test the "already paid" flow -  const alreadyPaidOrderResp = await MerchantPrivateApi.createOrder( -    merchant, -    "default", -    { -      order: { -        summary: "Buy me!", -        amount: "TESTKUDOS:5", -        fulfillment_url: "https://example.com/article42", -        public_reorder_url: "https://example.com/article42-share", -      }, -    }, -  ); - -  const apOrderId = alreadyPaidOrderResp.order_id; -  const apToken = alreadyPaidOrderResp.token; -  t.assertTrue(!!apToken); - -  { -    const url = new URL(`orders/${apOrderId}`, merchantBaseUrl); -    url.searchParams.set("token", apToken); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 402); -  } - -  // Check for already paid session ID, JSON -  { -    const url = new URL(`orders/${apOrderId}`, merchantBaseUrl); -    url.searchParams.set("token", apToken); -    url.searchParams.set("session_id", sessionId); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 402); -    const alreadyPaidOrderId = r.already_paid_order_id; -    t.assertDeepEqual(alreadyPaidOrderId, orderId); -  } - -  // Check for already paid session ID, HTML -  { -    const url = new URL(`orders/${apOrderId}`, merchantBaseUrl); -    url.searchParams.set("token", apToken); -    url.searchParams.set("session_id", sessionId); -    const httpResp = await httpLib.get(url.href, { -      headers: { Accept: "text/html" }, -    }); -    t.assertDeepEqual(httpResp.status, 302); -    const location = httpResp.headers.get("Location"); -    console.log("location header:", location); -    t.assertDeepEqual(location, "https://example.com/article42"); -  } -} - -async function testWithoutClaimToken( -  t: GlobalTestState, -  c: Context, -): Promise<void> { -  const wallet = new WalletCli(t, "withoutct"); -  const sessionId = "mysession2"; -  const { bank, exchange } = c; -  const { merchant, merchantBaseUrl } = c; -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "https://example.com/article42", -      public_reorder_url: "https://example.com/article42-share", -    }, -    create_token: false, -  }); - -  const orderId = orderResp.order_id; -  let talerPayUri: string; - -  { -    const httpResp = await httpLib.get( -      new URL(`orders/${orderId}`, merchantBaseUrl).href, -    ); -    const r = await httpResp.json(); -    t.assertDeepEqual(httpResp.status, 402); -    console.log(r); -  } - -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    t.assertDeepEqual(httpResp.status, 402); -    console.log(r); -    talerPayUri = r.taler_pay_uri; -    t.assertTrue(!!talerPayUri); -  } - -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const httpResp = await httpLib.get(url.href, { -      headers: { -        Accept: "text/html", -      }, -    }); -    const r = await httpResp.text(); -    t.assertDeepEqual(httpResp.status, 402); -    console.log(r); -  } - -  const preparePayResp = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri, -    }, -  ); - -  console.log(preparePayResp); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible); -  const contractTermsHash = preparePayResp.contractTermsHash; -  const proposalId = preparePayResp.proposalId; - -  // claimed, unpaid, access with wrong h_contract -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const hcWrong = encodeCrock(getRandomBytes(64)); -    url.searchParams.set("h_contract", hcWrong); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 403); -  } - -  // claimed, unpaid, access with wrong claim token -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const ctWrong = encodeCrock(getRandomBytes(16)); -    url.searchParams.set("token", ctWrong); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 403); -  } - -  // claimed, unpaid, no claim token -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 402); -  } - -  // claimed, unpaid, access with correct contract terms hash -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("h_contract", contractTermsHash); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 402); -  } - -  // claimed, unpaid, access without credentials -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    // No credentials, but the order doesn't require a claim token. -    // This effectively means that the order ID is already considered -    // enough authentication, at least to check for the basic order status -    t.assertDeepEqual(httpResp.status, 402); -  } - -  const confirmPayRes = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId: proposalId, -    }, -  ); - -  t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done); - -  // paid, access without credentials -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 200); -  } - -  // paid, access with wrong h_contract -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const hcWrong = encodeCrock(getRandomBytes(64)); -    url.searchParams.set("h_contract", hcWrong); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 403); -  } - -  // paid, access with wrong claim token -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const ctWrong = encodeCrock(getRandomBytes(16)); -    url.searchParams.set("token", ctWrong); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 403); -  } - -  // paid, access with correct h_contract -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    url.searchParams.set("h_contract", contractTermsHash); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 200); -  } - -  // paid, JSON -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 200); -    const respFulfillmentUrl = r.fulfillment_url; -    t.assertDeepEqual(respFulfillmentUrl, "https://example.com/article42"); -  } - -  // paid, HTML -  { -    const url = new URL(`orders/${orderId}`, merchantBaseUrl); -    const httpResp = await httpLib.get(url.href, { -      headers: { Accept: "text/html" }, -    }); -    t.assertDeepEqual(httpResp.status, 200); -  } - -  const confirmPayRes2 = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId: proposalId, -      sessionId: sessionId, -    }, -  ); - -  t.assertTrue(confirmPayRes2.type === ConfirmPayResultType.Done); - -  // Create another order with identical fulfillment URL to test the "already paid" flow -  const alreadyPaidOrderResp = await MerchantPrivateApi.createOrder( -    merchant, -    "default", -    { -      order: { -        summary: "Buy me!", -        amount: "TESTKUDOS:5", -        fulfillment_url: "https://example.com/article42", -        public_reorder_url: "https://example.com/article42-share", -      }, -    }, -  ); - -  const apOrderId = alreadyPaidOrderResp.order_id; -  const apToken = alreadyPaidOrderResp.token; -  t.assertTrue(!!apToken); - -  { -    const url = new URL(`orders/${apOrderId}`, merchantBaseUrl); -    url.searchParams.set("token", apToken); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 402); -  } - -  // Check for already paid session ID, JSON -  { -    const url = new URL(`orders/${apOrderId}`, merchantBaseUrl); -    url.searchParams.set("token", apToken); -    url.searchParams.set("session_id", sessionId); -    const httpResp = await httpLib.get(url.href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 402); -    const alreadyPaidOrderId = r.already_paid_order_id; -    t.assertDeepEqual(alreadyPaidOrderId, orderId); -  } - -  // Check for already paid session ID, HTML -  { -    const url = new URL(`orders/${apOrderId}`, merchantBaseUrl); -    url.searchParams.set("token", apToken); -    url.searchParams.set("session_id", sessionId); -    const httpResp = await httpLib.get(url.href, { -      headers: { Accept: "text/html" }, -    }); -    t.assertDeepEqual(httpResp.status, 302); -    const location = httpResp.headers.get("Location"); -    console.log("location header:", location); -    t.assertDeepEqual(location, "https://example.com/article42"); -  } -} - -/** - * Checks for the /orders/{id} endpoint of the merchant. - * - * The tests here should exercise all code paths in the executable - * specification of the endpoint. - */ -export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) { -  const { bank, exchange, merchant } = await createSimpleTestkudosEnvironment( -    t, -  ); - -  // Base URL for the default instance. -  const merchantBaseUrl = merchant.makeInstanceBaseUrl(); - -  { -    const httpResp = await httpLib.get(new URL("config", merchantBaseUrl).href); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(r.currency, "TESTKUDOS"); -  } - -  { -    const httpResp = await httpLib.get( -      new URL("orders/foo", merchantBaseUrl).href, -    ); -    const r = await httpResp.json(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 404); -    // FIXME: also check Taler error code -  } - -  { -    const httpResp = await httpLib.get( -      new URL("orders/foo", merchantBaseUrl).href, -      { -        headers: { -          Accept: "text/html", -        }, -      }, -    ); -    const r = await httpResp.text(); -    console.log(r); -    t.assertDeepEqual(httpResp.status, 404); -    // FIXME: also check Taler error code -  } - -  await testWithClaimToken(t, { -    merchant, -    merchantBaseUrl, -    exchange, -    bank, -  }); - -  await testWithoutClaimToken(t, { -    merchant, -    merchantBaseUrl, -    exchange, -    bank, -  }); -} - -runMerchantSpecPublicOrdersTest.suites = ["merchant"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-pay-paid.ts b/packages/taler-wallet-cli/src/integrationtests/test-pay-paid.ts deleted file mode 100644 index 2ef91e4a8..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-pay-paid.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { -  withdrawViaBank, -  createFaultInjectedMerchantTestkudosEnvironment, -} from "../harness/helpers.js"; -import { -  PreparePayResultType, -  codecForMerchantOrderStatusUnpaid, -  ConfirmPayResultType, -  URL, -} from "@gnu-taler/taler-util"; -import axiosImp from "axios"; -const axios = axiosImp.default; -import { FaultInjectionRequestContext } from "../harness/faultInjection.js"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; - -/** - * Run test for the wallets repurchase detection mechanism - * based on the fulfillment URL. - * - * FIXME: This test is now almost the same as test-paywall-flow, - * since we can't initiate payment via a "claimed" private order status - * response. - */ -export async function runPayPaidTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, faultyExchange, faultyMerchant } = -    await createFaultInjectedMerchantTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { -    wallet, -    bank, -    exchange: faultyExchange, -    amount: "TESTKUDOS:20", -  }); - -  /** -   * ========================================================================= -   * Create an order and let the wallet pay under a session ID -   * -   * We check along the way that the JSON response to /orders/{order_id} -   * returns the right thing. -   * ========================================================================= -   */ - -  const merchant = faultyMerchant; - -  let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "https://example.com/article42", -      public_reorder_url: "https://example.com/article42-share", -    }, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -    sessionId: "mysession-one", -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  t.assertTrue(orderStatus.already_paid_order_id === undefined); -  let publicOrderStatusUrl = orderStatus.order_status_url; - -  let publicOrderStatusResp = await axios.get(publicOrderStatusUrl, { -    validateStatus: () => true, -  }); - -  if (publicOrderStatusResp.status != 402) { -    throw Error( -      `expected status 402 (before claiming), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  console.log(pubUnpaidStatus); - -  let preparePayResp = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: pubUnpaidStatus.taler_pay_uri, -    }, -  ); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible); - -  const proposalId = preparePayResp.proposalId; - -  publicOrderStatusResp = await axios.get(publicOrderStatusUrl, { -    validateStatus: () => true, -  }); - -  if (publicOrderStatusResp.status != 402) { -    throw Error( -      `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  const confirmPayRes = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId: proposalId, -    }, -  ); - -  t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done); - -  publicOrderStatusResp = await axios.get(publicOrderStatusUrl, { -    validateStatus: () => true, -  }); - -  console.log(publicOrderStatusResp.data); - -  if (publicOrderStatusResp.status != 200) { -    console.log(publicOrderStatusResp.data); -    throw Error( -      `expected status 200 (after paying), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  /** -   * ========================================================================= -   * Now change up the session ID and do payment re-play! -   * ========================================================================= -   */ - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -    sessionId: "mysession-two", -  }); - -  console.log( -    "order status under mysession-two:", -    JSON.stringify(orderStatus, undefined, 2), -  ); - -  // Should be claimed (not paid!) because of a new session ID -  t.assertTrue(orderStatus.order_status === "claimed"); - -  let numPayRequested = 0; -  let numPaidRequested = 0; - -  faultyMerchant.faultProxy.addFault({ -    async modifyRequest(ctx: FaultInjectionRequestContext) { -      const url = new URL(ctx.requestUrl); -      if (url.pathname.endsWith("/pay")) { -        numPayRequested++; -      } else if (url.pathname.endsWith("/paid")) { -        numPaidRequested++; -      } -    }, -  }); - -  let orderRespTwo = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "https://example.com/article42", -      public_reorder_url: "https://example.com/article42-share", -    }, -  }); - -  let orderStatusTwo = await MerchantPrivateApi.queryPrivateOrderStatus( -    merchant, -    { -      orderId: orderRespTwo.order_id, -      sessionId: "mysession-two", -    }, -  ); - -  t.assertTrue(orderStatusTwo.order_status === "unpaid"); - -  // 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( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: orderStatusTwo.taler_pay_uri, -    }, -  ); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.AlreadyConfirmed); -  t.assertTrue(preparePayResp.paid); - -  // Make sure the wallet is actually doing the replay properly. -  t.assertTrue(numPaidRequested == 1); -  t.assertTrue(numPayRequested == 0); -} - -runPayPaidTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-claim.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-claim.ts deleted file mode 100644 index e93d2c44c..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-payment-claim.ts +++ /dev/null @@ -1,110 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  GlobalTestState, -  MerchantPrivateApi, -  WalletCli, -} from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -} 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. - */ -export async function runPaymentClaimTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  const walletTwo = new WalletCli(t, "two"); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  // Set up order. - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -    }, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  const talerPayUri = orderStatus.taler_pay_uri; - -  // Make wallet pay for the order - -  const preparePayResult = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri, -    }, -  ); - -  t.assertTrue( -    preparePayResult.status === PreparePayResultType.PaymentPossible, -  ); - -  t.assertThrowsTalerErrorAsync(async () => { -    await walletTwo.client.call(WalletApiOperation.PreparePayForUri, { -      talerPayUri, -    }); -  }); - -  await wallet.client.call(WalletApiOperation.ConfirmPay, { -    proposalId: preparePayResult.proposalId, -  }); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  walletTwo.deleteDatabase(); - -  const err = await t.assertThrowsTalerErrorAsync(async () => { -    await walletTwo.client.call(WalletApiOperation.PreparePayForUri, { -      talerPayUri, -    }); -  }); - -  t.assertTrue(err.hasErrorCode(TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED)); - -  await t.shutdown(); -} - -runPaymentClaimTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts deleted file mode 100644 index dea538e35..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Sample fault injection test. - */ - -/** - * Imports. - */ -import { -  GlobalTestState, -  MerchantService, -  ExchangeService, -  setupDb, -  BankService, -  WalletCli, -  MerchantPrivateApi, -  getPayto, -} from "../harness/harness.js"; -import { -  FaultInjectedExchangeService, -  FaultInjectionRequestContext, -  FaultInjectionResponseContext, -} from "../harness/faultInjection.js"; -import { CoreApiResponse } from "@gnu-taler/taler-util"; -import { defaultCoinConfig } from "../harness/denomStructures.js"; -import { -  WalletApiOperation, -  BankApi, -  BankAccessApi, -} from "@gnu-taler/taler-wallet-core"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runPaymentFaultTest(t: GlobalTestState) { -  // Set up test environment - -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); - -  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  await exchange.addBankAccount("1", exchangeBankAccount); -  exchange.addOfferedCoins(defaultCoinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  const faultyExchange = new FaultInjectedExchangeService(t, exchange, 8091); - -  // Print all requests to the exchange -  faultyExchange.faultProxy.addFault({ -    async modifyRequest(ctx: FaultInjectionRequestContext) { -      console.log("got request", ctx); -    }, -    async modifyResponse(ctx: FaultInjectionResponseContext) { -      console.log("got response", ctx); -    }, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  merchant.addExchange(faultyExchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  // Create withdrawal operation - -  const user = await BankApi.createRandomBankUser(bank); -  const wop = await BankAccessApi.createWithdrawalOperation( -    bank, -    user, -    "TESTKUDOS:20", -  ); - -  // Hand it to the wallet - -  await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, { -    talerWithdrawUri: wop.taler_withdraw_uri, -  }); - -  await wallet.runPending(); - -  // Withdraw - -  await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, { -    exchangeBaseUrl: faultyExchange.baseUrl, -    talerWithdrawUri: wop.taler_withdraw_uri, -  }); -  await wallet.runPending(); - -  // Confirm it - -  await BankApi.confirmWithdrawalOperation(bank, user, wop); - -  await wallet.runUntilDone(); - -  // Check balance - -  await wallet.client.call(WalletApiOperation.GetBalances, {}); - -  // Set up order. - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -    }, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  // Make wallet pay for the order - -  let apiResp: CoreApiResponse; - -  const prepResp = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: orderStatus.taler_pay_uri, -    }, -  ); - -  const proposalId = prepResp.proposalId; - -  await wallet.runPending(); - -  // Drop 3 responses from the exchange. -  let faultCount = 0; -  faultyExchange.faultProxy.addFault({ -    async modifyResponse(ctx: FaultInjectionResponseContext) { -      if (!ctx.request.requestUrl.endsWith("/deposit")) { -        return; -      } -      if (faultCount < 3) { -        console.log(`blocking /deposit request #${faultCount}`); -        faultCount++; -        ctx.dropResponse = true; -      } else { -        console.log(`letting through /deposit request #${faultCount}`); -      } -    }, -  }); - -  // confirmPay won't work, as the exchange is unreachable - -  await wallet.client.call(WalletApiOperation.ConfirmPay, { -    // FIXME: should be validated, don't cast! -    proposalId: proposalId, -  }); - -  await wallet.runUntilDone(); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); -} - -runPaymentFaultTest.suites = ["wallet"]; -runPaymentFaultTest.timeoutMs = 120000; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-forgettable.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-forgettable.ts deleted file mode 100644 index 3bdd6bef3..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-payment-forgettable.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -  makeTestPayment, -} from "../harness/helpers.js"; - -/** - * Run test for payment with a contract that has forgettable fields. - */ -export async function runPaymentForgettableTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  { -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -      extra: { -        foo: { bar: "baz" }, -        $forgettable: { -          foo: "gnu", -        }, -      }, -    }; - -    await makeTestPayment(t, { wallet, merchant, order }); -  } - -  console.log("testing with forgettable field without hash"); - -  { -    const order = { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -      extra: { -        foo: { bar: "baz" }, -        $forgettable: { -          foo: true, -        }, -      }, -    }; - -    await makeTestPayment(t, { wallet, merchant, order }); -  } - -  await wallet.runUntilDone(); -} - -runPaymentForgettableTest.suites = ["wallet", "merchant"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-idempotency.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-idempotency.ts deleted file mode 100644 index 1099a8188..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-payment-idempotency.ts +++ /dev/null @@ -1,121 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { 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"; - -/** - * Test the wallet-core payment API, especially that repeated operations - * return the expected result. - */ -export async function runPaymentIdempotencyTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  // Set up order. - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -    }, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  const talerPayUri = orderStatus.taler_pay_uri; - -  // Make wallet pay for the order - -  const preparePayResult = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: orderStatus.taler_pay_uri, -    }, -  ); - -  const preparePayResultRep = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: orderStatus.taler_pay_uri, -    }, -  ); - -  t.assertTrue( -    preparePayResult.status === PreparePayResultType.PaymentPossible, -  ); -  t.assertTrue( -    preparePayResultRep.status === PreparePayResultType.PaymentPossible, -  ); - -  const proposalId = preparePayResult.proposalId; - -  const confirmPayResult = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId: proposalId, -    }, -  ); - -  console.log("confirm pay result", confirmPayResult); - -  await wallet.runUntilDone(); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  const preparePayResultAfter = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri, -    }, -  ); - -  console.log("result after:", preparePayResultAfter); - -  t.assertTrue( -    preparePayResultAfter.status === PreparePayResultType.AlreadyConfirmed, -  ); -  t.assertTrue(preparePayResultAfter.paid === true); - -  await t.shutdown(); -} - -runPaymentIdempotencyTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-multiple.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-multiple.ts deleted file mode 100644 index 46325c05f..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-payment-multiple.ts +++ /dev/null @@ -1,163 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  GlobalTestState, -  setupDb, -  BankService, -  ExchangeService, -  MerchantService, -  WalletCli, -  MerchantPrivateApi, -  getPayto -} from "../harness/harness.js"; -import { withdrawViaBank } from "../harness/helpers.js"; -import { coin_ct10, coin_u1 } from "../harness/denomStructures.js"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; - -async function setupTest( -  t: GlobalTestState, -): Promise<{ -  merchant: MerchantService; -  exchange: ExchangeService; -  bank: BankService; -}> { -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); - -  exchange.addOfferedCoins([coin_ct10, coin_u1]); - -  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  await exchange.addBankAccount("1", exchangeBankAccount); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  await merchant.addInstance({ -    id: "minst1", -    name: "minst1", -    paytoUris: [getPayto("minst1")], -  }); - -  console.log("setup done!"); - -  return { -    merchant, -    bank, -    exchange, -  }; -} - -/** - * Run test. - * - * This test uses a very sub-optimal denomination structure. - */ -export async function runPaymentMultipleTest(t: GlobalTestState) { -  // Set up test environment - -  const { merchant, bank, exchange } = await setupTest(t); - -  const wallet = new WalletCli(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:100" }); - -  // Set up order. - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:80", -      fulfillment_url: "taler://fulfillment-success/thx", -    }, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  // Make wallet pay for the order - -  const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, { -    talerPayUri: orderStatus.taler_pay_uri, -  }); - -  await wallet.client.call(WalletApiOperation.ConfirmPay, { -    // FIXME: should be validated, don't cast! -    proposalId: r1.proposalId, -  }); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  await t.shutdown(); -} - -runPaymentMultipleTest.suites = ["wallet"]; -runPaymentMultipleTest.timeoutMs = 120000; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts deleted file mode 100644 index 737620ce7..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState, WalletCli } from "../harness/harness.js"; -import { makeTestPayment } from "../harness/helpers.js"; -import { -  WalletApiOperation, -  BankApi, -  BankAccessApi, -  BankServiceHandle, -  NodeHttpLib, -} from "@gnu-taler/taler-wallet-core"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runPaymentDemoTest(t: GlobalTestState) { -  // Withdraw digital cash into the wallet. -  let bankInterface: BankServiceHandle = { -    baseUrl: "https://bank.demo.taler.net/", -    bankAccessApiBaseUrl: "https://bank.demo.taler.net/", -    http: new NodeHttpLib(), -  }; -  let user = await BankApi.createRandomBankUser(bankInterface); -  let wop = await BankAccessApi.createWithdrawalOperation( -    bankInterface, -    user, -    "KUDOS:20", -  ); - -  let wallet = new WalletCli(t); -  await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, { -    talerWithdrawUri: wop.taler_withdraw_uri, -  }); - -  await wallet.runPending(); - -  // Confirm it - -  await BankApi.confirmWithdrawalOperation(bankInterface, user, wop); - -  // Withdraw - -  await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, { -    exchangeBaseUrl: "https://exchange.demo.taler.net/", -    talerWithdrawUri: wop.taler_withdraw_uri, -  }); -  await wallet.runUntilDone(); - -  let balanceBefore = await wallet.client.call( -    WalletApiOperation.GetBalances, -    {}, -  ); -  t.assertTrue(balanceBefore["balances"].length == 1); - -  const order = { -    summary: "Buy me!", -    amount: "KUDOS:5", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  let merchant = { -    makeInstanceBaseUrl: function (instanceName?: string) { -      return "https://backend.demo.taler.net/instances/donations/"; -    }, -    port: 0, -    name: "donations", -  }; - -  t.assertTrue("TALER_ENV_FRONTENDS_APITOKEN" in process.env); - -  await makeTestPayment( -    t, -    { -      merchant, -      wallet, -      order, -    }, -    { -      Authorization: `Bearer ${process.env["TALER_ENV_FRONTENDS_APITOKEN"]}`, -    }, -  ); - -  await wallet.runUntilDone(); - -  let balanceAfter = await wallet.client.call( -    WalletApiOperation.GetBalances, -    {}, -  ); -  t.assertTrue(balanceAfter["balances"].length == 1); -  t.assertTrue( -    balanceBefore["balances"][0]["available"] > -      balanceAfter["balances"][0]["available"], -  ); -} - -runPaymentDemoTest.excludeByDefault = true; -runPaymentDemoTest.suites = ["buildbot"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-transient.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-transient.ts deleted file mode 100644 index b57b355c6..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-payment-transient.ts +++ /dev/null @@ -1,185 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { -  withdrawViaBank, -  createFaultInjectedMerchantTestkudosEnvironment, -} from "../harness/helpers.js"; -import { -  FaultInjectionResponseContext, -} from "../harness/faultInjection.js"; -import { -  codecForMerchantOrderStatusUnpaid, -  ConfirmPayResultType, -  PreparePayResultType, -  TalerErrorCode, -  TalerErrorDetail, -  URL, -} from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import axiosImp from "axios"; -const axios = axiosImp.default; - -/** - * Run test for a payment where the merchant has a transient - * failure in /pay - */ -export async function runPaymentTransientTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet, -    bank, -    exchange, -    faultyMerchant, -  } = await createFaultInjectedMerchantTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  const merchant = faultyMerchant; - -  let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "https://example.com/article42", -      public_reorder_url: "https://example.com/article42-share", -    }, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -    sessionId: "mysession-one", -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  t.assertTrue(orderStatus.already_paid_order_id === undefined); -  let publicOrderStatusUrl = orderStatus.order_status_url; - -  let publicOrderStatusResp = await axios.get(publicOrderStatusUrl, { -    validateStatus: () => true, -  }); - -  if (publicOrderStatusResp.status != 402) { - - -    throw Error( -      `expected status 402 (before claiming), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  console.log(pubUnpaidStatus); - -  let preparePayResp = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: pubUnpaidStatus.taler_pay_uri, -    }, -  ); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible); - -  const proposalId = preparePayResp.proposalId; - -  publicOrderStatusResp = await axios.get(publicOrderStatusUrl, { -    validateStatus: () => true, -  }); - -  if (publicOrderStatusResp.status != 402) { -    throw Error( -      `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  let faultInjected = false; - -  faultyMerchant.faultProxy.addFault({ -    async modifyResponse(ctx: FaultInjectionResponseContext) { -      console.log("in modifyResponse"); -      const url = new URL(ctx.request.requestUrl); -      console.log("pathname is", url.pathname); -      if (!url.pathname.endsWith("/pay")) { -        return; -      } -      if (faultInjected) { -        console.log("not injecting pay fault"); -        return; -      } -      faultInjected = true; -      console.log("injecting pay fault"); -      const err: TalerErrorDetail = { -        code: TalerErrorCode.GENERIC_DB_COMMIT_FAILED, -        hint: "something went wrong", -      }; -      ctx.responseBody = Buffer.from(JSON.stringify(err)); -      ctx.statusCode = 500; -    }, -  }); - -  const confirmPayResp = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId, -    }, -  ); - -  console.log(confirmPayResp); - -  t.assertTrue(confirmPayResp.type === ConfirmPayResultType.Pending); -  t.assertTrue(faultInjected); - -  const confirmPayRespTwo = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId, -    }, -  ); - -  t.assertTrue(confirmPayRespTwo.type === ConfirmPayResultType.Done); - -  // Now ask the merchant if paid - -  console.log("requesting", publicOrderStatusUrl); -  publicOrderStatusResp = await axios.get(publicOrderStatusUrl, { -    validateStatus: () => true, -  }); - -  console.log(publicOrderStatusResp.data); - -  if (publicOrderStatusResp.status != 200) { -    console.log(publicOrderStatusResp.data); -    throw Error( -      `expected status 200 (after paying), but got ${publicOrderStatusResp.status}`, -    ); -  } -} - -runPaymentTransientTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-zero.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-zero.ts deleted file mode 100644 index c38b8b382..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-payment-zero.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -  makeTestPayment, -} from "../harness/helpers.js"; - -/** - * Run test for a payment for a "free" order with - * an amount of zero. - */ -export async function runPaymentZeroTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment(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 wallet.runUntilDone(); - -  await makeTestPayment(t, { -    wallet, -    merchant, -    order: { -      summary: "I am free!", -      amount: "TESTKUDOS:0", -      fulfillment_url: "taler://fulfillment-success/thx", -    }, -  }); - -  await wallet.runUntilDone(); - -  const transactions = await wallet.client.call( -    WalletApiOperation.GetTransactions, -    {}, -  ); - -  for (const tr of transactions.transactions) { -    t.assertDeepEqual(tr.pending, false); -  } -} - -runPaymentZeroTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment.ts deleted file mode 100644 index 66d10f996..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-payment.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -  makeTestPayment, -} from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runPaymentTest(t: GlobalTestState) { -  // Set up test environment - -  const { -    wallet, -    bank, -    exchange, -    merchant, -  } = await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  const order = { -    summary: "Buy me!", -    amount: "TESTKUDOS:5", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  await makeTestPayment(t, { wallet, merchant, order }); -  await wallet.runUntilDone(); - -  // Test JSON normalization of contract terms: Does the wallet -  // agree with the merchant? -  const order2 = { -    summary: "Testing “unicode” characters", -    amount: "TESTKUDOS:5", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  await makeTestPayment(t, { wallet, merchant, order: order2 }); -  await wallet.runUntilDone(); - -  // Test JSON normalization of contract terms: Does the wallet -  // agree with the merchant? -  const order3 = { -    summary: "Testing\nNewlines\rAnd\tStuff\nHere\b", -    amount: "TESTKUDOS:5", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  await makeTestPayment(t, { wallet, merchant, order: order3 }); - -  await wallet.runUntilDone(); -} - -runPaymentTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-paywall-flow.ts b/packages/taler-wallet-cli/src/integrationtests/test-paywall-flow.ts deleted file mode 100644 index a9601c625..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-paywall-flow.ts +++ /dev/null @@ -1,252 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { 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"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runPaywallFlowTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  /** -   * ========================================================================= -   * Create an order and let the wallet pay under a session ID -   * -   * We check along the way that the JSON response to /orders/{order_id} -   * returns the right thing. -   * ========================================================================= -   */ - -  let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "https://example.com/article42", -      public_reorder_url: "https://example.com/article42-share", -    }, -  }); - -  const firstOrderId = orderResp.order_id; - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -    sessionId: "mysession-one", -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  const talerPayUriOne = orderStatus.taler_pay_uri; - -  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, -  }); - -  if (publicOrderStatusResp.status != 402) { -    throw Error( -      `expected status 402 (before claiming), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  console.log(pubUnpaidStatus); - -  let preparePayResp = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: pubUnpaidStatus.taler_pay_uri, -    }, -  ); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible); - -  const proposalId = preparePayResp.proposalId; - -  console.log("requesting", publicOrderStatusUrl.href); -  publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { -    validateStatus: () => true, -  }); -  console.log("response body", publicOrderStatusResp.data); -  if (publicOrderStatusResp.status != 402) { -    throw Error( -      `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  const confirmPayRes = await wallet.client.call( -    WalletApiOperation.ConfirmPay, -    { -      proposalId: proposalId, -    }, -  ); - -  t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done); - -  publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { -    validateStatus: () => true, -  }); - -  console.log(publicOrderStatusResp.data); - -  if (publicOrderStatusResp.status != 200) { -    console.log(publicOrderStatusResp.data); -    throw Error( -      `expected status 200 (after paying), but got ${publicOrderStatusResp.status}`, -    ); -  } - -  /** -   * ========================================================================= -   * Now change up the session ID! -   * ========================================================================= -   */ - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -    sessionId: "mysession-two", -  }); - -  // Should be claimed (not paid!) because of a new session ID -  t.assertTrue(orderStatus.order_status === "claimed"); - -  // 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( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: talerPayUriOne, -    }, -  ); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.AlreadyConfirmed); -  t.assertTrue(preparePayResp.paid); - -  /** -   * ========================================================================= -   * Now we test re-purchase detection. -   * ========================================================================= -   */ - -  orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      // Same fulfillment URL as previously! -      fulfillment_url: "https://example.com/article42", -      public_reorder_url: "https://example.com/article42-share", -    }, -  }); - -  const secondOrderId = orderResp.order_id; - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: secondOrderId, -    sessionId: "mysession-three", -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  t.assertTrue(orderStatus.already_paid_order_id === undefined); -  publicOrderStatusUrl = new URL(orderStatus.order_status_url); - -  // 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( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: orderStatus.taler_pay_uri, -    }, -  ); - -  t.assertTrue(preparePayResp.status === PreparePayResultType.AlreadyConfirmed); -  t.assertTrue(preparePayResp.paid); - -  // The first order should now be paid under "mysession-three", -  // as the wallet did re-purchase detection -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: firstOrderId, -    sessionId: "mysession-three", -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  // Check that with a completely new session ID, the status would NOT -  // be paid. -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: firstOrderId, -    sessionId: "mysession-four", -  }); - -  t.assertTrue(orderStatus.order_status === "claimed"); - -  // Now check if the public status of the new order is correct. - -  console.log("requesting public status", publicOrderStatusUrl); - -  // Ask the order status of the claimed-but-unpaid order -  publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { -    validateStatus: () => true, -  }); - -  if (publicOrderStatusResp.status != 402) { -    throw Error(`expected status 402, but got ${publicOrderStatusResp.status}`); -  } - -  pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( -    publicOrderStatusResp.data, -  ); - -  console.log(publicOrderStatusResp.data); - -  t.assertTrue(pubUnpaidStatus.already_paid_order_id === firstOrderId); -} - -runPaywallFlowTest.suites = ["merchant", "wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts b/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts deleted file mode 100644 index 211f20494..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { AbsoluteTime, Duration, j2s } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState, WalletCli } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -} from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runPeerToPeerPullTest(t: GlobalTestState) { -  // Set up test environment - -  const { bank, exchange, merchant } = await createSimpleTestkudosEnvironment( -    t, -  ); - -  // Withdraw digital cash into the wallet. -  const wallet1 = new WalletCli(t, "w1"); -  const wallet2 = new WalletCli(t, "w2"); -  await withdrawViaBank(t, { -    wallet: wallet2, -    bank, -    exchange, -    amount: "TESTKUDOS:20", -  }); - -  await wallet1.runUntilDone(); - -  const purse_expiration = AbsoluteTime.toTimestamp( -    AbsoluteTime.addDuration( -      AbsoluteTime.now(), -      Duration.fromSpec({ days: 2 }), -    ), -  ); - -  const resp = await wallet1.client.call( -    WalletApiOperation.InitiatePeerPullPayment, -    { -      exchangeBaseUrl: exchange.baseUrl, -      partialContractTerms: { -        summary: "Hello World", -        amount: "TESTKUDOS:5", -        purse_expiration -      }, -    }, -  ); - -  const checkResp = await wallet2.client.call( -    WalletApiOperation.CheckPeerPullPayment, -    { -      talerUri: resp.talerUri, -    }, -  ); - -  console.log(`checkResp: ${j2s(checkResp)}`); - -  const acceptResp = await wallet2.client.call( -    WalletApiOperation.AcceptPeerPullPayment, -    { -      peerPullPaymentIncomingId: checkResp.peerPullPaymentIncomingId, -    }, -  ); - -  await wallet1.runUntilDone(); -  await wallet2.runUntilDone(); - -  const txn1 = await wallet1.client.call( -    WalletApiOperation.GetTransactions, -    {}, -  ); -  const txn2 = await wallet2.client.call( -    WalletApiOperation.GetTransactions, -    {}, -  ); - -  console.log(`txn1: ${j2s(txn1)}`); -  console.log(`txn2: ${j2s(txn2)}`); -} - -runPeerToPeerPullTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-push.ts b/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-push.ts deleted file mode 100644 index 4aaeca624..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-push.ts +++ /dev/null @@ -1,119 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { AbsoluteTime, Duration, j2s } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState, WalletCli } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -} from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runPeerToPeerPushTest(t: GlobalTestState) { -  // Set up test environment - -  const { bank, exchange } = await createSimpleTestkudosEnvironment(t); - -  const wallet1 = new WalletCli(t, "w1"); -  const wallet2 = new WalletCli(t, "w2"); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { -    wallet: wallet1, -    bank, -    exchange, -    amount: "TESTKUDOS:20", -  }); - -  await wallet1.runUntilDone(); - -  const purse_expiration = AbsoluteTime.toTimestamp( -    AbsoluteTime.addDuration( -      AbsoluteTime.now(), -      Duration.fromSpec({ days: 2 }), -    ), -  ); - -  { -    const resp = await wallet1.client.call( -      WalletApiOperation.InitiatePeerPushPayment, -      { -        partialContractTerms: { -          summary: "Hello World", -          amount: "TESTKUDOS:5", -          purse_expiration -        }, -      }, -    ); - -    console.log(resp); - -  } -  const resp = await wallet1.client.call( -    WalletApiOperation.InitiatePeerPushPayment, -    { -      partialContractTerms: { -        summary: "Hello World", -        amount: "TESTKUDOS:5", -        purse_expiration -      }, -    }, -  ); - -  console.log(resp); - -  const checkResp = await wallet2.client.call( -    WalletApiOperation.CheckPeerPushPayment, -    { -      talerUri: resp.talerUri, -    }, -  ); - -  console.log(checkResp); - -  const acceptResp = await wallet2.client.call( -    WalletApiOperation.AcceptPeerPushPayment, -    { -      peerPushPaymentIncomingId: checkResp.peerPushPaymentIncomingId, -    }, -  ); - -  console.log(acceptResp); - -  await wallet1.runUntilDone(); -  await wallet2.runUntilDone(); - -  const txn1 = await wallet1.client.call( -    WalletApiOperation.GetTransactions, -    {}, -  ); -  const txn2 = await wallet2.client.call( -    WalletApiOperation.GetTransactions, -    {}, -  ); - -  console.log(`txn1: ${j2s(txn1)}`); -  console.log(`txn2: ${j2s(txn2)}`); -} - -runPeerToPeerPushTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts b/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts deleted file mode 100644 index 4c2a2f94a..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { 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"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runRefundAutoTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  // Set up order. -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -      auto_refund: { -        d_us: 3000 * 1000, -      }, -    }, -    refund_delay: Duration.toTalerProtocolDuration( -      durationFromSpec({ minutes: 5 }), -    ), -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  // Make wallet pay for the order - -  const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, { -    talerPayUri: orderStatus.taler_pay_uri, -  }); - -  await wallet.client.call(WalletApiOperation.ConfirmPay, { -    // FIXME: should be validated, don't cast! -    proposalId: r1.proposalId, -  }); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  const ref = await MerchantPrivateApi.giveRefund(merchant, { -    amount: "TESTKUDOS:5", -    instance: "default", -    justification: "foo", -    orderId: orderResp.order_id, -  }); - -  console.log(ref); - -  // The wallet should now automatically pick up the refund. -  await wallet.runUntilDone(); - -  const transactions = await wallet.client.call( -    WalletApiOperation.GetTransactions, -    {}, -  ); -  console.log(JSON.stringify(transactions, undefined, 2)); - -  const transactionTypes = transactions.transactions.map((x) => x.type); -  t.assertDeepEqual(transactionTypes, ["withdrawal", "payment", "refund"]); - -  await t.shutdown(); -} - -runRefundAutoTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts b/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts deleted file mode 100644 index b6cefda86..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -  applyTimeTravel, -} from "../harness/helpers.js"; -import { -  AbsoluteTime, -  Duration, -  durationFromSpec, -} from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runRefundGoneTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  // Set up order. - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -      pay_deadline: AbsoluteTime.toTimestamp( -        AbsoluteTime.addDuration( -          AbsoluteTime.now(), -          durationFromSpec({ -            minutes: 10, -          }), -        ), -      ), -    }, -    refund_delay: Duration.toTalerProtocolDuration( -      durationFromSpec({ minutes: 1 }), -    ), -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  // Make wallet pay for the order - -  const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, { -    talerPayUri: orderStatus.taler_pay_uri, -  }); - -  const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, { -    proposalId: r1.proposalId, -  }); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  console.log(orderStatus); - -  await applyTimeTravel(durationFromSpec({ hours: 1 }), { exchange, wallet }); - -  await exchange.runAggregatorOnce(); - -  const ref = await MerchantPrivateApi.giveRefund(merchant, { -    amount: "TESTKUDOS:5", -    instance: "default", -    justification: "foo", -    orderId: orderResp.order_id, -  }); - -  console.log(ref); - -  let rr = await wallet.client.call(WalletApiOperation.ApplyRefund, { -    talerRefundUri: ref.talerRefundUri, -  }); - -  console.log("refund response:", rr); -  t.assertAmountEquals(rr.amountRefundGone, "TESTKUDOS:5"); - -  await wallet.runUntilDone(); - -  let r = await wallet.client.call(WalletApiOperation.GetBalances, {}); -  console.log(JSON.stringify(r, undefined, 2)); - -  const r3 = await wallet.client.call(WalletApiOperation.GetTransactions, {}); -  console.log(JSON.stringify(r3, undefined, 2)); - -  await t.shutdown(); -} - -runRefundGoneTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts b/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts deleted file mode 100644 index 8d1f6e873..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts +++ /dev/null @@ -1,202 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  GlobalTestState, -  delayMs, -  MerchantPrivateApi, -} from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -} from "../harness/helpers.js"; -import { -  TransactionType, -  Amounts, -  durationFromSpec, -  Duration, -} from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runRefundIncrementalTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  // Set up order. - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:10", -      fulfillment_url: "taler://fulfillment-success/thx", -    }, -    refund_delay: Duration.toTalerProtocolDuration( -      durationFromSpec({ minutes: 5 }), -    ), -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  // Make wallet pay for the order - -  const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, { -    talerPayUri: orderStatus.taler_pay_uri, -  }); - -  await wallet.client.call(WalletApiOperation.ConfirmPay, { -    proposalId: r1.proposalId, -  }); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  let ref = await MerchantPrivateApi.giveRefund(merchant, { -    amount: "TESTKUDOS:2.5", -    instance: "default", -    justification: "foo", -    orderId: orderResp.order_id, -  }); - -  console.log("first refund increase response", ref); - -  { -    let wr = await wallet.client.call(WalletApiOperation.ApplyRefund, { -      talerRefundUri: ref.talerRefundUri, -    }); -    console.log(wr); -    const txs = await wallet.client.call( -      WalletApiOperation.GetTransactions, -      {}, -    ); -    console.log( -      "transactions after applying first refund:", -      JSON.stringify(txs, undefined, 2), -    ); -  } - -  // Wait at least a second, because otherwise the increased -  // refund will be grouped with the previous one. -  await delayMs(1200); - -  ref = await MerchantPrivateApi.giveRefund(merchant, { -    amount: "TESTKUDOS:5", -    instance: "default", -    justification: "bar", -    orderId: orderResp.order_id, -  }); - -  console.log("second refund increase response", ref); - -  // Wait at least a second, because otherwise the increased -  // refund will be grouped with the previous one. -  await delayMs(1200); - -  ref = await MerchantPrivateApi.giveRefund(merchant, { -    amount: "TESTKUDOS:10", -    instance: "default", -    justification: "bar", -    orderId: orderResp.order_id, -  }); - -  console.log("third refund increase response", ref); - -  { -    let wr = await wallet.client.call(WalletApiOperation.ApplyRefund, { -      talerRefundUri: ref.talerRefundUri, -    }); -    console.log(wr); -  } - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:10"); - -  console.log(JSON.stringify(orderStatus, undefined, 2)); - -  await wallet.runUntilDone(); - -  const bal = await wallet.client.call(WalletApiOperation.GetBalances, {}); -  console.log(JSON.stringify(bal, undefined, 2)); - -  { -    const txs = await wallet.client.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", -      "refund", -    ]); - -    for (const tx of txs.transactions) { -      if (tx.type !== TransactionType.Refund) { -        continue; -      } -      t.assertAmountLeq(tx.amountEffective, tx.amountRaw); -    } - -    const raw = Amounts.sum( -      txs.transactions -        .filter((x) => x.type === TransactionType.Refund) -        .map((x) => x.amountRaw), -    ).amount; - -    t.assertAmountEquals("TESTKUDOS:10", raw); - -    const effective = Amounts.sum( -      txs.transactions -        .filter((x) => x.type === TransactionType.Refund) -        .map((x) => x.amountEffective), -    ).amount; - -    t.assertAmountEquals("TESTKUDOS:8.59", effective); -  } - -  await t.shutdown(); -} - -runRefundIncrementalTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund.ts b/packages/taler-wallet-cli/src/integrationtests/test-refund.ts deleted file mode 100644 index b63dad590..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-refund.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { Duration, durationFromSpec } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -} from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runRefundTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  // Set up order. - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      summary: "Buy me!", -      amount: "TESTKUDOS:5", -      fulfillment_url: "taler://fulfillment-success/thx", -    }, -    refund_delay: Duration.toTalerProtocolDuration( -      durationFromSpec({ minutes: 5 }), -    ), -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  // Make wallet pay for the order - -  const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, { -    talerPayUri: orderStatus.taler_pay_uri, -  }); - -  await wallet.client.call(WalletApiOperation.ConfirmPay, { -    proposalId: r1.proposalId, -  }); - -  // Check if payment was successful. - -  orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "paid"); - -  const ref = await MerchantPrivateApi.giveRefund(merchant, { -    amount: "TESTKUDOS:5", -    instance: "default", -    justification: "foo", -    orderId: orderResp.order_id, -  }); - -  console.log(ref); - -  let r = await wallet.client.call(WalletApiOperation.ApplyRefund, { -    talerRefundUri: ref.talerRefundUri, -  }); -  console.log(r); - -  await wallet.runUntilDone(); - -  { -    const r2 = await wallet.client.call(WalletApiOperation.GetBalances, {}); -    console.log(JSON.stringify(r2, undefined, 2)); -  } -  { -    const r2 = await wallet.client.call(WalletApiOperation.GetTransactions, {}); -    console.log(JSON.stringify(r2, undefined, 2)); -  } - -  await t.shutdown(); -} - -runRefundTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts b/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts deleted file mode 100644 index 0fbb4960e..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts +++ /dev/null @@ -1,215 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { CoinConfig } from "../harness/denomStructures.js"; -import { -  GlobalTestState, -  ExchangeService, -  MerchantService, -  WalletCli, -  setupDb, -  BankService, -  delayMs, -  getPayto, -} from "../harness/harness.js"; -import { -  withdrawViaBank, -  makeTestPayment, -  SimpleTestEnvironment, -} from "../harness/helpers.js"; - -async function revokeAllWalletCoins(req: { -  wallet: WalletCli; -  exchange: ExchangeService; -  merchant: MerchantService; -}): Promise<void> { -  const { wallet, exchange, merchant } = req; -  const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {}); -  console.log(coinDump); -  const usedDenomHashes = new Set<string>(); -  for (const coin of coinDump.coins) { -    usedDenomHashes.add(coin.denom_pub_hash); -  } -  for (const x of usedDenomHashes.values()) { -    await exchange.revokeDenomination(x); -  } -  await delayMs(1000); -  await exchange.keyup(); -  await delayMs(1000); -  await merchant.stop(); -  await merchant.start(); -  await merchant.pingUntilAvailable(); -} - -async function createTestEnvironment( -  t: GlobalTestState, -): Promise<SimpleTestEnvironment> { -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); -  exchange.addBankAccount("1", exchangeBankAccount); - -  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  const coin_u1: CoinConfig = { -    cipher: "RSA" as const, -    durationLegal: "3 years", -    durationSpend: "2 years", -    durationWithdraw: "7 days", -    rsaKeySize: 1024, -    name: `TESTKUDOS_u1`, -    value: `TESTKUDOS:1`, -    feeDeposit: `TESTKUDOS:0`, -    feeRefresh: `TESTKUDOS:0`, -    feeRefund: `TESTKUDOS:0`, -    feeWithdraw: `TESTKUDOS:0`, -  }; - -  exchange.addCoinConfigList([coin_u1]); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  await merchant.addInstance({ -    id: "minst1", -    name: "minst1", -    paytoUris: [getPayto("minst1")], -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  return { -    commonDb: db, -    exchange, -    merchant, -    wallet, -    bank, -    exchangeBankAccount, -  }; -} - -/** - * Basic time travel test. - */ -export async function runRevocationTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = await createTestEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" }); - -  console.log("revoking first time"); -  await revokeAllWalletCoins({ wallet, exchange, merchant }); - -  // FIXME: this shouldn't be necessary once https://bugs.taler.net/n/6565 -  // is implemented. -  await wallet.client.call(WalletApiOperation.AddExchange, { -    exchangeBaseUrl: exchange.baseUrl, -    forceUpdate: true, -  }); -  await wallet.runUntilDone(); -  await wallet.runUntilDone(); -  const bal = await wallet.client.call(WalletApiOperation.GetBalances, {}); -  console.log("wallet balance", bal); - -  const order = { -    summary: "Buy me!", -    amount: "TESTKUDOS:10", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  await makeTestPayment(t, { wallet, merchant, order }); - -  wallet.deleteDatabase(); - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" }); - -  const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {}); -  console.log(coinDump); -  const coinPubList = coinDump.coins.map((x) => x.coin_pub); -  await wallet.client.call(WalletApiOperation.ForceRefresh, { -    coinPubList, -  }); -  await wallet.runUntilDone(); - -  console.log("revoking second time"); -  await revokeAllWalletCoins({ wallet, exchange, merchant }); - -  // FIXME: this shouldn't be necessary once https://bugs.taler.net/n/6565 -  // is implemented. -  await wallet.client.call(WalletApiOperation.AddExchange, { -    exchangeBaseUrl: exchange.baseUrl, -    forceUpdate: true, -  }); -  await wallet.runUntilDone(); -  await wallet.runUntilDone(); -  { -    const bal = await wallet.client.call(WalletApiOperation.GetBalances, {}); -    console.log("wallet balance", bal); -  } - -  await makeTestPayment(t, { wallet, merchant, order }); -} - -runRevocationTest.timeoutMs = 120000; -runRevocationTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-timetravel-autorefresh.ts b/packages/taler-wallet-cli/src/integrationtests/test-timetravel-autorefresh.ts deleted file mode 100644 index 54b66e0b2..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-timetravel-autorefresh.ts +++ /dev/null @@ -1,216 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  ConfirmPayResultType, -  Duration, -  durationFromSpec, -  PreparePayResultType, -} from "@gnu-taler/taler-util"; -import { -  PendingOperationsResponse, -  WalletApiOperation, -} from "@gnu-taler/taler-wallet-core"; -import { makeNoFeeCoinConfig } from "../harness/denomStructures.js"; -import { -  BankService, -  ExchangeService, -  GlobalTestState, -  MerchantPrivateApi, -  MerchantService, -  setupDb, -  WalletCli, -  getPayto -} from "../harness/harness.js"; -import { startWithdrawViaBank, withdrawViaBank } from "../harness/helpers.js"; - -async function applyTimeTravel( -  timetravelDuration: Duration, -  s: { -    exchange?: ExchangeService; -    merchant?: MerchantService; -    wallet?: WalletCli; -  }, -): Promise<void> { -  if (s.exchange) { -    await s.exchange.stop(); -    s.exchange.setTimetravel(timetravelDuration); -    await s.exchange.start(); -    await s.exchange.pingUntilAvailable(); -  } - -  if (s.merchant) { -    await s.merchant.stop(); -    s.merchant.setTimetravel(timetravelDuration); -    await s.merchant.start(); -    await s.merchant.pingUntilAvailable(); -  } - -  if (s.wallet) { -    console.log("setting wallet time travel to", timetravelDuration); -    s.wallet.setTimetravel(timetravelDuration); -  } -} - -/** - * Basic time travel test. - */ -export async function runTimetravelAutorefreshTest(t: GlobalTestState) { -  // Set up test environment - -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); -  exchange.addBankAccount("1", exchangeBankAccount); - -  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  exchange.addCoinConfigList(makeNoFeeCoinConfig("TESTKUDOS")); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  await merchant.addInstance({ -    id: "minst1", -    name: "minst1", -    paytoUris: [getPayto("minst1")], -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" }); - -  // Travel into the future, the deposit expiration is two years -  // into the future. -  console.log("applying first time travel"); -  await applyTimeTravel(durationFromSpec({ days: 400 }), { -    wallet, -    exchange, -    merchant, -  }); - -  await wallet.runUntilDone(); - -  let p: PendingOperationsResponse; -  p = await wallet.client.call(WalletApiOperation.GetPendingOperations, {}); - -  console.log("pending operations after first time travel"); -  console.log(JSON.stringify(p, undefined, 2)); - -  await startWithdrawViaBank(t, { -    wallet, -    bank, -    exchange, -    amount: "TESTKUDOS:20", -  }); - -  await wallet.runUntilDone(); - -  // Travel into the future, the deposit expiration is two years -  // into the future. -  console.log("applying second time travel"); -  await applyTimeTravel(durationFromSpec({ years: 2, months: 6 }), { -    wallet, -    exchange, -    merchant, -  }); - -  // At this point, the original coins should've been refreshed. -  // It would be too late to refresh them now, as we're past -  // the two year deposit expiration. - -  await wallet.runUntilDone(); - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order: { -      fulfillment_url: "http://example.com", -      summary: "foo", -      amount: "TESTKUDOS:30", -    }, -  }); - -  const orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( -    merchant, -    { -      orderId: orderResp.order_id, -      instance: "default", -    }, -  ); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  const r = await wallet.client.call(WalletApiOperation.PreparePayForUri, { -    talerPayUri: orderStatus.taler_pay_uri, -  }); - -  console.log(r); - -  t.assertTrue(r.status === PreparePayResultType.PaymentPossible); - -  const cpr = await wallet.client.call(WalletApiOperation.ConfirmPay, { -    proposalId: r.proposalId, -  }); - -  t.assertTrue(cpr.type === ConfirmPayResultType.Done); -} - -runTimetravelAutorefreshTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-timetravel-withdraw.ts b/packages/taler-wallet-cli/src/integrationtests/test-timetravel-withdraw.ts deleted file mode 100644 index 9335af9f5..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-timetravel-withdraw.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { Duration, TransactionType } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  startWithdrawViaBank, -  withdrawViaBank, -} from "../harness/helpers.js"; - -/** - * Basic time travel test. - */ -export async function runTimetravelWithdrawTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant } = -    await createSimpleTestkudosEnvironment(t); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" }); - -  // Travel 400 days into the future, -  // as the deposit expiration is two years -  // into the future. -  const timetravelDuration: Duration = { -    d_ms: 1000 * 60 * 60 * 24 * 400, -  }; - -  await exchange.stop(); -  exchange.setTimetravel(timetravelDuration); -  await exchange.start(); -  await exchange.pingUntilAvailable(); -  await exchange.keyup(); - -  await merchant.stop(); -  merchant.setTimetravel(timetravelDuration); -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  console.log("starting withdrawal via bank"); - -  // This should fail, as the wallet didn't time travel yet. -  await startWithdrawViaBank(t, { -    wallet, -    bank, -    exchange, -    amount: "TESTKUDOS:20", -  }); - -  console.log("starting withdrawal done"); - -  // Check that transactions are correct for the failed withdrawal -  { -    console.log("running until done (should run into maxRetries limit)"); -    await wallet.runUntilDone({ maxRetries: 5 }); -    console.log("wallet done running"); -    const transactions = await wallet.client.call( -      WalletApiOperation.GetTransactions, -      {}, -    ); -    console.log(transactions); -    const types = transactions.transactions.map((x) => x.type); -    t.assertDeepEqual(types, ["withdrawal", "withdrawal"]); -    const wtrans = transactions.transactions[1]; -    t.assertTrue(wtrans.type === TransactionType.Withdrawal); -    t.assertTrue(wtrans.pending); -  } - -  // Now we also let the wallet time travel - -  wallet.setTimetravel(timetravelDuration); - -  // This doesn't work yet, see https://bugs.taler.net/n/6585 - -  // await wallet.runUntilDone({ maxRetries: 5 }); -} - -runTimetravelWithdrawTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts b/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts deleted file mode 100644 index d31e0c06b..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { WalletApiOperation, BankApi } from "@gnu-taler/taler-wallet-core"; -import { -  GlobalTestState, -  MerchantPrivateApi, -  getWireMethodForTest, -} from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runTippingTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, merchant, exchangeBankAccount } = -    await createSimpleTestkudosEnvironment(t); - -  const mbu = await BankApi.createRandomBankUser(bank); - -  const tipReserveResp = await MerchantPrivateApi.createTippingReserve( -    merchant, -    "default", -    { -      exchange_url: exchange.baseUrl, -      initial_balance: "TESTKUDOS:10", -      wire_method: getWireMethodForTest(), -    }, -  ); - -  console.log("tipReserveResp:", tipReserveResp); - -  t.assertDeepEqual( -    tipReserveResp.payto_uri, -    exchangeBankAccount.accountPaytoUri, -  ); - -  await BankApi.adminAddIncoming(bank, { -    amount: "TESTKUDOS:10", -    debitAccountPayto: mbu.accountPaytoUri, -    exchangeBankAccount, -    reservePub: tipReserveResp.reserve_pub, -  }); - -  await exchange.runWirewatchOnce(); - -  await merchant.stop(); -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  const r = await MerchantPrivateApi.queryTippingReserves(merchant, "default"); -  console.log("tipping reserves:", JSON.stringify(r, undefined, 2)); - -  t.assertTrue(r.reserves.length === 1); -  t.assertDeepEqual( -    r.reserves[0].exchange_initial_amount, -    r.reserves[0].merchant_initial_amount, -  ); - -  const tip = await MerchantPrivateApi.giveTip(merchant, "default", { -    amount: "TESTKUDOS:5", -    justification: "why not?", -    next_url: "https://example.com/after-tip", -  }); - -  console.log("created tip", tip); - -  const doTip = async (): Promise<void> => { -    const ptr = await wallet.client.call(WalletApiOperation.PrepareTip, { -      talerTipUri: tip.taler_tip_uri, -    }); - -    console.log(ptr); - -    t.assertAmountEquals(ptr.tipAmountRaw, "TESTKUDOS:5"); -    t.assertAmountEquals(ptr.tipAmountEffective, "TESTKUDOS:4.85"); - -    await wallet.client.call(WalletApiOperation.AcceptTip, { -      walletTipId: ptr.walletTipId, -    }); - -    await wallet.runUntilDone(); - -    const bal = await wallet.client.call(WalletApiOperation.GetBalances, {}); - -    console.log(bal); - -    t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:4.85"); - -    const txns = await wallet.client.call( -      WalletApiOperation.GetTransactions, -      {}, -    ); - -    console.log("Transactions:", JSON.stringify(txns, undefined, 2)); - -    t.assertDeepEqual(txns.transactions[0].type, "tip"); -    t.assertDeepEqual(txns.transactions[0].pending, false); -    t.assertAmountEquals( -      txns.transactions[0].amountEffective, -      "TESTKUDOS:4.85", -    ); -    t.assertAmountEquals(txns.transactions[0].amountRaw, "TESTKUDOS:5.0"); -  }; - -  // Check twice so make sure tip handling is idempotent -  await doTip(); -  await doTip(); -} - -runTippingTest.suites = ["wallet", "wallet-tipping"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts deleted file mode 100644 index fc2f3335d..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts +++ /dev/null @@ -1,168 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { j2s } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState, WalletCli } from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  withdrawViaBank, -} from "../harness/helpers.js"; -import { SyncService } from "../harness/sync.js"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runWalletBackupBasicTest(t: GlobalTestState) { -  // Set up test environment - -  const { commonDb, merchant, wallet, bank, exchange } = -    await createSimpleTestkudosEnvironment(t); - -  const sync = await SyncService.create(t, { -    currency: "TESTKUDOS", -    annualFee: "TESTKUDOS:0.5", -    database: commonDb.connStr, -    fulfillmentUrl: "taler://fulfillment-success", -    httpPort: 8089, -    name: "sync1", -    paymentBackendUrl: merchant.makeInstanceBaseUrl(), -    uploadLimitMb: 10, -  }); - -  await sync.start(); -  await sync.pingUntilAvailable(); - -  await wallet.client.call(WalletApiOperation.AddBackupProvider, { -    backupProviderBaseUrl: sync.baseUrl, -    activate: false, -    name: sync.baseUrl, -  }); - -  { -    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); -    t.assertDeepEqual(bi.providers[0].active, false); -  } - -  await wallet.client.call(WalletApiOperation.AddBackupProvider, { -    backupProviderBaseUrl: sync.baseUrl, -    activate: true, -    name: sync.baseUrl, -  }); - -  { -    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); -    t.assertDeepEqual(bi.providers[0].active, true); -  } - -  await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); - -  { -    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); -    console.log(bi); -    t.assertDeepEqual( -      bi.providers[0].paymentStatus.type, -      "insufficient-balance", -    ); -  } - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" }); - -  await wallet.runUntilDone(); - -  await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); - -  { -    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); -    console.log(bi); -  } - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:5" }); - -  await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); - -  { -    const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); -    console.log(bi); -  } - -  const backupRecovery = await wallet.client.call( -    WalletApiOperation.ExportBackupRecovery, -    {}, -  ); - -  const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {}); -  console.log(`backed up transactions ${j2s(txs)}`); - -  const wallet2 = new WalletCli(t, "wallet2"); - -  // Check that the second wallet is a fresh wallet. -  { -    const bal = await wallet2.client.call(WalletApiOperation.GetBalances, {}); -    t.assertTrue(bal.balances.length === 0); -  } - -  await wallet2.client.call(WalletApiOperation.ImportBackupRecovery, { -    recovery: backupRecovery, -  }); - -  await wallet2.client.call(WalletApiOperation.RunBackupCycle, {}); - -  // Check that now the old balance is available! -  { -    const bal = await wallet2.client.call(WalletApiOperation.GetBalances, {}); -    t.assertTrue(bal.balances.length === 1); -    console.log(bal); -  } - -  // Now do some basic checks that the restored wallet is still functional -  { -    const txs = await wallet2.client.call( -      WalletApiOperation.GetTransactions, -      {}, -    ); -    console.log(`restored transactions ${j2s(txs)}`); -    const bal1 = await wallet2.client.call(WalletApiOperation.GetBalances, {}); - -    t.assertAmountEquals(bal1.balances[0].available, "TESTKUDOS:14.1"); - -    await withdrawViaBank(t, { -      wallet: wallet2, -      bank, -      exchange, -      amount: "TESTKUDOS:10", -    }); - -    await exchange.runWirewatchOnce(); - -    await wallet2.runUntilDone(); - -    const txs2 = await wallet2.client.call( -      WalletApiOperation.GetTransactions, -      {}, -    ); -    console.log(`tx after withdraw after restore ${j2s(txs2)}`); - -    const bal2 = await wallet2.client.call(WalletApiOperation.GetBalances, {}); - -    t.assertAmountEquals(bal2.balances[0].available, "TESTKUDOS:23.82"); -  } -} - -runWalletBackupBasicTest.suites = ["wallet", "wallet-backup"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts deleted file mode 100644 index 8b52260e9..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts +++ /dev/null @@ -1,174 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { PreparePayResultType } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { -  GlobalTestState, -  WalletCli, -  MerchantPrivateApi, -} from "../harness/harness.js"; -import { -  createSimpleTestkudosEnvironment, -  makeTestPayment, -  withdrawViaBank, -} 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 sync = await SyncService.create(t, { -    currency: "TESTKUDOS", -    annualFee: "TESTKUDOS:0.5", -    database: commonDb.connStr, -    fulfillmentUrl: "taler://fulfillment-success", -    httpPort: 8089, -    name: "sync1", -    paymentBackendUrl: merchant.makeInstanceBaseUrl(), -    uploadLimitMb: 10, -  }); - -  await sync.start(); -  await sync.pingUntilAvailable(); - -  await wallet.client.call(WalletApiOperation.AddBackupProvider, { -    backupProviderBaseUrl: sync.baseUrl, -    activate: true, -    name: sync.baseUrl, -  }); - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" }); - -  await wallet.runUntilDone(); - -  await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); -  await wallet.runUntilDone(); -  await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); - -  const backupRecovery = await wallet.client.call( -    WalletApiOperation.ExportBackupRecovery, -    {}, -  ); - -  const wallet2 = new WalletCli(t, "wallet2"); - -  await wallet2.client.call(WalletApiOperation.ImportBackupRecovery, { -    recovery: backupRecovery, -  }); - -  await wallet2.client.call(WalletApiOperation.RunBackupCycle, {}); - -  console.log( -    "wallet1 balance before spend:", -    await wallet.client.call(WalletApiOperation.GetBalances, {}), -  ); - -  await makeTestPayment(t, { -    merchant, -    wallet, -    order: { -      summary: "foo", -      amount: "TESTKUDOS:7", -    }, -  }); - -  await wallet.runUntilDone(); - -  console.log( -    "wallet1 balance after spend:", -    await wallet.client.call(WalletApiOperation.GetBalances, {}), -  ); - -  { -    console.log( -      "wallet2 balance:", -      await wallet2.client.call(WalletApiOperation.GetBalances, {}), -    ); -  } - -  // Now we double-spend with the second wallet - -  { -    const instance = "default"; - -    const orderResp = await MerchantPrivateApi.createOrder(merchant, instance, { -      order: { -        amount: "TESTKUDOS:8", -        summary: "bla", -        fulfillment_url: "taler://fulfillment-success", -      }, -    }); - -    let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( -      merchant, -      { -        orderId: orderResp.order_id, -      }, -    ); - -    t.assertTrue(orderStatus.order_status === "unpaid"); - -    // Make wallet pay for the order - -    { -      console.log( -        "wallet2 balance before preparePay:", -        await wallet2.client.call(WalletApiOperation.GetBalances, {}), -      ); -    } - -    const preparePayResult = await wallet2.client.call( -      WalletApiOperation.PreparePayForUri, -      { -        talerPayUri: orderStatus.taler_pay_uri, -      }, -    ); - -    t.assertDeepEqual( -      preparePayResult.status, -      PreparePayResultType.PaymentPossible, -    ); - -    const res = await wallet2.client.call(WalletApiOperation.ConfirmPay, { -      proposalId: preparePayResult.proposalId, -    }); - -    console.log(res); - -    // FIXME: wait for a notification that indicates insufficient funds! - -    await withdrawViaBank(t, { -      wallet: wallet2, -      bank, -      exchange, -      amount: "TESTKUDOS:50", -    }); - -    const bal = await wallet2.client.call(WalletApiOperation.GetBalances, {}); -    console.log("bal", bal); - -    await wallet2.runUntilDone(); -  } -} - -runWalletBackupDoublespendTest.suites = ["wallet", "wallet-backup"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallet-balance.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-balance.ts deleted file mode 100644 index f5226c6c0..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-balance.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { Duration, PreparePayResultType } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; -import { -  ExchangeService, -  FakebankService, -  getRandomIban, -  GlobalTestState, -  MerchantPrivateApi, -  MerchantService, -  setupDb, -  WalletCli, -} from "../harness/harness.js"; -import { withdrawViaBank } from "../harness/helpers.js"; - -/** - * Test for wallet balance error messages / different types of insufficient balance. - * - * Related bugs: - * https://bugs.taler.net/n/7299 - */ -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); - -  // Withdraw digital cash into the wallet. - -  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); - -  const order = { -    summary: "Buy me!", -    amount: "TESTKUDOS:5", -    fulfillment_url: "taler://fulfillment-success/thx", -  }; - -  const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { -    order, -  }); - -  let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { -    orderId: orderResp.order_id, -  }); - -  t.assertTrue(orderStatus.order_status === "unpaid"); - -  // Make wallet pay for the order - -  const preparePayResult = await wallet.client.call( -    WalletApiOperation.PreparePayForUri, -    { -      talerPayUri: orderStatus.taler_pay_uri, -    }, -  ); - -  t.assertDeepEqual( -    preparePayResult.status, -    PreparePayResultType.InsufficientBalance, -  ); - -  await wallet.runUntilDone(); -} - -runWalletBalanceTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallet-cryptoworker.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-cryptoworker.ts deleted file mode 100644 index a9f1c4d80..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-cryptoworker.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { j2s } from "@gnu-taler/taler-util"; -import { -  checkReserve, -  CryptoDispatcher, -  depositCoin, -  downloadExchangeInfo, -  findDenomOrThrow, -  NodeHttpLib, -  refreshCoin, -  SynchronousCryptoWorkerFactoryNode, -  TalerError, -  topupReserveWithDemobank, -  WalletApiOperation, -  withdrawCoin, -} from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState, WalletCli } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; - -/** - * Run test for the different crypto workers. - */ -export async function runWalletCryptoWorkerTest(t: GlobalTestState) { -  const wallet1 = new WalletCli(t, "w1", { -    cryptoWorkerType: "sync", -  }); - -  await wallet1.client.call(WalletApiOperation.TestCrypto, {}); - -  const wallet2 = new WalletCli(t, "w2", { -    cryptoWorkerType: "node-worker-thread", -  }); - -  await wallet2.client.call(WalletApiOperation.TestCrypto, {}); -} - -runWalletCryptoWorkerTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts deleted file mode 100644 index 269a8b240..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts +++ /dev/null @@ -1,112 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { j2s } from "@gnu-taler/taler-util"; -import { -  checkReserve, -  CryptoDispatcher, -  depositCoin, -  downloadExchangeInfo, -  findDenomOrThrow, -  NodeHttpLib, -  refreshCoin, -  SynchronousCryptoWorkerFactoryNode, -  TalerError, -  topupReserveWithDemobank, -  withdrawCoin, -} from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal and payment. - */ -export async function runWalletDblessTest(t: GlobalTestState) { -  // Set up test environment - -  const { bank, exchange } = await createSimpleTestkudosEnvironment(t); - -  const http = new NodeHttpLib(); -  const cryptiDisp = new CryptoDispatcher(new SynchronousCryptoWorkerFactoryNode()); -  const cryptoApi = cryptiDisp.cryptoApi; - -  try { -    // Withdraw digital cash into the wallet. - -    const exchangeInfo = await downloadExchangeInfo(exchange.baseUrl, http); - -    const reserveKeyPair = await cryptoApi.createEddsaKeypair({}); - -    await topupReserveWithDemobank( -      http, -      reserveKeyPair.pub, -      bank.baseUrl, -      bank.bankAccessApiBaseUrl, -      exchangeInfo, -      "TESTKUDOS:10", -    ); - -    await exchange.runWirewatchOnce(); - -    await checkReserve(http, exchange.baseUrl, reserveKeyPair.pub); - -    const d1 = findDenomOrThrow(exchangeInfo, "TESTKUDOS:8"); - -    const coin = await withdrawCoin({ -      http, -      cryptoApi, -      reserveKeyPair: { -        reservePriv: reserveKeyPair.priv, -        reservePub: reserveKeyPair.pub, -      }, -      denom: d1, -      exchangeBaseUrl: exchange.baseUrl, -    }); - -    await depositCoin({ -      amount: "TESTKUDOS:4", -      coin: coin, -      cryptoApi, -      exchangeBaseUrl: exchange.baseUrl, -      http, -    }); - -    const refreshDenoms = [ -      findDenomOrThrow(exchangeInfo, "TESTKUDOS:1"), -      findDenomOrThrow(exchangeInfo, "TESTKUDOS:1"), -    ]; - -    await refreshCoin({ -      oldCoin: coin, -      cryptoApi, -      http, -      newDenoms: refreshDenoms, -    }); -  } catch (e) { -    if (e instanceof TalerError) { -      console.log(e); -      console.log(j2s(e.errorDetail)); -    } else { -      console.log(e); -    } -    throw e; -  } -} - -runWalletDblessTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts deleted file mode 100644 index 03c446db3..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts +++ /dev/null @@ -1,233 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Integration test for the wallet testing functionality used by the exchange - * test cases. - */ - -/** - * Imports. - */ -import { Amounts, CoinStatus } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; -import { -  BankService, -  ExchangeService, -  GlobalTestState, -  MerchantService, -  setupDb, -  WalletCli, -  getPayto, -} from "../harness/harness.js"; -import { SimpleTestEnvironment } from "../harness/helpers.js"; - -const merchantAuthToken = "secret-token:sandbox"; - -/** - * Run a test case with a simple TESTKUDOS Taler environment, consisting - * of one exchange, one bank and one merchant. - */ -export async function createMyEnvironment( -  t: GlobalTestState, -  coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")), -): Promise<SimpleTestEnvironment> { -  const db = await setupDb(t); - -  const bank = await BankService.create(t, { -    allowRegistrations: true, -    currency: "TESTKUDOS", -    database: db.connStr, -    httpPort: 8082, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  const merchant = await MerchantService.create(t, { -    name: "testmerchant-1", -    currency: "TESTKUDOS", -    httpPort: 8083, -    database: db.connStr, -  }); - -  const exchangeBankAccount = await bank.createExchangeAccount( -    "myexchange", -    "x", -  ); -  exchange.addBankAccount("1", exchangeBankAccount); - -  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  exchange.addCoinConfigList(coinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  merchant.addExchange(exchange); - -  await merchant.start(); -  await merchant.pingUntilAvailable(); - -  await merchant.addInstance({ -    id: "default", -    name: "Default Instance", -    paytoUris: [getPayto("merchant-default")], -  }); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  return { -    commonDb: db, -    exchange, -    merchant, -    wallet, -    bank, -    exchangeBankAccount, -  }; -} - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runWallettestingTest(t: GlobalTestState) { -  const { wallet, bank, exchange, merchant } = await createMyEnvironment(t); - -  await wallet.client.call(WalletApiOperation.RunIntegrationTest, { -    amountToSpend: "TESTKUDOS:5", -    amountToWithdraw: "TESTKUDOS:10", -    bankBaseUrl: bank.baseUrl, -    bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl, -    exchangeBaseUrl: exchange.baseUrl, -    merchantAuthToken: merchantAuthToken, -    merchantBaseUrl: merchant.makeInstanceBaseUrl(), -  }); - -  let txns = await wallet.client.call(WalletApiOperation.GetTransactions, {}); -  console.log(JSON.stringify(txns, undefined, 2)); -  let txTypes = txns.transactions.map((x) => x.type); - -  t.assertDeepEqual(txTypes, [ -    "withdrawal", -    "payment", -    "withdrawal", -    "payment", -    "refund", -    "payment", -  ]); - -  wallet.deleteDatabase(); - -  await wallet.client.call(WalletApiOperation.WithdrawTestBalance, { -    amount: "TESTKUDOS:10", -    bankBaseUrl: bank.baseUrl, -    bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl, -    exchangeBaseUrl: exchange.baseUrl, -  }); - -  await wallet.runUntilDone(); - -  await wallet.client.call(WalletApiOperation.TestPay, { -    amount: "TESTKUDOS:5", -    merchantAuthToken: merchantAuthToken, -    merchantBaseUrl: merchant.makeInstanceBaseUrl(), -    summary: "foo", -  }); - -  await wallet.runUntilDone(); - -  txns = await wallet.client.call(WalletApiOperation.GetTransactions, {}); -  console.log(JSON.stringify(txns, undefined, 2)); -  txTypes = txns.transactions.map((x) => x.type); - -  t.assertDeepEqual(txTypes, ["withdrawal", "payment"]); - -  wallet.deleteDatabase(); - -  await wallet.client.call(WalletApiOperation.WithdrawTestBalance, { -    amount: "TESTKUDOS:10", -    bankBaseUrl: bank.baseUrl, -    bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl, -    exchangeBaseUrl: exchange.baseUrl, -  }); - -  await wallet.runUntilDone(); - -  const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {}); - -  console.log("coin dump:", JSON.stringify(coinDump, undefined, 2)); - -  let susp: string | undefined; -  { -    for (const c of coinDump.coins) { -      if ( -        c.coin_status === CoinStatus.Fresh && -        0 === Amounts.cmp(c.denom_value, "TESTKUDOS:8") -      ) { -        susp = c.coin_pub; -      } -    } -  } - -  t.assertTrue(susp !== undefined); - -  console.log("suspending coin"); - -  await wallet.client.call(WalletApiOperation.SetCoinSuspended, { -    coinPub: susp, -    suspended: true, -  }); - -  // This should fail, as we've suspended a coin that we need -  // to pay. -  await t.assertThrowsAsync(async () => { -    await wallet.client.call(WalletApiOperation.TestPay, { -      amount: "TESTKUDOS:5", -      merchantAuthToken: merchantAuthToken, -      merchantBaseUrl: merchant.makeInstanceBaseUrl(), -      summary: "foo", -    }); -  }); - -  console.log("unsuspending coin"); - -  await wallet.client.call(WalletApiOperation.SetCoinSuspended, { -    coinPub: susp, -    suspended: false, -  }); - -  await wallet.client.call(WalletApiOperation.TestPay, { -    amount: "TESTKUDOS:5", -    merchantAuthToken: merchantAuthToken, -    merchantBaseUrl: merchant.makeInstanceBaseUrl(), -    summary: "foo", -  }); - -  await t.shutdown(); -} - -runWallettestingTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts deleted file mode 100644 index bf2dc0133..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { TalerErrorCode } from "@gnu-taler/taler-util"; -import { -  WalletApiOperation, -  BankApi, -  BankAccessApi, -} from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runWithdrawalAbortBankTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange } = await createSimpleTestkudosEnvironment(t); - -  // Create a withdrawal operation - -  const user = await BankApi.createRandomBankUser(bank); -  const wop = await BankAccessApi.createWithdrawalOperation( -    bank, -    user, -    "TESTKUDOS:10", -  ); - -  // Hand it to the wallet - -  await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, { -    talerWithdrawUri: wop.taler_withdraw_uri, -  }); - -  await wallet.runPending(); - -  // Abort it - -  await BankApi.abortWithdrawalOperation(bank, user, wop); -  //await BankApi.confirmWithdrawalOperation(bank, user, wop); - -  // Withdraw - -  // Difference: -  // -> with euFin, the wallet selects -  // -> with PyBank, the wallet stops _before_ -  // -  // WHY ?! -  // -  const e = await t.assertThrowsTalerErrorAsync(async () => { -    await wallet.client.call( -      WalletApiOperation.AcceptBankIntegratedWithdrawal, -      { -        exchangeBaseUrl: exchange.baseUrl, -        talerWithdrawUri: wop.taler_withdraw_uri, -      }, -    ); -  }); -  t.assertDeepEqual( -    e.errorDetail.code, -    TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK, -  ); - -  await t.shutdown(); -} - -runWithdrawalAbortBankTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts deleted file mode 100644 index dc7298e5d..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; -import { -  WalletApiOperation, -  BankApi, -  BankAccessApi, -} from "@gnu-taler/taler-wallet-core"; -import { j2s } from "@gnu-taler/taler-util"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange } = await createSimpleTestkudosEnvironment(t); - -  // Create a withdrawal operation - -  const user = await BankApi.createRandomBankUser(bank); -  const wop = await BankAccessApi.createWithdrawalOperation( -    bank, -    user, -    "TESTKUDOS:10", -  ); - -  // Hand it to the wallet - -  const r1 = await wallet.client.call( -    WalletApiOperation.GetWithdrawalDetailsForUri, -    { -      talerWithdrawUri: wop.taler_withdraw_uri, -    }, -  ); - -  await wallet.runPending(); - -  // Withdraw - -  const r2 = await wallet.client.call( -    WalletApiOperation.AcceptBankIntegratedWithdrawal, -    { -      exchangeBaseUrl: exchange.baseUrl, -      talerWithdrawUri: wop.taler_withdraw_uri, -    }, -  ); -  // Do it twice to check idempotency -  const r3 = await wallet.client.call( -    WalletApiOperation.AcceptBankIntegratedWithdrawal, -    { -      exchangeBaseUrl: exchange.baseUrl, -      talerWithdrawUri: wop.taler_withdraw_uri, -    }, -  ); -  await wallet.runPending(); - -  // Confirm it - -  await BankApi.confirmWithdrawalOperation(bank, user, wop); - -  await wallet.runUntilDone(); - -  // Check balance - -  const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {}); -  t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available); - -  const txn = await wallet.client.call(WalletApiOperation.GetTransactions, {}); -  console.log(`transactions: ${j2s(txn)}`); -} - -runWithdrawalBankIntegratedTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts deleted file mode 100644 index ec6e54e6c..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  GlobalTestState, -  WalletCli, -  setupDb, -  ExchangeService, -  FakebankService, -} from "../harness/harness.js"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; -import { URL } from "@gnu-taler/taler-util"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runWithdrawalFakebankTest(t: GlobalTestState) { -  // Set up test environment - -  const db = await setupDb(t); - -  const bank = await FakebankService.create(t, { -    currency: "TESTKUDOS", -    httpPort: 8082, -    allowRegistrations: true, -    // Not used by fakebank -    database: db.connStr, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  exchange.addBankAccount("1", { -    accountName: "exchange", -    accountPassword: "x", -    wireGatewayApiBaseUrl: new URL("/exchange/", bank.baseUrl).href, -    accountPaytoUri: "payto://x-taler-bank/localhost/exchange", -  }); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")); -  exchange.addCoinConfigList(coinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  await wallet.client.call(WalletApiOperation.AddExchange, { -    exchangeBaseUrl: exchange.baseUrl, -  }); - -  await wallet.client.call(WalletApiOperation.WithdrawFakebank, { -    exchange: exchange.baseUrl, -    amount: "TESTKUDOS:10", -    bank: bank.baseUrl, -  }); - -  await exchange.runWirewatchOnce(); - -  await wallet.runUntilDone(); - -  // Check balance - -  const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {}); -  t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available); - -  await t.shutdown(); -} - -runWithdrawalFakebankTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-high.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-high.ts deleted file mode 100644 index deb0e6dde..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-high.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { -  GlobalTestState, -  WalletCli, -  setupDb, -  ExchangeService, -  FakebankService, -} from "../harness/harness.js"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; -import { URL } from "@gnu-taler/taler-util"; - -/** - * Withdraw a high amount.  Mostly intended - * as a perf test. - */ -export async function runWithdrawalHighTest(t: GlobalTestState) { -  // Set up test environment - -  const db = await setupDb(t); - -  const bank = await FakebankService.create(t, { -    currency: "TESTKUDOS", -    httpPort: 8082, -    allowRegistrations: true, -    // Not used by fakebank -    database: db.connStr, -  }); - -  const exchange = ExchangeService.create(t, { -    name: "testexchange-1", -    currency: "TESTKUDOS", -    httpPort: 8081, -    database: db.connStr, -  }); - -  exchange.addBankAccount("1", { -    accountName: "exchange", -    accountPassword: "x", -    wireGatewayApiBaseUrl: new URL("/exchange/", bank.baseUrl).href, -    accountPaytoUri: "payto://x-taler-bank/localhost/exchange", -  }); - -  await bank.start(); - -  await bank.pingUntilAvailable(); - -  const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")); -  exchange.addCoinConfigList(coinConfig); - -  await exchange.start(); -  await exchange.pingUntilAvailable(); - -  console.log("setup done!"); - -  const wallet = new WalletCli(t); - -  await wallet.client.call(WalletApiOperation.AddExchange, { -    exchangeBaseUrl: exchange.baseUrl, -  }); - -  await wallet.client.call(WalletApiOperation.WithdrawFakebank, { -    exchange: exchange.baseUrl, -    amount: "TESTKUDOS:5000", -    bank: bank.baseUrl, -  }); - -  await exchange.runWirewatchOnce(); - -  await wallet.runUntilDone(); - -  // Check balance - -  const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {}); -  console.log(balResp); - -  await t.shutdown(); -} - -runWithdrawalHighTest.suites = ["wallet-perf"]; -runWithdrawalHighTest.excludeByDefault = true; diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts deleted file mode 100644 index b691ae508..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { GlobalTestState } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; -import { WalletApiOperation, BankApi } from "@gnu-taler/taler-wallet-core"; -import { -  AbsoluteTime, -  Duration, -  TalerProtocolTimestamp, -} from "@gnu-taler/taler-util"; - -/** - * Run test for basic, bank-integrated withdrawal. - */ -export async function runTestWithdrawalManualTest(t: GlobalTestState) { -  // Set up test environment - -  const { wallet, bank, exchange, exchangeBankAccount } = -    await createSimpleTestkudosEnvironment(t); - -  // Create a withdrawal operation - -  const user = await BankApi.createRandomBankUser(bank); - -  await wallet.client.call(WalletApiOperation.AddExchange, { -    exchangeBaseUrl: exchange.baseUrl, -  }); - -  const tStart = AbsoluteTime.now(); - -  // We expect this to return immediately. -  const wres = await wallet.client.call( -    WalletApiOperation.AcceptManualWithdrawal, -    { -      exchangeBaseUrl: exchange.baseUrl, -      amount: "TESTKUDOS:10", -    }, -  ); - -  // Check that the request did not go into long-polling. -  const duration = AbsoluteTime.difference(tStart, AbsoluteTime.now()); -  if (duration.d_ms > 5 * 1000) { -    throw Error("withdrawal took too long (longpolling issue)"); -  } - -  const reservePub: string = wres.reservePub; - -  await BankApi.adminAddIncoming(bank, { -    exchangeBankAccount, -    amount: "TESTKUDOS:10", -    debitAccountPayto: user.accountPaytoUri, -    reservePub: reservePub, -  }); - -  await exchange.runWirewatchOnce(); - -  await wallet.runUntilDone(); - -  // Check balance - -  const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {}); -  t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available); - -  await t.shutdown(); -} - -runTestWithdrawalManualTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts deleted file mode 100644 index 4b1c28bde..000000000 --- a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts +++ /dev/null @@ -1,496 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -import { CancellationToken, minimatch } from "@gnu-taler/taler-util"; -import * as child_process from "child_process"; -import * as fs from "fs"; -import * as os from "os"; -import * as path from "path"; -import url from "url"; -import { -  GlobalTestState, -  runTestWithState, -  shouldLingerInTest, -  TestRunResult, -} from "../harness/harness.js"; -import { runAgeRestrictionsMerchantTest } from "./test-age-restrictions-merchant.js"; -import { runBankApiTest } from "./test-bank-api.js"; -import { runClaimLoopTest } from "./test-claim-loop.js"; -import { runClauseSchnorrTest } from "./test-clause-schnorr.js"; -import { runDenomUnofferedTest } from "./test-denom-unoffered.js"; -import { runDepositTest } from "./test-deposit.js"; -import { runExchangeManagementTest } from "./test-exchange-management.js"; -import { runExchangeTimetravelTest } from "./test-exchange-timetravel.js"; -import { runFeeRegressionTest } from "./test-fee-regression.js"; -import { runForcedSelectionTest } from "./test-forced-selection.js"; -import { runLibeufinApiBankaccountTest } from "./test-libeufin-api-bankaccount.js"; -import { runLibeufinApiBankconnectionTest } from "./test-libeufin-api-bankconnection.js"; -import { runLibeufinApiFacadeTest } from "./test-libeufin-api-facade.js"; -import { runLibeufinApiFacadeBadRequestTest } from "./test-libeufin-api-facade-bad-request.js"; -import { runLibeufinApiPermissionsTest } from "./test-libeufin-api-permissions.js"; -import { runLibeufinApiSandboxCamtTest } from "./test-libeufin-api-sandbox-camt.js"; -import { runLibeufinApiSandboxTransactionsTest } from "./test-libeufin-api-sandbox-transactions.js"; -import { runLibeufinApiSchedulingTest } from "./test-libeufin-api-scheduling.js"; -import { runLibeufinApiUsersTest } from "./test-libeufin-api-users.js"; -import { runLibeufinBadGatewayTest } from "./test-libeufin-bad-gateway.js"; -import { runLibeufinBasicTest } from "./test-libeufin-basic.js"; -import { runLibeufinC5xTest } from "./test-libeufin-c5x.js"; -import { runLibeufinAnastasisFacadeTest } from "./test-libeufin-facade-anastasis.js"; -import { runLibeufinKeyrotationTest } from "./test-libeufin-keyrotation.js"; -import { runLibeufinNexusBalanceTest } from "./test-libeufin-nexus-balance.js"; -import { runLibeufinRefundTest } from "./test-libeufin-refund.js"; -import { runLibeufinRefundMultipleUsersTest } from "./test-libeufin-refund-multiple-users.js"; -import { runLibeufinSandboxWireTransferCliTest } from "./test-libeufin-sandbox-wire-transfer-cli.js"; -import { runLibeufinTutorialTest } from "./test-libeufin-tutorial.js"; -import { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion.js"; -import { runMerchantInstancesTest } from "./test-merchant-instances.js"; -import { runMerchantInstancesDeleteTest } from "./test-merchant-instances-delete.js"; -import { runMerchantInstancesUrlsTest } from "./test-merchant-instances-urls.js"; -import { runMerchantLongpollingTest } from "./test-merchant-longpolling.js"; -import { runMerchantRefundApiTest } from "./test-merchant-refund-api.js"; -import { runMerchantSpecPublicOrdersTest } from "./test-merchant-spec-public-orders.js"; -import { runPayPaidTest } from "./test-pay-paid.js"; -import { runPaymentTest } from "./test-payment.js"; -import { runPaymentClaimTest } from "./test-payment-claim.js"; -import { runPaymentFaultTest } from "./test-payment-fault.js"; -import { runPaymentForgettableTest } from "./test-payment-forgettable.js"; -import { runPaymentIdempotencyTest } from "./test-payment-idempotency.js"; -import { runPaymentMultipleTest } from "./test-payment-multiple.js"; -import { runPaymentDemoTest } from "./test-payment-on-demo.js"; -import { runPaymentTransientTest } from "./test-payment-transient.js"; -import { runPaymentZeroTest } from "./test-payment-zero.js"; -import { runPaywallFlowTest } from "./test-paywall-flow.js"; -import { runPeerToPeerPullTest } from "./test-peer-to-peer-pull.js"; -import { runPeerToPeerPushTest } from "./test-peer-to-peer-push.js"; -import { runRefundTest } from "./test-refund.js"; -import { runRefundAutoTest } from "./test-refund-auto.js"; -import { runRefundGoneTest } from "./test-refund-gone.js"; -import { runRefundIncrementalTest } from "./test-refund-incremental.js"; -import { runRevocationTest } from "./test-revocation.js"; -import { runTimetravelAutorefreshTest } from "./test-timetravel-autorefresh.js"; -import { runTimetravelWithdrawTest } from "./test-timetravel-withdraw.js"; -import { runTippingTest } from "./test-tipping.js"; -import { runWalletBackupBasicTest } from "./test-wallet-backup-basic.js"; -import { runWalletBackupDoublespendTest } from "./test-wallet-backup-doublespend.js"; -import { runWalletDblessTest } from "./test-wallet-dbless.js"; -import { runWallettestingTest } from "./test-wallettesting.js"; -import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank.js"; -import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated.js"; -import { runWithdrawalFakebankTest } from "./test-withdrawal-fakebank.js"; -import { runTestWithdrawalManualTest } from "./test-withdrawal-manual.js"; -import { runAgeRestrictionsPeerTest } from "./test-age-restrictions-peer.js"; -import { runWalletBalanceTest } from "./test-wallet-balance.js"; -import { runAgeRestrictionsMixedMerchantTest } from "./test-age-restrictions-mixed-merchant.js"; -import { runWalletCryptoWorkerTest } from "./test-wallet-cryptoworker.js"; -import { runWithdrawalHighTest } from "./test-withdrawal-high.js"; - -/** - * Test runner. - */ - -/** - * Spec for one test. - */ -interface TestMainFunction { -  (t: GlobalTestState): Promise<void>; -  timeoutMs?: number; -  excludeByDefault?: boolean; -  suites?: string[]; -} - -const allTests: TestMainFunction[] = [ -  runAgeRestrictionsMerchantTest, -  runAgeRestrictionsPeerTest, -  runAgeRestrictionsMixedMerchantTest, -  runBankApiTest, -  runClaimLoopTest, -  runClauseSchnorrTest, -  runWalletCryptoWorkerTest, -  runDepositTest, -  runDenomUnofferedTest, -  runExchangeManagementTest, -  runExchangeTimetravelTest, -  runFeeRegressionTest, -  runForcedSelectionTest, -  runLibeufinBasicTest, -  runLibeufinKeyrotationTest, -  runLibeufinTutorialTest, -  runLibeufinRefundTest, -  runLibeufinC5xTest, -  runLibeufinNexusBalanceTest, -  runLibeufinBadGatewayTest, -  runLibeufinRefundMultipleUsersTest, -  runLibeufinApiPermissionsTest, -  runLibeufinApiFacadeTest, -  runLibeufinApiFacadeBadRequestTest, -  runLibeufinAnastasisFacadeTest, -  runLibeufinApiSchedulingTest, -  runLibeufinApiUsersTest, -  runLibeufinApiBankaccountTest, -  runLibeufinApiBankconnectionTest, -  runLibeufinApiSandboxTransactionsTest, -  runLibeufinApiSandboxCamtTest, -  runLibeufinSandboxWireTransferCliTest, -  runMerchantExchangeConfusionTest, -  runMerchantInstancesTest, -  runMerchantInstancesDeleteTest, -  runMerchantInstancesUrlsTest, -  runMerchantLongpollingTest, -  runMerchantSpecPublicOrdersTest, -  runMerchantRefundApiTest, -  runPaymentClaimTest, -  runPaymentFaultTest, -  runPaymentForgettableTest, -  runPaymentIdempotencyTest, -  runPaymentMultipleTest, -  runPaymentTest, -  runPaymentDemoTest, -  runPaymentTransientTest, -  runPaymentZeroTest, -  runPayPaidTest, -  runPaywallFlowTest, -  runPeerToPeerPushTest, -  runPeerToPeerPullTest, -  runRefundAutoTest, -  runRefundGoneTest, -  runRefundIncrementalTest, -  runRefundTest, -  runRevocationTest, -  runTestWithdrawalManualTest, -  runWithdrawalFakebankTest, -  runTimetravelAutorefreshTest, -  runTimetravelWithdrawTest, -  runTippingTest, -  runWalletBackupBasicTest, -  runWalletBackupDoublespendTest, -  runWalletBalanceTest, -  runWithdrawalHighTest, -  runWallettestingTest, -  runWalletDblessTest, -  runWithdrawalAbortBankTest, -  runWithdrawalBankIntegratedTest, -]; - -export interface TestRunSpec { -  includePattern?: string; -  suiteSpec?: string; -  dryRun?: boolean; -  verbosity: number; -} - -export interface TestInfo { -  name: string; -  suites: string[]; -  excludeByDefault: boolean; -} - -function updateCurrentSymlink(testDir: string): void { -  const currLink = path.join( -    os.tmpdir(), -    `taler-integrationtests-${os.userInfo().username}-current`, -  ); -  try { -    fs.unlinkSync(currLink); -  } catch (e) { -    // Ignore -  } -  try { -    fs.symlinkSync(testDir, currLink); -  } catch (e) { -    console.log(e); -    // Ignore -  } -} - -export function getTestName(tf: TestMainFunction): string { -  const res = tf.name.match(/run([a-zA-Z0-9]*)Test/); -  if (!res) { -    throw Error("invalid test name, must be 'run${NAME}Test'"); -  } -  return res[1] -    .replace(/[a-z0-9][A-Z]/g, (x) => { -      return x[0] + "-" + x[1]; -    }) -    .toLowerCase(); -} - -interface RunTestChildInstruction { -  testName: string; -  testRootDir: string; -} - -export async function runTests(spec: TestRunSpec) { -  const testRootDir = fs.mkdtempSync( -    path.join(os.tmpdir(), "taler-integrationtests-"), -  ); -  updateCurrentSymlink(testRootDir); -  console.log(`testsuite root directory: ${testRootDir}`); - -  const testResults: TestRunResult[] = []; - -  let currentChild: child_process.ChildProcess | undefined; - -  const handleSignal = (s: NodeJS.Signals) => { -    console.log(`received signal ${s} in test parent`); -    if (currentChild) { -      currentChild.kill("SIGTERM"); -    } -    reportAndQuit(testRootDir, testResults, true); -  }; - -  process.on("SIGINT", (s) => handleSignal(s)); -  process.on("SIGTERM", (s) => handleSignal(s)); -  //process.on("unhandledRejection", handleSignal); -  //process.on("uncaughtException", handleSignal); - -  let suites: Set<string> | undefined; - -  if (spec.suiteSpec) { -    suites = new Set(spec.suiteSpec.split(",").map((x) => x.trim())); -  } - -  for (const [n, testCase] of allTests.entries()) { -    const testName = getTestName(testCase); -    if (spec.includePattern && !minimatch(testName, spec.includePattern)) { -      continue; -    } - -    if (suites) { -      const ts = new Set(testCase.suites ?? []); -      const intersection = new Set([...suites].filter((x) => ts.has(x))); -      if (intersection.size === 0) { -        continue; -      } -    } else { -      if (testCase.excludeByDefault) { -        continue; -      } -    } - -    if (spec.dryRun) { -      console.log(`dry run: would run test ${testName}`); -      continue; -    } - -    const testInstr: RunTestChildInstruction = { -      testName, -      testRootDir, -    }; - -    const myFilename = url.fileURLToPath(import.meta.url); - -    currentChild = child_process.fork(myFilename, ["__TWCLI_TESTWORKER"], { -      env: { -        TWCLI_RUN_TEST_INSTRUCTION: JSON.stringify(testInstr), -        ...process.env, -      }, -      stdio: ["pipe", "pipe", "pipe", "ipc"], -    }); - -    const testDir = path.join(testRootDir, testName); -    fs.mkdirSync(testDir, { recursive: true }); - -    const harnessLogFilename = path.join(testRootDir, testName, "harness.log"); -    const harnessLogStream = fs.createWriteStream(harnessLogFilename); - -    if (spec.verbosity > 0) { -      currentChild.stderr?.pipe(process.stderr); -      currentChild.stdout?.pipe(process.stdout); -    } - -    currentChild.stdout?.pipe(harnessLogStream); -    currentChild.stderr?.pipe(harnessLogStream); - -    const defaultTimeout = 60000; -    const testTimeoutMs = testCase.timeoutMs ?? defaultTimeout; - -    console.log(`running ${testName} with timeout ${testTimeoutMs}ms`); - -    const { token } = CancellationToken.timeout(testTimeoutMs); - -    const resultPromise: Promise<TestRunResult> = new Promise( -      (resolve, reject) => { -        let msg: TestRunResult | undefined; -        currentChild!.on("message", (m) => { -          if (token.isCancelled) { -            return; -          } -          msg = m as TestRunResult; -        }); -        currentChild!.on("exit", (code, signal) => { -          if (token.isCancelled) { -            return; -          } -          console.log(`process exited code=${code} signal=${signal}`); -          if (signal) { -            reject(new Error(`test worker exited with signal ${signal}`)); -          } else if (code != 0) { -            reject(new Error(`test worker exited with code ${code}`)); -          } else if (!msg) { -            reject( -              new Error( -                `test worker exited without giving back the test results`, -              ), -            ); -          } else { -            resolve(msg); -          } -        }); -        currentChild!.on("error", (err) => { -          if (token.isCancelled) { -            return; -          } -          reject(err); -        }); -      }, -    ); - -    let result: TestRunResult; - -    try { -      result = await token.racePromise(resultPromise); -    } catch (e: any) { -      console.error(`test ${testName} timed out`); -      if (token.isCancelled) { -        result = { -          status: "fail", -          reason: "timeout", -          timeSec: testTimeoutMs / 1000, -          name: testName, -        }; -        currentChild.kill("SIGTERM"); -      } else { -        throw Error(e); -      } -    } - -    harnessLogStream.close(); - -    console.log(`parent: got result ${JSON.stringify(result)}`); - -    testResults.push(result); -  } - -  reportAndQuit(testRootDir, testResults); -} - -export function reportAndQuit( -  testRootDir: string, -  testResults: TestRunResult[], -  interrupted: boolean = false, -): never { -  let numTotal = 0; -  let numFail = 0; -  let numSkip = 0; -  let numPass = 0; - -  for (const result of testResults) { -    numTotal++; -    if (result.status === "fail") { -      numFail++; -    } else if (result.status === "skip") { -      numSkip++; -    } else if (result.status === "pass") { -      numPass++; -    } -  } - -  const resultsFile = path.join(testRootDir, "results.json"); -  fs.writeFileSync( -    path.join(testRootDir, "results.json"), -    JSON.stringify({ testResults, interrupted }, undefined, 2), -  ); -  if (interrupted) { -    console.log("test suite was interrupted"); -  } -  console.log(`See ${resultsFile} for details`); -  console.log(`Skipped: ${numSkip}/${numTotal}`); -  console.log(`Failed: ${numFail}/${numTotal}`); -  console.log(`Passed: ${numPass}/${numTotal}`); - -  if (interrupted) { -    process.exit(3); -  } else if (numPass < numTotal - numSkip) { -    process.exit(1); -  } else { -    process.exit(0); -  } -} - -export function getTestInfo(): TestInfo[] { -  return allTests.map((x) => ({ -    name: getTestName(x), -    suites: x.suites ?? [], -    excludeByDefault: x.excludeByDefault ?? false, -  })); -} - -const runTestInstrStr = process.env["TWCLI_RUN_TEST_INSTRUCTION"]; -if (runTestInstrStr && process.argv.includes("__TWCLI_TESTWORKER")) { -  // Test will call taler-wallet-cli, so we must not propagate this variable. -  delete process.env["TWCLI_RUN_TEST_INSTRUCTION"]; -  const { testRootDir, testName } = JSON.parse( -    runTestInstrStr, -  ) as RunTestChildInstruction; -  console.log(`running test ${testName} in worker process`); - -  process.on("disconnect", () => { -    console.log("got disconnect from parent"); -    process.exit(3); -  }); - -  const runTest = async () => { -    let testMain: TestMainFunction | undefined; -    for (const t of allTests) { -      if (getTestName(t) === testName) { -        testMain = t; -        break; -      } -    } - -    if (!process.send) { -      console.error("can't communicate with parent"); -      process.exit(2); -    } - -    if (!testMain) { -      console.log(`test ${testName} not found`); -      process.exit(2); -    } - -    const testDir = path.join(testRootDir, testName); -    console.log(`running test ${testName}`); -    const gc = new GlobalTestState({ -      testDir, -    }); -    const testResult = await runTestWithState(gc, testMain, testName); -    process.send(testResult); -  }; - -  runTest() -    .then(() => { -      console.log(`test ${testName} finished in worker`); -      if (shouldLingerInTest()) { -        console.log("lingering ..."); -        return; -      } -      process.exit(0); -    }) -    .catch((e) => { -      console.log(e); -      process.exit(1); -    }); -} diff --git a/packages/taler-wallet-cli/src/lint.ts b/packages/taler-wallet-cli/src/lint.ts deleted file mode 100644 index 49fb9dc86..000000000 --- a/packages/taler-wallet-cli/src/lint.ts +++ /dev/null @@ -1,534 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * The deployment linter implements checks for a deployment - * of the GNU Taler exchange.  It is meant to help sysadmins - * when setting up an exchange. - * - * The linter does checks in the configuration and uses - * various tools of the exchange in test mode (-t). - * - * To be able to run the tools as the right user, the linter should be - * run as root. - * - * @author Florian Dold <dold@taler.net> - */ - -/** - * Imports. - */ -import { -  codecForExchangeKeysJson, -  codecForKeysManagementResponse, -  Configuration, -  decodeCrock, -} from "@gnu-taler/taler-util"; -import { -  NodeHttpLib, -  readSuccessResponseJsonOrThrow, -} from "@gnu-taler/taler-wallet-core"; -import { URL } from "url"; -import { spawn } from "child_process"; -import { delayMs } from "./harness/harness.js"; - -interface BasicConf { -  mainCurrency: string; -} - -interface PubkeyConf { -  masterPublicKey: string; -} - -const httpLib = new NodeHttpLib(); - -interface ShellResult { -  stdout: string; -  stderr: string; -  status: number; -} - -interface LintContext { -  /** -   * Be more verbose. -   */ -  verbose: boolean; - -  /** -   * Always continue even after errors. -   */ -  cont: boolean; - -  cfg: Configuration; - -  numErr: number; -} - -/** - * Run a shell command, return stdout. - */ -export async function sh( -  context: LintContext, -  command: string, -  env: { [index: string]: string | undefined } = process.env, -): Promise<ShellResult> { -  if (context.verbose) { -    console.log("executing command:", command); -  } -  return new Promise((resolve, reject) => { -    const stdoutChunks: Buffer[] = []; -    const stderrChunks: Buffer[] = []; -    const proc = spawn(command, { -      stdio: ["inherit", "pipe", "pipe"], -      shell: true, -      env: env, -    }); -    proc.stdout.on("data", (x) => { -      if (x instanceof Buffer) { -        stdoutChunks.push(x); -      } else { -        throw Error("unexpected data chunk type"); -      } -    }); -    proc.stderr.on("data", (x) => { -      if (x instanceof Buffer) { -        stderrChunks.push(x); -      } else { -        throw Error("unexpected data chunk type"); -      } -    }); -    proc.on("exit", (code, signal) => { -      if (code != 0 && context.verbose) { -        console.log(`child process exited (${code} / ${signal})`); -      } -      const bOut = Buffer.concat(stdoutChunks).toString("utf-8"); -      const bErr = Buffer.concat(stderrChunks).toString("utf-8"); -      resolve({ -        status: code ?? -1, -        stderr: bErr, -        stdout: bOut, -      }); -    }); -    proc.on("error", () => { -      reject(Error("Child process had error")); -    }); -  }); -} - -function checkBasicConf(context: LintContext): BasicConf { -  const cfg = context.cfg; -  const currencyEntry = cfg.getString("taler", "currency"); -  let mainCurrency: string | undefined; - -  if (!currencyEntry.value) { -    context.numErr++; -    console.log("error: currency not defined in section TALER option CURRENCY"); -    console.log("Aborting further checks."); -    process.exit(1); -  } else { -    mainCurrency = currencyEntry.value.toUpperCase(); -  } - -  if (mainCurrency === "KUDOS") { -    console.log( -      "warning: section TALER option CURRENCY contains toy currency value KUDOS", -    ); -  } - -  const roundUnit = cfg.getAmount("taler", "currency_round_unit"); -  const ru = roundUnit.required(); -  if (ru.currency.toLowerCase() != mainCurrency.toLowerCase()) { -    context.numErr++; -    console.log( -      "error: [TALER]/CURRENCY_ROUND_UNIT: currency does not match main currency", -    ); -  } -  return { mainCurrency }; -} - -function checkCoinConfig(context: LintContext, basic: BasicConf): void { -  const cfg = context.cfg; -  const coinPrefix1 = "COIN_"; -  const coinPrefix2 = "COIN-"; -  let numCoins = 0; - -  for (const secName of cfg.getSectionNames()) { -    if (!(secName.startsWith(coinPrefix1) || secName.startsWith(coinPrefix2))) { -      continue; -    } -    numCoins++; - -    // FIXME: check that section is well-formed -  } - -  if (numCoins == 0) { -    context.numErr++; -    console.log( -      "error: no coin denomination configured, please configure [coin-*] sections", -    ); -  } -} - -async function checkWireConfig(context: LintContext): Promise<void> { -  const cfg = context.cfg; -  const accountPrefix = "EXCHANGE-ACCOUNT-"; -  const accountCredentialsPrefix = "EXCHANGE-ACCOUNTCREDENTIALS-"; - -  let accounts = new Set<string>(); -  let credentials = new Set<string>(); - -  for (const secName of cfg.getSectionNames()) { -    if (secName.startsWith(accountPrefix)) { -      accounts.add(secName.slice(accountPrefix.length)); -      // FIXME: check settings -    } - -    if (secName.startsWith(accountCredentialsPrefix)) { -      credentials.add(secName.slice(accountCredentialsPrefix.length)); -      // FIXME: check settings -    } -  } - -  if (accounts.size === 0) { -    context.numErr++; -    console.log( -      "error: No accounts configured (no sections EXCHANGE-ACCOUNT-*).", -    ); -    if (!context.cont) { -      console.log("Aborting further checks."); -      process.exit(1); -    } -  } - -  for (const acc of accounts) { -    if (!credentials.has(acc)) { -      console.log( -        `warning: no credentials configured for exchange-account-${acc}`, -      ); -    } -  } - -  for (const acc of accounts) { -    // test credit history -    { -      const res = await sh( -        context, -        "su -l --shell /bin/sh " + -          `-c 'taler-exchange-wire-gateway-client -s exchange-accountcredentials-${acc} --credit-history' ` + -          "taler-exchange-wire", -      ); -      if (res.status != 0) { -        context.numErr++; -        console.log(res.stdout); -        console.log(res.stderr); -        console.log( -          "error: Could not run taler-exchange-wire-gateway-client. Please review logs above.", -        ); -        if (!context.cont) { -          console.log("Aborting further checks."); -          process.exit(1); -        } -      } -    } -  } - -  // TWG client -  { -    const res = await sh( -      context, -      `su -l --shell /bin/sh -c 'taler-exchange-wirewatch -t' taler-exchange-wire`, -    ); -    if (res.status != 0) { -      context.numErr++; -      console.log(res.stdout); -      console.log(res.stderr); -      console.log("error: Could not run wirewatch. Please review logs above."); -      if (!context.cont) { -        console.log("Aborting further checks."); -        process.exit(1); -      } -    } -  } - -  // Wirewatch -  { -    const res = await sh( -      context, -      `su -l --shell /bin/sh -c 'taler-exchange-wirewatch -t' taler-exchange-wire`, -    ); -    if (res.status != 0) { -      context.numErr++; -      console.log(res.stdout); -      console.log(res.stderr); -      console.log("error: Could not run wirewatch. Please review logs above."); -      if (!context.cont) { -        console.log("Aborting further checks."); -        process.exit(1); -      } -    } -  } - -  // Closer -  { -    const res = await sh( -      context, -      `su -l --shell /bin/sh -c 'taler-exchange-closer -t' taler-exchange-closer`, -    ); -    if (res.status != 0) { -      context.numErr++; -      console.log(res.stdout); -      console.log(res.stderr); -      console.log("error: Could not run closer. Please review logs above."); -      if (!context.cont) { -        console.log("Aborting further checks."); -        process.exit(1); -      } -    } -  } -} - -async function checkAggregatorConfig(context: LintContext) { -  const res = await sh( -    context, -    "su -l --shell /bin/sh -c 'taler-exchange-aggregator -t' taler-exchange-aggregator", -  ); -  if (res.status != 0) { -    context.numErr++; -    console.log(res.stdout); -    console.log(res.stderr); -    console.log("error: Could not run aggregator. Please review logs above."); -    if (!context.cont) { -      console.log("Aborting further checks."); -      process.exit(1); -    } -  } -} - -async function checkCloserConfig(context: LintContext) { -  const res = await sh( -    context, -    `su -l --shell /bin/sh -c 'taler-exchange-closer -t' taler-exchange-closer`, -  ); -  if (res.status != 0) { -    context.numErr++; -    console.log(res.stdout); -    console.log(res.stderr); -    console.log("error: Could not run closer. Please review logs above."); -    if (!context.cont) { -      console.log("Aborting further checks."); -      process.exit(1); -    } -  } -} - -function checkMasterPublicKeyConfig(context: LintContext): PubkeyConf { -  const cfg = context.cfg; -  const pub = cfg.getString("exchange", "master_public_key"); - -  const pubDecoded = decodeCrock(pub.required()); - -  if (pubDecoded.length != 32) { -    context.numErr++; -    console.log("error: invalid master public key"); -    if (!context.cont) { -      console.log("Aborting further checks."); -      process.exit(1); -    } -  } - -  return { -    masterPublicKey: pub.required(), -  }; -} - -export async function checkExchangeHttpd( -  context: LintContext, -  pubConf: PubkeyConf, -): Promise<void> { -  const cfg = context.cfg; -  const baseUrlEntry = cfg.getString("exchange", "base_url"); - -  if (!baseUrlEntry.isDefined) { -    context.numErr++; -    console.log( -      "error: configuration needs to specify section EXCHANGE option BASE_URL", -    ); -    if (!context.cont) { -      console.log("Aborting further checks."); -      process.exit(1); -    } -  } - -  const baseUrl = baseUrlEntry.required(); - -  if (!baseUrl.startsWith("http")) { -    context.numErr++; -    console.log( -      "error: section EXCHANGE option BASE_URL needs to be an http or https URL", -    ); -    if (!context.cont) { -      console.log("Aborting further checks."); -      process.exit(1); -    } -  } - -  if (!baseUrl.endsWith("/")) { -    context.numErr++; -    console.log( -      "error: section EXCHANGE option BASE_URL needs to end with a slash", -    ); -    if (!context.cont) { -      console.log("Aborting further checks."); -      process.exit(1); -    } -  } - -  if (!baseUrl.startsWith("https://")) { -    console.log( -      "warning: section EXCHANGE option BASE_URL: it is recommended to serve the exchange via HTTPS", -    ); -  } - -  { -    const mgmtUrl = new URL("management/keys", baseUrl); -    const resp = await httpLib.get(mgmtUrl.href); - -    const futureKeys = await readSuccessResponseJsonOrThrow( -      resp, -      codecForKeysManagementResponse(), -    ); - -    if (futureKeys.future_denoms.length > 0) { -      console.log( -        `warning: exchange has denomination keys that need to be signed by the offline signing procedure`, -      ); -    } - -    if (futureKeys.future_signkeys.length > 0) { -      console.log( -        `warning: exchange has signing keys that need to be signed by the offline signing procedure`, -      ); -    } -  } - -  // Check if we can use /keys already -  { -    const keysUrl = new URL("keys", baseUrl); - -    const resp = await Promise.race([httpLib.get(keysUrl.href), delayMs(2000)]); - -    if (!resp) { -      context.numErr++; -      console.log( -        "error: request to /keys timed out. " + -          "Make sure to sign and upload denomination and signing keys " + -          "with taler-exchange-offline.", -      ); -      if (!context.cont) { -        console.log("Aborting further checks."); -        process.exit(1); -      } -    } else { -      const keys = await readSuccessResponseJsonOrThrow( -        resp, -        codecForExchangeKeysJson(), -      ); - -      if (keys.master_public_key !== pubConf.masterPublicKey) { -        context.numErr++; -        console.log( -          "error: master public key of exchange does not match public key of live exchange", -        ); -        if (!context.cont) { -          console.log("Aborting further checks."); -          process.exit(1); -        } -      } -    } -  } - -  // Check /wire -  { -    const keysUrl = new URL("wire", baseUrl); - -    const resp = await Promise.race([httpLib.get(keysUrl.href), delayMs(2000)]); - -    if (!resp) { -      context.numErr++; -      console.log( -        "error: request to /wire timed out. " + -          "Make sure to sign and upload accounts and wire fees " + -          "using the taler-exchange-offline tool.", -      ); -      if (!context.cont) { -        console.log("Aborting further checks."); -        process.exit(1); -      } -    } else { -      if (resp.status !== 200) { -        console.log( -          "error:  Can't access exchange /wire.  Please check " + -            "the logs of taler-exchange-httpd for further information.", -        ); -      } -    } -  } -} - -/** - * Do some basic checks in the configuration of a Taler deployment. - */ -export async function lintExchangeDeployment( -  verbose: boolean, -  cont: boolean, -): Promise<void> { -  if (process.getuid!() != 0) { -    console.log( -      "warning: the exchange deployment linter is designed to be run as root", -    ); -  } - -  const cfg = Configuration.load(); - -  const context: LintContext = { -    cont, -    verbose, -    cfg, -    numErr: 0, -  }; - -  const basic = checkBasicConf(context); - -  checkCoinConfig(context, basic); - -  await checkWireConfig(context); - -  await checkAggregatorConfig(context); - -  await checkCloserConfig(context); - -  const pubConf = checkMasterPublicKeyConfig(context); - -  await checkExchangeHttpd(context, pubConf); - -  if (context.numErr == 0) { -    console.log("Linting completed without errors."); -    process.exit(0); -  } else { -    console.log(`Linting completed with ${context.numErr} errors.`); -    process.exit(1); -  } -}  | 
