gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: integration test for paywall


From: gnunet
Subject: [taler-wallet-core] branch master updated: integration test for paywall flow
Date: Wed, 12 Aug 2020 20:57:00 +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 e9ed3b18 integration test for paywall flow
e9ed3b18 is described below

commit e9ed3b18672af919efa12364b97fd2b7efe21cd9
Author: Florian Dold <florian.dold@gmail.com>
AuthorDate: Thu Aug 13 00:26:55 2020 +0530

    integration test for paywall flow
---
 packages/taler-integrationtests/src/harness.ts     |  32 +++-
 .../src/scenario-prompt-payment.ts                 |   7 +-
 .../src/scenario-rerun-payment-multiple.ts         |  14 +-
 .../src/test-payment-fault.ts                      |  14 +-
 .../src/test-payment-idempotency.ts                |  14 +-
 .../src/test-payment-multiple.ts                   |  14 +-
 .../taler-integrationtests/src/test-payment.ts     |  14 +-
 .../src/test-paywall-flow.ts                       | 206 +++++++++++++++++++++
 .../src/test-refund-incremental.ts                 |  21 +--
 packages/taler-integrationtests/src/test-refund.ts |  14 +-
 .../taler-wallet-core/src/types/walletTypes.ts     |  44 ++++-
 11 files changed, 319 insertions(+), 75 deletions(-)

diff --git a/packages/taler-integrationtests/src/harness.ts 
b/packages/taler-integrationtests/src/harness.ts
index 0cf76916..8f9c540f 100644
--- a/packages/taler-integrationtests/src/harness.ts
+++ b/packages/taler-integrationtests/src/harness.ts
@@ -50,6 +50,9 @@ import {
   GetWithdrawalDetailsForUriRequest,
   WithdrawUriInfoResponse,
   codecForWithdrawUriInfoResponse,
+  ConfirmPayRequest,
+  ConfirmPayResult,
+  codecForConfirmPayResult,
 } from "taler-wallet-core";
 import { URL } from "url";
 import axios from "axios";
@@ -58,6 +61,7 @@ import {
   codecForPostOrderResponse,
   PostOrderRequest,
   PostOrderResponse,
+  MerchantOrderPrivateStatusResponse,
 } from "./merchantApiTypes";
 import {
   EddsaKeyPair,
@@ -886,6 +890,13 @@ export interface MerchantConfig {
   database: string;
 }
 
+
+export interface PrivateOrderStatusQuery {
+  instance?: string,
+  orderId: string,
+  sessionId?: string,
+}
+
 export class MerchantService {
   static fromExistingConfig(gc: GlobalTestState, name: string) {
     const cfgFilename = gc.testDir + `/merchant-${name}.conf`;
@@ -982,17 +993,20 @@ export class MerchantService {
     });
   }
 
-  async queryPrivateOrderStatus(instanceName: string, orderId: string) {
+  async queryPrivateOrderStatus(query: PrivateOrderStatusQuery): 
Promise<MerchantOrderPrivateStatusResponse> {
     const reqUrl = new URL(
-      `private/orders/${orderId}`,
-      this.makeInstanceBaseUrl(instanceName),
+      `private/orders/${query.orderId}`,
+      this.makeInstanceBaseUrl(query.instance),
     );
+    if (query.sessionId) {
+      reqUrl.searchParams.set("session_id", query.sessionId);
+    }
     const resp = await axios.get(reqUrl.href);
     return codecForMerchantOrderPrivateStatusResponse().decode(resp.data);
   }
 
-  makeInstanceBaseUrl(instanceName: string): string {
-    if (instanceName === "default") {
+  makeInstanceBaseUrl(instanceName?: string): string {
+    if (instanceName === undefined || instanceName === "default") {
       return `http://localhost:${this.merchantConfig.httpPort}/`;
     } else {
       return 
`http://localhost:${this.merchantConfig.httpPort}/instances/${instanceName}/`;
@@ -1177,6 +1191,14 @@ export class WalletCli {
     throw new OperationFailedError(resp.error);
   }
 
+  async confirmPay(req: ConfirmPayRequest): Promise<ConfirmPayResult> {
+    const resp = await this.apiRequest("confirmPay", req);
+    if (resp.type === "response") {
+      return codecForConfirmPayResult().decode(resp.result);
+    }
+    throw new OperationFailedError(resp.error);
+  }
+
   async addExchange(req: AddExchangeRequest): Promise<void> {
     const resp = await this.apiRequest("addExchange", req);
     if (resp.type === "response") {
diff --git a/packages/taler-integrationtests/src/scenario-prompt-payment.ts 
b/packages/taler-integrationtests/src/scenario-prompt-payment.ts
index f60c6704..3e4bfc6c 100644
--- a/packages/taler-integrationtests/src/scenario-prompt-payment.ts
+++ b/packages/taler-integrationtests/src/scenario-prompt-payment.ts
@@ -47,10 +47,9 @@ runTest(async (t: GlobalTestState) => {
     },
   });
 
-  let orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  let orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "unpaid");
 
diff --git 
a/packages/taler-integrationtests/src/scenario-rerun-payment-multiple.ts 
b/packages/taler-integrationtests/src/scenario-rerun-payment-multiple.ts
index a755aa93..525ba9a2 100644
--- a/packages/taler-integrationtests/src/scenario-rerun-payment-multiple.ts
+++ b/packages/taler-integrationtests/src/scenario-rerun-payment-multiple.ts
@@ -60,10 +60,9 @@ async function withdrawAndPay(
     },
   });
 
-  let orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  let orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "unpaid");
 
@@ -82,10 +81,9 @@ async function withdrawAndPay(
 
   // Check if payment was successful.
 
-  orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "paid");
 }
diff --git a/packages/taler-integrationtests/src/test-payment-fault.ts 
b/packages/taler-integrationtests/src/test-payment-fault.ts
index 2ee5c705..4babdc50 100644
--- a/packages/taler-integrationtests/src/test-payment-fault.ts
+++ b/packages/taler-integrationtests/src/test-payment-fault.ts
@@ -153,10 +153,9 @@ runTest(async (t: GlobalTestState) => {
     },
   });
 
-  let orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  let orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "unpaid");
 
@@ -196,10 +195,9 @@ runTest(async (t: GlobalTestState) => {
 
   // Check if payment was successful.
 
-  orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "paid");
 });
diff --git a/packages/taler-integrationtests/src/test-payment-idempotency.ts 
b/packages/taler-integrationtests/src/test-payment-idempotency.ts
index 4d672771..bc641a35 100644
--- a/packages/taler-integrationtests/src/test-payment-idempotency.ts
+++ b/packages/taler-integrationtests/src/test-payment-idempotency.ts
@@ -49,10 +49,9 @@ runTest(async (t: GlobalTestState) => {
     },
   });
 
-  let orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  let orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "unpaid");
 
@@ -85,10 +84,9 @@ runTest(async (t: GlobalTestState) => {
 
   // Check if payment was successful.
 
-  orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "paid");
 
diff --git a/packages/taler-integrationtests/src/test-payment-multiple.ts 
b/packages/taler-integrationtests/src/test-payment-multiple.ts
index 80092a9a..00b3c0b6 100644
--- a/packages/taler-integrationtests/src/test-payment-multiple.ts
+++ b/packages/taler-integrationtests/src/test-payment-multiple.ts
@@ -130,10 +130,9 @@ runTest(async (t: GlobalTestState) => {
     },
   });
 
-  let orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  let orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "unpaid");
 
@@ -152,10 +151,9 @@ runTest(async (t: GlobalTestState) => {
 
   // Check if payment was successful.
 
-  orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "paid");
 
diff --git a/packages/taler-integrationtests/src/test-payment.ts 
b/packages/taler-integrationtests/src/test-payment.ts
index 77645909..12b4267b 100644
--- a/packages/taler-integrationtests/src/test-payment.ts
+++ b/packages/taler-integrationtests/src/test-payment.ts
@@ -48,10 +48,9 @@ runTest(async (t: GlobalTestState) => {
     },
   });
 
-  let orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  let orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "unpaid");
 
@@ -71,10 +70,9 @@ runTest(async (t: GlobalTestState) => {
 
   // Check if payment was successful.
 
-  orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "paid");
 
diff --git a/packages/taler-integrationtests/src/test-paywall-flow.ts 
b/packages/taler-integrationtests/src/test-paywall-flow.ts
new file mode 100644
index 00000000..b329a9c6
--- /dev/null
+++ b/packages/taler-integrationtests/src/test-paywall-flow.ts
@@ -0,0 +1,206 @@
+/*
+ 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 } from "./harness";
+import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
+import {
+  PreparePayResultType,
+  codecForMerchantOrderStatusUnpaid,
+  ConfirmPayResultType,
+} from "taler-wallet-core";
+import axios from "axios";
+
+/**
+ * Run test for basic, bank-integrated withdrawal.
+ */
+runTest(async (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 merchant.createOrder("default", {
+    order: {
+      summary: "Buy me!",
+      amount: "TESTKUDOS:5",
+      fulfillment_url: "https://example.com/article42";,
+    },
+  });
+
+  const firstOrderId = orderResp.order_id;
+
+  let orderStatus = await merchant.queryPrivateOrderStatus({
+    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 pubUnpaidStatusResp = codecForMerchantOrderStatusUnpaid().decode(
+    publicOrderStatusResp.data,
+  );
+
+  console.log(pubUnpaidStatusResp);
+
+  let preparePayResp = await wallet.preparePay({
+    talerPayUri: pubUnpaidStatusResp.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}`,
+    );
+  }
+
+  let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
+    publicOrderStatusResp.data,
+  );
+
+  const confirmPayRes = await wallet.confirmPay({
+    proposalId: proposalId,
+  });
+
+  t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
+
+  publicOrderStatusResp = await axios.get(publicOrderStatusUrl, {
+    validateStatus: () => true,
+  });
+
+  if (publicOrderStatusResp.status != 410) {
+    throw Error(
+      `expected status 410 (after paying), but got 
${publicOrderStatusResp.status}`,
+    );
+  }
+
+  /**
+   * =========================================================================
+   * Now change up the session ID!
+   * =========================================================================
+   */
+
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+    sessionId: "mysession-two",
+  });
+
+  // Should be unpaid because of a new session ID
+  t.assertTrue(orderStatus.order_status === "unpaid");
+
+  publicOrderStatusUrl = orderStatus.order_status_url;
+
+  // Pay with new taler://pay URI, which should
+  // have the new session ID!
+  // Wallet should now automatically re-play payment.
+  preparePayResp = await wallet.preparePay({
+    talerPayUri: orderStatus.taler_pay_uri,
+  });
+
+  t.assertTrue(preparePayResp.status === 
PreparePayResultType.AlreadyConfirmed);
+  t.assertTrue(preparePayResp.paid);
+
+  /**
+   * =========================================================================
+   * Now we test re-purchase detection.
+   * =========================================================================
+   */
+
+  orderResp = await merchant.createOrder("default", {
+    order: {
+      summary: "Buy me!",
+      amount: "TESTKUDOS:5",
+      // Same fulfillment URL as previously!
+      fulfillment_url: "https://example.com/article42";,
+    },
+  });
+
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+    sessionId: "mysession-three",
+  });
+
+  t.assertTrue(orderStatus.order_status === "unpaid");
+
+  t.assertTrue(orderStatus.already_paid_order_id === undefined);
+  publicOrderStatusUrl = 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.preparePay({
+    talerPayUri: orderStatus.taler_pay_uri,
+  });
+
+  t.assertTrue(preparePayResp.status === 
PreparePayResultType.AlreadyConfirmed);
+  t.assertTrue(preparePayResp.paid);
+
+  // Ask the order status of the claimed-but-unpaid order
+  publicOrderStatusResp = await axios.get(publicOrderStatusUrl, {
+    validateStatus: () => true,
+  });
+
+  if (publicOrderStatusResp.status != 403) {
+    throw Error(
+      `expected status 403, but got ${publicOrderStatusResp.status}`,
+    );
+  }
+
+  pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
+    publicOrderStatusResp.data,
+  );
+
+  t.assertTrue(pubUnpaidStatusResp.already_paid_order_id === firstOrderId);
+});
diff --git a/packages/taler-integrationtests/src/test-refund-incremental.ts 
b/packages/taler-integrationtests/src/test-refund-incremental.ts
index 0667b10d..59a36b94 100644
--- a/packages/taler-integrationtests/src/test-refund-incremental.ts
+++ b/packages/taler-integrationtests/src/test-refund-incremental.ts
@@ -47,10 +47,9 @@ runTest(async (t: GlobalTestState) => {
     },
   });
 
-  let orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  let orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "unpaid");
 
@@ -69,10 +68,9 @@ runTest(async (t: GlobalTestState) => {
 
   // Check if payment was successful.
 
-  orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "paid");
 
@@ -103,10 +101,9 @@ runTest(async (t: GlobalTestState) => {
   });
   console.log(r);
 
-  orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "paid");
 
diff --git a/packages/taler-integrationtests/src/test-refund.ts 
b/packages/taler-integrationtests/src/test-refund.ts
index e1fdbfc5..d0d0a0a0 100644
--- a/packages/taler-integrationtests/src/test-refund.ts
+++ b/packages/taler-integrationtests/src/test-refund.ts
@@ -47,10 +47,9 @@ runTest(async (t: GlobalTestState) => {
     },
   });
 
-  let orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  let orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "unpaid");
 
@@ -69,10 +68,9 @@ runTest(async (t: GlobalTestState) => {
 
   // Check if payment was successful.
 
-  orderStatus = await merchant.queryPrivateOrderStatus(
-    "default",
-    orderResp.order_id,
-  );
+  orderStatus = await merchant.queryPrivateOrderStatus({
+    orderId: orderResp.order_id,
+  });
 
   t.assertTrue(orderStatus.order_status === "paid");
 
diff --git a/packages/taler-wallet-core/src/types/walletTypes.ts 
b/packages/taler-wallet-core/src/types/walletTypes.ts
index 6f634052..8521af3f 100644
--- a/packages/taler-wallet-core/src/types/walletTypes.ts
+++ b/packages/taler-wallet-core/src/types/walletTypes.ts
@@ -221,6 +221,29 @@ export interface ConfirmPayResultPending {
 
 export type ConfirmPayResult = ConfirmPayResultDone | ConfirmPayResultPending;
 
+export const codecForConfirmPayResultPending = (): Codec<
+  ConfirmPayResultPending
+> =>
+  buildCodecForObject<ConfirmPayResultPending>()
+    .property("lastError", codecForAny())
+    .property("type", codecForConstString(ConfirmPayResultType.Pending))
+    .build("ConfirmPayResultPending");
+
+export const codecForConfirmPayResultDone = (): Codec<
+  ConfirmPayResultDone
+> =>
+  buildCodecForObject<ConfirmPayResultDone>()
+    .property("type", codecForConstString(ConfirmPayResultType.Done))
+    .property("nextUrl", codecForString())
+    .build("ConfirmPayResultDone");
+
+export const codecForConfirmPayResult = (): Codec<ConfirmPayResult> =>
+  buildCodecForUnion<ConfirmPayResult>()
+    .discriminateOn("type")
+    .alternative(ConfirmPayResultType.Pending, 
codecForConfirmPayResultPending())
+    .alternative(ConfirmPayResultType.Done, codecForConfirmPayResultDone())
+    .build("ConfirmPayResult");
+
 /**
  * Information about all sender wire details known to the wallet,
  * as well as exchanges that accept these wire types.
@@ -400,13 +423,22 @@ export const codecForPreparePayResultAlreadyConfirmed = 
(): Codec<
     .property("contractTerms", codecForAny())
     .build("PreparePayResultAlreadyConfirmed");
 
-export const codecForPreparePayResult = (): Codec<PreparePayResult> => 
+export const codecForPreparePayResult = (): Codec<PreparePayResult> =>
   buildCodecForUnion<PreparePayResult>()
-      .discriminateOn("status")
-      .alternative(PreparePayResultType.AlreadyConfirmed, 
codecForPreparePayResultAlreadyConfirmed())
-      .alternative(PreparePayResultType.InsufficientBalance, 
codecForPreparePayResultInsufficientBalance())
-      .alternative(PreparePayResultType.PaymentPossible, 
codecForPreparePayResultPaymentPossible())
-      .build("PreparePayResult");
+    .discriminateOn("status")
+    .alternative(
+      PreparePayResultType.AlreadyConfirmed,
+      codecForPreparePayResultAlreadyConfirmed(),
+    )
+    .alternative(
+      PreparePayResultType.InsufficientBalance,
+      codecForPreparePayResultInsufficientBalance(),
+    )
+    .alternative(
+      PreparePayResultType.PaymentPossible,
+      codecForPreparePayResultPaymentPossible(),
+    )
+    .build("PreparePayResult");
 
 export type PreparePayResult =
   | PreparePayResultInsufficientBalance

-- 
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]