taler-typescript-core

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

commit 9bff5113ba72a2d2c3032c73246686c13905e7eb
parent 257911ee08b5876d1daf8033ee5f699d99abfca2
Author: Florian Dold <dold@taler.net>
Date:   Tue, 21 Jul 2026 01:43:39 +0200

util: test taler URI stringify/parse round-trips

Diffstat:
Apackages/taler-util/src/taleruri-roundtrip.test.ts | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+), 0 deletions(-)

diff --git a/packages/taler-util/src/taleruri-roundtrip.test.ts b/packages/taler-util/src/taleruri-roundtrip.test.ts @@ -0,0 +1,67 @@ +/* + 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 { TalerUris, TalerUriAction } from "./taleruri.js"; +import { Result } from "./result.js"; + +const payUri = (orderId: string, sessionId = "sess") => ({ + type: TalerUriAction.Pay as const, + merchantBaseUrl: "https://m.example.com/" as any, + orderId, + sessionId, +}); + +test("pay URI: a component with structural characters round-trips", (t) => { + for (const orderId of ["a/b", "a?c=evil", "a#b", "a&b", "plain-01", "a b"]) { + const s = TalerUris.stringify(payUri(orderId)); + const r = TalerUris.parse(s); + assert.strictEqual(r.tag, "ok", `parse failed for orderId ${orderId}: ${s}`); + const v = Result.unpack(r) as any; + assert.strictEqual(v.orderId, orderId, `orderId not preserved: ${s}`); + // the merchant host must survive intact + assert.strictEqual(v.merchantBaseUrl, "https://m.example.com/", s); + } +}); + +test("pay URI: a dot-segment component cannot be represented", (t) => { + // "." and ".." are resolved away by the URL parser, even percent-encoded, + // so they cannot be a path segment and must be rejected rather than + // silently erasing the host. + assert.throws(() => TalerUris.stringify(payUri(".."))); + assert.throws(() => TalerUris.stringify(payUri("."))); +}); + +test("withdrawal-transfer-result URI round-trips", (t) => { + const parsed = TalerUris.parse( + "taler://withdrawal-transfer-result?ref=abc&status=success", + ); + assert.strictEqual(parsed.tag, "ok"); + const v = Result.unpack(parsed) as any; + assert.strictEqual(v.ref, "abc"); + assert.strictEqual(v.status, "success"); + + const s = TalerUris.stringify(v); + const reparsed = TalerUris.parse(s); + assert.strictEqual(reparsed.tag, "ok", `not re-parseable: ${s}`); + assert.deepStrictEqual(Result.unpack(reparsed), v); +}); + +test("withdrawal-transfer-result URI requires a ref", (t) => { + const r = TalerUris.parse("taler://withdrawal-transfer-result?status=success"); + assert.strictEqual(r.tag, "error"); +});