Download packages/core/src/tools/enter-plan-mode.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 6.32 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/tools/enter-plan-mode.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/core/src/tools/enter-plan-mode.test.ts
-
curl -L -o enter-plan-mode.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/tools/enter-plan-mode.test.ts
6.32 kB
| /** | |
| * @license | |
| * Copyright 2026 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; | |
| import { EnterPlanModeTool } from './enter-plan-mode.js'; | |
| import { createMockMessageBus } from '../test-utils/mock-message-bus.js'; | |
| import type { Config } from '../config/config.js'; | |
| import type { MessageBus } from '../confirmation-bus/message-bus.js'; | |
| import { ToolConfirmationOutcome } from './tools.js'; | |
| import { ApprovalMode } from '../policy/types.js'; | |
| import fs from 'node:fs'; | |
| vi.mock('node:fs', async () => { | |
| const actual = await vi.importActual<typeof import('node:fs')>('node:fs'); | |
| return { | |
| ...actual, | |
| default: { | |
| // @ts-expect-error - Property 'default' does not exist on type 'typeof import("node:fs")' | |
| ...actual.default, | |
| existsSync: vi.fn(), | |
| mkdirSync: vi.fn(), | |
| }, | |
| existsSync: vi.fn(), | |
| mkdirSync: vi.fn(), | |
| }; | |
| }); | |
| describe('EnterPlanModeTool', () => { | |
| let tool: EnterPlanModeTool; | |
| let mockMessageBus: ReturnType<typeof createMockMessageBus>; | |
| let mockConfig: Partial<Config>; | |
| beforeEach(() => { | |
| mockMessageBus = createMockMessageBus(); | |
| vi.mocked(mockMessageBus.publish).mockResolvedValue(undefined); | |
| mockConfig = { | |
| setApprovalMode: vi.fn(), | |
| storage: { | |
| getPlansDir: vi.fn().mockReturnValue('/mock/plans/dir'), | |
| } as unknown as Config['storage'], | |
| }; | |
| tool = new EnterPlanModeTool( | |
| mockConfig as Config, | |
| mockMessageBus as unknown as MessageBus, | |
| ); | |
| }); | |
| afterEach(() => { | |
| vi.restoreAllMocks(); | |
| }); | |
| describe('shouldConfirmExecute', () => { | |
| it('should return info confirmation details when policy says ASK_USER', async () => { | |
| const invocation = tool.build({}); | |
| // Mock getMessageBusDecision to return ASK_USER | |
| vi.spyOn( | |
| invocation as unknown as { | |
| getMessageBusDecision: () => Promise<string>; | |
| }, | |
| 'getMessageBusDecision', | |
| ).mockResolvedValue('ask_user'); | |
| const result = await invocation.shouldConfirmExecute( | |
| new AbortController().signal, | |
| ); | |
| expect(result).not.toBe(false); | |
| if (result === false) return; | |
| expect(result.type).toBe('info'); | |
| expect(result.title).toBe('Enter Plan Mode'); | |
| if (result.type === 'info') { | |
| expect(result.prompt).toBe( | |
| 'This will restrict the agent to read-only tools to allow for safe planning.', | |
| ); | |
| } | |
| }); | |
| it('should return false when policy decision is ALLOW', async () => { | |
| const invocation = tool.build({}); | |
| // Mock getMessageBusDecision to return ALLOW | |
| vi.spyOn( | |
| invocation as unknown as { | |
| getMessageBusDecision: () => Promise<string>; | |
| }, | |
| 'getMessageBusDecision', | |
| ).mockResolvedValue('allow'); | |
| const result = await invocation.shouldConfirmExecute( | |
| new AbortController().signal, | |
| ); | |
| expect(result).toBe(false); | |
| }); | |
| it('should throw error when policy decision is DENY', async () => { | |
| const invocation = tool.build({}); | |
| // Mock getMessageBusDecision to return DENY | |
| vi.spyOn( | |
| invocation as unknown as { | |
| getMessageBusDecision: () => Promise<string>; | |
| }, | |
| 'getMessageBusDecision', | |
| ).mockResolvedValue('deny'); | |
| await expect( | |
| invocation.shouldConfirmExecute(new AbortController().signal), | |
| ).rejects.toThrow(/denied by policy/); | |
| }); | |
| }); | |
| describe('execute', () => { | |
| it('should set approval mode to PLAN and return message', async () => { | |
| const invocation = tool.build({}); | |
| vi.mocked(fs.existsSync).mockReturnValue(true); | |
| const result = await invocation.execute({ | |
| abortSignal: new AbortController().signal, | |
| }); | |
| expect(mockConfig.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.PLAN, | |
| ); | |
| expect(result.llmContent).toContain('Switching to Plan mode'); | |
| expect(result.returnDisplay).toBe('Switching to Plan mode'); | |
| }); | |
| it('should create plans directory if it does not exist', async () => { | |
| const invocation = tool.build({}); | |
| vi.mocked(fs.existsSync).mockReturnValue(false); | |
| await invocation.execute({ abortSignal: new AbortController().signal }); | |
| expect(fs.mkdirSync).toHaveBeenCalledWith('/mock/plans/dir', { | |
| recursive: true, | |
| }); | |
| }); | |
| it('should include optional reason in output display but not in llmContent', async () => { | |
| const reason = 'Design new database schema'; | |
| const invocation = tool.build({ reason }); | |
| vi.mocked(fs.existsSync).mockReturnValue(true); | |
| const result = await invocation.execute({ | |
| abortSignal: new AbortController().signal, | |
| }); | |
| expect(mockConfig.setApprovalMode).toHaveBeenCalledWith( | |
| ApprovalMode.PLAN, | |
| ); | |
| expect(result.llmContent).toBe('Switching to Plan mode.'); | |
| expect(result.llmContent).not.toContain(reason); | |
| expect(result.returnDisplay).toContain(reason); | |
| }); | |
| it('should not enter plan mode if cancelled', async () => { | |
| const invocation = tool.build({}); | |
| // Simulate getting confirmation details | |
| vi.spyOn( | |
| invocation as unknown as { | |
| getMessageBusDecision: () => Promise<string>; | |
| }, | |
| 'getMessageBusDecision', | |
| ).mockResolvedValue('ask_user'); | |
| const details = await invocation.shouldConfirmExecute( | |
| new AbortController().signal, | |
| ); | |
| expect(details).not.toBe(false); | |
| if (details) { | |
| // Simulate user cancelling | |
| await details.onConfirm(ToolConfirmationOutcome.Cancel); | |
| } | |
| const result = await invocation.execute({ | |
| abortSignal: new AbortController().signal, | |
| }); | |
| expect(mockConfig.setApprovalMode).not.toHaveBeenCalled(); | |
| expect(result.returnDisplay).toBe('Cancelled'); | |
| expect(result.llmContent).toContain('User cancelled'); | |
| }); | |
| }); | |
| describe('validateToolParams', () => { | |
| it('should allow empty params', () => { | |
| const result = tool.validateToolParams({}); | |
| expect(result).toBeNull(); | |
| }); | |
| it('should allow reason param', () => { | |
| const result = tool.validateToolParams({ reason: 'test' }); | |
| expect(result).toBeNull(); | |
| }); | |
| }); | |
| }); | |