taler-typescript-core

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

commit 2bc5eec8bd0ea1adb44f184adc22891b6b7c9617
parent 80e1b2669fc32fedb13673da5238f7d26badfb77
Author: Florian Dold <dold@taler.net>
Date:   Fri, 31 Jul 2026 11:54:20 +0200

harness: check exchange base URL completion against a real exchange

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

Diffstat:
Apackages/taler-harness/src/integrationtests/test-exchange-base-url-completion.ts | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-harness/src/integrationtests/testrunner.ts | 2++
2 files changed, 106 insertions(+), 0 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-exchange-base-url-completion.ts b/packages/taler-harness/src/integrationtests/test-exchange-base-url-completion.ts @@ -0,0 +1,104 @@ +/* + This file is part of GNU Taler + (C) 2026 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + 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/> + */ + +/** + * Imports. + */ +import { j2s } from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js"; +import { GlobalTestState } from "../harness/harness.js"; + +/** + * Check that completing an exchange base URL actually talks to the exchange, + * instead of just tidying up the string it was given. + */ +export async function runExchangeBaseUrlCompletionTest( + t: GlobalTestState, +): Promise<void> { + const { walletClient, exchange, bankClient } = + await createSimpleTestkudosEnvironmentV3(t); + + // A URL that a real exchange answers on is completed as-is. + { + const res = await walletClient.call( + WalletApiOperation.CompleteExchangeBaseUrl, + { url: exchange.baseUrl }, + ); + t.assertDeepEqual(res.status, "ok"); + if (res.status !== "ok") throw Error("unreachable"); + t.assertDeepEqual(res.completion, exchange.baseUrl); + } + + // The same URL without its trailing slash names the same exchange. + { + const res = await walletClient.call( + WalletApiOperation.CompleteExchangeBaseUrl, + { url: exchange.baseUrl.replace(/\/$/, "") }, + ); + t.assertDeepEqual(res.status, "ok"); + if (res.status !== "ok") throw Error("unreachable"); + t.assertDeepEqual(res.completion, exchange.baseUrl); + } + + // Something is listening, but it is not an exchange. + { + const res = await walletClient.call( + WalletApiOperation.CompleteExchangeBaseUrl, + { url: bankClient.baseUrl }, + ); + console.log(`completing the bank URL gave ${j2s(res)}`); + t.assertDeepEqual(res.status, "bad-exchange"); + } + + // Nothing is listening on that port. + { + const res = await walletClient.call( + WalletApiOperation.CompleteExchangeBaseUrl, + { url: "http://localhost:9/" }, + ); + console.log(`completing an unused port gave ${j2s(res)}`); + t.assertDeepEqual(res.status, "bad-network"); + } + + // Rejected before any request is made. + { + const res = await walletClient.call( + WalletApiOperation.CompleteExchangeBaseUrl, + { url: "ftp://exchange.example.com/" }, + ); + t.assertDeepEqual(res.status, "bad-syntax"); + } + + // A host the wallet already knows is offered when completion fails, so that + // a typo does not leave the user with nothing. + { + await walletClient.call(WalletApiOperation.UpdateExchangeEntry, { + exchangeBaseUrl: exchange.baseUrl, + force: true, + }); + const exchangeHost = new URL(exchange.baseUrl).hostname; + const res = await walletClient.call( + WalletApiOperation.CompleteExchangeBaseUrl, + { url: `http://${exchangeHost}x:9/` }, + ); + console.log(`completing a typo gave ${j2s(res)}`); + if (res.status === "ok") throw Error("expected completion to fail"); + t.assertTrue((res.suggestions ?? []).includes(exchange.baseUrl)); + } +} + +runExchangeBaseUrlCompletionTest.suites = ["wallet", "exchange"]; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -62,6 +62,7 @@ import { runDonauKeychangeTest } from "./test-donau-keychange.js"; import { runDonauMinusTTest } from "./test-donau-minus-t.js"; import { runDonauMultiTest } from "./test-donau-multi.js"; import { runDonauTest } from "./test-donau.js"; +import { runExchangeBaseUrlCompletionTest } from "./test-exchange-base-url-completion.js"; import { runExchangeDepositTest } from "./test-exchange-deposit.js"; import { runExchangeEphemeralTest } from "./test-exchange-ephemeral.js"; import { runExchangeKycAuthTest } from "./test-exchange-kyc-auth.js"; @@ -291,6 +292,7 @@ const allTests: TestMainFunction[] = [ runCoinselLegacy2024Test, runKycChallengerTest, runExchangePurseTest, + runExchangeBaseUrlCompletionTest, runExchangeDepositTest, runExchangeEphemeralTest, runMerchantExchangeConfusionTest,