taler-typescript-core

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

commit 79fc1f13f908040b209d1ebae785ff3bc8d05729
parent ef1b8fd44acc566cb57f00b887a617ea7b65e111
Author: Florian Dold <dold@taler.net>
Date:   Thu, 20 Aug 2026 19:06:40 +0200

wallet-core: continue idempotent token allocation batches

Diffstat:
Mpackages/taler-wallet-core/src/common.test.ts | 45++++++++++++++++++++++++++++++++++++++++++---
Mpackages/taler-wallet-core/src/common.ts | 2+-
2 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/packages/taler-wallet-core/src/common.test.ts b/packages/taler-wallet-core/src/common.test.ts @@ -13,11 +13,16 @@ 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/> */ -import { Duration } from "@gnu-taler/taler-util"; +import { Duration, TransactionIdStr } from "@gnu-taler/taler-util"; import assert from "node:assert"; import { test } from "node:test"; -import { augmentTransferOptions, getRetryDuration } from "./common.js"; -import { KycAuthTransferOptionRaw } from "./db-common.js"; +import { + augmentTransferOptions, + getRetryDuration, + spendTokens, +} from "./common.js"; +import { KycAuthTransferOptionRaw, WalletToken } from "./db-common.js"; +import { WalletDbTransaction } from "./dbtx.js"; test("the retry delay grows but stays bounded", (t) => { const first = Duration.toMilliseconds(getRetryDuration(0)); @@ -53,3 +58,37 @@ test("augmenting transfer options does not expose internal metadata", () => { }, ]); }); + +test("spending tokens continues after an idempotently allocated token", async () => { + const transactionId = "txn:payment:proposal" as TransactionIdStr; + const tokens = new Map<string, WalletToken>([ + [ + "already-allocated", + { + tokenUsePub: "already-allocated", + transactionId, + } as unknown as WalletToken, + ], + [ + "newly-allocated", + { + tokenUsePub: "newly-allocated", + } as unknown as WalletToken, + ], + ]); + const tx = { + async getToken(pub: string): Promise<WalletToken | undefined> { + return tokens.get(pub); + }, + async upsertToken(token: WalletToken): Promise<void> { + tokens.set(token.tokenUsePub, token); + }, + } as WalletDbTransaction; + + await spendTokens(tx, { + transactionId, + tokenPubs: ["already-allocated", "newly-allocated"], + }); + + assert.strictEqual(tokens.get("newly-allocated")?.transactionId, transactionId); +}); diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts @@ -298,7 +298,7 @@ export async function spendTokens( if (token.transactionId !== tsi.transactionId) { throw Error(`token already being spent in a different transaction`); } else { - return; + continue; } }