summaryrefslogtreecommitdiff
path: root/packages/cli/src/config
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/config')
-rw-r--r--packages/cli/src/config/settings.test.ts58
-rw-r--r--packages/cli/src/config/settings.ts41
2 files changed, 80 insertions, 19 deletions
diff --git a/packages/cli/src/config/settings.test.ts b/packages/cli/src/config/settings.test.ts
index 6c075b38..cfa5e2b5 100644
--- a/packages/cli/src/config/settings.test.ts
+++ b/packages/cli/src/config/settings.test.ts
@@ -96,6 +96,7 @@ describe('Settings Loading and Merging', () => {
expect(settings.user.settings).toEqual({});
expect(settings.workspace.settings).toEqual({});
expect(settings.merged).toEqual({});
+ expect(settings.errors.length).toBe(0);
});
it('should load user settings if only user file exists', () => {
@@ -300,27 +301,61 @@ describe('Settings Loading and Merging', () => {
});
it('should handle JSON parsing errors gracefully', () => {
- (mockFsExistsSync as Mock).mockReturnValue(true);
+ (mockFsExistsSync as Mock).mockReturnValue(true); // Both files "exist"
+ const invalidJsonContent = 'invalid json';
+ const userReadError = new SyntaxError(
+ "Expected ',' or '}' after property value in JSON at position 10",
+ );
+ const workspaceReadError = new SyntaxError(
+ 'Unexpected token i in JSON at position 0',
+ );
+
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
- // Make it return invalid json for the paths it will try to read
- if (p === USER_SETTINGS_PATH || p === MOCK_WORKSPACE_SETTINGS_PATH)
- return 'invalid json';
- return '';
+ if (p === USER_SETTINGS_PATH) {
+ // Simulate JSON.parse throwing for user settings
+ vi.spyOn(JSON, 'parse').mockImplementationOnce(() => {
+ throw userReadError;
+ });
+ return invalidJsonContent; // Content that would cause JSON.parse to throw
+ }
+ if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
+ // Simulate JSON.parse throwing for workspace settings
+ vi.spyOn(JSON, 'parse').mockImplementationOnce(() => {
+ throw workspaceReadError;
+ });
+ return invalidJsonContent;
+ }
+ return '{}'; // Default for other reads
},
);
- const consoleErrorSpy = vi
- .spyOn(console, 'error')
- .mockImplementation(() => {});
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({});
- expect(consoleErrorSpy).toHaveBeenCalledTimes(2);
- consoleErrorSpy.mockRestore();
+ // 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);
+
+ // Restore JSON.parse mock if it was spied on specifically for this test
+ vi.restoreAllMocks(); // Or more targeted restore if needed
});
it('should resolve environment variables in user settings', () => {
@@ -585,13 +620,14 @@ describe('Settings Loading and Merging', () => {
'MY_AGENTS.md',
);
expect(loadedSettings.merged.contextFileName).toBe('MY_AGENTS.md');
- expect(loadedSettings.merged.theme).toBe('matrix');
+ expect(loadedSettings.merged.theme).toBe('matrix'); // User setting should still be there
expect(fs.writeFileSync).toHaveBeenCalledWith(
MOCK_WORKSPACE_SETTINGS_PATH,
JSON.stringify({ contextFileName: 'MY_AGENTS.md' }, null, 2),
'utf-8',
);
+ // Workspace theme overrides user theme
loadedSettings.setValue(SettingScope.Workspace, 'theme', 'ocean');
expect(loadedSettings.workspace.settings.theme).toBe('ocean');
diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts
index 427fb898..d3a4dacb 100644
--- a/packages/cli/src/config/settings.ts
+++ b/packages/cli/src/config/settings.ts
@@ -7,7 +7,7 @@
import * as fs from 'fs';
import * as path from 'path';
import { homedir } from 'os';
-import { MCPServerConfig } from '@gemini-code/core';
+import { MCPServerConfig, getErrorMessage } from '@gemini-code/core';
import stripJsonComments from 'strip-json-comments';
import { DefaultLight } from '../ui/themes/default-light.js';
import { DefaultDark } from '../ui/themes/default.js';
@@ -47,19 +47,30 @@ export interface Settings {
// Add other settings here.
}
+export interface SettingsError {
+ message: string;
+ path: string;
+}
+
export interface SettingsFile {
settings: Settings;
path: string;
}
export class LoadedSettings {
- constructor(user: SettingsFile, workspace: SettingsFile) {
+ constructor(
+ user: SettingsFile,
+ workspace: SettingsFile,
+ errors: SettingsError[],
+ ) {
this.user = user;
this.workspace = workspace;
+ this.errors = errors;
this._merged = this.computeMergedSettings();
}
readonly user: SettingsFile;
readonly workspace: SettingsFile;
+ readonly errors: SettingsError[];
private _merged: Settings;
@@ -147,6 +158,7 @@ function resolveEnvVarsInObject<T>(obj: T): T {
export function loadSettings(workspaceDir: string): LoadedSettings {
let userSettings: Settings = {};
let workspaceSettings: Settings = {};
+ const settingsErrors: SettingsError[] = [];
// Load user settings
try {
@@ -163,8 +175,11 @@ export function loadSettings(workspaceDir: string): LoadedSettings {
userSettings.theme = DefaultDark.name;
}
}
- } catch (error) {
- console.error('Error reading user settings file:', error);
+ } catch (error: unknown) {
+ settingsErrors.push({
+ message: getErrorMessage(error),
+ path: USER_SETTINGS_PATH,
+ });
}
const workspaceSettingsPath = path.join(
@@ -190,13 +205,23 @@ export function loadSettings(workspaceDir: string): LoadedSettings {
workspaceSettings.theme = DefaultDark.name;
}
}
- } catch (error) {
- console.error('Error reading workspace settings file:', error);
+ } catch (error: unknown) {
+ settingsErrors.push({
+ message: getErrorMessage(error),
+ path: workspaceSettingsPath,
+ });
}
return new LoadedSettings(
- { path: USER_SETTINGS_PATH, settings: userSettings },
- { path: workspaceSettingsPath, settings: workspaceSettings },
+ {
+ path: USER_SETTINGS_PATH,
+ settings: userSettings,
+ },
+ {
+ path: workspaceSettingsPath,
+ settings: workspaceSettings,
+ },
+ settingsErrors,
);
}