commit 68f2a74022ae484e93e3db7fa2c067cad543773f
parent 8a8ad0432263595db99c0fd61a7623bfe995c7bb
Author: Antoine A <>
Date: Fri, 11 Apr 2025 13:44:10 +0200
harness: clean and improve peer tests
Diffstat:
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)}`,