taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 68f2a74022ae484e93e3db7fa2c067cad543773f
parent 8a8ad0432263595db99c0fd61a7623bfe995c7bb
Author: Antoine A <>
Date:   Fri, 11 Apr 2025 13:44:10 +0200

harness: clean and improve peer tests

Diffstat:
Mpackages/taler-harness/src/harness/harness.ts | 7+++++++
Mpackages/taler-harness/src/integrationtests/test-peer-pull.ts | 321+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mpackages/taler-harness/src/integrationtests/test-peer-push.ts | 333+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mpackages/taler-wallet-core/src/testing.ts | 7+++----
4 files changed, 417 insertions(+), 251 deletions(-)

diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts @@ -45,6 +45,8 @@ import { TalerMerchantApi, TalerMerchantManagementHttpClient, TalerProtocolDuration, + Transaction, + TransactionIdStr, WalletNotification, WireGatewayApiClient, codecForAccountKycRedirects, @@ -65,6 +67,7 @@ import { readSuccessResponseJsonOrThrow, } from "@gnu-taler/taler-util/http"; import { + WalletApiOperation, WalletCoreApiClient, WalletCoreRequestType, WalletCoreResponseType, @@ -2375,6 +2378,10 @@ export class WalletClient { return getClientFromRemoteWallet(this.remoteWallet); } + async getTx(id: TransactionIdStr): Promise<Transaction> { + return this.call(WalletApiOperation.GetTransactionById, {transactionId: id}) + } + waitForNotificationCond<T>( cond: (n: WalletNotification) => T | undefined | false, ): Promise<T> { diff --git a/packages/taler-harness/src/integrationtests/test-peer-pull.ts b/packages/taler-harness/src/integrationtests/test-peer-pull.ts @@ -18,8 +18,10 @@ import { AbsoluteTime, AmountString, Duration, + TalerErrorCode, TransactionMajorState, TransactionMinorState, + TransactionPeerPullCredit, TransactionType, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; @@ -30,6 +32,7 @@ import { } from "../harness/environments.js"; import { GlobalTestState, + WalletClient, } from "../harness/harness.js"; const purse_expiration = AbsoluteTime.toProtocolTimestamp( @@ -46,23 +49,61 @@ export async function runPeerPullTest(t: GlobalTestState) { const [ { walletClient: wallet1, bankClient, exchange }, { walletClient: wallet2 }, + { walletClient: wallet3 }, + { walletClient: wallet4 }, ] = await Promise.all([ createSimpleTestkudosEnvironmentV3(t), createWalletDaemonWithClient(t, { name: "w2" + }), + createWalletDaemonWithClient(t, { + name: "w3" + }), + createWalletDaemonWithClient(t, { + name: "w4" }) ]); // Withdraw digital cash into the wallet. - const withdrawRes = await withdrawViaBankV3(t, { - walletClient: wallet2, - bankClient, - exchange, - amount: "TESTKUDOS:20", - }); - await withdrawRes.withdrawalFinishedCond; - - t.logStep("Check debit amount logic"); + await Promise.all([wallet1, wallet2, wallet3, wallet4].map(async w => { + const withdrawRes = await withdrawViaBankV3(t, { + walletClient: w, + bankClient, + exchange, + amount: "TESTKUDOS:20", + }); + await withdrawRes.withdrawalFinishedCond; + })); + + async function init_peer_pull_credit(w: WalletClient, summary: string, amount: AmountString = "TESTKUDOS:2"): Promise<TransactionPeerPullCredit> { + const initiate = await w.call( + WalletApiOperation.InitiatePeerPullCredit, + { + exchangeBaseUrl: exchange.baseUrl, + partialContractTerms: { + summary, + amount, + purse_expiration, + }, + }, + ); + + await w.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: initiate.transactionId, + txState: { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.Ready + }, + }); + + const tx = await w.call(WalletApiOperation.GetTransactionById, { + transactionId: initiate.transactionId, + }); + t.assertDeepEqual(tx.type, TransactionType.PeerPullCredit); + return tx + } + + t.logStep("P2P pull amount logic"); { await wallet1.call(WalletApiOperation.AddExchange, { exchangeBaseUrl: exchange.baseUrl @@ -71,17 +112,17 @@ export async function runPeerPullTest(t: GlobalTestState) { checkfive, checkzero ] = await Promise.all([ - wallet1.client.call( + wallet1.call( WalletApiOperation.CheckPeerPullCredit, { - amount: "TESTKUDOS:5" as AmountString, + amount: "TESTKUDOS:5", exchangeBaseUrl: exchange.baseUrl, }, ), - wallet1.client.call( + wallet1.call( WalletApiOperation.CheckPeerPullCredit, { - amount: "TESTKUDOS:0" as AmountString, + amount: "TESTKUDOS:0", exchangeBaseUrl: exchange.baseUrl, }, ), @@ -94,119 +135,239 @@ export async function runPeerPullTest(t: GlobalTestState) { t.assertDeepEqual(checkzero.numCoins, 0); } - t.logStep("P2P pull confirm"); + t.logStep("P2P pull errors"); { - const initiate = await wallet1.client.call( - WalletApiOperation.InitiatePeerPullCredit, - { - exchangeBaseUrl: exchange.baseUrl, - partialContractTerms: { - summary: "Hello World", - amount: "TESTKUDOS:5", - purse_expiration, - }, - }, - ); + const tx = await init_peer_pull_credit(wallet1, "confirm", "TESTKUDOS:1000"); - await wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, - txState: { - major: TransactionMajorState.Pending, - minor: TransactionMinorState.Ready - }, - }); - - const tx = await wallet1.call(WalletApiOperation.GetTransactionById, { - transactionId: initiate.transactionId, - }); + const e = await t.assertThrowsTalerErrorAsync(wallet1.call( + WalletApiOperation.PreparePeerPullDebit, + { talerUri: tx.talerUri! } + )); + t.assertTrue(e.errorDetail.code === TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE) + } - t.assertDeepEqual(tx.type, TransactionType.PeerPullCredit); - t.assertTrue(!!tx.talerUri); + t.logStep("P2P pull confirm"); + { + const tx = await init_peer_pull_credit(wallet1, "confirm"); - const prepare = await wallet2.client.call( - WalletApiOperation.PreparePeerPullDebit, - { - talerUri: tx.talerUri, - }, + const [prepare2, prepare3, prepare4] = await Promise.all([wallet2, wallet3, wallet4].map(w => + w.call( + WalletApiOperation.PreparePeerPullDebit, + { talerUri: tx.talerUri! } + )) ); - await wallet2.client.call(WalletApiOperation.ConfirmPeerPullDebit, { - transactionId: prepare.transactionId, + { + const idempotent = await wallet2.call(WalletApiOperation.PreparePeerPullDebit, + { talerUri: tx.talerUri! } + ); + t.assertTrue(prepare2.transactionId === idempotent.transactionId); + } + + await wallet2.call(WalletApiOperation.ConfirmPeerPullDebit, { + transactionId: prepare2.transactionId, + }); + await wallet3.call(WalletApiOperation.ConfirmPeerPullDebit, { + transactionId: prepare3.transactionId, }); + // Idempotent + await Promise.all([ + wallet2.call(WalletApiOperation.ConfirmPeerPullDebit, { + transactionId: prepare2.transactionId, + }), + wallet3.call(WalletApiOperation.ConfirmPeerPullDebit, { + transactionId: prepare3.transactionId, + }), + ]); + await Promise.all([ wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, + transactionId: tx.transactionId, txState: { major: TransactionMajorState.Done, }, }), wallet2.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: prepare.transactionId, + transactionId: prepare2.transactionId, txState: { major: TransactionMajorState.Done, }, - }) + }), + wallet3.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare3.transactionId, + txState: { + major: TransactionMajorState.Aborted, + }, + }), + // FIXME should be aborted + wallet4.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare4.transactionId, + txState: { + major: TransactionMajorState.Dialog, + minor: TransactionMinorState.Proposed + }, + }), ]); } - t.logStep("P2P pull abort"); + /*t.logStep("P2P pull self"); TODO timeout { - const initiate = await wallet1.client.call( - WalletApiOperation.InitiatePeerPullCredit, - { - exchangeBaseUrl: exchange.baseUrl, - partialContractTerms: { - summary: "Hello World", - amount: "TESTKUDOS:5" as AmountString, - purse_expiration, + const tx = await init_peer_pull_credit(wallet1, "self"); + const prepare = await wallet1.call( + WalletApiOperation.PreparePeerPullDebit, + { talerUri: tx.talerUri! } + ); + await wallet1.call(WalletApiOperation.ConfirmPeerPullDebit, { + transactionId: prepare.transactionId, + }) + await Promise.all([ + wallet1.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: tx.transactionId, + txState: { + major: TransactionMajorState.Aborted, }, - }, + }), + wallet1.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare.transactionId, + txState: { + major: TransactionMajorState.Aborted, + }, + }), + ]); + }*/ + + t.logStep("P2P pull conflict"); + { + const tx = await init_peer_pull_credit(wallet1, "conflict"); + + const [prepare2, prepare3] = await Promise.all([wallet2, wallet3].map(w => + w.call( + WalletApiOperation.PreparePeerPullDebit, + { talerUri: tx.talerUri! } + )) ); - await wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, - txState: { - major: TransactionMajorState.Pending, - minor: TransactionMinorState.Ready, - }, - }); + await exchange.stop(); - const tx = await wallet1.call(WalletApiOperation.GetTransactionById, { - transactionId: initiate.transactionId, - }); + await Promise.all([ + wallet2.call(WalletApiOperation.ConfirmPeerPullDebit, { + transactionId: prepare2.transactionId, + }), + wallet3.call(WalletApiOperation.ConfirmPeerPullDebit, { + transactionId: prepare3.transactionId, + }), + ]); - t.assertDeepEqual(tx.type, TransactionType.PeerPullCredit); - t.assertTrue(!!tx.talerUri); + await exchange.start() - const prepare = await wallet2.client.call( - WalletApiOperation.PreparePeerPullDebit, - { - talerUri: tx.talerUri, - }, + await Promise.all([wallet2, wallet3].map(w => + w.call(WalletApiOperation.TestingWaitTransactionsFinal, {})) ); + const [tx2, tx3] = await Promise.all([wallet2.getTx(prepare2.transactionId), wallet3.getTx(prepare3.transactionId)]); + if (tx2.txState.major === TransactionMajorState.Done) { + t.assertTrue(tx3.txState.major === TransactionMajorState.Aborted) + } else { + t.assertTrue(tx3.txState.major === TransactionMajorState.Done) + t.assertTrue(tx2.txState.major === TransactionMajorState.Aborted) + } + } + + t.logStep("P2P pull abort"); + { + const tx = await init_peer_pull_credit(wallet1, "abort"); + + const [prepare2, prepare3] = await Promise.all([wallet2, wallet3].map(w => + w.call( + WalletApiOperation.PreparePeerPullDebit, + { talerUri: tx.talerUri! } + ) + )); await wallet1.call(WalletApiOperation.AbortTransaction, { - transactionId: initiate.transactionId, + transactionId: tx.transactionId, }); - await wallet2.client.call(WalletApiOperation.ConfirmPeerPullDebit, { - transactionId: prepare.transactionId, + await wallet2.call(WalletApiOperation.ConfirmPeerPullDebit, { + transactionId: prepare2.transactionId, }); await Promise.all([ wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, + transactionId: tx.transactionId, txState: { major: TransactionMajorState.Aborted, }, }), wallet2.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: prepare.transactionId, + transactionId: prepare2.transactionId, txState: { major: TransactionMajorState.Aborted, }, + }), + // FIXME should be aborted + wallet3.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare3.transactionId, + txState: { + major: TransactionMajorState.Dialog, + minor: TransactionMinorState.Proposed + }, + }), + ]); + } + + t.logStep("P2P push expire"); + { + const tx = await init_peer_pull_credit(wallet1, "expire"); + + const [prepare2, prepare3] = await Promise.all([wallet2, wallet3].map(w => + w.call( + WalletApiOperation.PreparePeerPullDebit, + { talerUri: tx.talerUri! } + ) + )); + + const timetravelOffsetMs = Duration.toMilliseconds( + Duration.fromSpec({ days: 5 }), + ); + + await exchange.stop(); + exchange.setTimetravel(timetravelOffsetMs); + await exchange.start(); + await exchange.pingUntilAvailable(); + await exchange.runExpireOnce(); + + await Promise.all([wallet1, wallet2, wallet3].map(w => + w.call(WalletApiOperation.TestingSetTimetravel, { + offsetMs: timetravelOffsetMs, }) + )); + + await wallet2.call(WalletApiOperation.ConfirmPeerPullDebit, { + transactionId: prepare2.transactionId, + }); + + await Promise.all([ + wallet1.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: tx.transactionId, + txState: { + major: TransactionMajorState.Expired, + }, + }), + wallet2.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare2.transactionId, + txState: { + major: TransactionMajorState.Aborted, + }, + }), + // FIXME should be aborted + wallet3.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare3.transactionId, + txState: { + major: TransactionMajorState.Dialog, + minor: TransactionMinorState.Proposed + }, + }), ]); } } diff --git a/packages/taler-harness/src/integrationtests/test-peer-push.ts b/packages/taler-harness/src/integrationtests/test-peer-push.ts @@ -16,10 +16,12 @@ import { AbsoluteTime, + AmountString, Duration, TalerErrorCode, TransactionMajorState, TransactionMinorState, + TransactionPeerPushDebit, TransactionType, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; @@ -28,7 +30,7 @@ import { createWalletDaemonWithClient, withdrawViaBankV3, } from "../harness/environments.js"; -import { GlobalTestState } from "../harness/harness.js"; +import { GlobalTestState, WalletClient } from "../harness/harness.js"; const purse_expiration = AbsoluteTime.toProtocolTimestamp( AbsoluteTime.addDuration( @@ -44,10 +46,18 @@ export async function runPeerPushTest(t: GlobalTestState) { const [ { walletClient: wallet1, bankClient, exchange }, { walletClient: wallet2 }, + { walletClient: wallet3 }, + { walletClient: wallet4 }, ] = await Promise.all([ createSimpleTestkudosEnvironmentV3(t), createWalletDaemonWithClient(t, { name: "w2" + }), + createWalletDaemonWithClient(t, { + name: "w3" + }), + createWalletDaemonWithClient(t, { + name: "w4" }) ]); @@ -56,11 +66,39 @@ export async function runPeerPushTest(t: GlobalTestState) { walletClient: wallet1, bankClient, exchange, - amount: "TESTKUDOS:20", + amount: "TESTKUDOS:200", }); await withdrawRes.withdrawalFinishedCond; - t.logStep("Check debit amount logic"); + async function init_peer_push_debit(w: WalletClient, summary: string, amount: AmountString = "TESTKUDOS:5"): Promise<TransactionPeerPushDebit> { + const initiate = await wallet1.call( + WalletApiOperation.InitiatePeerPushDebit, + { + partialContractTerms: { + summary, + amount, + purse_expiration, + }, + }, + ); + + await wallet1.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: initiate.transactionId, + txState: { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.Ready + }, + }); + + const tx = await wallet1.call( + WalletApiOperation.GetTransactionById, + { transactionId: initiate.transactionId } + ); + t.assertDeepEqual(tx.type, TransactionType.PeerPushDebit); + return tx + } + + t.logStep("P2P push amount logic"); { const [ maxpeer, @@ -88,8 +126,8 @@ export async function runPeerPushTest(t: GlobalTestState) { ]); t.assertDeepEqual(maxpeer.exchangeBaseUrl, exchange.baseUrl); - t.assertAmountEquals(maxpeer.rawAmount, "TESTKUDOS:19.1"); - t.assertAmountEquals(maxpeer.effectiveAmount, "TESTKUDOS:19.53"); + t.assertAmountEquals(maxpeer.rawAmount, "TESTKUDOS:192.27"); + t.assertAmountEquals(maxpeer.effectiveAmount, "TESTKUDOS:196.12"); t.assertAmountEquals(checkzero.amountRaw, "TESTKUDOS:0"); t.assertAmountEquals(checkzero.amountEffective, "TESTKUDOS:0"); @@ -98,26 +136,25 @@ export async function runPeerPushTest(t: GlobalTestState) { t.assertAmountEquals(checkfive.amountEffective, "TESTKUDOS:5.49"); } - t.logStep("P2P push confirm"); + t.logStep("P2P push errors"); { - const initiate = await wallet1.call( - WalletApiOperation.InitiatePeerPushDebit, - { + const ex1 = await t.assertThrowsTalerErrorAsync( + wallet1.call(WalletApiOperation.InitiatePeerPushDebit, { partialContractTerms: { - summary: "Hello World 🥺", - amount: "TESTKUDOS:5", + summary: "(this will fail)", + amount: "TESTKUDOS:250", purse_expiration, }, - }, + }) ); + t.assertTrue(ex1.errorDetail.code === TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION); + // FIXME propagate the error correctly + // t.assertTrue(ex1.errorDetail.code === TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE); + } - await wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, - txState: { - major: TransactionMajorState.Pending, - minor: TransactionMinorState.Ready - }, - }); + t.logStep("P2P push confirm"); + { + const tx = await init_peer_push_debit(wallet1, "confirm"); // Check balance update { @@ -125,232 +162,194 @@ export async function runPeerPushTest(t: GlobalTestState) { t.assertAmountEquals(bal.balances[0].pendingOutgoing, "TESTKUDOS:5.49"); } - const tx = await wallet1.call( - WalletApiOperation.GetTransactionById, - { transactionId: initiate.transactionId } - ); - t.assertDeepEqual(tx.type, TransactionType.PeerPushDebit); - t.assertTrue(!!tx.talerUri); - - const prepare = await wallet2.call( - WalletApiOperation.PreparePeerPushCredit, - { talerUri: tx.talerUri } + const [prepare2, prepare3, prepare4] = await Promise.all([wallet2, wallet3, wallet4].map(w => + w.call( + WalletApiOperation.PreparePeerPushCredit, + { talerUri: tx.talerUri! } + )) ); { const idempotent = await wallet2.call( WalletApiOperation.PreparePeerPushCredit, - { talerUri: tx.talerUri } + { talerUri: tx.talerUri! } ); - t.assertTrue(prepare.transactionId === idempotent.transactionId); + t.assertTrue(prepare2.transactionId === idempotent.transactionId); } - await wallet2.call( - WalletApiOperation.ConfirmPeerPushCredit, - { transactionId: prepare.transactionId } - ); - await wallet2.call( - WalletApiOperation.ConfirmPeerPushCredit, - { transactionId: prepare.transactionId } - ); + await wallet2.call(WalletApiOperation.ConfirmPeerPushCredit, { + transactionId: prepare2.transactionId, + }); + await wallet3.call(WalletApiOperation.ConfirmPeerPushCredit, { + transactionId: prepare3.transactionId, + }); + + // Idempotent + await Promise.all([ + wallet2.call(WalletApiOperation.ConfirmPeerPushCredit, { + transactionId: prepare2.transactionId, + }), + wallet3.call(WalletApiOperation.ConfirmPeerPushCredit, { + transactionId: prepare3.transactionId, + }), + ]); await Promise.all([ wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, + transactionId: tx.transactionId, txState: { major: TransactionMajorState.Done, }, }), wallet2.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: prepare.transactionId, + transactionId: prepare2.transactionId, txState: { major: TransactionMajorState.Done, }, - }) + }), + // FIXME should be aborted + wallet3.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare3.transactionId, + txState: { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.Merge + }, + }), + // FIXME should be aborted + wallet4.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare4.transactionId, + txState: { + major: TransactionMajorState.Dialog, + minor: TransactionMinorState.Proposed + }, + }), ]); - const bal = await wallet1.call(WalletApiOperation.GetBalances, {}); - t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:14.04"); - + { + const bal = await wallet1.call(WalletApiOperation.GetBalances, {}); + t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:190.63"); + } - // Check wallet1 scan after completion - const reprepare1 = await wallet1.call( + // Check scan after completion + const prepare1 = await wallet1.call( WalletApiOperation.PreparePeerPushCredit, - { talerUri: tx.talerUri } + { talerUri: tx.talerUri! } ); // FIXME this should fails await wallet1.call( WalletApiOperation.ConfirmPeerPushCredit, - { transactionId: reprepare1.transactionId } - ); - // FIXME this should fail - - - // Check wallet2 rescan after completion - const reprepare2 = await wallet2.call( - WalletApiOperation.PreparePeerPushCredit, - { talerUri: tx.talerUri } + { transactionId: prepare1.transactionId } ); - t.assertTrue(prepare.transactionId === reprepare2.transactionId) - } - - t.logStep("P2P push insufficient balance"); - { - const ex1 = await t.assertThrowsTalerErrorAsync( - wallet1.call(WalletApiOperation.InitiatePeerPushDebit, { - partialContractTerms: { - summary: "(this will fail)", - amount: "TESTKUDOS:15", - purse_expiration, - }, - }) - ); - t.assertTrue(ex1.errorDetail.code === TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION); - // FIXME propagate the error correctly - // t.assertTrue(ex1.errorDetail.code === TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE); + // FIXME this should also fail } t.logStep("P2P push abort"); { - const initiate = await wallet1.call( - WalletApiOperation.InitiatePeerPushDebit, - { - partialContractTerms: { - amount: "TESTKUDOS:7", - summary: "Hi!", - purse_expiration, - }, - }, - ); + const tx = await init_peer_push_debit(wallet1, "abort"); - await wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, - txState: { - major: TransactionMajorState.Pending, - minor: TransactionMinorState.Ready, - }, - }); + const [prepare2, prepare3] = await Promise.all([wallet2, wallet3].map(w => + w.call( + WalletApiOperation.PreparePeerPushCredit, + { talerUri: tx.talerUri! } + ) + )); - const tx = await wallet1.call( - WalletApiOperation.GetTransactionById, - { - transactionId: initiate.transactionId, - }, - ); - t.assertDeepEqual(tx.type, TransactionType.PeerPushDebit); - t.assertTrue(!!tx.talerUri); + await wallet1.call(WalletApiOperation.AbortTransaction, { + transactionId: tx.transactionId, + }); - const prepare = await wallet2.client.call( - WalletApiOperation.PreparePeerPushCredit, - { - talerUri: tx.talerUri, + await wallet1.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: tx.transactionId, + txState: { + major: TransactionMajorState.Aborted, }, - ); - - await wallet1.call(WalletApiOperation.AbortTransaction, { - transactionId: initiate.transactionId, }); - await wallet2.client.call(WalletApiOperation.ConfirmPeerPushCredit, { - transactionId: prepare.transactionId, + await wallet2.call(WalletApiOperation.ConfirmPeerPushCredit, { + transactionId: prepare2.transactionId, }); await Promise.all([ wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, + transactionId: tx.transactionId, txState: { major: TransactionMajorState.Aborted, }, }), - // FIXME check wallet2 is also aborted - /*wallet2.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: prepare.transactionId, + // FIXME should be aborted + wallet2.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare2.transactionId, txState: { - major: TransactionMajorState.Done, + major: TransactionMajorState.Pending, + minor: TransactionMinorState.Merge }, - })*/ + }), + // FIXME should be aborted + wallet3.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare3.transactionId, + txState: { + major: TransactionMajorState.Dialog, + minor: TransactionMinorState.Proposed + }, + }), ]); const bal = await wallet1.call(WalletApiOperation.GetBalances, {}); - t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:13.49"); + t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:189.99"); } t.logStep("P2P push expire"); { - const initiate = await wallet1.call( - WalletApiOperation.InitiatePeerPushDebit, - { - partialContractTerms: { - summary: "second tx, will expire", - amount: "TESTKUDOS:5", - purse_expiration, - }, - }, - ); - - await wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, - txState: { - major: TransactionMajorState.Pending, - minor: TransactionMinorState.Ready, - }, - }); - - const tx = await wallet1.call( - WalletApiOperation.GetTransactionById, - { - transactionId: initiate.transactionId, - }, - ); - t.assertDeepEqual(tx.type, TransactionType.PeerPushDebit); - t.assertTrue(!!tx.talerUri); + const tx = await init_peer_push_debit(wallet1, "expire"); - const prepare = await wallet2.call(WalletApiOperation.PreparePeerPushCredit, { - talerUri: tx.talerUri, - }); + const [prepare2, prepare3] = await Promise.all([wallet2, wallet3].map(w => + w.call( + WalletApiOperation.PreparePeerPushCredit, + { talerUri: tx.talerUri! } + ) + )); const timetravelOffsetMs = Duration.toMilliseconds( Duration.fromSpec({ days: 5 }), ); - console.log("stopping exchange to apply time-travel"); - await exchange.stop(); exchange.setTimetravel(timetravelOffsetMs); await exchange.start(); await exchange.pingUntilAvailable(); - - console.log("running expire"); await exchange.runExpireOnce(); - console.log("done running expire"); - console.log("purse should now be expired"); - - await Promise.all([ - wallet1.call(WalletApiOperation.TestingSetTimetravel, { + await Promise.all([wallet1, wallet2, wallet3].map(w => + w.call(WalletApiOperation.TestingSetTimetravel, { offsetMs: timetravelOffsetMs, - }), - wallet2.call(WalletApiOperation.TestingSetTimetravel, { - offsetMs: timetravelOffsetMs, - }), - ]); + }) + )); await wallet2.client.call(WalletApiOperation.ConfirmPeerPushCredit, { - transactionId: prepare.transactionId, + transactionId: prepare2.transactionId, }); await Promise.all([ wallet1.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: initiate.transactionId, + transactionId: tx.transactionId, txState: { major: TransactionMajorState.Aborted, }, }), - // FIXME check wallet2 is also aborted - /*wallet2.call(WalletApiOperation.TestingWaitTransactionState, { - transactionId: prepare.transactionId, + // FIXME should be aborted + wallet2.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare2.transactionId, txState: { - major: TransactionMajorState.Done, + major: TransactionMajorState.Pending, + minor: TransactionMinorState.Merge }, - })*/ + }), + // FIXME should be aborted + wallet3.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: prepare3.transactionId, + txState: { + major: TransactionMajorState.Dialog, + minor: TransactionMinorState.Proposed + }, + }), ]); } } diff --git a/packages/taler-wallet-core/src/testing.ts b/packages/taler-wallet-core/src/testing.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2020 Taler Systems S.A. + (C) 2020-2025 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 @@ -637,9 +637,8 @@ export async function waitTransactionState( return matchState(tx.txState, txState); } }, - filterNotification(notif) { - return notif.type === NotificationType.TransactionStateTransition; - }, + filterNotification: (notif) => + notif.type === NotificationType.TransactionStateTransition && notif.transactionId === transactionId }); logger.info( `done waiting for ${transactionId} to be in ${JSON.stringify(txState)}`,