/** * Tests for Completion Harness */ import { afterEach, beforeEach, describe, expect, test } from 'bun:test'; import { CLIContext } from './cli-context'; import { CompletionHarness } from './completion-harness'; import type { IntegrationTestContext } from './integration'; import { setupIntegrationTest } from './integration'; describe('CompletionHarness', () => { let ctx: IntegrationTestContext; let cli: CLIContext; let harness: CompletionHarness; beforeEach(async () => { ctx = await setupIntegrationTest(); cli = await CLIContext.create('src/cli/index.ts', { CELILO_DB_PATH: ctx.dbPath, CELILO_DATA_DIR: ctx.dataDir, }); harness = new CompletionHarness(cli); }); afterEach(async () => { await cli.dispose(); await ctx.cleanup(); }); describe('getCompletions', () => { test('returns top-level commands', async () => { const completions = await harness.getCompletions(['celilo']); expect(completions).toContain('module'); expect(completions).toContain('service'); expect(completions).toContain('machine'); }); test('returns subcommands for service', async () => { const completions = await harness.getCompletions(['celilo', 'service']); expect(completions).toContain('add'); expect(completions).toContain('list'); expect(completions).toContain('verify'); expect(completions).toContain('remove'); }); test('returns subcommands for module', async () => { const completions = await harness.getCompletions(['celilo', 'module']); expect(completions).toContain('import'); expect(completions).toContain('list'); expect(completions).toContain('config'); expect(completions).toContain('remove'); }); test('returns empty array for invalid command', async () => { const completions = await harness.getCompletions(['celilo', 'invalid']); expect(completions).toEqual([]); }); test('returns empty array for too many args', async () => { const completions = await harness.getCompletions(['celilo', 'service', 'list', 'extra']); expect(completions).toEqual([]); }); test('supports partial word matching with partial=true', async () => { // Without partial flag: completes AFTER 'service' (all subcommands) const afterCompletions = await harness.getCompletions(['celilo', 'service']); expect(afterCompletions).toContain('add'); expect(afterCompletions).toContain('list'); // With partial=true: completes 'se' itself (prefix matching commands) const partialCompletions = await harness.getCompletions(['celilo', 'se'], true); expect(partialCompletions).toContain('service'); expect(partialCompletions).not.toContain('module'); }); }); describe('expectCompletionsInclude', () => { test('passes when all expected values are present', async () => { await harness.expectCompletionsInclude(['celilo', 'service'], ['add', 'list']); }); test('fails when expected value is missing', async () => { await expect( harness.expectCompletionsInclude(['celilo', 'service'], ['nonexistent']), ).rejects.toThrow(); }); }); describe('expectCompletionsExact', () => { test('passes when completions match exactly', async () => { const completions = await harness.getCompletions(['celilo', 'service']); await harness.expectCompletionsExact(['celilo', 'service'], completions); }); test('fails when completions do not match', async () => { await expect( harness.expectCompletionsExact(['celilo', 'service'], ['add']), ).rejects.toThrow(); }); }); describe('expectNoCompletions', () => { test('passes when no completions are returned', async () => { await harness.expectNoCompletions(['celilo', 'invalid']); }); test('fails when completions are returned', async () => { await expect(harness.expectNoCompletions(['celilo', 'service'])).rejects.toThrow(); }); }); describe('expectSomeCompletions', () => { test('passes when completions are returned', async () => { await harness.expectSomeCompletions(['celilo']); }); test('fails when no completions are returned', async () => { await expect(harness.expectSomeCompletions(['celilo', 'invalid'])).rejects.toThrow(); }); }); });