taler-typescript-core

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

commit 95c78fb581a415b3a8660065ac75f17aef22902b
parent ba14f5cc8f5a4b9c3768c9471da6a688f6e06c59
Author: Florian Dold <florian@dold.me>
Date:   Fri, 26 Jun 2026 22:59:58 +0200

harness: expand merchant-tokenfamilies test to repro taler-merchant-httpd crash

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-merchant-tokenfamilies.ts | 109++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 108 insertions(+), 1 deletion(-)

diff --git a/packages/taler-harness/src/integrationtests/test-merchant-tokenfamilies.ts b/packages/taler-harness/src/integrationtests/test-merchant-tokenfamilies.ts @@ -32,16 +32,20 @@ import { TalerMerchantInstanceHttpClient, TalerProtocolTimestamp, TokenFamilyKind, + TransactionMajorState, + TransactionMinorState, } from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { defaultCoinConfig } from "../harness/denomStructures.js"; import { applyTimeTravelV2, createSimpleTestkudosEnvironmentV3, + withdrawViaBankV4, } from "../harness/environments.js"; import { GlobalTestState, waitMs } from "../harness/harness.js"; export async function runMerchantTokenfamiliesTest(t: GlobalTestState) { - let { merchant, walletClient, merchantAdminAccessToken, exchange } = + let { merchant, walletClient, bank, merchantAdminAccessToken, exchange } = await createSimpleTestkudosEnvironmentV3( t, defaultCoinConfig.map((x) => x("TESTKUDOS")), @@ -54,6 +58,15 @@ export async function runMerchantTokenfamiliesTest(t: GlobalTestState) { }, ); + // withdraw some test money + const wres = await withdrawViaBankV4(t, { + walletClient, + exchange, + amount: "TESTKUDOS:40", + bank, + }); + await wres.withdrawalFinishedCond; + const merchantClient = new TalerMerchantInstanceHttpClient( merchant.makeInstanceBaseUrl(), ); @@ -241,6 +254,100 @@ export async function runMerchantTokenfamiliesTest(t: GlobalTestState) { await waitMs(200); } } + + // Now let's to multiple tokens + + { + let suffix: string; + suffix = encodeCrock(getRandomBytes(16)); + + const slugDiscount1 = `slugdiscount2-${suffix}`; + await merchantClient.createTokenFamily(merchantAdminAccessToken, { + name: "discount1", + slug: slugDiscount1, + description: "My Discount 1 (valid two minutes)", + duration: Duration.toTalerProtocolDuration( + Duration.fromSpec({ minutes: 2 }), + ), + valid_after: TalerProtocolTimestamp.now(), + valid_before: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.addDuration( + AbsoluteTime.now(), + Duration.fromSpec({ years: 1 }), + ), + ), + kind: TokenFamilyKind.Discount, + validity_granularity: Duration.toTalerProtocolDuration( + Duration.fromSpec({ minutes: 1 }), + ), + }); + + const req: PostOrderRequest = { + order: { + version: OrderVersion.V1, + summary: "Test Payment", + choices: [ + { + amount: "TESTKUDOS:1", + description: "Article and discount", + outputs: [ + { + type: OrderOutputType.Token, + token_family_slug: slugDiscount1, + count: 3, + }, + ], + }, + { + amount: "TESTKUDOS:1", + inputs: [ + { + type: OrderInputType.Token, + token_family_slug: slugDiscount1, + }, + ], + outputs: [], + }, + ], + }, + }; + + const createResp = succeedOrThrow( + await merchantClient.createOrder(merchantAdminAccessToken, req), + ); + + const st = succeedOrThrow( + await merchantClient.getOrderDetails( + merchantAdminAccessToken, + createResp.order_id, + ), + ); + t.assertTrue(st.order_status === "unpaid"); + const wr = await walletClient.call(WalletApiOperation.PreparePayForUriV2, { + talerPayUri: st.taler_pay_uri, + }); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: wr.transactionId, + txState: { + major: TransactionMajorState.Dialog, + minor: TransactionMinorState.Proposed, + }, + }); + + await walletClient.call(WalletApiOperation.ConfirmPay, { + transactionId: wr.transactionId, + choiceIndex: 0, + }); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: wr.transactionId, + txState: { + major: TransactionMajorState.Done, + minor: TransactionMinorState.Proposed, + }, + }); + } } runMerchantTokenfamiliesTest.suites = ["merchant"];