Move settings error throwing to loadSettings (#7605)

This commit is contained in:
Tommaso Sciortino
2025-09-03 10:41:53 -07:00
committed by GitHub
parent d2ae869bb4
commit e6e60861e5
14 changed files with 50 additions and 205 deletions

View File

@@ -60,7 +60,7 @@ import {
type Settings,
loadEnvironment,
} from './settings.js';
import { GEMINI_DIR } from '@google/gemini-cli-core';
import { FatalConfigError, GEMINI_DIR } from '@google/gemini-cli-core';
const MOCK_WORKSPACE_DIR = '/mock/workspace';
// Use the (mocked) SETTINGS_DIRECTORY_NAME for consistency
@@ -146,7 +146,6 @@ describe('Settings Loading and Merging', () => {
},
security: {},
});
expect(settings.errors.length).toBe(0);
});
it('should load system settings if only system file exists', () => {
@@ -317,75 +316,13 @@ describe('Settings Loading and Merging', () => {
});
});
it('should merge user and workspace settings, with workspace taking precedence', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
const userSettingsContent = {
ui: {
theme: 'dark',
},
tools: {
sandbox: false,
},
context: {
fileName: 'USER_CONTEXT.md',
},
};
const workspaceSettingsContent = {
tools: {
sandbox: true,
core: ['tool1'],
},
context: {
fileName: 'WORKSPACE_CONTEXT.md',
},
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.user.settings).toEqual(userSettingsContent);
expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
expect(settings.merged).toEqual({
general: {},
ui: {
theme: 'dark',
customThemes: {},
},
tools: {
sandbox: true,
core: ['tool1'],
},
context: {
fileName: 'WORKSPACE_CONTEXT.md',
includeDirectories: [],
},
advanced: {
excludedEnvVars: [],
},
extensions: {
disabled: [],
workspacesWithMigrationNudge: [],
},
mcp: {},
mcpServers: {},
model: {
chatCompression: {},
},
security: {},
});
});
it('should merge system, user and workspace settings, with system taking precedence over workspace, and workspace over user', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) =>
p === getSystemSettingsPath() ||
p === USER_SETTINGS_PATH ||
p === MOCK_WORKSPACE_SETTINGS_PATH,
);
const systemSettingsContent = {
ui: {
theme: 'system-theme',
@@ -869,7 +806,10 @@ describe('Settings Loading and Merging', () => {
});
it('should merge excludedProjectEnvVars with workspace taking precedence over user', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) =>
p === USER_SETTINGS_PATH || p === MOCK_WORKSPACE_SETTINGS_PATH,
);
const userSettingsContent = {
general: {},
advanced: { excludedEnvVars: ['DEBUG', 'NODE_ENV', 'USER_VAR'] },
@@ -910,7 +850,10 @@ describe('Settings Loading and Merging', () => {
});
it('should default contextFileName to undefined if not in any settings file', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) =>
p === USER_SETTINGS_PATH || p === MOCK_WORKSPACE_SETTINGS_PATH,
);
const userSettingsContent = { ui: { theme: 'dark' } };
const workspaceSettingsContent = { tools: { sandbox: true } };
(fs.readFileSync as Mock).mockImplementation(
@@ -986,7 +929,10 @@ describe('Settings Loading and Merging', () => {
});
it('should merge MCP servers correctly, with workspace taking precedence', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) =>
p === USER_SETTINGS_PATH || p === MOCK_WORKSPACE_SETTINGS_PATH,
);
const userSettingsContent = {
mcpServers: {
'user-server': {
@@ -1405,50 +1351,7 @@ describe('Settings Loading and Merging', () => {
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
// Check that settings are empty due to parsing errors
expect(settings.user.settings).toEqual({});
expect(settings.workspace.settings).toEqual({});
expect(settings.merged).toEqual({
general: {},
ui: {
customThemes: {},
},
mcp: {},
mcpServers: {},
context: {
includeDirectories: [],
},
model: {
chatCompression: {},
},
advanced: {
excludedEnvVars: [],
},
extensions: {
disabled: [],
workspacesWithMigrationNudge: [],
},
security: {},
});
// Check that error objects are populated in settings.errors
expect(settings.errors).toBeDefined();
// Assuming both user and workspace files cause errors and are added in order
expect(settings.errors.length).toEqual(2);
const userError = settings.errors.find(
(e) => e.path === USER_SETTINGS_PATH,
);
expect(userError).toBeDefined();
expect(userError?.message).toBe(userReadError.message);
const workspaceError = settings.errors.find(
(e) => e.path === MOCK_WORKSPACE_SETTINGS_PATH,
);
expect(workspaceError).toBeDefined();
expect(workspaceError?.message).toBe(workspaceReadError.message);
expect(() => loadSettings(MOCK_WORKSPACE_DIR)).toThrow(FatalConfigError);
// Restore JSON.parse mock if it was spied on specifically for this test
vi.restoreAllMocks(); // Or more targeted restore if needed