import { describe, it, expect, beforeEach } from 'vitest'; import { CommandRegistry, createCommandRegistry } from '../../src/commands/CommandRegistry'; import type { CliCommand } from '../../src/commands/CliCommand'; describe('CommandRegistry', () => { let registry: CommandRegistry; beforeEach(() => { registry = createCommandRegistry(); }); const sampleCommand: CliCommand = { name: 'test', description: 'Test command', usage: 'test ', args: [ { name: 'arg', description: 'Test argument', required: true }, ], handler: (args) => { return [ { action: 'createMeeple', params: { id: args.positional[0], color: 'red' }, }, ]; }, }; describe('register', () => { it('should register a command', () => { registry.register(sampleCommand); expect(registry.has('test')).toBe(true); expect(registry.get('test')).toBe(sampleCommand); }); it('should register multiple commands', () => { const cmd1: CliCommand = { name: 'cmd1', description: 'Command 1', usage: 'cmd1', handler: () => [], }; const cmd2: CliCommand = { name: 'cmd2', description: 'Command 2', usage: 'cmd2', handler: () => [], }; registry.registerAll([cmd1, cmd2]); expect(registry.has('cmd1')).toBe(true); expect(registry.has('cmd2')).toBe(true); expect(registry.getCount()).toBe(2); }); }); describe('get', () => { it('should return undefined for non-existent command', () => { const cmd = registry.get('non-existent'); expect(cmd).toBeUndefined(); }); it('should return existing command', () => { registry.register(sampleCommand); const cmd = registry.get('test'); expect(cmd?.name).toBe('test'); }); }); describe('unregister', () => { it('should remove a command', () => { registry.register(sampleCommand); expect(registry.has('test')).toBe(true); registry.unregister('test'); expect(registry.has('test')).toBe(false); }); }); describe('getAll', () => { it('should return all registered commands', () => { registry.register(sampleCommand); const cmd2: CliCommand = { name: 'cmd2', description: 'Command 2', usage: 'cmd2', handler: () => [], }; registry.register(cmd2); const all = registry.getAll(); expect(all.length).toBe(2); expect(all.map((c) => c.name)).toEqual(['test', 'cmd2']); }); }); describe('execute', () => { it('should execute a command successfully', () => { registry.register(sampleCommand); const result = registry.execute('test meeple-1'); expect(result.success).toBe(true); expect(result.steps.length).toBe(1); expect(result.steps[0].action).toBe('createMeeple'); expect(result.steps[0].params).toEqual({ id: 'meeple-1', color: 'red' }); }); it('should return error for unknown command', () => { const result = registry.execute('unknown arg1'); expect(result.success).toBe(false); expect(result.error).toContain('Unknown command'); expect(result.steps).toEqual([]); }); it('should return error for missing required argument', () => { registry.register(sampleCommand); const result = registry.execute('test'); expect(result.success).toBe(false); expect(result.error).toContain('Missing required argument'); }); it('should execute command with flags', () => { const cmdWithFlags: CliCommand = { name: 'move', description: 'Move command', usage: 'move [--to=region]', args: [ { name: 'id', description: 'ID', required: true }, ], flags: [ { name: 'to', description: 'Target', type: 'string' }, ], handler: (args) => { return [ { action: 'movePlacement', params: { placementId: args.positional[0], targetRegionId: args.flags.to, }, }, ]; }, }; registry.register(cmdWithFlags); const result = registry.execute('move p1 --to=board'); expect(result.success).toBe(true); expect(result.steps[0].params).toEqual({ placementId: 'p1', targetRegionId: 'board', }); }); it('should handle command with optional args', () => { const cmdOptional: CliCommand = { name: 'draw', description: 'Draw cards', usage: 'draw [count]', args: [ { name: 'count', description: 'Count', required: false, default: '1' }, ], handler: (args) => { return []; }, }; registry.register(cmdOptional); const result = registry.execute('draw'); expect(result.success).toBe(true); }); }); describe('help', () => { beforeEach(() => { registry.register({ name: 'move', description: 'Move a placement', usage: 'move ', args: [ { name: 'id', description: 'Placement ID', required: true }, { name: 'target', description: 'Target region', required: true }, ], flags: [ { name: 'key', description: 'Slot key', type: 'string', alias: 'k' }, ], handler: () => [], }); registry.register({ name: 'flip', description: 'Flip a placement', usage: 'flip ', args: [ { name: 'id', description: 'Placement ID', required: true }, ], handler: () => [], }); }); it('should show all commands help', () => { const help = registry.help(); expect(help).toContain('Available commands'); expect(help).toContain('move'); expect(help).toContain('flip'); expect(help).toContain('help '); }); it('should show specific command help', () => { const help = registry.help('move'); expect(help).toContain('Command: move'); expect(help).toContain('Move a placement'); expect(help).toContain('Arguments:'); expect(help).toContain('Flags:'); }); it('should show error for unknown command help', () => { const help = registry.help('unknown'); expect(help).toContain('Unknown command'); }); it('should show command with alias', () => { const help = registry.help('move'); expect(help).toContain('-k,'); }); }); describe('clear', () => { it('should clear all commands', () => { registry.register(sampleCommand); registry.register({ name: 'cmd2', description: 'Command 2', usage: 'cmd2', handler: () => [], }); expect(registry.getCount()).toBe(2); registry.clear(); expect(registry.getCount()).toBe(0); expect(registry.getAll()).toEqual([]); }); }); });