import { describe, it, expect } from 'vitest'; import { parseCommand, parseCommandSchema, validateCommand } from '../../src/utils/command'; describe('parseCommandSchema', () => { it('should parse empty schema', () => { const schema = parseCommandSchema(''); expect(schema).toEqual({ name: '', params: [], options: {}, flags: {}, }); }); it('should parse required params', () => { const schema = parseCommandSchema('move '); expect(schema.params).toEqual([ { name: 'from', required: true, variadic: false }, { name: 'to', required: true, variadic: false }, ]); }); it('should parse optional params', () => { const schema = parseCommandSchema('move [to]'); expect(schema.params).toEqual([ { name: 'from', required: true, variadic: false }, { name: 'to', required: false, variadic: false }, ]); }); it('should parse variadic params', () => { const schema = parseCommandSchema('move [targets...]'); expect(schema.params).toEqual([ { name: 'from', required: true, variadic: false }, { name: 'targets', required: false, variadic: true }, ]); }); it('should parse required variadic params', () => { const schema = parseCommandSchema('move '); expect(schema.params).toEqual([ { name: 'targets', required: true, variadic: true }, ]); }); it('should parse long flags', () => { const schema = parseCommandSchema('move [--force] [--quiet]'); expect(schema.flags).toEqual({ force: { name: 'force', short: undefined }, quiet: { name: 'quiet', short: undefined }, }); }); it('should parse short flags', () => { const schema = parseCommandSchema('move [-f] [-q]'); expect(schema.flags).toEqual({ f: { name: 'f', short: 'f' }, q: { name: 'q', short: 'q' }, }); }); it('should parse long options', () => { const schema = parseCommandSchema('move --x: string [--y: string]'); expect(Object.keys(schema.options)).toEqual(['x', 'y']); expect(schema.options.x).toMatchObject({ name: 'x', required: true }); expect(schema.options.x.schema).toBeDefined(); expect(schema.options.y).toMatchObject({ name: 'y', required: false }); expect(schema.options.y.schema).toBeDefined(); }); it('should parse short options', () => { const schema = parseCommandSchema('move -x: string [-y: string]'); expect(Object.keys(schema.options)).toEqual(['x', 'y']); expect(schema.options.x).toMatchObject({ name: 'x', short: 'x', required: true }); expect(schema.options.x.schema).toBeDefined(); expect(schema.options.y).toMatchObject({ name: 'y', short: 'y', required: false }); expect(schema.options.y.schema).toBeDefined(); }); it('should parse mixed schema', () => { const schema = parseCommandSchema('move [--force] [-f] [--speed: string -s]'); expect(schema).toEqual({ name: 'move', params: [ { name: 'from', required: true, variadic: false, schema: undefined }, { name: 'to', required: true, variadic: false, schema: undefined }, ], flags: { force: { name: 'force', short: undefined }, f: { name: 'f', short: 'f' }, }, options: { speed: { name: 'speed', short: 's', required: false, schema: expect.any(Object), defaultValue: undefined }, }, }); }); it('should handle complex schema', () => { const schema = parseCommandSchema('place [x...] [--rotate: number] [--force] [-f]'); expect(schema.name).toBe('place'); expect(schema.params).toHaveLength(3); expect(Object.keys(schema.flags)).toHaveLength(2); expect(Object.keys(schema.options)).toHaveLength(1); }); }); describe('validateCommand', () => { it('should validate correct command', () => { const schema = parseCommandSchema('move '); const command = parseCommand('move meeple1 region1'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: true }); }); it('should reject wrong command name', () => { const schema = parseCommandSchema('move '); const command = parseCommand('place meeple1'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: false, errors: expect.arrayContaining([ expect.stringContaining('命令名称不匹配'), ]), }); }); it('should reject missing required params', () => { const schema = parseCommandSchema('move '); const command = parseCommand('move meeple1'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: false, errors: expect.arrayContaining([ expect.stringContaining('参数不足'), ]), }); }); it('should accept optional params missing', () => { const schema = parseCommandSchema('move [to]'); const command = parseCommand('move meeple1'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: true }); }); it('should reject extra params without variadic', () => { const schema = parseCommandSchema('move '); const command = parseCommand('move meeple1 region1 extra'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: false, errors: expect.arrayContaining([ expect.stringContaining('参数过多'), ]), }); }); it('should accept extra params with variadic', () => { const schema = parseCommandSchema('move [targets...]'); const command = parseCommand('move meeple1 region1 region2 region3'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: true }); }); it('should reject missing required option', () => { const schema = parseCommandSchema('move --speed: string'); const command = parseCommand('move meeple1'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: false, errors: expect.arrayContaining([ expect.stringContaining('缺少必需选项'), ]), }); }); it('should accept present required option', () => { const schema = parseCommandSchema('move --speed: string'); const command = parseCommand('move meeple1 --speed 10'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: true }); }); it('should accept optional option missing', () => { const schema = parseCommandSchema('move [--speed: string]'); const command = parseCommand('move meeple1'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: true }); }); it('should accept flags present or not', () => { const schema = parseCommandSchema('move [--force]'); const cmd1 = parseCommand('move meeple1'); const cmd2 = parseCommand('move meeple1 --force'); expect(validateCommand(cmd1, schema)).toEqual({ valid: true }); expect(validateCommand(cmd2, schema)).toEqual({ valid: true }); }); it('should validate short form option', () => { const schema = parseCommandSchema('move -s: string'); const command = parseCommand('move meeple1 -s 10'); const result = validateCommand(command, schema); expect(result).toEqual({ valid: true }); }); it('should provide detailed error messages', () => { const schema = parseCommandSchema('place --rotate: string'); const command = parseCommand('place meeple1'); const result = validateCommand(command, schema); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.length).toBeGreaterThanOrEqual(1); } }); }); describe('integration', () => { it('should work together parse and validate', () => { const schemaStr = 'place [--x: string] [--y: string] [--force] [-f]'; const schema = parseCommandSchema(schemaStr); const validCmd = parseCommand('place meeple1 board --x 5 --force'); expect(validateCommand(validCmd, schema)).toEqual({ valid: true }); const invalidCmd = parseCommand('place meeple1'); const result = validateCommand(invalidCmd, schema); expect(result.valid).toBe(false); }); it('should parse short alias syntax', () => { const schema = parseCommandSchema('move [--verbose: boolean -v]'); expect(Object.keys(schema.flags)).toHaveLength(1); expect(schema.flags.verbose).toEqual({ name: 'verbose', short: 'v' }); }); it('should parse command with short alias', () => { const schema = parseCommandSchema('move [--verbose -v]'); const command = parseCommand('move meeple1 -v'); const result = validateCommand(command, schema); expect(result.valid).toBe(true); expect(command.flags.v).toBe(true); }); it('should parse command with short alias option', () => { const schema = parseCommandSchema('move [--speed: number -s]'); const command = parseCommand('move meeple1 -s 100'); const result = validateCommand(command, schema); expect(result.valid).toBe(true); expect(command.options.s).toBe('100'); }); });