From 578bd4b1ed12049800556460359cb55a1e8545a2 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 20 Feb 2024 00:57:02 +0100 Subject: taler-harness: test for balance during a pending refresh operation --- packages/taler-harness/src/harness/helpers.ts | 4 +- .../src/integrationtests/test-wallet-refresh.ts | 70 ++++++++++++++++++++-- packages/taler-wallet-core/src/deposits.ts | 20 +++++-- packages/taler-wallet-core/src/refresh.ts | 14 +++++ 4 files changed, 98 insertions(+), 10 deletions(-) diff --git a/packages/taler-harness/src/harness/helpers.ts b/packages/taler-harness/src/harness/helpers.ts index 7a91353c8..f567a87ff 100644 --- a/packages/taler-harness/src/harness/helpers.ts +++ b/packages/taler-harness/src/harness/helpers.ts @@ -25,7 +25,6 @@ */ import { AmountString, - TalerCorebankApiClient, ConfirmPayResultType, Duration, Logger, @@ -33,6 +32,7 @@ import { MerchantContractTerms, NotificationType, PreparePayResultType, + TalerCorebankApiClient, TransactionMajorState, WalletNotification, } from "@gnu-taler/taler-util"; @@ -678,7 +678,7 @@ export async function makeTestPaymentV2( ); const r2 = await walletClient.call(WalletApiOperation.ConfirmPay, { - proposalId: preparePayResult.proposalId, + transactionId: preparePayResult.transactionId, }); t.assertTrue(r2.type === ConfirmPayResultType.Done); diff --git a/packages/taler-harness/src/integrationtests/test-wallet-refresh.ts b/packages/taler-harness/src/integrationtests/test-wallet-refresh.ts index 9f8d1503e..b86dfadcf 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-refresh.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-refresh.ts @@ -17,14 +17,21 @@ /** * Imports. */ -import { Wallet, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState } from "../harness/harness.js"; +import { + AmountString, + NotificationType, + TransactionMajorState, + TransactionType, + j2s, +} from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { parseTransactionIdentifier } from "../../../taler-wallet-core/src/transactions.js"; +import { GlobalTestState, generateRandomPayto } from "../harness/harness.js"; import { createSimpleTestkudosEnvironmentV2, - withdrawViaBankV2, makeTestPaymentV2, + withdrawViaBankV2, } from "../harness/helpers.js"; -import { TransactionType, j2s } from "@gnu-taler/taler-util"; /** * Run test for refreshe after a payment. @@ -77,6 +84,61 @@ export async function runWalletRefreshTest(t: GlobalTestState) { ); t.assertDeepEqual(refreshTx.type, TransactionType.Refresh); + + // Now we test a pending refresh operation. + { + await exchange.stop(); + + const refreshCreatedCond = walletClient.waitForNotificationCond((x) => { + if ( + x.type === NotificationType.TransactionStateTransition && + parseTransactionIdentifier(x.transactionId)?.tag === + TransactionType.Refresh + ) { + return true; + } + return false; + }); + + const refreshDoneCond = walletClient.waitForNotificationCond((x) => { + if ( + x.type === NotificationType.TransactionStateTransition && + parseTransactionIdentifier(x.transactionId)?.tag === + TransactionType.Refresh && + x.newTxState.major === TransactionMajorState.Done + ) { + return true; + } + return false; + }); + + const depositGroupResult = await walletClient.client.call( + WalletApiOperation.CreateDepositGroup, + { + amount: "TESTKUDOS:10.5" as AmountString, + depositPaytoUri: generateRandomPayto("foo"), + }, + ); + + await refreshCreatedCond; + + // Here, the refresh operation should be in a pending state. + + const bal1 = await walletClient.call(WalletApiOperation.GetBalances, {}); + + await exchange.start(); + + await refreshDoneCond; + + const bal2 = await walletClient.call(WalletApiOperation.GetBalances, {}); + + // The refresh operation completing should not change the available balance, + // as we're accounting pending refreshes towards the available (but not material!) balance. + t.assertAmountEquals( + bal1.balances[0].available, + bal2.balances[0].available, + ); + } } runWalletRefreshTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts index dc3831595..6f247501e 100644 --- a/packages/taler-wallet-core/src/deposits.ts +++ b/packages/taler-wallet-core/src/deposits.ts @@ -95,7 +95,11 @@ import { generateDepositPermissions, getTotalPaymentCost, } from "./pay-merchant.js"; -import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js"; +import { + CreateRefreshGroupResult, + createRefreshGroup, + getTotalRefreshCost, +} from "./refresh.js"; import { constructTransactionIdentifier, notifyTransition, @@ -532,7 +536,7 @@ async function refundDepositGroup( const currency = Amounts.currencyOf(depositGroup.totalPayCost); - await ws.db.runReadWriteTx( + const res = await ws.db.runReadWriteTx( [ "depositGroups", "refreshGroups", @@ -553,8 +557,9 @@ async function refundDepositGroup( coinPub: depositGroup.payCoinSelection.coinPubs[i], }); } + let refreshRes: CreateRefreshGroupResult | undefined = undefined; if (isDone) { - const rgid = await createRefreshGroup( + refreshRes = await createRefreshGroup( ws, tx, currency, @@ -565,12 +570,19 @@ async function refundDepositGroup( depositGroupId: newDg.depositGroupId, }), ); - newDg.abortRefreshGroupId = rgid.refreshGroupId; + newDg.abortRefreshGroupId = refreshRes.refreshGroupId; } await tx.depositGroups.put(newDg); + return { refreshRes }; }, ); + if (res?.refreshRes) { + for (const notif of res.refreshRes.notifications) { + ws.notify(notif); + } + } + return TaskRunResult.backoff(); } diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts index df0ab25f9..09cd75bdd 100644 --- a/packages/taler-wallet-core/src/refresh.ts +++ b/packages/taler-wallet-core/src/refresh.ts @@ -54,6 +54,7 @@ import { TransactionState, TransactionType, URL, + WalletNotification, } from "@gnu-taler/taler-util"; import { readSuccessResponseJsonOrThrow, @@ -1254,6 +1255,7 @@ async function applyRefresh( export interface CreateRefreshGroupResult { refreshGroupId: string; + notifications: WalletNotification[]; } /** @@ -1310,6 +1312,8 @@ export async function createRefreshGroup( await tx.refreshGroups.put(refreshGroup); + const newTxState = computeRefreshTransactionState(refreshGroup); + logger.trace(`created refresh group ${refreshGroupId}`); const ctx = new RefreshTransactionContext(ws, refreshGroupId); @@ -1321,6 +1325,16 @@ export async function createRefreshGroup( return { refreshGroupId, + notifications: [ + { + type: NotificationType.TransactionStateTransition, + transactionId: ctx.transactionId, + oldTxState: { + major: TransactionMajorState.None, + }, + newTxState, + }, + ], }; } -- cgit v1.2.3