taler-typescript-core

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

commit 0f232a22481e1b6f65218a1fe40b8f70db78b79c
parent 7524980ecc59b0b2a8f101e342aaa1897d6f7fc2
Author: Florian Dold <dold@taler.net>
Date:   Tue, 21 Jul 2026 02:47:35 +0200

util: test clk positional conversion, defaults, subcommands, and key collisions

Diffstat:
Mpackages/taler-util/src/clk.test.ts | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+), 0 deletions(-)

diff --git a/packages/taler-util/src/clk.test.ts b/packages/taler-util/src/clk.test.ts @@ -38,3 +38,76 @@ test("bla", (t) => { assert.ok(success); }); + +// Run `fn` with process.exit turned into a throw, so a clk parse error fails the +// test instead of tearing down the runner. Console output is captured. +function captureRun(fn: () => void): { errors: string[]; exited: boolean } { + const origExit = process.exit; + const origErr = console.error; + const origLog = console.log; + const errors: string[] = []; + let exited = false; + console.error = (...a: unknown[]) => { + errors.push(a.map(String).join(" ")); + }; + console.log = () => {}; + process.exit = ((code?: number): never => { + exited = true; + throw new Error(`clk exited with ${code ?? 0}`); + }) as typeof process.exit; + try { + fn(); + } catch (e) { + if (!exited) throw e; + } finally { + process.exit = origExit; + console.error = origErr; + console.log = origLog; + } + return { errors, exited }; +} + +test("CLK-1: positional arguments are converted with their Converter", (t) => { + let captured: any; + const prog = clk.program("p1"); + prog.requiredArgument("n", clk.INT).action((args) => { + captured = args; + }); + captureRun(() => prog.run(["prog", "42"])); + assert.strictEqual(captured?.p1?.n, 42); +}); + +test("CLK-2: defaults apply to optional options and arguments; flags default to false", (t) => { + let captured: any; + const prog = clk.program("p2"); + prog + .maybeOption("opt", ["--opt"], clk.STRING, { default: "dflt" }) + .maybeArgument("arg", clk.STRING, { default: "argdflt" }) + .flag("verbose", ["--verbose"]) + .action((args) => { + captured = args; + }); + captureRun(() => prog.run(["prog"])); + assert.strictEqual(captured?.p2?.opt, "dflt"); + assert.strictEqual(captured?.p2?.arg, "argdflt"); + assert.strictEqual(captured?.p2?.verbose, false); +}); + +test("CLK-3: a group can have both positional arguments and subcommands", (t) => { + let captured: any; + const prog = clk.program("p3"); + prog.requiredArgument("region", clk.STRING); + const sub = prog.subcommand("act", "act"); + sub.action((args) => { + captured = args; + }); + captureRun(() => prog.run(["prog", "myregion", "act"])); + assert.strictEqual(captured?.p3?.region, "myregion"); +}); + +test("CLK-4: a subcommand may not reuse an ancestor's argument key", (t) => { + const prog = clk.program("dup"); + const sub = prog.subcommand("dup", "sub"); + sub.action(() => {}); + assert.throws(() => prog.run(["prog", "sub"])); +});