diff --git a/src/cli.ts b/src/cli.ts index e0d6426..b9a86af 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -231,7 +231,17 @@ export function createCLI(): Command { .command('create-duckdb') .description('add a new DuckDB database connection') .argument('') - .option('-d, --database-path ') + .addOption( + new Option( + '--database-path ', + 'path to DuckDB database file or MotherDuck database (e.g., "md:my_database")' + ) + ) + .addOption( + new Option('--mother-duck-token ', 'MotherDuck API token').env( + 'MOTHERDUCK_TOKEN' + ) + ) .action(createDuckDbConnectionCommand); connections diff --git a/test/commands/connections.spec.ts b/test/commands/connections.spec.ts index eeea863..d14b06d 100644 --- a/test/commands/connections.spec.ts +++ b/test/commands/connections.spec.ts @@ -25,6 +25,8 @@ import {Command} from '@commander-js/extra-typings'; import {createCLI} from '../../src/cli'; import path from 'path'; import {errorMessage} from '../../src/util'; +import fs from 'fs'; +import os from 'os'; let cli: Command; let args: string[]; @@ -95,5 +97,127 @@ describe('commands', () => { ); }); }); + + describe('create-duckdb', () => { + let tempConfigPath: string; + + beforeEach(() => { + // Create a temporary config file for each test + const tempDir = fs.mkdtempSync( + path.join(os.tmpdir(), 'malloy-cli-test-') + ); + tempConfigPath = path.join(tempDir, 'config.json'); + fs.writeFileSync( + tempConfigPath, + JSON.stringify({connections: []}, null, 2) + ); + }); + + afterEach(() => { + // Clean up temporary config file + if (tempConfigPath && fs.existsSync(tempConfigPath)) { + const configDir = path.dirname(tempConfigPath); + fs.unlinkSync(tempConfigPath); + fs.rmdirSync(configDir); + } + }); + + it('creates a DuckDB connection with motherDuckToken from command line', async () => { + await runWith( + '-c', + tempConfigPath, + 'connections', + 'create-duckdb', + 'test-motherduck', + '--database-path', + 'md:my_database', + '--mother-duck-token', + 'test-token-123' + ); + + // Verify the connection was created with the token + const configContent = JSON.parse( + fs.readFileSync(tempConfigPath, 'utf-8') + ); + const connection = configContent.connections.find( + (c: {name: string}) => c.name === 'test-motherduck' + ); + expect(connection).toBeDefined(); + expect(connection.backend).toBe('duckdb'); + expect(connection.databasePath).toBe('md:my_database'); + expect(connection.motherDuckToken).toBe('test-token-123'); + }); + + it('creates a DuckDB connection with motherDuckToken from environment variable', async () => { + const originalEnv = process.env.MOTHERDUCK_TOKEN; + process.env.MOTHERDUCK_TOKEN = 'env-token-456'; + + try { + await runWith( + '-c', + tempConfigPath, + 'connections', + 'create-duckdb', + 'test-motherduck-env', + '--database-path', + 'md:' + ); + + // Verify the connection was created with the token from env + const configContent = JSON.parse( + fs.readFileSync(tempConfigPath, 'utf-8') + ); + const connection = configContent.connections.find( + (c: {name: string}) => c.name === 'test-motherduck-env' + ); + expect(connection).toBeDefined(); + expect(connection.backend).toBe('duckdb'); + expect(connection.databasePath).toBe('md:'); + expect(connection.motherDuckToken).toBe('env-token-456'); + } finally { + // Restore original environment variable + if (originalEnv !== undefined) { + process.env.MOTHERDUCK_TOKEN = originalEnv; + } else { + delete process.env.MOTHERDUCK_TOKEN; + } + } + }); + + it('creates a DuckDB connection without motherDuckToken', async () => { + // Clear any existing MOTHERDUCK_TOKEN from the environment + const originalEnv = process.env.MOTHERDUCK_TOKEN; + delete process.env.MOTHERDUCK_TOKEN; + + try { + await runWith( + '-c', + tempConfigPath, + 'connections', + 'create-duckdb', + 'test-duckdb-local', + '--database-path', + '/path/to/local.duckdb' + ); + + // Verify the connection was created without the token + const configContent = JSON.parse( + fs.readFileSync(tempConfigPath, 'utf-8') + ); + const connection = configContent.connections.find( + (c: {name: string}) => c.name === 'test-duckdb-local' + ); + expect(connection).toBeDefined(); + expect(connection.backend).toBe('duckdb'); + expect(connection.databasePath).toBe('/path/to/local.duckdb'); + expect(connection.motherDuckToken).toBeUndefined(); + } finally { + // Restore original environment variable + if (originalEnv !== undefined) { + process.env.MOTHERDUCK_TOKEN = originalEnv; + } + } + }); + }); }); });