Download packages/core/src/hooks/hookRegistry.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 22.5 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/hooks/hookRegistry.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/core/src/hooks/hookRegistry.test.ts
-
curl -L -o hookRegistry.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/hooks/hookRegistry.test.ts
22.5 kB
| /** | |
| * @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 { HookRegistry } from './hookRegistry.js'; | |
| import type { Storage } from '../config/storage.js'; | |
| import { | |
| ConfigSource, | |
| HookEventName, | |
| HookType, | |
| HOOKS_CONFIG_FIELDS, | |
| type CommandHookConfig, | |
| type HookDefinition, | |
| } from './types.js'; | |
| import type { Config } from '../config/config.js'; | |
| // Mock fs | |
| vi.mock('fs', () => ({ | |
| existsSync: vi.fn(), | |
| readFileSync: vi.fn(), | |
| })); | |
| // Mock debugLogger using vi.hoisted | |
| const mockDebugLogger = vi.hoisted(() => ({ | |
| log: vi.fn(), | |
| warn: vi.fn(), | |
| error: vi.fn(), | |
| debug: vi.fn(), | |
| })); | |
| vi.mock('../utils/debugLogger.js', () => ({ | |
| debugLogger: mockDebugLogger, | |
| })); | |
| const { mockTrustedHooksManager, mockCoreEvents } = vi.hoisted(() => ({ | |
| mockTrustedHooksManager: { | |
| getUntrustedHooks: vi.fn().mockReturnValue([]), | |
| trustHooks: vi.fn(), | |
| }, | |
| mockCoreEvents: { | |
| emitConsoleLog: vi.fn(), | |
| emitFeedback: vi.fn(), | |
| }, | |
| })); | |
| vi.mock('./trustedHooks.js', () => ({ | |
| TrustedHooksManager: vi.fn(() => mockTrustedHooksManager), | |
| })); | |
| vi.mock('../utils/events.js', () => ({ | |
| coreEvents: mockCoreEvents, | |
| })); | |
| describe('HookRegistry', () => { | |
| let hookRegistry: HookRegistry; | |
| let mockConfig: Config; | |
| let mockStorage: Storage; | |
| beforeEach(() => { | |
| vi.resetAllMocks(); | |
| mockStorage = { | |
| getGeminiDir: vi.fn().mockReturnValue('/project/.gemini'), | |
| } as unknown as Storage; | |
| mockConfig = { | |
| storage: mockStorage, | |
| getExtensions: vi.fn().mockReturnValue([]), | |
| getHooks: vi.fn().mockReturnValue({}), | |
| getProjectHooks: vi.fn().mockReturnValue({}), | |
| getDisabledHooks: vi.fn().mockReturnValue([]), | |
| isTrustedFolder: vi.fn().mockReturnValue(true), | |
| getProjectRoot: vi.fn().mockReturnValue('/project'), | |
| } as unknown as Config; | |
| hookRegistry = new HookRegistry(mockConfig); | |
| }); | |
| afterEach(() => { | |
| vi.restoreAllMocks(); | |
| }); | |
| describe('initialize', () => { | |
| it('should initialize successfully with no hooks', async () => { | |
| vi.mocked(fs.existsSync).mockReturnValue(false); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.debug).toHaveBeenCalledWith( | |
| 'Hook registry initialized with 0 hook entries', | |
| ); | |
| }); | |
| it('should not load hooks if folder is not trusted', async () => { | |
| vi.mocked(mockConfig.isTrustedFolder).mockReturnValue(false); | |
| const mockHooksConfig = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/test.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mockHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| 'Project hooks disabled because the folder is not trusted.', | |
| ); | |
| }); | |
| it('should load hooks from project configuration', async () => { | |
| const mockHooksConfig = { | |
| BeforeTool: [ | |
| { | |
| matcher: 'EditTool', | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/check_style.sh', | |
| timeout: 60, | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| // Update mock to return the hooks configuration | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mockHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| const hooks = hookRegistry.getAllHooks(); | |
| expect(hooks).toHaveLength(1); | |
| expect(hooks[0].eventName).toBe(HookEventName.BeforeTool); | |
| expect(hooks[0].config.type).toBe(HookType.Command); | |
| expect((hooks[0].config as CommandHookConfig).command).toBe( | |
| './hooks/check_style.sh', | |
| ); | |
| expect(hooks[0].matcher).toBe('EditTool'); | |
| expect(hooks[0].source).toBe(ConfigSource.Project); | |
| }); | |
| it('should load plugin hooks', async () => { | |
| const mockHooksConfig = { | |
| AfterTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/after-tool.sh', | |
| timeout: 30, | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| // Update mock to return the hooks configuration | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mockHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| const hooks = hookRegistry.getAllHooks(); | |
| expect(hooks).toHaveLength(1); | |
| expect(hooks[0].eventName).toBe(HookEventName.AfterTool); | |
| expect(hooks[0].config.type).toBe(HookType.Command); | |
| expect((hooks[0].config as CommandHookConfig).command).toBe( | |
| './hooks/after-tool.sh', | |
| ); | |
| }); | |
| it('should handle invalid configuration gracefully', async () => { | |
| const invalidHooksConfig = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'invalid-type', // Invalid hook type | |
| command: './hooks/test.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| // Update mock to return invalid configuration | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| invalidHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.warn).toHaveBeenCalled(); | |
| }); | |
| it('should validate hook configurations', async () => { | |
| const mockHooksConfig = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'invalid', | |
| command: './hooks/test.sh', | |
| }, | |
| { | |
| type: 'command', | |
| // Missing command field | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| // Update mock to return invalid configuration | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mockHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.warn).toHaveBeenCalled(); // At least some warnings should be logged | |
| }); | |
| it('should respect disabled hooks using friendly name', async () => { | |
| const mockHooksConfig = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| name: 'disabled-hook', | |
| type: 'command', | |
| command: './hooks/test.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| // Update mock to return the hooks configuration | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mockHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| vi.mocked(mockConfig.getDisabledHooks).mockReturnValue(['disabled-hook']); | |
| await hookRegistry.initialize(); | |
| const hooks = hookRegistry.getAllHooks(); | |
| expect(hooks).toHaveLength(1); | |
| expect(hooks[0].enabled).toBe(false); | |
| expect( | |
| hookRegistry.getHooksForEvent(HookEventName.BeforeTool), | |
| ).toHaveLength(0); | |
| }); | |
| }); | |
| describe('getHooksForEvent', () => { | |
| beforeEach(async () => { | |
| const mockHooksConfig = { | |
| BeforeTool: [ | |
| { | |
| matcher: 'EditTool', | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/edit_check.sh', | |
| }, | |
| ], | |
| }, | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/general_check.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| AfterTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/after-tool.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| // Update mock to return the hooks configuration | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mockHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| }); | |
| it('should return hooks for specific event', () => { | |
| const beforeToolHooks = hookRegistry.getHooksForEvent( | |
| HookEventName.BeforeTool, | |
| ); | |
| expect(beforeToolHooks).toHaveLength(2); | |
| const afterToolHooks = hookRegistry.getHooksForEvent( | |
| HookEventName.AfterTool, | |
| ); | |
| expect(afterToolHooks).toHaveLength(1); | |
| }); | |
| it('should return empty array for events with no hooks', () => { | |
| const notificationHooks = hookRegistry.getHooksForEvent( | |
| HookEventName.Notification, | |
| ); | |
| expect(notificationHooks).toHaveLength(0); | |
| }); | |
| }); | |
| describe('setHookEnabled', () => { | |
| beforeEach(async () => { | |
| const mockHooksConfig = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/test.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| // Update mock to return the hooks configuration | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mockHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| }); | |
| it('should enable and disable hooks', () => { | |
| const hookName = './hooks/test.sh'; | |
| // Initially enabled | |
| let hooks = hookRegistry.getHooksForEvent(HookEventName.BeforeTool); | |
| expect(hooks).toHaveLength(1); | |
| // Disable | |
| hookRegistry.setHookEnabled(hookName, false); | |
| hooks = hookRegistry.getHooksForEvent(HookEventName.BeforeTool); | |
| expect(hooks).toHaveLength(0); | |
| // Re-enable | |
| hookRegistry.setHookEnabled(hookName, true); | |
| hooks = hookRegistry.getHooksForEvent(HookEventName.BeforeTool); | |
| expect(hooks).toHaveLength(1); | |
| }); | |
| it('should warn when hook not found', () => { | |
| hookRegistry.setHookEnabled('non-existent-hook', false); | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| 'No hooks found matching "non-existent-hook"', | |
| ); | |
| }); | |
| it('should prefer hook name over command for identification', async () => { | |
| const mockHooksConfig = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| name: 'friendly-name', | |
| type: 'command', | |
| command: './hooks/test.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mockHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| // Should be enabled initially | |
| let hooks = hookRegistry.getHooksForEvent(HookEventName.BeforeTool); | |
| expect(hooks).toHaveLength(1); | |
| // Disable using friendly name | |
| hookRegistry.setHookEnabled('friendly-name', false); | |
| hooks = hookRegistry.getHooksForEvent(HookEventName.BeforeTool); | |
| expect(hooks).toHaveLength(0); | |
| // Identification by command should NOT work when name is present | |
| hookRegistry.setHookEnabled('./hooks/test.sh', true); | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| 'No hooks found matching "./hooks/test.sh"', | |
| ); | |
| }); | |
| it('should use command as identifier when name is missing', async () => { | |
| const mockHooksConfig = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/no-name.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mockHooksConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| // Should be enabled initially | |
| let hooks = hookRegistry.getHooksForEvent(HookEventName.BeforeTool); | |
| expect(hooks).toHaveLength(1); | |
| // Disable using command | |
| hookRegistry.setHookEnabled('./hooks/no-name.sh', false); | |
| hooks = hookRegistry.getHooksForEvent(HookEventName.BeforeTool); | |
| expect(hooks).toHaveLength(0); | |
| }); | |
| }); | |
| describe('malformed configuration handling', () => { | |
| it('should handle non-array definitions gracefully', async () => { | |
| const malformedConfig = { | |
| BeforeTool: 'not-an-array', // Should be an array of HookDefinition | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| malformedConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| expect.stringContaining('is not an array'), | |
| ); | |
| }); | |
| it('should handle object instead of array for definitions', async () => { | |
| const malformedConfig = { | |
| AfterTool: { hooks: [] }, // Should be an array, not a single object | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| malformedConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| expect.stringContaining('is not an array'), | |
| ); | |
| }); | |
| it('should handle null definition gracefully', async () => { | |
| const malformedConfig = { | |
| BeforeTool: [null], // Invalid: null definition | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| malformedConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| expect.stringContaining('Discarding invalid hook definition'), | |
| null, | |
| ); | |
| }); | |
| it('should handle definition without hooks array', async () => { | |
| const malformedConfig = { | |
| BeforeTool: [ | |
| { | |
| matcher: 'EditTool', | |
| // Missing hooks array | |
| }, | |
| ], | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| malformedConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| expect.stringContaining('Discarding invalid hook definition'), | |
| expect.objectContaining({ matcher: 'EditTool' }), | |
| ); | |
| }); | |
| it('should handle non-array hooks property', async () => { | |
| const malformedConfig = { | |
| BeforeTool: [ | |
| { | |
| matcher: 'EditTool', | |
| hooks: 'not-an-array', // Should be an array | |
| }, | |
| ], | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| malformedConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| expect.stringContaining('Discarding invalid hook definition'), | |
| expect.objectContaining({ hooks: 'not-an-array', matcher: 'EditTool' }), | |
| ); | |
| }); | |
| it('should handle non-object hookConfig in hooks array', async () => { | |
| const malformedConfig = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| 'not-an-object', // Should be an object | |
| 42, // Should be an object | |
| null, // Should be an object | |
| ], | |
| }, | |
| ], | |
| }; | |
| mockTrustedHooksManager.getUntrustedHooks.mockReturnValue([]); | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| malformedConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| expect(hookRegistry.getAllHooks()).toHaveLength(0); | |
| expect(mockDebugLogger.warn).toHaveBeenCalledTimes(3); // One warning for each invalid hookConfig | |
| }); | |
| it('should handle mixed valid and invalid hook configurations', async () => { | |
| const mixedConfig = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './valid-hook.sh', | |
| }, | |
| 'invalid-string', | |
| { | |
| type: 'invalid-type', | |
| command: './invalid-type.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| mixedConfig as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| // Should only load the valid hook | |
| const hooks = hookRegistry.getAllHooks(); | |
| expect(hooks).toHaveLength(1); | |
| expect((hooks[0].config as CommandHookConfig).command).toBe( | |
| './valid-hook.sh', | |
| ); | |
| // Verify the warnings for invalid configurations | |
| // 1st warning: non-object hookConfig ('invalid-string') | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| expect.stringContaining('Discarding invalid hook configuration'), | |
| 'invalid-string', | |
| ); | |
| // 2nd warning: validateHookConfig logs invalid type | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| expect.stringContaining('Invalid hook BeforeTool from project type'), | |
| ); | |
| // 3rd warning: processHookDefinition logs the failed hookConfig | |
| expect(mockDebugLogger.warn).toHaveBeenCalledWith( | |
| expect.stringContaining('Discarding invalid hook configuration'), | |
| expect.objectContaining({ type: 'invalid-type' }), | |
| ); | |
| }); | |
| it('should skip known config fields and warn on invalid event names', async () => { | |
| const configWithExtras: Record<string, unknown> = { | |
| InvalidEvent: [], | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './test.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| // Add all known config fields dynamically | |
| for (const field of HOOKS_CONFIG_FIELDS) { | |
| configWithExtras[field] = field === 'disabled' ? [] : true; | |
| } | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| configWithExtras as unknown as { | |
| [K in HookEventName]?: HookDefinition[]; | |
| }, | |
| ); | |
| await hookRegistry.initialize(); | |
| // Should only load the valid hook | |
| expect(hookRegistry.getAllHooks()).toHaveLength(1); | |
| // Should skip all known config fields without warnings | |
| for (const field of HOOKS_CONFIG_FIELDS) { | |
| expect(mockDebugLogger.warn).not.toHaveBeenCalledWith( | |
| expect.stringContaining(`Invalid hook event name: ${field}`), | |
| ); | |
| } | |
| // Should warn on truly invalid event name | |
| expect(mockCoreEvents.emitFeedback).toHaveBeenCalledWith( | |
| 'warning', | |
| expect.stringContaining('Invalid hook event name: "InvalidEvent"'), | |
| ); | |
| }); | |
| }); | |
| describe('project hook warnings', () => { | |
| it('should check for untrusted project hooks when folder is trusted', async () => { | |
| const projectHooks = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/untrusted.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| projectHooks as unknown as { [K in HookEventName]?: HookDefinition[] }, | |
| ); | |
| vi.mocked(mockConfig.getProjectHooks).mockReturnValue( | |
| projectHooks as unknown as { [K in HookEventName]?: HookDefinition[] }, | |
| ); | |
| // Simulate untrusted hooks found | |
| mockTrustedHooksManager.getUntrustedHooks.mockReturnValue([ | |
| './hooks/untrusted.sh', | |
| ]); | |
| await hookRegistry.initialize(); | |
| expect(mockTrustedHooksManager.getUntrustedHooks).toHaveBeenCalledWith( | |
| '/project', | |
| projectHooks, | |
| ); | |
| expect(mockCoreEvents.emitFeedback).toHaveBeenCalledWith( | |
| 'warning', | |
| expect.stringContaining( | |
| 'WARNING: The following project-level hooks have been detected', | |
| ), | |
| ); | |
| expect(mockTrustedHooksManager.trustHooks).toHaveBeenCalledWith( | |
| '/project', | |
| projectHooks, | |
| ); | |
| }); | |
| it('should not warn if hooks are already trusted', async () => { | |
| const projectHooks = { | |
| BeforeTool: [ | |
| { | |
| hooks: [ | |
| { | |
| type: 'command', | |
| command: './hooks/trusted.sh', | |
| }, | |
| ], | |
| }, | |
| ], | |
| }; | |
| vi.mocked(mockConfig.getHooks).mockReturnValue( | |
| projectHooks as unknown as { [K in HookEventName]?: HookDefinition[] }, | |
| ); | |
| vi.mocked(mockConfig.getProjectHooks).mockReturnValue( | |
| projectHooks as unknown as { [K in HookEventName]?: HookDefinition[] }, | |
| ); | |
| // Simulate no untrusted hooks | |
| mockTrustedHooksManager.getUntrustedHooks.mockReturnValue([]); | |
| await hookRegistry.initialize(); | |
| expect(mockCoreEvents.emitFeedback).not.toHaveBeenCalled(); | |
| expect(mockTrustedHooksManager.trustHooks).not.toHaveBeenCalled(); | |
| }); | |
| it('should not check for untrusted hooks if folder is not trusted', async () => { | |
| vi.mocked(mockConfig.isTrustedFolder).mockReturnValue(false); | |
| await hookRegistry.initialize(); | |
| expect(mockTrustedHooksManager.getUntrustedHooks).not.toHaveBeenCalled(); | |
| }); | |
| }); | |
| }); | |