diff --git a/dev/src/cli/cli.ts b/dev/src/cli/cli.ts index f227c979f..fb66c6f96 100644 --- a/dev/src/cli/cli.ts +++ b/dev/src/cli/cli.ts @@ -202,12 +202,16 @@ export function createProgram(): Command { colorize: {all: true}, }); - const program = new Command('ADK CLI'); + const program = new Command('adk'); program .addOption(new Option('-v, --version', 'Get ADK CLI version')) - .action(() => { - console.log(version); + .action((options: {version?: boolean}) => { + if (options.version) { + console.log(version); + return; + } + program.outputHelp(); }); program diff --git a/dev/test/cli/cli_test.ts b/dev/test/cli/cli_test.ts index 75d003802..7f5588dc3 100644 --- a/dev/test/cli/cli_test.ts +++ b/dev/test/cli/cli_test.ts @@ -5,6 +5,7 @@ */ import {LogLevel, setLogLevel} from '@google/adk'; +import {CommanderError} from 'commander'; import {afterEach, beforeEach, describe, expect, it, Mock, vi} from 'vitest'; import {createProgram} from '../../src/cli/cli.js'; import {createAgent} from '../../src/cli/cli_create.js'; @@ -84,6 +85,79 @@ describe('CLI Entrypoint', () => { }); }); + describe('bare invocation', () => { + let stdout: string; + let stderr: string; + + beforeEach(() => { + stdout = ''; + stderr = ''; + program.configureOutput({ + writeOut: (str) => { + stdout += str; + }, + writeErr: (str) => { + stderr += str; + }, + }); + }); + + const parseExpectingError = async ( + args: string[], + ): Promise => { + try { + await program.parseAsync(['node', 'cli_entrypoint.js', ...args]); + } catch (e: unknown) { + return e as CommanderError; + } + return undefined; + }; + + it('should print usage listing every subcommand and request no exit', async () => { + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + + expect(await parseExpectingError([])).toBeUndefined(); + + expect(stdout).toContain('Usage: adk'); + expect(stdout).toContain('Commands:'); + for (const name of [ + 'web', + 'api_server', + 'create', + 'run', + 'deploy', + 'integration', + ]) { + expect(stdout).toContain(name); + } + expect(stderr).toBe(''); + expect(logSpy).not.toHaveBeenCalledWith('1.0.0-test'); + }); + + it('should print the version without usage for -v and --version', async () => { + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + + expect(await parseExpectingError(['--version'])).toBeUndefined(); + expect(await parseExpectingError(['-v'])).toBeUndefined(); + + expect(logSpy).toHaveBeenCalledWith('1.0.0-test'); + expect(logSpy).toHaveBeenCalledTimes(2); + expect(stdout).toBe(''); + expect(stderr).toBe(''); + }); + + it('should exit non-zero for an unknown subcommand', async () => { + const err = await parseExpectingError(['bogus']); + if (!err) { + expect.fail('expected commander to reject an unknown subcommand'); + } + + expect(err.code).toBe('commander.excessArguments'); + expect(err.exitCode).toBe(1); + expect(stdout).toBe(''); + }); + }); + describe('command: web', () => { it('should start AdkApiServer with default options', async () => { await parse(['web']);