taler-typescript-core

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

commit 29118e7c544af1a90465050e1f25da2a1451edd1
parent 73127e67bf2bf796981c49571ef37877b094cc46
Author: Florian Dold <dold@taler.net>
Date:   Tue, 21 Jul 2026 01:55:23 +0200

util: test x-taler-bank normalization against the C reference

Diffstat:
Apackages/taler-util/src/payto-xtalerbank.test.ts | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+), 0 deletions(-)

diff --git a/packages/taler-util/src/payto-xtalerbank.test.ts b/packages/taler-util/src/payto-xtalerbank.test.ts @@ -0,0 +1,72 @@ +/* + 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/> + */ + +import { test } from "node:test"; +import assert from "node:assert"; +import { Paytos } from "./payto.js"; +import { Result } from "./result.js"; + +/** + * Reference implementation of normalize_payto_x_taler_bank from the C exchange + * (exchange/src/util/payto.c): lowercase every character up to and including + * the host (before the 4th slash), preserve the rest, and do not resolve dot + * segments. H_PAYTO is compared across implementations, so the TypeScript + * normalization must match this byte for byte. + */ +function cNormalizeXTalerBank(fullPayto: string): string { + let slashes = 0; + let out = ""; + for (const c of fullPayto) { + if (c === "/") slashes++; + out += slashes < 4 ? c.toLowerCase() : c; + } + return out; +} + +const cases = [ + "payto://x-taler-bank/bank.example.com/alice", + "payto://x-taler-bank/Bank.Example.COM/alice", + "payto://x-taler-bank/bank.com/SUB/acct", + "payto://x-taler-bank/Bank.COM/SUB/Acct", + "payto://x-taler-bank/bank.com/../../evil/acct", + "payto://x-taler-bank/localhost/exchange", +]; + +test("x-taler-bank normalization matches the C reference", (t) => { + for (const input of cases) { + const r = Paytos.fromString(input); + assert.strictEqual(r.tag, "ok", `parse failed: ${input}`); + const got = Paytos.toNormalizedString(Result.unpack(r)); + assert.strictEqual(got, cNormalizeXTalerBank(input), `for ${input}`); + } +}); + +test("x-taler-bank: host case and dot segments do not change H_PAYTO independence", (t) => { + // Two accounts differing only in the host case are the same account. + const a = Paytos.fromString("payto://x-taler-bank/Bank.COM/acct"); + const b = Paytos.fromString("payto://x-taler-bank/bank.com/acct"); + assert.strictEqual( + Paytos.toNormalizedString(Result.unpack(a)), + Paytos.toNormalizedString(Result.unpack(b)), + ); + // Two accounts differing in the sub-path case are NOT the same. + const c = Paytos.fromString("payto://x-taler-bank/bank.com/SUB/acct"); + const d = Paytos.fromString("payto://x-taler-bank/bank.com/sub/acct"); + assert.notStrictEqual( + Paytos.toNormalizedString(Result.unpack(c)), + Paytos.toNormalizedString(Result.unpack(d)), + ); +});