gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: wallet-core: use testingWaitT


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: use testingWaitTransactionsFinal to wait for transactions
Date: Sat, 01 Jul 2023 01:43:33 +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 5695ae0a9 wallet-core: use testingWaitTransactionsFinal to wait for 
transactions
5695ae0a9 is described below

commit 5695ae0a9f469ddbcd86e675f8f74b30032be457
Author: Florian Dold <florian@dold.me>
AuthorDate: Sat Jul 1 01:43:29 2023 +0200

    wallet-core: use testingWaitTransactionsFinal to wait for transactions
---
 packages/taler-harness/src/harness/harness.ts      | 32 +++++++++-
 packages/taler-harness/src/harness/helpers.ts      | 70 ++++++++++++++++++++++
 .../src/integrationtests/test-payment.ts           | 29 ++++-----
 .../taler-wallet-core/src/operations/refresh.ts    | 28 ++++++---
 .../taler-wallet-core/src/operations/testing.ts    | 14 +++--
 packages/taler-wallet-core/src/wallet-api-types.ts | 11 ++++
 packages/taler-wallet-core/src/wallet.ts           |  3 +
 7 files changed, 157 insertions(+), 30 deletions(-)

diff --git a/packages/taler-harness/src/harness/harness.ts 
b/packages/taler-harness/src/harness/harness.ts
index 7b2f980cc..1120eae84 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -41,17 +41,21 @@ import {
   Logger,
   MerchantReserveCreateConfirmation,
   MerchantTemplateAddDetails,
+  NotificationType,
   parsePaytoUri,
   stringToBytes,
   TalerError,
   TalerProtocolDuration,
+  TransactionMajorState,
   WalletNotification,
 } from "@gnu-taler/taler-util";
 import {
   BankApi,
   BankServiceHandle,
   HarnessExchangeBankAccount,
+  OpenedPromise,
   openPromise,
+  WalletApiOperation,
   WalletCoreApiClient,
   WalletCoreRequestType,
   WalletCoreResponseType,
@@ -934,7 +938,12 @@ export class FakebankService
     );
     await this.pingUntilAvailable();
     for (const acc of this.accounts) {
-      await BankApi.registerAccount(this, acc.accountName, 
acc.accountPassword, {});
+      await BankApi.registerAccount(
+        this,
+        acc.accountName,
+        acc.accountPassword,
+        {},
+      );
     }
   }
 
@@ -2246,9 +2255,26 @@ export interface WalletClientArgs {
   onNotification?(n: WalletNotification): void;
 }
 
+export type CancelFn = () => void;
+export type NotificationHandler = (n: WalletNotification) => void;
+
+/**
+ * Convenience wrapper around a (remote) wallet handle.
+ */
 export class WalletClient {
   remoteWallet: RemoteWallet | undefined = undefined;
   private waiter: WalletNotificationWaiter = makeNotificationWaiter();
+  notificationHandlers: NotificationHandler[] = [];
+
+  addNotificationListener(f: NotificationHandler): CancelFn {
+    this.notificationHandlers.push(f);
+    return () => {
+      const idx = this.notificationHandlers.indexOf(f);
+      if (idx >= 0) {
+        this.notificationHandlers.splice(idx, 1);
+      }
+    };
+  }
 
   async call<Op extends keyof WalletOperations>(
     operation: Op,
@@ -2260,6 +2286,7 @@ export class WalletClient {
     const client = getClientFromRemoteWallet(this.remoteWallet);
     return client.call(operation, payload);
   }
+
   constructor(private args: WalletClientArgs) {}
 
   async connect(): Promise<void> {
@@ -2272,6 +2299,9 @@ export class WalletClient {
           walletClient.args.onNotification(n);
         }
         waiter.notify(n);
+        for (const h of walletClient.notificationHandlers) {
+          h(n);
+        }
       },
     });
     this.remoteWallet = w;
diff --git a/packages/taler-harness/src/harness/helpers.ts 
b/packages/taler-harness/src/harness/helpers.ts
index fd6e9aa2e..8c62aef37 100644
--- a/packages/taler-harness/src/harness/helpers.ts
+++ b/packages/taler-harness/src/harness/helpers.ts
@@ -689,3 +689,73 @@ export async function makeTestPayment(
 
   t.assertTrue(orderStatus.order_status === "paid");
 }
+
+/**
+ * Make a simple payment and check that it succeeded.
+ */
+export async function makeTestPaymentV2(
+  t: GlobalTestState,
+  args: {
+    merchant: MerchantServiceInterface;
+    walletClient: WalletClient;
+    order: Partial<MerchantContractTerms>;
+    instance?: string;
+  },
+  auth: WithAuthorization = {},
+): Promise<void> {
+  // Set up order.
+
+  const { walletClient, 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 walletClient.call(
+    WalletApiOperation.PreparePayForUri,
+    {
+      talerPayUri: orderStatus.taler_pay_uri,
+    },
+  );
+
+  t.assertTrue(
+    preparePayResult.status === PreparePayResultType.PaymentPossible,
+  );
+
+  const r2 = await walletClient.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-harness/src/integrationtests/test-payment.ts 
b/packages/taler-harness/src/integrationtests/test-payment.ts
index f184e57e7..9d1ce0e22 100644
--- a/packages/taler-harness/src/integrationtests/test-payment.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment.ts
@@ -20,9 +20,9 @@
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState } from "../harness/harness.js";
 import {
-  createSimpleTestkudosEnvironment,
-  withdrawViaBank,
-  makeTestPayment,
+  createSimpleTestkudosEnvironmentV2,
+  withdrawViaBankV2,
+  makeTestPaymentV2,
 } from "../harness/helpers.js";
 import { j2s } from "@gnu-taler/taler-util";
 
@@ -32,12 +32,14 @@ import { j2s } from "@gnu-taler/taler-util";
 export async function runPaymentTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { walletClient, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironmentV2(t);
 
   // Withdraw digital cash into the wallet.
 
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+  await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: 
"TESTKUDOS:20" });
+
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
   const order = {
     summary: "Buy me!",
@@ -45,8 +47,8 @@ export async function runPaymentTest(t: GlobalTestState) {
     fulfillment_url: "taler://fulfillment-success/thx",
   };
 
-  await makeTestPayment(t, { wallet, merchant, order });
-  await wallet.runUntilDone();
+  await makeTestPaymentV2(t, { walletClient, merchant, order });
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
   // Test JSON normalization of contract terms: Does the wallet
   // agree with the merchant?
@@ -56,8 +58,8 @@ export async function runPaymentTest(t: GlobalTestState) {
     fulfillment_url: "taler://fulfillment-success/thx",
   };
 
-  await makeTestPayment(t, { wallet, merchant, order: order2 });
-  await wallet.runUntilDone();
+  await makeTestPaymentV2(t, { walletClient, merchant, order: order2 });
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
   // Test JSON normalization of contract terms: Does the wallet
   // agree with the merchant?
@@ -67,11 +69,10 @@ export async function runPaymentTest(t: GlobalTestState) {
     fulfillment_url: "taler://fulfillment-success/thx",
   };
 
-  await makeTestPayment(t, { wallet, merchant, order: order3 });
-
-  await wallet.runUntilDone();
+  await makeTestPaymentV2(t, { walletClient, merchant, order: order3 });
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
 
-  const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
+  const bal = await walletClient.call(WalletApiOperation.GetBalances, {});
   console.log(`balance after 3 payments: ${j2s(bal)}`);
   t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:3.8");
   t.assertAmountEquals(bal.balances[0].pendingIncoming, "TESTKUDOS:0");
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index fd6281eda..6eb221c1c 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -263,22 +263,25 @@ async function refreshCreateSession(
         availableAmount,
       )} too small`,
     );
-    // FIXME: State transition notification missing.
-    await ws.db
+    const transitionInfo = await ws.db
       .mktx((x) => [x.coins, x.coinAvailability, x.refreshGroups])
       .runReadWrite(async (tx) => {
         const rg = await tx.refreshGroups.get(refreshGroupId);
         if (!rg) {
           return;
         }
+        const oldTxState = computeRefreshTransactionState(rg);
         rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Finished;
         const updateRes = updateGroupStatus(rg);
         if (updateRes.final) {
           await makeCoinsVisible(ws, tx, transactionId);
         }
         await tx.refreshGroups.put(rg);
+        const newTxState = computeRefreshTransactionState(rg);
+        return { oldTxState, newTxState };
       });
     ws.notify({ type: NotificationType.BalanceChange });
+    notifyTransition(ws, transactionId, transitionInfo);
     return;
   }
 
@@ -438,7 +441,7 @@ async function refreshMelt(
 
   if (resp.status === HttpStatusCode.NotFound) {
     const errDetails = await readUnexpectedResponseDetails(resp);
-    await ws.db
+    const transitionInfo = await ws.db
       .mktx((x) => [x.refreshGroups, x.coins, x.coinAvailability])
       .runReadWrite(async (tx) => {
         const rg = await tx.refreshGroups.get(refreshGroupId);
@@ -451,6 +454,7 @@ async function refreshMelt(
         if (rg.statusPerCoin[coinIndex] !== RefreshCoinStatus.Pending) {
           return;
         }
+        const oldTxState = computeRefreshTransactionState(rg);
         rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Failed;
         rg.lastErrorPerCoin[coinIndex] = errDetails;
         const updateRes = updateGroupStatus(rg);
@@ -458,8 +462,14 @@ async function refreshMelt(
           await makeCoinsVisible(ws, tx, transactionId);
         }
         await tx.refreshGroups.put(rg);
+        const newTxState = computeRefreshTransactionState(rg);
+        return {
+          oldTxState,
+          newTxState,
+        };
       });
     ws.notify({ type: NotificationType.BalanceChange });
+    notifyTransition(ws, transactionId, transitionInfo);
     return;
   }
 
@@ -739,7 +749,7 @@ async function refreshReveal(
     }
   }
 
-  await ws.db
+  const transitionInfo = await ws.db
     .mktx((x) => [
       x.coins,
       x.denominations,
@@ -756,6 +766,7 @@ async function refreshReveal(
       if (!rs) {
         return;
       }
+      const oldTxState = computeRefreshTransactionState(rg);
       rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Finished;
       updateGroupStatus(rg);
       for (const coin of coins) {
@@ -763,7 +774,10 @@ async function refreshReveal(
       }
       await makeCoinsVisible(ws, tx, transactionId);
       await tx.refreshGroups.put(rg);
+      const newTxState = computeRefreshTransactionState(rg);
+      return { oldTxState, newTxState };
     });
+  notifyTransition(ws, transactionId, transitionInfo);
   logger.trace("refresh finished (end of reveal)");
 }
 
@@ -778,7 +792,7 @@ export async function processRefreshGroup(
     .mktx((x) => [x.refreshGroups])
     .runReadOnly(async (tx) => tx.refreshGroups.get(refreshGroupId));
   if (!refreshGroup) {
-    return TaskRunResult.finished()
+    return TaskRunResult.finished();
   }
   if (refreshGroup.timestampFinished) {
     return TaskRunResult.finished();
@@ -1235,10 +1249,6 @@ export async function suspendRefreshGroup(
     tag: TransactionType.Refresh,
     refreshGroupId,
   });
-  const retryTag = constructTaskIdentifier({
-    tag: PendingTaskType.Refresh,
-    refreshGroupId,
-  });
   let res = await ws.db
     .mktx((x) => [x.refreshGroups])
     .runReadWrite(async (tx) => {
diff --git a/packages/taler-wallet-core/src/operations/testing.ts 
b/packages/taler-wallet-core/src/operations/testing.ts
index 8c84702b8..ea373e914 100644
--- a/packages/taler-wallet-core/src/operations/testing.ts
+++ b/packages/taler-wallet-core/src/operations/testing.ts
@@ -450,7 +450,7 @@ export async function runIntegrationTest(
   logger.trace("integration test: all done!");
 }
 
-async function waitUntilDone(ws: InternalWalletState): Promise<void> {
+export async function waitUntilDone(ws: InternalWalletState): Promise<void> {
   logger.info("waiting until all transactions are in a final state");
   ws.ensureTaskLoopRunning();
   let p: OpenedPromise<void> | undefined = undefined;
@@ -459,11 +459,13 @@ async function waitUntilDone(ws: InternalWalletState): 
Promise<void> {
       return;
     }
     if (notif.type === NotificationType.TransactionStateTransition) {
-      p.resolve();
-    }
-    // Work-around, refresh transactions don't properly emit transition 
notifications yet.
-    if (notif.type === NotificationType.PendingOperationProcessed) {
-      p.resolve();
+      switch (notif.newTxState.major) {
+        case TransactionMajorState.Pending:
+        case TransactionMajorState.Aborting:
+          break;
+        default:
+          p.resolve();
+      }
     }
   });
   while (1) {
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts 
b/packages/taler-wallet-core/src/wallet-api-types.ts
index 6bcee0299..cea548db6 100644
--- a/packages/taler-wallet-core/src/wallet-api-types.ts
+++ b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -206,6 +206,7 @@ export enum WalletApiOperation {
   Recycle = "recycle",
   ApplyDevExperiment = "applyDevExperiment",
   ValidateIban = "validateIban",
+  TestingWaitTransactionsFinal = "testingWaitTransactionsFinal",
 }
 
 // group: Initialization
@@ -949,6 +950,15 @@ export type DumpCoinsOp = {
   response: CoinDumpJson;
 };
 
+/**
+ * Wait until all transactions are in a final state.
+ */
+export type TestingWaitTransactionsFinal = {
+  op: WalletApiOperation.TestingWaitTransactionsFinal;
+  request: EmptyObject;
+  response: EmptyObject;
+};
+
 /**
  * Set a coin as (un-)suspended.
  * Suspended coins won't be used for payments.
@@ -1051,6 +1061,7 @@ export type WalletOperations = {
   [WalletApiOperation.Recycle]: RecycleOp;
   [WalletApiOperation.ApplyDevExperiment]: ApplyDevExperimentOp;
   [WalletApiOperation.ValidateIban]: ValidateIbanOp;
+  [WalletApiOperation.TestingWaitTransactionsFinal]: 
TestingWaitTransactionsFinal;
 };
 
 export type WalletCoreRequestType<
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 8f11a3d28..11030af2b 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -242,6 +242,7 @@ import {
   runIntegrationTest,
   runIntegrationTest2,
   testPay,
+  waitUntilDone,
   withdrawTestBalance,
 } from "./operations/testing.js";
 import {
@@ -1550,6 +1551,8 @@ async function dispatchRequestInternal<Op extends 
WalletApiOperation>(
     case WalletApiOperation.GetVersion: {
       return getVersion(ws);
     }
+    case WalletApiOperation.TestingWaitTransactionsFinal:
+      return await waitUntilDone(ws);
     // default:
     //  assertUnreachable(operation);
   }

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