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:
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,