gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: add exchange management test


From: gnunet
Subject: [taler-wallet-core] branch master updated: add exchange management test case
Date: Wed, 12 Aug 2020 16:15:39 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new c5ec3413 add exchange management test case
c5ec3413 is described below

commit c5ec34136874be87210711fd65eea1c1a6feec50
Author: Florian Dold <florian.dold@gmail.com>
AuthorDate: Wed Aug 12 19:45:34 2020 +0530

    add exchange management test case
---
 .../taler-integrationtests/src/faultInjection.ts   |   2 +-
 packages/taler-integrationtests/src/harness.ts     |  51 ++++-
 .../src/test-exchange-management.ts                | 232 +++++++++++++++++++++
 packages/taler-wallet-core/src/index.ts            |   2 +
 .../taler-wallet-core/src/operations/exchanges.ts  |   2 +-
 packages/taler-wallet-core/src/types/talerTypes.ts |   9 +-
 .../taler-wallet-core/src/types/walletTypes.ts     |  12 ++
 7 files changed, 306 insertions(+), 4 deletions(-)

diff --git a/packages/taler-integrationtests/src/faultInjection.ts 
b/packages/taler-integrationtests/src/faultInjection.ts
index 26bfeee5..46ab1c5f 100644
--- a/packages/taler-integrationtests/src/faultInjection.ts
+++ b/packages/taler-integrationtests/src/faultInjection.ts
@@ -182,7 +182,7 @@ export class FaultProxy {
     this.currentFaultSpecs.push(f);
   }
 
-  clearFault() {
+  clearAllFaults() {
     this.currentFaultSpecs = [];
   }
 }
diff --git a/packages/taler-integrationtests/src/harness.ts 
b/packages/taler-integrationtests/src/harness.ts
index e8a0941d..0cf76916 100644
--- a/packages/taler-integrationtests/src/harness.ts
+++ b/packages/taler-integrationtests/src/harness.ts
@@ -44,6 +44,12 @@ import {
   codecForPreparePayResultPaymentPossible,
   codecForPreparePayResult,
   OperationFailedError,
+  AddExchangeRequest,
+  ExchangesListRespose,
+  codecForExchangesListResponse,
+  GetWithdrawalDetailsForUriRequest,
+  WithdrawUriInfoResponse,
+  codecForWithdrawUriInfoResponse,
 } from "taler-wallet-core";
 import { URL } from "url";
 import axios from "axios";
@@ -60,6 +66,7 @@ import {
   eddsaGetPublic,
   createEddsaKeyPair,
 } from "taler-wallet-core/lib/crypto/talerCrypto";
+import { WithdrawalDetails } from "taler-wallet-core/lib/types/transactions";
 
 const exec = util.promisify(require("child_process").exec);
 
@@ -244,6 +251,22 @@ export class GlobalTestState {
     process.on("uncaughtException", () => this.shutdownSync());
   }
 
+  async assertThrowsOperationErrorAsync(
+    block: () => Promise<void>,
+  ): Promise<OperationFailedError> {
+    try {
+      await block();
+    } catch (e) {
+      if (e instanceof OperationFailedError) {
+        return e;
+      }
+      throw Error(`expected OperationFailedError to be thrown, but got ${e}`);
+    }
+    throw Error(
+      `expected OperationFailedError to be thrown, but block finished without 
throwing`,
+    );
+  }
+
   assertTrue(b: boolean): asserts b {
     if (!b) {
       throw Error("test assertion failed");
@@ -488,7 +511,7 @@ export class BankService {
     return new BankService(gc, bc, cfgFilename);
   }
 
-  setSuggestedExchange(e: ExchangeService, exchangePayto: string) {
+  setSuggestedExchange(e: ExchangeServiceInterface, exchangePayto: string) {
     const config = Configuration.load(this.configFile);
     config.setString("bank", "suggested_exchange", e.baseUrl);
     config.setString("bank", "suggested_exchange_payto", exchangePayto);
@@ -1153,4 +1176,30 @@ export class WalletCli {
     }
     throw new OperationFailedError(resp.error);
   }
+
+  async addExchange(req: AddExchangeRequest): Promise<void> {
+    const resp = await this.apiRequest("addExchange", req);
+    if (resp.type === "response") {
+      return;
+    }
+    throw new OperationFailedError(resp.error);
+  }
+
+  async listExchanges(): Promise<ExchangesListRespose> {
+    const resp = await this.apiRequest("listExchanges", {});
+    if (resp.type === "response") {
+      return codecForExchangesListResponse().decode(resp.result);
+    }
+    throw new OperationFailedError(resp.error);
+  }
+
+  async getWithdrawalDetailsForUri(
+    req: GetWithdrawalDetailsForUriRequest,
+  ): Promise<WithdrawUriInfoResponse> {
+    const resp = await this.apiRequest("getWithdrawalDetailsForUri", req);
+    if (resp.type === "response") {
+      return codecForWithdrawUriInfoResponse().decode(resp.result);
+    }
+    throw new OperationFailedError(resp.error); 
+  }
 }
diff --git a/packages/taler-integrationtests/src/test-exchange-management.ts 
b/packages/taler-integrationtests/src/test-exchange-management.ts
new file mode 100644
index 00000000..990a6fa4
--- /dev/null
+++ b/packages/taler-integrationtests/src/test-exchange-management.ts
@@ -0,0 +1,232 @@
+/*
+ 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 {
+  runTest,
+  GlobalTestState,
+  WalletCli,
+  setupDb,
+  BankService,
+  ExchangeService,
+  MerchantService,
+  defaultCoinConfig,
+} from "./harness";
+import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
+import {
+  PreparePayResultType,
+  ExchangesListRespose,
+  URL,
+} from "taler-wallet-core";
+import {
+  FaultInjectedExchangeService,
+  FaultInjectionResponseContext,
+} from "./faultInjection";
+
+/**
+ * Test if the wallet handles outdated exchange versions correct.y
+ */
+runTest(async (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);
+
+  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: "minst1",
+    name: "minst1",
+    paytoUris: ["payto://x-taler-bank/minst1"],
+  });
+
+  await merchant.addInstance({
+    id: "default",
+    name: "Default Instance",
+    paytoUris: [`payto://x-taler-bank/merchant-default`],
+  });
+
+  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: ExchangesListRespose;
+
+  exchangesList = await wallet.listExchanges();
+  t.assertTrue(exchangesList.exchanges.length === 0);
+
+  // Try before fault is injected
+  await wallet.addExchange({
+    exchangeBaseUrl: faultyExchange.baseUrl,
+  });
+
+  exchangesList = await wallet.listExchanges();
+  t.assertTrue(exchangesList.exchanges.length === 1);
+
+  await wallet.addExchange({
+    exchangeBaseUrl: faultyExchange.baseUrl,
+  });
+
+  console.log("listing exchanges");
+
+  exchangesList = await wallet.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.listExchanges();
+  t.assertTrue(exchangesList.exchanges.length === 0);
+
+  faultyExchange.faultProxy.addFault({
+    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");
+      }
+    },
+  });
+
+  await t.assertThrowsOperationErrorAsync(async () => {
+    await wallet.addExchange({
+      exchangeBaseUrl: faultyExchange.baseUrl,
+    });
+  });
+
+  exchangesList = await wallet.listExchanges();
+  t.assertTrue(exchangesList.exchanges.length === 0);
+
+  /*
+   * =========================================================================
+   * Check what happens if the exchange returns an old, unsupported
+   * version for /keys
+   * =========================================================================
+   */
+
+  wallet.deleteDatabase();
+  faultyExchange.faultProxy.clearAllFaults();
+
+  faultyExchange.faultProxy.addFault({
+    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");
+      }
+    },
+  });
+
+  await t.assertThrowsOperationErrorAsync(async () => {
+    await wallet.addExchange({
+      exchangeBaseUrl: faultyExchange.baseUrl,
+    });
+  });
+
+  exchangesList = await wallet.listExchanges();
+  t.assertTrue(exchangesList.exchanges.length === 0);
+
+  /*
+   * =========================================================================
+   * 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 bank.createRandomBankUser();
+  const wop = await bank.createWithdrawalOperation(user, "TESTKUDOS:10");
+
+  // Hand it to the wallet
+
+  const wd = await wallet.getWithdrawalDetailsForUri({
+    talerWithdrawUri: wop.taler_withdraw_uri,
+  });
+
+  // Make sure the faulty exchange isn't used for the suggestion.
+  t.assertTrue(wd.possibleExchanges.length === 0);
+});
diff --git a/packages/taler-wallet-core/src/index.ts 
b/packages/taler-wallet-core/src/index.ts
index 954798e4..784c9d63 100644
--- a/packages/taler-wallet-core/src/index.ts
+++ b/packages/taler-wallet-core/src/index.ts
@@ -75,3 +75,5 @@ export * from "./crypto/workers/nodeThreadWorker";
 export * from "./types/notifications";
 
 export { Configuration } from "./util/talerconfig";
+
+export { URL } from "./util/url";
\ No newline at end of file
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 8967173c..a7771f6d 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -88,7 +88,7 @@ async function setExchangeError(
   baseUrl: string,
   err: OperationErrorDetails,
 ): Promise<void> {
-  console.log(`last error for exchange ${baseUrl}:`, err);
+  logger.warn(`last error for exchange ${baseUrl}:`, err);
   const mut = (exchange: ExchangeRecord): ExchangeRecord => {
     exchange.lastError = err;
     return exchange;
diff --git a/packages/taler-wallet-core/src/types/talerTypes.ts 
b/packages/taler-wallet-core/src/types/talerTypes.ts
index bb0118a7..99f44ea2 100644
--- a/packages/taler-wallet-core/src/types/talerTypes.ts
+++ b/packages/taler-wallet-core/src/types/talerTypes.ts
@@ -47,7 +47,7 @@ import {
   Duration,
   codecForDuration,
 } from "../util/time";
-import { ExchangeListItem } from "./walletTypes";
+import { ExchangeListItem, codecForExchangeListItem } from "./walletTypes";
 import { codecForAmountString } from "../util/amounts";
 
 /**
@@ -940,6 +940,13 @@ export interface WithdrawUriInfoResponse {
   possibleExchanges: ExchangeListItem[];
 }
 
+export const codecForWithdrawUriInfoResponse = (): 
Codec<WithdrawUriInfoResponse> =>
+  buildCodecForObject<WithdrawUriInfoResponse>()
+    .property("amount", codecForAmountString())
+    .property("defaultExchangeBaseUrl", codecOptional(codecForString()))
+    .property("possibleExchanges", codecForList(codecForExchangeListItem()))
+    .build("WithdrawUriInfoResponse");
+
 /**
  * Response body for the following endpoint:
  *
diff --git a/packages/taler-wallet-core/src/types/walletTypes.ts 
b/packages/taler-wallet-core/src/types/walletTypes.ts
index ec57e7d2..6f634052 100644
--- a/packages/taler-wallet-core/src/types/walletTypes.ts
+++ b/packages/taler-wallet-core/src/types/walletTypes.ts
@@ -554,6 +554,18 @@ export interface ExchangeListItem {
   paytoUris: string[];
 }
 
+export const codecForExchangeListItem = (): Codec<ExchangeListItem> =>
+  buildCodecForObject<ExchangeListItem>()
+    .property("currency", codecForString())
+    .property("exchangeBaseUrl", codecForString())
+    .property("paytoUris", codecForList(codecForString()))
+    .build("ExchangeListItem");
+
+export const codecForExchangesListResponse = (): Codec<ExchangesListRespose> =>
+  buildCodecForObject<ExchangesListRespose>()
+    .property("exchanges", codecForList(codecForExchangeListItem()))
+    .build("ExchangesListRespose");
+
 export interface AcceptManualWithdrawalResult {
   /**
    * Payto URIs that can be used to fund the withdrawal.

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]