Files
OmniRoute/tests/unit/cli-tunnel-create-command.test.ts
Goni Sulaiman 6e35ad01cc fix(cli): remove duplicate positional argument in tunnel create command (#12368)
Validado em lote numa worktree combinada com os 6 PRs destas duas levas sobre o tip de `release/v3.8.51`: os seis boardaram **sem um único conflito**, `typecheck:core` limpo e **54/54** nos 7 arquivos de teste que trazem.

O drift de `i18n:check` (`docs/security/GUARDRAILS.md`, `STEALTH_GUIDE.md` — source-changed) foi medido também no tip puro e é idêntico: base-red pré-existente, não desta leva.
2026-09-03 23:28:12 -03:00

91 lines
3.7 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
// #12295: `omniroute tunnel create <type>` crashed with
// "Cannot read properties of undefined (reading optsWithGlobals)" because
// `.command("create [type]")` + `.addArgument(new Argument("[type]", ...))`
// registered TWO positional arguments. Commander then passed
// (type, type2, opts, command) to the action callback, but the handler
// destructured only (type, opts, cmd) — so `cmd` was bound to the real opts
// object and `cmd.parent` was undefined.
test("tunnel create subcommand declares exactly one positional argument (#12295)", async () => {
const { Command } = await import("commander");
const { registerTunnel } = await import("../../bin/cli/commands/tunnel.mjs");
const program = new Command();
registerTunnel(program);
const tunnelCmd = program.commands.find((c) => c.name() === "tunnel");
assert.ok(tunnelCmd, "tunnel subcommand must exist");
const createCmd = tunnelCmd.commands.find((c) => c.name() === "create");
assert.ok(createCmd, "create subcommand must exist");
// Before the fix, Commander registered two arguments named "type" because
// both .command("create [type]") and .addArgument(...) contributed one.
// After the fix, only .addArgument(...) defines the positional.
// Commander stores positional arguments in _args; the public args getter
// returns only required args, so optional args (like [type]) only appear in _args.
assert.equal(
createCmd._args.length,
1,
`create subcommand must have exactly 1 positional argument, got ${createCmd._args.length}`
);
});
test("tunnel create subcommand action handler accesses parent via Command instance (#12295)", async () => {
const { Command } = await import("commander");
const { registerTunnel } = await import("../../bin/cli/commands/tunnel.mjs");
const program = new Command();
registerTunnel(program);
const tunnelCmd = program.commands.find((c) => c.name() === "tunnel");
const createCmd = tunnelCmd.commands.find((c) => c.name() === "create");
assert.ok(createCmd._actionHandler, "create subcommand must have an action handler");
// Before the fix, the action callback received (type, type2, opts, command)
// because of the double positional. Destructuring (type, opts, cmd) then
// bound cmd to the opts object, making cmd.parent undefined and
// cmd.parent.optsWithGlobals() throw.
// After the fix, there's only one positional, so (type, opts, cmd) correctly
// binds cmd to the Command instance where cmd.parent === tunnelCmd.
// We can verify this by checking the parent chain on the createCmd itself:
assert.equal(
createCmd.parent,
tunnelCmd,
"create subcommand's parent must be the tunnel command"
);
assert.equal(
createCmd.parent.parent,
program,
"tunnel command's parent must be the root program"
);
});
test("tunnel create subcommand accepts valid tunnel type choices", async () => {
const { Command } = await import("commander");
const { registerTunnel } = await import("../../bin/cli/commands/tunnel.mjs");
const program = new Command();
registerTunnel(program);
const tunnelCmd = program.commands.find((c) => c.name() === "tunnel");
const createCmd = tunnelCmd.commands.find((c) => c.name() === "create");
// The addArgument with choices should still be registered.
assert.equal(createCmd._args.length, 1, "must have exactly one positional after addArgument");
// Verify choices are present on the argument.
const arg = createCmd._args[0];
assert.ok(arg, "first argument must exist");
assert.deepEqual(
arg.argChoices,
["cloudflare", "tailscale", "ngrok"],
"argument choices must match VALID_TUNNEL_TYPES"
);
assert.equal(arg.defaultValue, "cloudflare", "default type must be cloudflare");
});