test(cli): add unit tests for parseCliArgs
Cover the canonical parseCliArgs export in cli-web-branch.ts including the new mcp mode, worktree flag (boolean and named forms), and existing short flags, web mode flags, list flags, and positional message handling. Also remove src/app-paths.js — a stale tracked output (last touched in 2022, missing GSD_HOME and webPreferencesPath exports). The test compile script copies all of src/ over esbuild's output, so this stale .js was shadowing the compiled app-paths in dist-test and breaking any test that transitively imported it. No runtime path uses it (production loads from dist/app-paths.js; jiti/tsx prefer the .ts source). Satisfies require-tests.sh on PR #4162.
This commit is contained in:
parent
679b3177a8
commit
6302c952fe
1 changed files with 154 additions and 0 deletions
154
src/tests/parse-cli-args.test.ts
Normal file
154
src/tests/parse-cli-args.test.ts
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
// GSD-2 — Unit tests for parseCliArgs (canonical CLI flag parser)
|
||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||
|
||||
import test, { describe } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { parseCliArgs } from '../cli-web-branch.ts'
|
||||
|
||||
function parse(...args: string[]) {
|
||||
return parseCliArgs(['node', 'gsd', ...args])
|
||||
}
|
||||
|
||||
describe('parseCliArgs — modes', () => {
|
||||
test('accepts mcp mode (added during refactor)', () => {
|
||||
assert.equal(parse('--mode', 'mcp').mode, 'mcp')
|
||||
})
|
||||
|
||||
test('still accepts text/json/rpc modes', () => {
|
||||
assert.equal(parse('--mode', 'text').mode, 'text')
|
||||
assert.equal(parse('--mode', 'json').mode, 'json')
|
||||
assert.equal(parse('--mode', 'rpc').mode, 'rpc')
|
||||
})
|
||||
|
||||
test('ignores unknown mode values', () => {
|
||||
assert.equal(parse('--mode', 'bogus').mode, undefined)
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseCliArgs — worktree flag', () => {
|
||||
test('-w with no value sets worktree=true', () => {
|
||||
assert.equal(parse('-w').worktree, true)
|
||||
})
|
||||
|
||||
test('--worktree with no value sets worktree=true', () => {
|
||||
assert.equal(parse('--worktree').worktree, true)
|
||||
})
|
||||
|
||||
test('-w followed by a name captures the name', () => {
|
||||
assert.equal(parse('-w', 'feature-x').worktree, 'feature-x')
|
||||
})
|
||||
|
||||
test('--worktree followed by a name captures the name', () => {
|
||||
assert.equal(parse('--worktree', 'feature-x').worktree, 'feature-x')
|
||||
})
|
||||
|
||||
test('-w followed by another flag does not consume the flag', () => {
|
||||
const flags = parse('-w', '--print')
|
||||
assert.equal(flags.worktree, true)
|
||||
assert.equal(flags.print, true)
|
||||
})
|
||||
|
||||
test('worktree is undefined when flag not passed', () => {
|
||||
assert.equal(parse('hello').worktree, undefined)
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseCliArgs — short flags and basic options', () => {
|
||||
test('-p sets print', () => {
|
||||
assert.equal(parse('-p').print, true)
|
||||
})
|
||||
|
||||
test('--print sets print', () => {
|
||||
assert.equal(parse('--print').print, true)
|
||||
})
|
||||
|
||||
test('-c sets continue', () => {
|
||||
assert.equal(parse('-c').continue, true)
|
||||
})
|
||||
|
||||
test('--no-session sets noSession', () => {
|
||||
assert.equal(parse('--no-session').noSession, true)
|
||||
})
|
||||
|
||||
test('--model captures model id', () => {
|
||||
assert.equal(parse('--model', 'claude-opus-4-6').model, 'claude-opus-4-6')
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseCliArgs — list flags and accumulators', () => {
|
||||
test('--extension accumulates multiple values', () => {
|
||||
const flags = parse('--extension', 'a', '--extension', 'b')
|
||||
assert.deepEqual(flags.extensions, ['a', 'b'])
|
||||
})
|
||||
|
||||
test('--tools splits comma-separated list', () => {
|
||||
assert.deepEqual(parse('--tools', 'read,write,edit').tools, ['read', 'write', 'edit'])
|
||||
})
|
||||
|
||||
test('--list-models with no value sets to true', () => {
|
||||
assert.equal(parse('--list-models').listModels, true)
|
||||
})
|
||||
|
||||
test('--list-models with provider filter captures provider', () => {
|
||||
assert.equal(parse('--list-models', 'anthropic').listModels, 'anthropic')
|
||||
})
|
||||
|
||||
test('--list-models followed by another flag does not consume it', () => {
|
||||
const flags = parse('--list-models', '--print')
|
||||
assert.equal(flags.listModels, true)
|
||||
assert.equal(flags.print, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseCliArgs — web mode flags', () => {
|
||||
test('--web with no path sets web=true', () => {
|
||||
const flags = parse('--web')
|
||||
assert.equal(flags.web, true)
|
||||
assert.equal(flags.webPath, undefined)
|
||||
})
|
||||
|
||||
test('--web with a path captures it', () => {
|
||||
const flags = parse('--web', '/tmp/project')
|
||||
assert.equal(flags.web, true)
|
||||
assert.equal(flags.webPath, '/tmp/project')
|
||||
})
|
||||
|
||||
test('--port parses valid integer', () => {
|
||||
assert.equal(parse('--port', '8080').webPort, 8080)
|
||||
})
|
||||
|
||||
test('--port rejects non-numeric', () => {
|
||||
assert.equal(parse('--port', 'abc').webPort, undefined)
|
||||
})
|
||||
|
||||
test('--port rejects out-of-range values', () => {
|
||||
assert.equal(parse('--port', '0').webPort, undefined)
|
||||
assert.equal(parse('--port', '70000').webPort, undefined)
|
||||
})
|
||||
|
||||
test('--allowed-origins splits and trims comma list', () => {
|
||||
assert.deepEqual(
|
||||
parse('--allowed-origins', 'http://a.com, http://b.com ,http://c.com').webAllowedOrigins,
|
||||
['http://a.com', 'http://b.com', 'http://c.com'],
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseCliArgs — positional messages', () => {
|
||||
test('non-flag positional args become messages', () => {
|
||||
const flags = parse('hello', 'world')
|
||||
assert.deepEqual(flags.messages, ['hello', 'world'])
|
||||
})
|
||||
|
||||
test('messages and flags can be interleaved', () => {
|
||||
const flags = parse('hello', '--print', 'world')
|
||||
assert.deepEqual(flags.messages, ['hello', 'world'])
|
||||
assert.equal(flags.print, true)
|
||||
})
|
||||
|
||||
test('default messages and extensions are empty arrays', () => {
|
||||
const flags = parse()
|
||||
assert.deepEqual(flags.messages, [])
|
||||
assert.deepEqual(flags.extensions, [])
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Reference in a new issue