/** * Tests for Command Tree Parser */ import { afterEach, beforeEach, describe, expect, test } from 'bun:test'; import { CLIContext } from '../test-utils/cli-context'; import type { IntegrationTestContext } from '../test-utils/integration'; import { setupIntegrationTest } from '../test-utils/integration'; import { CommandTreeParser } from './command-tree-parser'; describe('CommandTreeParser', () => { let ctx: IntegrationTestContext; let cli: CLIContext; let parser: CommandTreeParser; beforeEach(async () => { ctx = await setupIntegrationTest(); cli = await CLIContext.create('src/cli/index.ts', { CELILO_DB_PATH: ctx.dbPath, CELILO_DATA_DIR: ctx.dataDir, }); parser = new CommandTreeParser(cli); }); afterEach(async () => { await cli.dispose(); await ctx.cleanup(); }); describe('discover', () => { test('discovers top-level commands', async () => { const tree = await parser.discover(); // Debug: print what we actually found console.log('Discovered commands:', Array.from(tree.keys())); // Should have core commands expect(tree.has('module')).toBe(true); expect(tree.has('service')).toBe(true); expect(tree.has('machine')).toBe(true); expect(tree.has('system')).toBe(true); expect(tree.has('status')).toBe(true); }); test('discovers module subcommands', async () => { const tree = await parser.discover(); const module = tree.get('module'); expect(module).toBeDefined(); expect(module?.level).toBe(0); const subcommandNames = module?.subcommands.map((s) => s.name); expect(subcommandNames).toContain('import'); expect(subcommandNames).toContain('list'); expect(subcommandNames).toContain('config'); expect(subcommandNames).toContain('remove'); expect(subcommandNames).toContain('generate'); }); test('discovers service subcommands', async () => { const tree = await parser.discover(); const service = tree.get('service'); expect(service).toBeDefined(); const subcommandNames = service?.subcommands.map((s) => s.name); expect(subcommandNames).toContain('add'); expect(subcommandNames).toContain('list'); expect(subcommandNames).toContain('verify'); expect(subcommandNames).toContain('remove'); }); test('discovers machine subcommands', async () => { const tree = await parser.discover(); const machine = tree.get('machine'); expect(machine).toBeDefined(); const subcommandNames = machine?.subcommands.map((s) => s.name); expect(subcommandNames).toContain('add'); expect(subcommandNames).toContain('list'); expect(subcommandNames).toContain('remove'); }); test('sets correct level for subcommands', async () => { const tree = await parser.discover(); const service = tree.get('service'); expect(service?.level).toBe(0); if (service?.subcommands) { for (const subcommand of service.subcommands) { expect(subcommand.level).toBe(1); } } }); test('handles commands with no subcommands', async () => { const tree = await parser.discover(); // 'help' may not have subcommands const help = tree.get('help'); if (help) { expect(help.subcommands).toEqual([]); } }); }); describe('parseHelpText', () => { test('parses command names from help text', () => { const helpText = ` Usage: celilo [options] Commands: module Manage modules service Manage container services machine Manage machine pool help, --help, -h Show help information `; // Access private method via type assertion for testing // biome-ignore lint/suspicious/noExplicitAny: accessing private method for testing const parseMethod = (parser as any).parseHelpText.bind(parser); const commands = parseMethod(helpText); expect(commands).toContain('module'); expect(commands).toContain('service'); expect(commands).toContain('machine'); expect(commands).toContain('help'); }); test('filters out non-command words', () => { const helpText = ` Usage: celilo Commands: module Manage modules for Not a command the Not a command `; // biome-ignore lint/suspicious/noExplicitAny: accessing private method for testing const parseMethod = (parser as any).parseHelpText.bind(parser); const commands = parseMethod(helpText); expect(commands).toContain('module'); expect(commands).not.toContain('for'); expect(commands).not.toContain('the'); }); test('removes duplicate commands', () => { const helpText = ` Commands: module First mention service Manage services module Duplicate mention `; // biome-ignore lint/suspicious/noExplicitAny: accessing private method for testing const parseMethod = (parser as any).parseHelpText.bind(parser); const commands = parseMethod(helpText); expect(commands.filter((c: string) => c === 'module')).toHaveLength(1); }); test('handles hyphenated command names', () => { const helpText = ` Commands: module-import Import a module service-add Add a service `; // biome-ignore lint/suspicious/noExplicitAny: accessing private method for testing const parseMethod = (parser as any).parseHelpText.bind(parser); const commands = parseMethod(helpText); expect(commands).toContain('module-import'); expect(commands).toContain('service-add'); }); }); });