Download packages/cli/src/commands/extensions/enable.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 8.67 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/commands/extensions/enable.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/cli/src/commands/extensions/enable.test.ts
-
curl -L -o enable.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/commands/extensions/enable.test.ts
8.67 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { | |
| vi, | |
| describe, | |
| it, | |
| expect, | |
| beforeEach, | |
| afterEach, | |
| type Mock, | |
| } from 'vitest'; | |
| import { format } from 'node:util'; | |
| import { type Argv } from 'yargs'; | |
| import { handleEnable, enableCommand } from './enable.js'; | |
| import { ExtensionManager } from '../../config/extension-manager.js'; | |
| import { | |
| loadSettings, | |
| SettingScope, | |
| type LoadedSettings, | |
| } from '../../config/settings.js'; | |
| import { FatalConfigError } from '@google/gemini-cli-core'; | |
| // Mock dependencies | |
| const emitConsoleLog = vi.hoisted(() => vi.fn()); | |
| const debugLogger = vi.hoisted(() => ({ | |
| log: vi.fn((message, ...args) => { | |
| emitConsoleLog('log', format(message, ...args)); | |
| }), | |
| error: vi.fn((message, ...args) => { | |
| emitConsoleLog('error', format(message, ...args)); | |
| }), | |
| })); | |
| vi.mock('@google/gemini-cli-core', async (importOriginal) => { | |
| const actual = | |
| await importOriginal<typeof import('@google/gemini-cli-core')>(); | |
| return { | |
| ...actual, | |
| coreEvents: { | |
| emitConsoleLog, | |
| }, | |
| debugLogger, | |
| getErrorMessage: vi.fn((error: { message: string }) => error.message), | |
| FatalConfigError: class extends Error { | |
| constructor(message: string) { | |
| super(message); | |
| this.name = 'FatalConfigError'; | |
| } | |
| }, | |
| }; | |
| }); | |
| vi.mock('../../config/extension-manager.js'); | |
| vi.mock('../../config/settings.js'); | |
| vi.mock('../../config/extensions/consent.js'); | |
| vi.mock('../../config/extensions/extensionSettings.js'); | |
| vi.mock('../utils.js', () => ({ | |
| exitCli: vi.fn(), | |
| })); | |
| const mockEnablementInstance = vi.hoisted(() => ({ | |
| getDisplayState: vi.fn(), | |
| enable: vi.fn(), | |
| clearSessionDisable: vi.fn(), | |
| autoEnableServers: vi.fn(), | |
| })); | |
| vi.mock('../../config/mcp/mcpServerEnablement.js', () => ({ | |
| McpServerEnablementManager: { | |
| getInstance: () => mockEnablementInstance, | |
| }, | |
| })); | |
| describe('extensions enable command', () => { | |
| const mockLoadSettings = vi.mocked(loadSettings); | |
| const mockExtensionManager = vi.mocked(ExtensionManager); | |
| beforeEach(async () => { | |
| vi.clearAllMocks(); | |
| mockLoadSettings.mockReturnValue({ | |
| merged: {}, | |
| } as unknown as LoadedSettings); | |
| mockExtensionManager.prototype.loadExtensions = vi | |
| .fn() | |
| .mockResolvedValue(undefined); | |
| mockExtensionManager.prototype.enableExtension = vi.fn(); | |
| mockExtensionManager.prototype.getExtensions = vi.fn().mockReturnValue([]); | |
| mockEnablementInstance.getDisplayState.mockReset(); | |
| mockEnablementInstance.enable.mockReset(); | |
| mockEnablementInstance.clearSessionDisable.mockReset(); | |
| mockEnablementInstance.autoEnableServers.mockReset(); | |
| mockEnablementInstance.autoEnableServers.mockResolvedValue([]); | |
| }); | |
| afterEach(() => { | |
| vi.restoreAllMocks(); | |
| }); | |
| describe('handleEnable', () => { | |
| it.each([ | |
| { | |
| name: 'my-extension', | |
| scope: undefined, | |
| expectedScope: SettingScope.User, | |
| expectedLog: | |
| 'Extension "my-extension" successfully enabled in all scopes.', | |
| }, | |
| { | |
| name: 'my-extension', | |
| scope: 'workspace', | |
| expectedScope: SettingScope.Workspace, | |
| expectedLog: | |
| 'Extension "my-extension" successfully enabled for scope "workspace".', | |
| }, | |
| ])( | |
| 'should enable an extension in the $expectedScope scope when scope is $scope', | |
| async ({ name, scope, expectedScope, expectedLog }) => { | |
| const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir'); | |
| await handleEnable({ name, scope }); | |
| expect(mockExtensionManager).toHaveBeenCalledWith( | |
| expect.objectContaining({ | |
| workspaceDir: '/test/dir', | |
| }), | |
| ); | |
| expect( | |
| mockExtensionManager.prototype.loadExtensions, | |
| ).toHaveBeenCalled(); | |
| expect( | |
| mockExtensionManager.prototype.enableExtension, | |
| ).toHaveBeenCalledWith(name, expectedScope); | |
| expect(emitConsoleLog).toHaveBeenCalledWith('log', expectedLog); | |
| mockCwd.mockRestore(); | |
| }, | |
| ); | |
| it('should throw FatalConfigError when extension enabling fails', async () => { | |
| const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir'); | |
| const error = new Error('Enable failed'); | |
| ( | |
| mockExtensionManager.prototype.enableExtension as Mock | |
| ).mockImplementation(() => { | |
| throw error; | |
| }); | |
| const promise = handleEnable({ name: 'my-extension' }); | |
| await expect(promise).rejects.toThrow(FatalConfigError); | |
| await expect(promise).rejects.toThrow('Enable failed'); | |
| mockCwd.mockRestore(); | |
| }); | |
| it('should auto-enable disabled MCP servers for the extension', async () => { | |
| const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir'); | |
| mockEnablementInstance.autoEnableServers.mockResolvedValue([ | |
| 'test-server', | |
| ]); | |
| mockExtensionManager.prototype.getExtensions = vi | |
| .fn() | |
| .mockReturnValue([ | |
| { name: 'my-extension', mcpServers: { 'test-server': {} } }, | |
| ]); | |
| await handleEnable({ name: 'my-extension' }); | |
| expect(mockEnablementInstance.autoEnableServers).toHaveBeenCalledWith([ | |
| 'test-server', | |
| ]); | |
| expect(emitConsoleLog).toHaveBeenCalledWith( | |
| 'log', | |
| expect.stringContaining("MCP server 'test-server' was disabled"), | |
| ); | |
| mockCwd.mockRestore(); | |
| }); | |
| it('should not log when MCP servers are already enabled', async () => { | |
| const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir'); | |
| mockEnablementInstance.autoEnableServers.mockResolvedValue([]); | |
| mockExtensionManager.prototype.getExtensions = vi | |
| .fn() | |
| .mockReturnValue([ | |
| { name: 'my-extension', mcpServers: { 'test-server': {} } }, | |
| ]); | |
| await handleEnable({ name: 'my-extension' }); | |
| expect(mockEnablementInstance.autoEnableServers).toHaveBeenCalledWith([ | |
| 'test-server', | |
| ]); | |
| expect(emitConsoleLog).not.toHaveBeenCalledWith( | |
| 'log', | |
| expect.stringContaining("MCP server 'test-server' was disabled"), | |
| ); | |
| mockCwd.mockRestore(); | |
| }); | |
| }); | |
| describe('enableCommand', () => { | |
| const command = enableCommand; | |
| it('should have correct command and describe', () => { | |
| expect(command.command).toBe('enable [--scope] <name>'); | |
| expect(command.describe).toBe('Enables an extension.'); | |
| }); | |
| describe('builder', () => { | |
| interface MockYargs { | |
| positional: Mock; | |
| option: Mock; | |
| check: Mock; | |
| } | |
| let yargsMock: MockYargs; | |
| beforeEach(() => { | |
| yargsMock = { | |
| positional: vi.fn().mockReturnThis(), | |
| option: vi.fn().mockReturnThis(), | |
| check: vi.fn().mockReturnThis(), | |
| }; | |
| }); | |
| it('should configure positional and option arguments', () => { | |
| (command.builder as (yargs: Argv) => Argv)( | |
| yargsMock as unknown as Argv, | |
| ); | |
| expect(yargsMock.positional).toHaveBeenCalledWith('name', { | |
| describe: 'The name of the extension to enable.', | |
| type: 'string', | |
| }); | |
| expect(yargsMock.option).toHaveBeenCalledWith('scope', { | |
| describe: | |
| 'The scope to enable the extension in. If not set, will be enabled in all scopes.', | |
| type: 'string', | |
| }); | |
| expect(yargsMock.check).toHaveBeenCalled(); | |
| }); | |
| it('check function should throw for invalid scope', () => { | |
| (command.builder as (yargs: Argv) => Argv)( | |
| yargsMock as unknown as Argv, | |
| ); | |
| const checkCallback = yargsMock.check.mock.calls[0][0]; | |
| const expectedError = `Invalid scope: invalid. Please use one of ${Object.values( | |
| SettingScope, | |
| ) | |
| .map((s) => s.toLowerCase()) | |
| .join(', ')}.`; | |
| expect(() => checkCallback({ scope: 'invalid' })).toThrow( | |
| expectedError, | |
| ); | |
| }); | |
| }); | |
| it('handler should call handleEnable', async () => { | |
| const mockCwd = vi.spyOn(process, 'cwd').mockReturnValue('/test/dir'); | |
| interface TestArgv { | |
| name: string; | |
| scope: string; | |
| [key: string]: unknown; | |
| } | |
| const argv: TestArgv = { | |
| name: 'test-ext', | |
| scope: 'workspace', | |
| _: [], | |
| $0: '', | |
| }; | |
| await (command.handler as unknown as (args: TestArgv) => Promise<void>)( | |
| argv, | |
| ); | |
| expect( | |
| mockExtensionManager.prototype.enableExtension, | |
| ).toHaveBeenCalledWith('test-ext', SettingScope.Workspace); | |
| mockCwd.mockRestore(); | |
| }); | |
| }); | |
| }); | |