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:
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"]));
+});