mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-15 00:17:09 +00:00
fix(commands): follow up command routing hardening regressions
This commit is contained in:
1 parent
22cb91adb6
commit
d52465058e
20 files changed
+634
-56
No files matched your search
@@ -1,6 +1,9 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
|
||||
import { parseApiCommandArgs } from '../../../src/commands/api-command';
|
||||
import {
|
||||
collectUnexpectedApiArgs,
|
||||
parseApiCommandArgs,
|
||||
} from '../../../src/commands/api-command/shared';
|
||||
|
||||
describe('api-command arg parser', () => {
|
||||
test('keeps positional API name when boolean flags precede it', () => {
|
||||
@@ -56,7 +59,9 @@ describe('api-command arg parser', () => {
|
||||
const parsed = parseApiCommandArgs(['my-api', '--target', 'invalid-target']);
|
||||
|
||||
expect(parsed.target).toBeUndefined();
|
||||
expect(parsed.errors).toEqual(['Invalid --target value "invalid-target". Use: claude or droid']);
|
||||
expect(parsed.errors).toEqual([
|
||||
'Invalid --target value "invalid-target". Use: claude or droid',
|
||||
]);
|
||||
});
|
||||
|
||||
test('collects missing-value error for --target with no value', () => {
|
||||
@@ -80,4 +85,65 @@ describe('api-command arg parser', () => {
|
||||
expect(parsed.target).toBe('droid');
|
||||
expect(parsed.errors).toEqual([]);
|
||||
});
|
||||
|
||||
test('collects unknown options and unexpected trailing positionals', () => {
|
||||
const parsed = parseApiCommandArgs(['my-api', '--taret', 'droid', '--yes']);
|
||||
|
||||
expect(parsed.name).toBe('my-api');
|
||||
expect(parsed.errors).toEqual(['Unknown option: --taret', 'Unexpected arguments: droid']);
|
||||
});
|
||||
|
||||
test('rejects extra positionals for single-name commands by default', () => {
|
||||
const parsed = parseApiCommandArgs(['source', 'destination', '--yes']);
|
||||
|
||||
expect(parsed.positionals).toEqual(['source', 'destination']);
|
||||
expect(parsed.errors).toEqual(['Unexpected arguments: destination']);
|
||||
});
|
||||
|
||||
test('allows copy-style two-positional parsing when requested', () => {
|
||||
const parsed = parseApiCommandArgs(['source', 'destination', '--yes'], {
|
||||
maxPositionals: 2,
|
||||
});
|
||||
|
||||
expect(parsed.positionals).toEqual(['source', 'destination']);
|
||||
expect(parsed.errors).toEqual([]);
|
||||
});
|
||||
|
||||
test('preserves dash-prefixed names after option terminator', () => {
|
||||
const parsed = parseApiCommandArgs(['--yes', '--', '-my-api', 'backup'], {
|
||||
maxPositionals: 2,
|
||||
});
|
||||
|
||||
expect(parsed.positionals).toEqual(['-my-api', 'backup']);
|
||||
expect(parsed.errors).toEqual([]);
|
||||
});
|
||||
|
||||
test('accepts single-dash model values without treating them as unknown flags', () => {
|
||||
const parsed = parseApiCommandArgs(['my-api', '--model', '-preview']);
|
||||
|
||||
expect(parsed.model).toBe('-preview');
|
||||
expect(parsed.errors).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('collectUnexpectedApiArgs', () => {
|
||||
test('rejects extra args after a no-arg command', () => {
|
||||
const parsed = collectUnexpectedApiArgs(['--register', 'extra'], {
|
||||
knownFlags: ['--register'],
|
||||
maxPositionals: 0,
|
||||
});
|
||||
|
||||
expect(parsed.positionals).toEqual(['extra']);
|
||||
expect(parsed.errors).toEqual(['Unexpected arguments: extra']);
|
||||
});
|
||||
|
||||
test('reports unknown flags separately from leftover positionals', () => {
|
||||
const parsed = collectUnexpectedApiArgs(['--bogus', 'value', '--yes'], {
|
||||
knownFlags: ['--yes'],
|
||||
maxPositionals: 0,
|
||||
});
|
||||
|
||||
expect(parsed.positionals).toEqual(['value']);
|
||||
expect(parsed.errors).toEqual(['Unknown option: --bogus', 'Unexpected arguments: value']);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user