diff options
Diffstat (limited to 'packages/taler-harness/src/integrationtests/test-peer-repair.ts')
-rw-r--r-- | packages/taler-harness/src/integrationtests/test-peer-repair.ts | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/packages/taler-harness/src/integrationtests/test-peer-repair.ts b/packages/taler-harness/src/integrationtests/test-peer-repair.ts new file mode 100644 index 000000000..8bc7ed0a1 --- /dev/null +++ b/packages/taler-harness/src/integrationtests/test-peer-repair.ts @@ -0,0 +1,213 @@ +/* + This file is part of GNU Taler + (C) 2023 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 <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ +import { + AbsoluteTime, + AmountString, + Duration, + NotificationType, + TransactionMajorState, + TransactionMinorState, + TransactionType, + WalletNotification, +} from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import * as fs from "node:fs"; +import { GlobalTestState } from "../harness/harness.js"; +import { + createSimpleTestkudosEnvironmentV2, + createWalletDaemonWithClient, + withdrawViaBankV2, +} from "../harness/helpers.js"; + +export async function runPeerRepairTest(t: GlobalTestState) { + // Set up test environment + + const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t); + + let allW1Notifications: WalletNotification[] = []; + let allW2Notifications: WalletNotification[] = []; + + let w1 = await createWalletDaemonWithClient(t, { + name: "w1", + persistent: true, + handleNotification(wn) { + allW1Notifications.push(wn); + }, + }); + const w2 = await createWalletDaemonWithClient(t, { + name: "w2", + handleNotification(wn) { + allW2Notifications.push(wn); + }, + }); + + // Withdraw digital cash into the wallet. + let wallet1 = w1.walletClient; + const wallet2 = w2.walletClient; + + const withdrawalDoneCond = wallet1.waitForNotificationCond( + (x) => + x.type === NotificationType.TransactionStateTransition && + x.newTxState.major === TransactionMajorState.Done && + x.transactionId.startsWith("txn:withdrawal:"), + ); + + await withdrawViaBankV2(t, { + walletClient: wallet1, + bank, + exchange, + amount: "TESTKUDOS:5", + }); + + await withdrawalDoneCond; + const w1DbPath = w1.walletService.dbPath; + const w1DbCopyPath = w1.walletService.dbPath + ".copy"; + fs.copyFileSync(w1DbPath, w1DbCopyPath); + + const purse_expiration = AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.addDuration( + AbsoluteTime.now(), + Duration.fromSpec({ days: 2 }), + ), + ); + + const resp1 = await wallet1.client.call( + WalletApiOperation.InitiatePeerPushDebit, + { + exchangeBaseUrl: exchange.baseUrl, + partialContractTerms: { + summary: "Hello World", + amount: "TESTKUDOS:3" as AmountString, + purse_expiration, + }, + }, + ); + + const peerPushDebitReady1Cond = wallet1.waitForNotificationCond( + (x) => + x.type === NotificationType.TransactionStateTransition && + x.transactionId === resp1.transactionId && + x.newTxState.major === TransactionMajorState.Pending && + x.newTxState.minor === TransactionMinorState.Ready, + ); + + await peerPushDebitReady1Cond; + + const txDetails = await wallet1.call(WalletApiOperation.GetTransactionById, { + transactionId: resp1.transactionId, + }); + t.assertDeepEqual(txDetails.type, TransactionType.PeerPushDebit); + t.assertTrue(!!txDetails.talerUri); + + const resp2 = await wallet2.client.call( + WalletApiOperation.PreparePeerPushCredit, + { + talerUri: txDetails.talerUri, + }, + ); + + const peerPushCreditDone1Cond = wallet2.waitForNotificationCond( + (x) => + x.type === NotificationType.TransactionStateTransition && + x.transactionId === resp2.transactionId && + x.newTxState.major === TransactionMajorState.Done, + ); + + const peerPushDebitDone1Cond = wallet1.waitForNotificationCond( + (x) => + x.type === NotificationType.TransactionStateTransition && + x.transactionId === resp1.transactionId && + x.newTxState.major === TransactionMajorState.Done, + ); + + await wallet2.client.call(WalletApiOperation.ConfirmPeerPushCredit, { + transactionId: resp2.transactionId, + }); + + await peerPushCreditDone1Cond; + await peerPushDebitDone1Cond; + + w1.walletClient.remoteWallet?.close(); + await w1.walletService.stop(); + + fs.copyFileSync(w1DbCopyPath, w1DbPath); + + console.log(`copied back to ${w1DbPath}`); + + w1 = await createWalletDaemonWithClient(t, { + name: "w1", + persistent: true, + handleNotification(wn) { + allW1Notifications.push(wn); + }, + }); + wallet1 = w1.walletClient; + + console.log("attempting peer-push-debit, should fail."); + + const initResp2 = await wallet1.client.call( + WalletApiOperation.InitiatePeerPushDebit, + { + exchangeBaseUrl: exchange.baseUrl, + partialContractTerms: { + summary: "Hello World", + amount: "TESTKUDOS:3" as AmountString, + purse_expiration, + }, + }, + ); + + const peerPushDebitFailingCond = wallet1.waitForNotificationCond( + (x) => + x.type === NotificationType.TransactionStateTransition && + x.transactionId === initResp2.transactionId && + x.newTxState.major === TransactionMajorState.Pending && + x.errorInfo != null, + ); + + console.log(`waiting for error on ${initResp2.transactionId}`); + + await peerPushDebitFailingCond; + + console.log("reached error"); + + // Now withdraw so we have enough coins to re-select + + const withdraw2Res = await withdrawViaBankV2(t, { + walletClient: wallet1, + bank, + exchange, + amount: "TESTKUDOS:5", + }); + + await withdraw2Res.withdrawalFinishedCond; + + const peerPushDebitReady2Cond = wallet1.waitForNotificationCond( + (x) => + x.type === NotificationType.TransactionStateTransition && + x.transactionId === initResp2.transactionId && + x.newTxState.major === TransactionMajorState.Pending && + x.newTxState.minor === TransactionMinorState.Ready, + ); + + await peerPushDebitReady2Cond; +} + +runPeerRepairTest.suites = ["wallet"]; |