import { Command } from '@commander-js/extra-typings'; import { describe, expect, it, vi } from 'vitest'; import type { KtxCliCommandContext } from '../../src/cli-program.js'; import { registerSqlCommands } from '../../src/commands/sql-commands.js'; function makeContext(overrides: Partial = {}): KtxCliCommandContext { let exitCode = 0; return { io: { stdout: { write: vi.fn() }, stderr: { write: vi.fn() }, }, deps: {}, packageInfo: { name: '@kaelio/ktx', version: '0.0.0-test' }, setExitCode: (code) => { exitCode = code; }, runInit: vi.fn(), writeDebug: vi.fn(), ...overrides, get exitCode() { return exitCode; }, } as KtxCliCommandContext; } describe('registerSqlCommands', () => { it('routes positional SQL through the sql runner', async () => { const program = new Command().exitOverride().option('--project-dir '); const sql = vi.fn(async () => 0); const context = makeContext({ deps: { sql } }); registerSqlCommands(program, context); await expect( program.parseAsync( ['--project-dir', '/tmp/ktx-sql', 'sql', '--connection', 'warehouse', 'select', '1'], { from: 'user' }, ), ).resolves.toBe(program); expect(sql).toHaveBeenCalledWith( { command: 'execute', projectDir: '/tmp/ktx-sql', connectionId: 'warehouse', sql: 'select 1', maxRows: 1000, output: undefined, json: false, cliVersion: '0.0.0-test', }, context.io, ); }); it('supports the short connection flag', async () => { const program = new Command().exitOverride().option('--project-dir '); const sql = vi.fn(async () => 0); const context = makeContext({ deps: { sql } }); registerSqlCommands(program, context); await expect( program.parseAsync(['--project-dir', '/tmp/ktx-sql', 'sql', '-c', 'warehouse', 'select 1'], { from: 'user', }), ).resolves.toBe(program); expect(sql).toHaveBeenCalledWith(expect.objectContaining({ connectionId: 'warehouse', sql: 'select 1' }), context.io); }); it('rejects missing SQL before invoking the runner', async () => { const program = new Command().exitOverride().option('--project-dir '); const sql = vi.fn(async () => 0); registerSqlCommands(program, makeContext({ deps: { sql } })); await expect( program.parseAsync(['--project-dir', '/tmp/ktx-sql', 'sql', '--connection', 'warehouse'], { from: 'user', }), ).rejects.toThrow('missing required argument'); expect(sql).not.toHaveBeenCalled(); }); it('rejects maxRows above the CLI cap', async () => { const program = new Command().exitOverride().option('--project-dir '); const sql = vi.fn(async () => 0); registerSqlCommands(program, makeContext({ deps: { sql } })); await expect( program.parseAsync( ['--project-dir', '/tmp/ktx-sql', 'sql', '--connection', 'warehouse', '--max-rows', '10001', 'select 1'], { from: 'user' }, ), ).rejects.toThrow('must be an integer between 1 and 10000'); expect(sql).not.toHaveBeenCalled(); }); });