taler-typescript-core

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

commit 070db02d43068eb75ff39fad81de715ced293e36
parent 2730c3ab5bb7db22c7493f98d07a56672e762d84
Author: Florian Dold <dold@taler.net>
Date:   Sat,  5 Sep 2026 23:13:49 +0200

harness: cover cumulative withdrawal KYC previews

Cover a second withdrawal that crosses the balance limit, alongside a
single large withdrawal and a zero-limit rule. Assert the preview does
not start KYC and that completed KYC raises the preview allowance.

Issue: https://bugs.taler.net/n/10410

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-kyc-balance-withdrawal.ts | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mpackages/taler-harness/src/integrationtests/testrunner.ts | 8+++++++-
2 files changed, 143 insertions(+), 4 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-kyc-balance-withdrawal.ts b/packages/taler-harness/src/integrationtests/test-kyc-balance-withdrawal.ts @@ -18,6 +18,7 @@ * Imports. */ import { + Amounts, Configuration, encodeCrock, ExchangeWalletKycStatus, @@ -67,7 +68,7 @@ function adjustExchangeConfig(config: Configuration): void { config.setString("KYC-CHECK-C1", "fallback", "FREEZE"); } -export async function runKycBalanceWithdrawalTest(t: GlobalTestState) { +async function runBalanceWithdrawal(t: GlobalTestState, cumulative: boolean) { // Set up test environment const { @@ -81,10 +82,69 @@ export async function runKycBalanceWithdrawalTest(t: GlobalTestState) { adjustExchangeConfig, }); + await walletClient.call(WalletApiOperation.AddExchange, { + uri: exchange.baseUrl, + }); + + if (cumulative) { + const firstPreview = await walletClient.call( + WalletApiOperation.GetWithdrawalDetailsForAmount, + { + exchangeBaseUrl: exchange.baseUrl, + amount: "TESTKUDOS:8", + }, + ); + t.assertDeepEqual(firstPreview.kycRequired, false); + const first = await withdrawViaBankV3(t, { + amount: "TESTKUDOS:8", + bankClient, + exchange, + walletClient, + }); + await first.withdrawalFinishedCond; + } + + const amount = cumulative ? "TESTKUDOS:8" : "TESTKUDOS:20"; + const expectedBalance = cumulative ? "TESTKUDOS:15.68" : "TESTKUDOS:19.84"; + const before = await walletClient.call( + WalletApiOperation.GetExchangeEntryByUrl, + { + exchangeBaseUrl: exchange.baseUrl, + }, + ); + const preview = await walletClient.call( + WalletApiOperation.GetWithdrawalDetailsForAmount, + { + exchangeBaseUrl: exchange.baseUrl, + amount, + }, + ); + t.assertDeepEqual(preview.kycRequired, true); + t.assertDeepEqual(preview.balanceKyc, { + currentBalance: cumulative ? "TESTKUDOS:7.84" : "TESTKUDOS:0", + projectedBalance: expectedBalance, + threshold: "TESTKUDOS:10", + remaining: cumulative ? "TESTKUDOS:2.16" : "TESTKUDOS:10", + }); + const after = await walletClient.call( + WalletApiOperation.GetExchangeEntryByUrl, + { + exchangeBaseUrl: exchange.baseUrl, + }, + ); + t.assertDeepEqual(after.walletKycReservePub, before.walletKycReservePub); + t.assertDeepEqual(after.walletKycStatus, before.walletKycStatus); + t.assertDeepEqual( + after.walletKycRequestedThreshold, + before.walletKycRequestedThreshold, + ); + t.assertTrue(after.walletKycRequestedThreshold == null); + t.logStep("preview warns about balance KYC without starting it"); + // Withdraw digital cash into the wallet. const wres = await withdrawViaBankV3(t, { - amount: "TESTKUDOS:20", + amount, bankClient, exchange, walletClient, @@ -137,7 +197,7 @@ export async function runKycBalanceWithdrawalTest(t: GlobalTestState) { exchangeEntry.walletKycRequestedThreshold, // The wallet asks the exchange to authorize its expected balance, not // merely the lower limit that this withdrawal crossed. - "TESTKUDOS:19.84", + expectedBalance, ); const kycReservePub = exchangeEntry.walletKycReservePub; @@ -185,6 +245,31 @@ export async function runKycBalanceWithdrawalTest(t: GlobalTestState) { t.logStep("done waiting for withdrawal to finish"); + // Completed KYC raises the applicable preview threshold to 30. + for (const [amount, required] of [ + ["TESTKUDOS:8", false], + ["TESTKUDOS:20", true], + ] as const) { + const nextPreview = await walletClient.call( + WalletApiOperation.GetWithdrawalDetailsForAmount, + { + exchangeBaseUrl: exchange.baseUrl, + amount, + }, + ); + t.assertDeepEqual(nextPreview.kycRequired, required); + t.assertDeepEqual(nextPreview.balanceKyc, { + currentBalance: expectedBalance, + projectedBalance: Amounts.stringify( + Amounts.add(expectedBalance, nextPreview.amountEffective).amount, + ), + threshold: "TESTKUDOS:30", + remaining: Amounts.stringify( + Amounts.sub("TESTKUDOS:30", expectedBalance).amount, + ), + }); + } + // Now test the *next* threshold, but this time using manual withdrawal t.logStep("testing manual withdrawal"); @@ -226,4 +311,52 @@ export async function runKycBalanceWithdrawalTest(t: GlobalTestState) { }); } +export async function runKycBalanceWithdrawalTest(t: GlobalTestState) { + await runBalanceWithdrawal(t, false); +} + +/** Regression for https://bugs.gnunet.org/view.php?id=10410. */ +export async function runKycBalanceWithdrawalCumulativeTest( + t: GlobalTestState, +) { + await runBalanceWithdrawal(t, true); +} + runKycBalanceWithdrawalTest.suites = ["wallet"]; +runKycBalanceWithdrawalCumulativeTest.suites = ["wallet"]; + +export async function runKycBalanceWithdrawalZeroLimitTest(t: GlobalTestState) { + const { walletClient, exchange } = await createKycTestkudosEnvironmentFull( + t, + { + adjustExchangeConfig(config) { + adjustExchangeConfig(config); + config.setString("KYC-RULE-R1", "operation_type", "withdraw"); + config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:0"); + config.setString("KYC-RULE-R2", "enabled", "no"); + }, + }, + ); + const preview = await walletClient.call( + WalletApiOperation.GetWithdrawalDetailsForAmount, + { + exchangeBaseUrl: exchange.baseUrl, + amount: "TESTKUDOS:8", + }, + ); + t.assertDeepEqual(preview.kycRequired, true); + t.assertDeepEqual(preview.kycSoftLimit, "TESTKUDOS:0"); + t.assertDeepEqual(preview.balanceKyc, { + currentBalance: "TESTKUDOS:0", + projectedBalance: "TESTKUDOS:7.84", + }); + const entry = await walletClient.call( + WalletApiOperation.GetExchangeEntryByUrl, + { + exchangeBaseUrl: exchange.baseUrl, + }, + ); + t.assertTrue(entry.walletKycRequestedThreshold == null); +} + +runKycBalanceWithdrawalZeroLimitTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -83,7 +83,11 @@ import { runKnownAccountsTest } from "./test-known-accounts.js"; import { runKycAmpFailureTest } from "./test-kyc-amp-failure.js"; import { runKycAmpTimeoutTest } from "./test-kyc-amp-timeout.js"; import { runKycBalanceWithdrawalChangeManualTest } from "./test-kyc-balance-withdrawal-change-manual.js"; -import { runKycBalanceWithdrawalTest } from "./test-kyc-balance-withdrawal.js"; +import { + runKycBalanceWithdrawalTest, + runKycBalanceWithdrawalCumulativeTest, + runKycBalanceWithdrawalZeroLimitTest, +} from "./test-kyc-balance-withdrawal.js"; import { runKycChallengerTest } from "./test-kyc-challenger.js"; import { runKycDecisionAttrTest } from "./test-kyc-decision-attr.js"; import { runKycDecisionEventsTest } from "./test-kyc-decision-events.js"; @@ -429,6 +433,8 @@ const allTests: TestMainFunction[] = [ runKycPeerPullTest, runKycDepositAggregateTest, runKycBalanceWithdrawalTest, + runKycBalanceWithdrawalCumulativeTest, + runKycBalanceWithdrawalZeroLimitTest, runKycNewMeasureTest, runKycSkipExpirationTest, runKycTwoFormsTest,