mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
expose previewFeatures flag in a2a (#14550)
This commit is contained in:
@@ -71,6 +71,7 @@ export async function loadConfig(
|
||||
ideMode: false,
|
||||
folderTrust: settings.folderTrust === true,
|
||||
extensionLoader,
|
||||
previewFeatures: settings.general?.previewFeatures,
|
||||
};
|
||||
|
||||
const fileService = new FileDiscoveryService(workspaceDir);
|
||||
|
||||
197
packages/a2a-server/src/config/settings.test.ts
Normal file
197
packages/a2a-server/src/config/settings.test.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import * as os from 'node:os';
|
||||
import { loadSettings, USER_SETTINGS_PATH } from './settings.js';
|
||||
|
||||
const mocks = vi.hoisted(() => {
|
||||
const suffix = Math.random().toString(36).slice(2);
|
||||
return {
|
||||
suffix,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('node:os', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('node:os')>();
|
||||
const path = await import('node:path');
|
||||
return {
|
||||
...actual,
|
||||
homedir: () => path.join(actual.tmpdir(), `gemini-home-${mocks.suffix}`),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@google/gemini-cli-core', () => ({
|
||||
GEMINI_DIR: '.gemini',
|
||||
debugLogger: {
|
||||
error: vi.fn(),
|
||||
},
|
||||
getErrorMessage: (error: unknown) => String(error),
|
||||
}));
|
||||
|
||||
describe('loadSettings', () => {
|
||||
const mockHomeDir = path.join(os.tmpdir(), `gemini-home-${mocks.suffix}`);
|
||||
const mockWorkspaceDir = path.join(
|
||||
os.tmpdir(),
|
||||
`gemini-workspace-${mocks.suffix}`,
|
||||
);
|
||||
const mockGeminiHomeDir = path.join(mockHomeDir, '.gemini');
|
||||
const mockGeminiWorkspaceDir = path.join(mockWorkspaceDir, '.gemini');
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Create the directories using the real fs
|
||||
if (!fs.existsSync(mockGeminiHomeDir)) {
|
||||
fs.mkdirSync(mockGeminiHomeDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(mockGeminiWorkspaceDir)) {
|
||||
fs.mkdirSync(mockGeminiWorkspaceDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Clean up settings files before each test
|
||||
if (fs.existsSync(USER_SETTINGS_PATH)) {
|
||||
fs.rmSync(USER_SETTINGS_PATH);
|
||||
}
|
||||
const workspaceSettingsPath = path.join(
|
||||
mockGeminiWorkspaceDir,
|
||||
'settings.json',
|
||||
);
|
||||
if (fs.existsSync(workspaceSettingsPath)) {
|
||||
fs.rmSync(workspaceSettingsPath);
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
try {
|
||||
if (fs.existsSync(mockHomeDir)) {
|
||||
fs.rmSync(mockHomeDir, { recursive: true, force: true });
|
||||
}
|
||||
if (fs.existsSync(mockWorkspaceDir)) {
|
||||
fs.rmSync(mockWorkspaceDir, { recursive: true, force: true });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to cleanup temp dirs', e);
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should load nested previewFeatures from user settings', () => {
|
||||
const settings = {
|
||||
general: {
|
||||
previewFeatures: true,
|
||||
},
|
||||
};
|
||||
fs.writeFileSync(USER_SETTINGS_PATH, JSON.stringify(settings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
expect(result.general?.previewFeatures).toBe(true);
|
||||
});
|
||||
|
||||
it('should load nested previewFeatures from workspace settings', () => {
|
||||
const settings = {
|
||||
general: {
|
||||
previewFeatures: true,
|
||||
},
|
||||
};
|
||||
const workspaceSettingsPath = path.join(
|
||||
mockGeminiWorkspaceDir,
|
||||
'settings.json',
|
||||
);
|
||||
fs.writeFileSync(workspaceSettingsPath, JSON.stringify(settings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
expect(result.general?.previewFeatures).toBe(true);
|
||||
});
|
||||
|
||||
it('should prioritize workspace settings over user settings', () => {
|
||||
const userSettings = {
|
||||
general: {
|
||||
previewFeatures: false,
|
||||
},
|
||||
};
|
||||
fs.writeFileSync(USER_SETTINGS_PATH, JSON.stringify(userSettings));
|
||||
|
||||
const workspaceSettings = {
|
||||
general: {
|
||||
previewFeatures: true,
|
||||
},
|
||||
};
|
||||
const workspaceSettingsPath = path.join(
|
||||
mockGeminiWorkspaceDir,
|
||||
'settings.json',
|
||||
);
|
||||
fs.writeFileSync(workspaceSettingsPath, JSON.stringify(workspaceSettings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
expect(result.general?.previewFeatures).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle missing previewFeatures', () => {
|
||||
const settings = {
|
||||
general: {},
|
||||
};
|
||||
fs.writeFileSync(USER_SETTINGS_PATH, JSON.stringify(settings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
expect(result.general?.previewFeatures).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should load other top-level settings correctly', () => {
|
||||
const settings = {
|
||||
showMemoryUsage: true,
|
||||
coreTools: ['tool1', 'tool2'],
|
||||
mcpServers: {
|
||||
server1: {
|
||||
command: 'cmd',
|
||||
args: ['arg'],
|
||||
},
|
||||
},
|
||||
fileFiltering: {
|
||||
respectGitIgnore: true,
|
||||
},
|
||||
};
|
||||
fs.writeFileSync(USER_SETTINGS_PATH, JSON.stringify(settings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
expect(result.showMemoryUsage).toBe(true);
|
||||
expect(result.coreTools).toEqual(['tool1', 'tool2']);
|
||||
expect(result.mcpServers).toHaveProperty('server1');
|
||||
expect(result.fileFiltering?.respectGitIgnore).toBe(true);
|
||||
});
|
||||
|
||||
it('should overwrite top-level settings from workspace (shallow merge)', () => {
|
||||
const userSettings = {
|
||||
showMemoryUsage: false,
|
||||
fileFiltering: {
|
||||
respectGitIgnore: true,
|
||||
enableRecursiveFileSearch: true,
|
||||
},
|
||||
};
|
||||
fs.writeFileSync(USER_SETTINGS_PATH, JSON.stringify(userSettings));
|
||||
|
||||
const workspaceSettings = {
|
||||
showMemoryUsage: true,
|
||||
fileFiltering: {
|
||||
respectGitIgnore: false,
|
||||
},
|
||||
};
|
||||
const workspaceSettingsPath = path.join(
|
||||
mockGeminiWorkspaceDir,
|
||||
'settings.json',
|
||||
);
|
||||
fs.writeFileSync(workspaceSettingsPath, JSON.stringify(workspaceSettings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
// Primitive value overwritten
|
||||
expect(result.showMemoryUsage).toBe(true);
|
||||
|
||||
// Object value completely replaced (shallow merge behavior)
|
||||
expect(result.fileFiltering?.respectGitIgnore).toBe(false);
|
||||
expect(result.fileFiltering?.enableRecursiveFileSearch).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -20,7 +20,9 @@ import stripJsonComments from 'strip-json-comments';
|
||||
export const USER_SETTINGS_DIR = path.join(homedir(), GEMINI_DIR);
|
||||
export const USER_SETTINGS_PATH = path.join(USER_SETTINGS_DIR, 'settings.json');
|
||||
|
||||
// Reconcile with https://github.com/google-gemini/gemini-cli/blob/b09bc6656080d4d12e1d06734aae2ec33af5c1ed/packages/cli/src/config/settings.ts#L53
|
||||
// TODO: Ensure full compatibility with V2 nested settings structure (settings.schema.json).
|
||||
// This involves updating the interface and implementing migration logic to support legacy V1 (flat) settings,
|
||||
// similar to how packages/cli/src/config/settings.ts handles it.
|
||||
export interface Settings {
|
||||
mcpServers?: Record<string, MCPServerConfig>;
|
||||
coreTools?: string[];
|
||||
@@ -29,6 +31,9 @@ export interface Settings {
|
||||
showMemoryUsage?: boolean;
|
||||
checkpointing?: CheckpointingSettings;
|
||||
folderTrust?: boolean;
|
||||
general?: {
|
||||
previewFeatures?: boolean;
|
||||
};
|
||||
|
||||
// Git-aware file filtering settings
|
||||
fileFiltering?: {
|
||||
|
||||
Reference in New Issue
Block a user