Skip to content

test: programmatic CLI harness for src/index.ts action handlers #363

Description

@pcarleton

src/index.ts currently has zero test coverage of its commander action handlers — all testing happens at the scenario/runner layer. This came up in #360: we wanted to test that authorization --file <path> rejects an invalid settings file, but there's no way to exercise the real codepath without either spawning a child process or duplicating the merge logic in the test (which can drift).

Proposed approach

Refactor src/index.ts so the Command can be constructed without auto-parsing:

// src/index.ts
export function createProgram(): Command { /* register all subcommands */ }

// only parse when run as the bin entrypoint
if (require.main === module) createProgram().parse();

Tests then drive it in-process:

const exit = vi.spyOn(process, 'exit').mockImplementation(((c) => {
  throw new Error(`exit:${c}`);
}) as never);
const stderr = vi.spyOn(console, 'error').mockImplementation(() => {});
vi.mock('./runner/authorization-server');  // stub the network layer

await expect(
  createProgram().exitOverride().parseAsync(
    ['node', 'x', 'authorization', '--file', tmpInvalidJson]
  )
).rejects.toThrow('exit:1');
expect(stderr.mock.calls.join('\n')).toContain("Invalid settings file");

Why not spawn the binary

execa against dist/ is the strongest guarantee but is slow (process per case) and depends on a build step. Programmatic .parseAsync() is fast enough to cover every flag/validation path and matches how commander's own suite tests itself.

Scope

  • Extract createProgram(); gate program.parse() on require.main === module (or move to a bin.ts shim)
  • Shared test helpers: process.exit / console.error spies, temp-file fixture writer
  • Initial coverage: authorization --file (valid file, missing url, unknown key, bad JSON, neither --url nor --file)
  • Follow-on: extend to server / client flag validation as needed

Related: #360, #225

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions