taler-typescript-core

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

commit 03b7cfb3310b0e29e26c0d760ab60e3a14c32098
parent 21b6b42e20192fd8a8a7c75c2d7fc394ef8ee4ee
Author: Florian Dold <dold@taler.net>
Date:   Thu,  3 Sep 2026 15:42:47 +0200

wallet-core: test availability rebuild, token domains, refund totals

Diffstat:
Mpackages/taler-wallet-core/src/coinAvailability.test.ts | 46++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-wallet-core/src/pay-merchant.test.ts | 18++++++++++++++++++
Mpackages/taler-wallet-core/src/tokenSelection.test.ts | 12++++++++++++
3 files changed, 76 insertions(+), 0 deletions(-)

diff --git a/packages/taler-wallet-core/src/coinAvailability.test.ts b/packages/taler-wallet-core/src/coinAvailability.test.ts @@ -274,3 +274,49 @@ test("source finalization does not make spent coins available again", async () = assert.strictEqual(availability.visibleCoinCount, 1); assert.strictEqual(availabilityWrites, 1); }); + +test("coin availability rebuild tolerates coins without a denomination", async () => { + const availability = makeAvailability(); + const orphan = makeCoin("orphan", CoinStatus.Fresh, 1); + orphan.denomPubHash = "gone-denom-pub-hash"; + const coins = [makeCoin("fresh-visible", CoinStatus.Fresh, 1), orphan]; + const created: WalletCoinAvailability[] = []; + const tx = { + async getCoinAvailabilities() { + return [availability]; + }, + async listAllCoins() { + return coins; + }, + async getDenomination() { + return undefined; + }, + async listAllRefreshGroups() { + return [ + { + refreshGroupId: "refresh-group", + oldCoinPubs: ["purged-old-coin"], + statusPerCoin: [RefreshCoinStatus.Pending], + }, + ]; + }, + async listAllRefreshSessions() { + return [ + { + refreshGroupId: "refresh-group", + coinIndex: 0, + newDenoms: [{ denomPubHash: availability.denomPubHash, count: 3 }], + }, + ]; + }, + async upsertCoinAvailability(av: WalletCoinAvailability) { + if (av !== availability) created.push(av); + }, + } as unknown as WalletDbTransaction; + + await recomputeCoinAvailability(tx); + + assert.strictEqual(availability.freshCoinCount, 1); + assert.strictEqual(availability.visibleCoinCount, 1); + assert.deepStrictEqual(created, []); +}); diff --git a/packages/taler-wallet-core/src/pay-merchant.test.ts b/packages/taler-wallet-core/src/pay-merchant.test.ts @@ -962,3 +962,21 @@ test("merchant repair recovers the contribution of a coin the merchant rejected" { coinPub: "coin-three", amount: "TESTKUDOS:3" }, ]); }); + +test("refund totals leave out refunds that did not happen", () => { + const done = refundGroup("done", "TESTKUDOS:3", "TESTKUDOS:2.5"); + const pending = refundGroup("pending", "TESTKUDOS:1", "TESTKUDOS:0.5"); + pending.status = RefundGroupStatus.Pending; + const failed = refundGroup("failed", "TESTKUDOS:7", "TESTKUDOS:0"); + failed.status = RefundGroupStatus.Failed; + const aborted = refundGroup("aborted", "TESTKUDOS:5", "TESTKUDOS:0"); + aborted.status = RefundGroupStatus.Aborted; + + assert.deepStrictEqual( + getRefundTotals([done, pending, failed, aborted], "TESTKUDOS"), + { + raw: "TESTKUDOS:4", + effective: "TESTKUDOS:3", + }, + ); +}); diff --git a/packages/taler-wallet-core/src/tokenSelection.test.ts b/packages/taler-wallet-core/src/tokenSelection.test.ts @@ -249,3 +249,15 @@ test("payment repair retains its tokens and excludes other reservations", () => assert.strictEqual(result.details.tokensAvailable, 2); } }); + +test("an unparseable issuer domain does not match instead of failing", (t) => { + const issuer = "https://backend.test.taler.net/"; + + assert.strictEqual( + verifyTokenMerchant("https://shop.example/", issuer, { + class: MerchantContractTokenKind.Discount, + expected_domains: ["localhost", "shop."], + }), + TokenMerchantVerificationResult.Unexpected, + ); +});