mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
232 lines
7.1 KiB
TypeScript
232 lines
7.1 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import {
|
|
describe,
|
|
it,
|
|
expect,
|
|
vi,
|
|
beforeEach,
|
|
afterEach,
|
|
type Mock,
|
|
} from 'vitest';
|
|
import { settingsCommand } from './settings.js';
|
|
import yargs from 'yargs';
|
|
import { debugLogger, type GeminiCLIExtension } from '@google/gemini-cli-core';
|
|
import type { getExtensionAndManager } from './utils.js';
|
|
import type {
|
|
updateSetting,
|
|
getScopedEnvContents,
|
|
} from '../../config/extensions/extensionSettings.js';
|
|
import {
|
|
promptForSetting,
|
|
ExtensionSettingScope,
|
|
} from '../../config/extensions/extensionSettings.js';
|
|
import type { exitCli } from '../utils.js';
|
|
import type { ExtensionManager } from '../../config/extension-manager.js';
|
|
|
|
const mockGetExtensionAndManager: Mock<typeof getExtensionAndManager> =
|
|
vi.hoisted(() => vi.fn());
|
|
const mockUpdateSetting: Mock<typeof updateSetting> = vi.hoisted(() => vi.fn());
|
|
const mockGetScopedEnvContents: Mock<typeof getScopedEnvContents> = vi.hoisted(
|
|
() => vi.fn(),
|
|
);
|
|
const mockExitCli: Mock<typeof exitCli> = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock('./utils.js', () => ({
|
|
getExtensionAndManager: mockGetExtensionAndManager,
|
|
}));
|
|
|
|
vi.mock('../../config/extensions/extensionSettings.js', () => ({
|
|
updateSetting: mockUpdateSetting,
|
|
promptForSetting: vi.fn(),
|
|
ExtensionSettingScope: {
|
|
USER: 'user',
|
|
WORKSPACE: 'workspace',
|
|
},
|
|
getScopedEnvContents: mockGetScopedEnvContents,
|
|
}));
|
|
|
|
vi.mock('@google/gemini-cli-core', () => ({
|
|
debugLogger: {
|
|
log: vi.fn(),
|
|
error: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('../utils.js', () => ({
|
|
exitCli: mockExitCli,
|
|
}));
|
|
|
|
describe('settings command', () => {
|
|
let debugLogSpy: Mock;
|
|
let debugErrorSpy: Mock;
|
|
|
|
beforeEach(() => {
|
|
debugLogSpy = debugLogger.log as Mock;
|
|
debugErrorSpy = debugLogger.error as Mock;
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('set command', () => {
|
|
it('should log error and exit if extension is not found', async () => {
|
|
mockGetExtensionAndManager.mockResolvedValue({
|
|
extension: null,
|
|
extensionManager: null,
|
|
});
|
|
|
|
await yargs([])
|
|
.command(settingsCommand)
|
|
.parseAsync('settings set foo bar');
|
|
|
|
expect(mockExitCli).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should log error and exit if extension config is not found', async () => {
|
|
const mockExtensionManager = {
|
|
loadExtensionConfig: vi.fn().mockResolvedValue(null),
|
|
} as unknown as ExtensionManager;
|
|
mockGetExtensionAndManager.mockResolvedValue({
|
|
extension: { path: '/path/to/ext' } as unknown as GeminiCLIExtension,
|
|
extensionManager: mockExtensionManager,
|
|
});
|
|
|
|
await yargs([])
|
|
.command(settingsCommand)
|
|
.parseAsync('settings set foo bar');
|
|
|
|
expect(debugErrorSpy).toHaveBeenCalledWith(
|
|
'Could not find configuration for extension "foo".',
|
|
);
|
|
expect(mockExitCli).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should call updateSetting with correct arguments', async () => {
|
|
const mockExtensionManager = {
|
|
loadExtensionConfig: vi.fn().mockResolvedValue({}),
|
|
} as unknown as ExtensionManager;
|
|
const extension = { path: '/path/to/ext', id: 'ext-id' };
|
|
mockGetExtensionAndManager.mockResolvedValue({
|
|
extension: extension as unknown as GeminiCLIExtension,
|
|
extensionManager: mockExtensionManager,
|
|
});
|
|
|
|
await yargs([])
|
|
.command(settingsCommand)
|
|
.parseAsync('settings set foo bar --scope workspace');
|
|
|
|
expect(mockUpdateSetting).toHaveBeenCalledWith(
|
|
{},
|
|
'ext-id',
|
|
'bar',
|
|
promptForSetting,
|
|
ExtensionSettingScope.WORKSPACE,
|
|
);
|
|
expect(mockExitCli).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('list command', () => {
|
|
it('should log error and exit if extension is not found', async () => {
|
|
mockGetExtensionAndManager.mockResolvedValue({
|
|
extension: null,
|
|
extensionManager: null,
|
|
});
|
|
|
|
await yargs([]).command(settingsCommand).parseAsync('settings list foo');
|
|
|
|
expect(mockExitCli).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should log message and exit if extension has no settings', async () => {
|
|
const mockExtensionManager = {
|
|
loadExtensionConfig: vi.fn().mockResolvedValue({ settings: [] }),
|
|
} as unknown as ExtensionManager;
|
|
mockGetExtensionAndManager.mockResolvedValue({
|
|
extension: { path: '/path/to/ext' } as unknown as GeminiCLIExtension,
|
|
extensionManager: mockExtensionManager,
|
|
});
|
|
|
|
await yargs([]).command(settingsCommand).parseAsync('settings list foo');
|
|
|
|
expect(debugLogSpy).toHaveBeenCalledWith(
|
|
'Extension "foo" has no settings to configure.',
|
|
);
|
|
expect(mockExitCli).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should list settings correctly', async () => {
|
|
const mockExtensionManager = {
|
|
loadExtensionConfig: vi.fn().mockResolvedValue({
|
|
settings: [
|
|
{
|
|
name: 'Setting 1',
|
|
envVar: 'SETTING_1',
|
|
description: 'Desc 1',
|
|
sensitive: false,
|
|
},
|
|
{
|
|
name: 'Setting 2',
|
|
envVar: 'SETTING_2',
|
|
description: 'Desc 2',
|
|
sensitive: true,
|
|
},
|
|
{
|
|
name: 'Setting 3',
|
|
envVar: 'SETTING_3',
|
|
description: 'Desc 3',
|
|
sensitive: false,
|
|
},
|
|
],
|
|
}),
|
|
} as unknown as ExtensionManager;
|
|
const extension = { path: '/path/to/ext', id: 'ext-id' };
|
|
mockGetExtensionAndManager.mockResolvedValue({
|
|
extension: extension as unknown as GeminiCLIExtension,
|
|
extensionManager: mockExtensionManager,
|
|
});
|
|
|
|
mockGetScopedEnvContents.mockImplementation((_config, _id, scope) => {
|
|
if (scope === ExtensionSettingScope.USER) {
|
|
return Promise.resolve({
|
|
SETTING_1: 'val1',
|
|
SETTING_2: 'val2',
|
|
});
|
|
}
|
|
if (scope === ExtensionSettingScope.WORKSPACE) {
|
|
return Promise.resolve({
|
|
SETTING_3: 'val3',
|
|
});
|
|
}
|
|
return Promise.resolve({});
|
|
});
|
|
|
|
await yargs([]).command(settingsCommand).parseAsync('settings list foo');
|
|
|
|
expect(debugLogSpy).toHaveBeenCalledWith('Settings for "foo":');
|
|
// Setting 1 (User)
|
|
expect(debugLogSpy).toHaveBeenCalledWith('\n- Setting 1 (SETTING_1)');
|
|
expect(debugLogSpy).toHaveBeenCalledWith(' Description: Desc 1');
|
|
expect(debugLogSpy).toHaveBeenCalledWith(' Value: val1 (user)');
|
|
// Setting 2 (Sensitive)
|
|
expect(debugLogSpy).toHaveBeenCalledWith('\n- Setting 2 (SETTING_2)');
|
|
expect(debugLogSpy).toHaveBeenCalledWith(' Description: Desc 2');
|
|
expect(debugLogSpy).toHaveBeenCalledWith(
|
|
' Value: [value stored in keychain] (user)',
|
|
);
|
|
// Setting 3 (Workspace)
|
|
expect(debugLogSpy).toHaveBeenCalledWith('\n- Setting 3 (SETTING_3)');
|
|
expect(debugLogSpy).toHaveBeenCalledWith(' Description: Desc 3');
|
|
expect(debugLogSpy).toHaveBeenCalledWith(' Value: val3 (workspace)');
|
|
|
|
expect(mockExitCli).toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|