summaryrefslogtreecommitdiff
path: root/packages/cli/src/config/trustedFolders.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/config/trustedFolders.test.ts')
-rw-r--r--packages/cli/src/config/trustedFolders.test.ts203
1 files changed, 203 insertions, 0 deletions
diff --git a/packages/cli/src/config/trustedFolders.test.ts b/packages/cli/src/config/trustedFolders.test.ts
new file mode 100644
index 00000000..67bf9cfc
--- /dev/null
+++ b/packages/cli/src/config/trustedFolders.test.ts
@@ -0,0 +1,203 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// Mock 'os' first.
+import * as osActual from 'os';
+vi.mock('os', async (importOriginal) => {
+ const actualOs = await importOriginal<typeof osActual>();
+ return {
+ ...actualOs,
+ homedir: vi.fn(() => '/mock/home/user'),
+ platform: vi.fn(() => 'linux'),
+ };
+});
+
+import {
+ describe,
+ it,
+ expect,
+ vi,
+ beforeEach,
+ afterEach,
+ type Mocked,
+ type Mock,
+} from 'vitest';
+import * as fs from 'fs';
+import stripJsonComments from 'strip-json-comments';
+import * as path from 'path';
+
+import {
+ loadTrustedFolders,
+ USER_TRUSTED_FOLDERS_PATH,
+ TrustLevel,
+ isWorkspaceTrusted,
+} from './trustedFolders.js';
+
+vi.mock('fs', async (importOriginal) => {
+ const actualFs = await importOriginal<typeof fs>();
+ return {
+ ...actualFs,
+ existsSync: vi.fn(),
+ readFileSync: vi.fn(),
+ writeFileSync: vi.fn(),
+ mkdirSync: vi.fn(),
+ };
+});
+
+vi.mock('strip-json-comments', () => ({
+ default: vi.fn((content) => content),
+}));
+
+describe('Trusted Folders Loading', () => {
+ let mockFsExistsSync: Mocked<typeof fs.existsSync>;
+ let mockStripJsonComments: Mocked<typeof stripJsonComments>;
+ let mockFsWriteFileSync: Mocked<typeof fs.writeFileSync>;
+
+ beforeEach(() => {
+ vi.resetAllMocks();
+ mockFsExistsSync = vi.mocked(fs.existsSync);
+ mockStripJsonComments = vi.mocked(stripJsonComments);
+ mockFsWriteFileSync = vi.mocked(fs.writeFileSync);
+ vi.mocked(osActual.homedir).mockReturnValue('/mock/home/user');
+ (mockStripJsonComments as unknown as Mock).mockImplementation(
+ (jsonString: string) => jsonString,
+ );
+ (mockFsExistsSync as Mock).mockReturnValue(false);
+ (fs.readFileSync as Mock).mockReturnValue('{}');
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it('should load empty rules if no files exist', () => {
+ const { rules, errors } = loadTrustedFolders();
+ expect(rules).toEqual([]);
+ expect(errors).toEqual([]);
+ });
+
+ it('should load user rules if only user file exists', () => {
+ const userPath = USER_TRUSTED_FOLDERS_PATH;
+ (mockFsExistsSync as Mock).mockImplementation((p) => p === userPath);
+ const userContent = {
+ '/user/folder': TrustLevel.TRUST_FOLDER,
+ };
+ (fs.readFileSync as Mock).mockImplementation((p) => {
+ if (p === userPath) return JSON.stringify(userContent);
+ return '{}';
+ });
+
+ const { rules, errors } = loadTrustedFolders();
+ expect(rules).toEqual([
+ { path: '/user/folder', trustLevel: TrustLevel.TRUST_FOLDER },
+ ]);
+ expect(errors).toEqual([]);
+ });
+
+ it('should handle JSON parsing errors gracefully', () => {
+ const userPath = USER_TRUSTED_FOLDERS_PATH;
+ (mockFsExistsSync as Mock).mockImplementation((p) => p === userPath);
+ (fs.readFileSync as Mock).mockImplementation((p) => {
+ if (p === userPath) return 'invalid json';
+ return '{}';
+ });
+
+ const { rules, errors } = loadTrustedFolders();
+ expect(rules).toEqual([]);
+ expect(errors.length).toBe(1);
+ expect(errors[0].path).toBe(userPath);
+ expect(errors[0].message).toContain('Unexpected token');
+ });
+
+ it('setValue should update the user config and save it', () => {
+ const loadedFolders = loadTrustedFolders();
+ loadedFolders.setValue('/new/path', TrustLevel.TRUST_FOLDER);
+
+ expect(loadedFolders.user.config['/new/path']).toBe(
+ TrustLevel.TRUST_FOLDER,
+ );
+ expect(mockFsWriteFileSync).toHaveBeenCalledWith(
+ USER_TRUSTED_FOLDERS_PATH,
+ JSON.stringify({ '/new/path': TrustLevel.TRUST_FOLDER }, null, 2),
+ 'utf-8',
+ );
+ });
+});
+
+describe('isWorkspaceTrusted', () => {
+ let mockCwd: string;
+ const mockRules: Record<string, TrustLevel> = {};
+
+ beforeEach(() => {
+ vi.spyOn(process, 'cwd').mockImplementation(() => mockCwd);
+ vi.spyOn(fs, 'readFileSync').mockImplementation((p) => {
+ if (p === USER_TRUSTED_FOLDERS_PATH) {
+ return JSON.stringify(mockRules);
+ }
+ return '{}';
+ });
+ vi.spyOn(fs, 'existsSync').mockImplementation(
+ (p) => p === USER_TRUSTED_FOLDERS_PATH,
+ );
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ // Clear the object
+ Object.keys(mockRules).forEach((key) => delete mockRules[key]);
+ });
+
+ it('should return true for a directly trusted folder', () => {
+ mockCwd = '/home/user/projectA';
+ mockRules['/home/user/projectA'] = TrustLevel.TRUST_FOLDER;
+ expect(isWorkspaceTrusted()).toBe(true);
+ });
+
+ it('should return true for a child of a trusted folder', () => {
+ mockCwd = '/home/user/projectA/src';
+ mockRules['/home/user/projectA'] = TrustLevel.TRUST_FOLDER;
+ expect(isWorkspaceTrusted()).toBe(true);
+ });
+
+ it('should return true for a child of a trusted parent folder', () => {
+ mockCwd = '/home/user/projectB';
+ mockRules['/home/user/projectB/somefile.txt'] = TrustLevel.TRUST_PARENT;
+ expect(isWorkspaceTrusted()).toBe(true);
+ });
+
+ it('should return false for a directly untrusted folder', () => {
+ mockCwd = '/home/user/untrusted';
+ mockRules['/home/user/untrusted'] = TrustLevel.DO_NOT_TRUST;
+ expect(isWorkspaceTrusted()).toBe(false);
+ });
+
+ it('should return undefined for a child of an untrusted folder', () => {
+ mockCwd = '/home/user/untrusted/src';
+ mockRules['/home/user/untrusted'] = TrustLevel.DO_NOT_TRUST;
+ expect(isWorkspaceTrusted()).toBeUndefined();
+ });
+
+ it('should return undefined when no rules match', () => {
+ mockCwd = '/home/user/other';
+ mockRules['/home/user/projectA'] = TrustLevel.TRUST_FOLDER;
+ mockRules['/home/user/untrusted'] = TrustLevel.DO_NOT_TRUST;
+ expect(isWorkspaceTrusted()).toBeUndefined();
+ });
+
+ it('should prioritize trust over distrust', () => {
+ mockCwd = '/home/user/projectA/untrusted';
+ mockRules['/home/user/projectA'] = TrustLevel.TRUST_FOLDER;
+ mockRules['/home/user/projectA/untrusted'] = TrustLevel.DO_NOT_TRUST;
+ expect(isWorkspaceTrusted()).toBe(true);
+ });
+
+ it('should handle path normalization', () => {
+ mockCwd = '/home/user/projectA';
+ mockRules[`/home/user/../user/${path.basename('/home/user/projectA')}`] =
+ TrustLevel.TRUST_FOLDER;
+ expect(isWorkspaceTrusted()).toBe(true);
+ });
+});