/* 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 */ /** * Imports. */ import { AmountString, NotificationType, TalerMerchantApi, TransactionIdStr, TransactionMajorState, TransactionType, j2s, } from "@gnu-taler/taler-util"; import { WalletApiOperation, parseTransactionIdentifier, } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState, generateRandomPayto } from "../harness/harness.js"; import { createSimpleTestkudosEnvironmentV3, makeTestPaymentV2, withdrawViaBankV3, } from "../harness/helpers.js"; /** * Run test for refreshe after a payment. */ export async function runWalletRefreshTest(t: GlobalTestState) { // Set up test environment const { walletClient, bankClient, exchange, merchant } = await createSimpleTestkudosEnvironmentV3(t); // Withdraw digital cash into the wallet. await withdrawViaBankV3(t, { walletClient, bankClient, exchange, amount: "TESTKUDOS:20", }); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", }; await makeTestPaymentV2(t, { walletClient, merchant, order }); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); const txns = await walletClient.call(WalletApiOperation.GetTransactions, { includeRefreshes: true, }); console.log(j2s(txns)); t.assertDeepEqual(txns.transactions.length, 3); const refreshListTx = txns.transactions.find( (x) => x.type === TransactionType.Refresh, ); t.assertTrue(!!refreshListTx); const refreshTx = await walletClient.call( WalletApiOperation.GetTransactionById, { transactionId: refreshListTx.transactionId, }, ); 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, ); } const wres = await withdrawViaBankV3(t, { walletClient, bankClient, exchange, amount: "TESTKUDOS:20", }); await wres.withdrawalFinishedCond; // Test failing a refresh transaction { await exchange.stop(); let refreshTransactionId: TransactionIdStr | undefined = undefined; const refreshCreatedCond = walletClient.waitForNotificationCond((x) => { if ( x.type === NotificationType.TransactionStateTransition && parseTransactionIdentifier(x.transactionId)?.tag === TransactionType.Refresh ) { refreshTransactionId = x.transactionId as TransactionIdStr; return true; } return false; }); const depositGroupResult = await walletClient.client.call( WalletApiOperation.CreateDepositGroup, { amount: "TESTKUDOS:10.5" as AmountString, depositPaytoUri: generateRandomPayto("foo"), }, ); await refreshCreatedCond; t.assertTrue(!!refreshTransactionId); await walletClient.call(WalletApiOperation.FailTransaction, { transactionId: refreshTransactionId, }); const txn = await walletClient.call(WalletApiOperation.GetTransactionById, { transactionId: refreshTransactionId, }); t.assertDeepEqual(txn.type, TransactionType.Refresh); t.assertDeepEqual(txn.txState.major, TransactionMajorState.Failed); t.assertTrue(!!refreshTransactionId); } } runWalletRefreshTest.suites = ["wallet"];