Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions dev/src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,9 @@ function parseLogLevel(value: string): string {
return normalized;
}

function getLogLevelFromOptions(options: {
verbose?: boolean;
log_level?: string;
}) {
if (options.verbose) {
return LogLevel.DEBUG;
}

if (typeof options.log_level === 'string') {
// `??`, not `||`: LogLevel.DEBUG is 0 and would otherwise be discarded.
return LOG_LEVEL_MAP.get(options.log_level) ?? LogLevel.INFO;
}

return LogLevel.INFO;
function getLogLevelFromOptions(options: {log_level?: string}): LogLevel {
// `??`, not `||`: LogLevel.DEBUG is 0 and would otherwise be discarded.
return LOG_LEVEL_MAP.get(options.log_level ?? 'info') ?? LogLevel.INFO;
}

function getAbsolutePath(p: string): string {
Expand Down Expand Up @@ -135,8 +124,13 @@ const ORIGINS_OPTION = new Option(
).default('');
const VERBOSE_OPTION = new Option(
'-v, --verbose [boolean]',
'Optional. The verbose level of the server',
).default(false);
'Optional. Enable verbose (DEBUG) logging. Shortcut for --log_level debug; an explicitly passed --log_level wins.',
)
// Commander applies an implied value only while the target option sits at
// its default, which is the precedence rule the Python SDK gets from
// `ctx.get_parameter_source("log_level") == ParameterSource.DEFAULT`.
.implies({log_level: 'debug'})
.default(false);
const LOG_LEVEL_OPTION = new Option(
'--log_level <string>',
'Optional. The log level of the server',
Expand Down Expand Up @@ -557,6 +551,8 @@ export function createProgram(): Command {
)
.option('--force', 'Force run skipped tests.')
.action(async (options: Record<string, string>) => {
setAdkCoreLogLevel(getLogLevelFromOptions(options));

runIntegrationTests({
agentsDir: options['agents_dir'],
testsDir: options['tests_dir'],
Expand Down
101 changes: 101 additions & 0 deletions dev/test/cli/cli_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,107 @@ describe('CLI Entrypoint', () => {
});
});

describe('option: --verbose', () => {
it('lets an explicit level win over a following --verbose', async () => {
await parse(['web', '--log_level', 'error', '--verbose']);

expect(setLogLevel).toHaveBeenCalledWith(LogLevel.ERROR);
});

it('lets an explicit level win over a preceding --verbose', async () => {
await parse(['web', '--verbose', '--log_level', 'error']);

expect(setLogLevel).toHaveBeenCalledWith(LogLevel.ERROR);
});

it('lets an explicit level win even when it equals the default', async () => {
await parse(['web', '--log_level', 'info', '--verbose']);

expect(setLogLevel).toHaveBeenCalledWith(LogLevel.INFO);
});

it('keeps debug when the explicit level also asks for debug', async () => {
await parse(['web', '--log_level', 'debug', '--verbose']);

expect(setLogLevel).toHaveBeenCalledWith(LogLevel.DEBUG);
});

it('lets an explicit level win on api_server', async () => {
await parse(['api_server', '--log_level', 'warn', '--verbose']);

expect(setLogLevel).toHaveBeenCalledWith(LogLevel.WARN);
});

it('lets an explicit level win on run', async () => {
await parse(argvFor('run', '--log_level', 'error', '--verbose'));

expect(setLogLevel).toHaveBeenCalledWith(LogLevel.ERROR);
expect(runAgent).toHaveBeenCalled();
});

it.each([
[['--verbose'], 'debug'],
[['--log_level=error', '--verbose'], 'error'],
[[], 'info'],
])(
'bakes the level resolved from `%s` into the Cloud Run deploy',
async (flags, logLevel) => {
await parse(['deploy', 'cloud_run', ...flags]);

expect(vi.mocked(deployToCloudRun).mock.calls[0][0]).toMatchObject({
logLevel,
});
},
);

it.each(['agent_engine', 'reasoning_engine'])(
'bakes debug into `deploy %s --verbose`',
async (command) => {
await parse(['deploy', command, '--verbose']);

expect(vi.mocked(deployToAgentEngine).mock.calls[0][0]).toMatchObject({
logLevel: 'debug',
});
},
);

it('lets an explicit level win on deploy agent_engine', async () => {
await parse(['deploy', 'agent_engine', '--log_level=warn', '--verbose']);

expect(vi.mocked(deployToAgentEngine).mock.calls[0][0]).toMatchObject({
logLevel: 'warn',
});
});

it.each([
[[], LogLevel.INFO],
[['--verbose'], LogLevel.DEBUG],
[['--log_level', 'error', '--verbose'], LogLevel.ERROR],
])(
'applies the resolved level on `integration conformance %s`',
async (flags, expected) => {
await parse(['integration', 'conformance', ...flags]);

expect(setLogLevel).toHaveBeenCalledWith(expected);
expect(runIntegrationTests).toHaveBeenCalled();
},
);

it.each(LOG_LEVEL_COMMANDS)(
'documents the shortcut and its precedence in `%s` help',
(commandPath) => {
const help = findCommand(program, commandPath)
.helpInformation()
.replace(/\s+/g, ' ');

expect(help).toContain(
'Enable verbose (DEBUG) logging. Shortcut for --log_level debug; ' +
'an explicitly passed --log_level wins.',
);
},
);
});

describe('command: web', () => {
it('should start AdkApiServer with default options', async () => {
await parse(['web']);
Expand Down