summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/hooks/useToolScheduler.test.ts
diff options
context:
space:
mode:
authorjoshualitt <[email protected]>2025-08-06 10:50:02 -0700
committerGitHub <[email protected]>2025-08-06 17:50:02 +0000
commit6133bea388a2de69c71a6be6f1450707f2ce4dfb (patch)
tree367de1d618069ea80e47d7e86c4fb8f82ad032a7 /packages/cli/src/ui/hooks/useToolScheduler.test.ts
parent882a97aff998b2f19731e9966d135f1db5a59914 (diff)
feat(core): Introduce `DeclarativeTool` and `ToolInvocation`. (#5613)
Diffstat (limited to 'packages/cli/src/ui/hooks/useToolScheduler.test.ts')
-rw-r--r--packages/cli/src/ui/hooks/useToolScheduler.test.ts180
1 files changed, 97 insertions, 83 deletions
diff --git a/packages/cli/src/ui/hooks/useToolScheduler.test.ts b/packages/cli/src/ui/hooks/useToolScheduler.test.ts
index 5395d18a..ee5251d3 100644
--- a/packages/cli/src/ui/hooks/useToolScheduler.test.ts
+++ b/packages/cli/src/ui/hooks/useToolScheduler.test.ts
@@ -15,7 +15,6 @@ import { PartUnion, FunctionResponse } from '@google/genai';
import {
Config,
ToolCallRequestInfo,
- Tool,
ToolRegistry,
ToolResult,
ToolCallConfirmationDetails,
@@ -25,6 +24,9 @@ import {
Status as ToolCallStatusType,
ApprovalMode,
Icon,
+ BaseTool,
+ AnyDeclarativeTool,
+ AnyToolInvocation,
} from '@google/gemini-cli-core';
import {
HistoryItemWithoutId,
@@ -53,46 +55,55 @@ const mockConfig = {
getDebugMode: () => false,
};
-const mockTool: Tool = {
- name: 'mockTool',
- displayName: 'Mock Tool',
- description: 'A mock tool for testing',
- icon: Icon.Hammer,
- toolLocations: vi.fn(),
- isOutputMarkdown: false,
- canUpdateOutput: false,
- schema: {},
- validateToolParams: vi.fn(),
- execute: vi.fn(),
- shouldConfirmExecute: vi.fn(),
- getDescription: vi.fn((args) => `Description for ${JSON.stringify(args)}`),
-};
+class MockTool extends BaseTool<object, ToolResult> {
+ constructor(
+ name: string,
+ displayName: string,
+ canUpdateOutput = false,
+ shouldConfirm = false,
+ isOutputMarkdown = false,
+ ) {
+ super(
+ name,
+ displayName,
+ 'A mock tool for testing',
+ Icon.Hammer,
+ {},
+ isOutputMarkdown,
+ canUpdateOutput,
+ );
+ if (shouldConfirm) {
+ this.shouldConfirmExecute = vi.fn(
+ async (): Promise<ToolCallConfirmationDetails | false> => ({
+ type: 'edit',
+ title: 'Mock Tool Requires Confirmation',
+ onConfirm: mockOnUserConfirmForToolConfirmation,
+ fileName: 'mockToolRequiresConfirmation.ts',
+ fileDiff: 'Mock tool requires confirmation',
+ originalContent: 'Original content',
+ newContent: 'New content',
+ }),
+ );
+ }
+ }
-const mockToolWithLiveOutput: Tool = {
- ...mockTool,
- name: 'mockToolWithLiveOutput',
- displayName: 'Mock Tool With Live Output',
- canUpdateOutput: true,
-};
+ execute = vi.fn();
+ shouldConfirmExecute = vi.fn();
+}
+const mockTool = new MockTool('mockTool', 'Mock Tool');
+const mockToolWithLiveOutput = new MockTool(
+ 'mockToolWithLiveOutput',
+ 'Mock Tool With Live Output',
+ true,
+);
let mockOnUserConfirmForToolConfirmation: Mock;
-
-const mockToolRequiresConfirmation: Tool = {
- ...mockTool,
- name: 'mockToolRequiresConfirmation',
- displayName: 'Mock Tool Requires Confirmation',
- shouldConfirmExecute: vi.fn(
- async (): Promise<ToolCallConfirmationDetails | false> => ({
- type: 'edit',
- title: 'Mock Tool Requires Confirmation',
- onConfirm: mockOnUserConfirmForToolConfirmation,
- fileName: 'mockToolRequiresConfirmation.ts',
- fileDiff: 'Mock tool requires confirmation',
- originalContent: 'Original content',
- newContent: 'New content',
- }),
- ),
-};
+const mockToolRequiresConfirmation = new MockTool(
+ 'mockToolRequiresConfirmation',
+ 'Mock Tool Requires Confirmation',
+ false,
+ true,
+);
describe('useReactToolScheduler in YOLO Mode', () => {
let onComplete: Mock;
@@ -646,28 +657,21 @@ describe('useReactToolScheduler', () => {
});
it('should schedule and execute multiple tool calls', async () => {
- const tool1 = {
- ...mockTool,
- name: 'tool1',
- displayName: 'Tool 1',
- execute: vi.fn().mockResolvedValue({
- llmContent: 'Output 1',
- returnDisplay: 'Display 1',
- summary: 'Summary 1',
- } as ToolResult),
- shouldConfirmExecute: vi.fn().mockResolvedValue(null),
- };
- const tool2 = {
- ...mockTool,
- name: 'tool2',
- displayName: 'Tool 2',
- execute: vi.fn().mockResolvedValue({
- llmContent: 'Output 2',
- returnDisplay: 'Display 2',
- summary: 'Summary 2',
- } as ToolResult),
- shouldConfirmExecute: vi.fn().mockResolvedValue(null),
- };
+ const tool1 = new MockTool('tool1', 'Tool 1');
+ tool1.execute.mockResolvedValue({
+ llmContent: 'Output 1',
+ returnDisplay: 'Display 1',
+ summary: 'Summary 1',
+ } as ToolResult);
+ tool1.shouldConfirmExecute.mockResolvedValue(null);
+
+ const tool2 = new MockTool('tool2', 'Tool 2');
+ tool2.execute.mockResolvedValue({
+ llmContent: 'Output 2',
+ returnDisplay: 'Display 2',
+ summary: 'Summary 2',
+ } as ToolResult);
+ tool2.shouldConfirmExecute.mockResolvedValue(null);
mockToolRegistry.getTool.mockImplementation((name) => {
if (name === 'tool1') return tool1;
@@ -805,20 +809,7 @@ describe('mapToDisplay', () => {
args: { foo: 'bar' },
};
- const baseTool: Tool = {
- name: 'testTool',
- displayName: 'Test Tool Display',
- description: 'Test Description',
- isOutputMarkdown: false,
- canUpdateOutput: false,
- schema: {},
- icon: Icon.Hammer,
- toolLocations: vi.fn(),
- validateToolParams: vi.fn(),
- execute: vi.fn(),
- shouldConfirmExecute: vi.fn(),
- getDescription: vi.fn((args) => `Desc: ${JSON.stringify(args)}`),
- };
+ const baseTool = new MockTool('testTool', 'Test Tool Display');
const baseResponse: ToolCallResponseInfo = {
callId: 'testCallId',
@@ -840,13 +831,15 @@ describe('mapToDisplay', () => {
// This helps ensure that tool and confirmationDetails are only accessed when they are expected to exist.
type MapToDisplayExtraProps =
| {
- tool?: Tool;
+ tool?: AnyDeclarativeTool;
+ invocation?: AnyToolInvocation;
liveOutput?: string;
response?: ToolCallResponseInfo;
confirmationDetails?: ToolCallConfirmationDetails;
}
| {
- tool: Tool;
+ tool: AnyDeclarativeTool;
+ invocation?: AnyToolInvocation;
response?: ToolCallResponseInfo;
confirmationDetails?: ToolCallConfirmationDetails;
}
@@ -857,10 +850,12 @@ describe('mapToDisplay', () => {
}
| {
confirmationDetails: ToolCallConfirmationDetails;
- tool?: Tool;
+ tool?: AnyDeclarativeTool;
+ invocation?: AnyToolInvocation;
response?: ToolCallResponseInfo;
};
+ const baseInvocation = baseTool.build(baseRequest.args);
const testCases: Array<{
name: string;
status: ToolCallStatusType;
@@ -873,7 +868,7 @@ describe('mapToDisplay', () => {
{
name: 'validating',
status: 'validating',
- extraProps: { tool: baseTool },
+ extraProps: { tool: baseTool, invocation: baseInvocation },
expectedStatus: ToolCallStatus.Executing,
expectedName: baseTool.displayName,
expectedDescription: baseTool.getDescription(baseRequest.args),
@@ -883,6 +878,7 @@ describe('mapToDisplay', () => {
status: 'awaiting_approval',
extraProps: {
tool: baseTool,
+ invocation: baseInvocation,
confirmationDetails: {
onConfirm: vi.fn(),
type: 'edit',
@@ -903,7 +899,7 @@ describe('mapToDisplay', () => {
{
name: 'scheduled',
status: 'scheduled',
- extraProps: { tool: baseTool },
+ extraProps: { tool: baseTool, invocation: baseInvocation },
expectedStatus: ToolCallStatus.Pending,
expectedName: baseTool.displayName,
expectedDescription: baseTool.getDescription(baseRequest.args),
@@ -911,7 +907,7 @@ describe('mapToDisplay', () => {
{
name: 'executing no live output',
status: 'executing',
- extraProps: { tool: baseTool },
+ extraProps: { tool: baseTool, invocation: baseInvocation },
expectedStatus: ToolCallStatus.Executing,
expectedName: baseTool.displayName,
expectedDescription: baseTool.getDescription(baseRequest.args),
@@ -919,7 +915,11 @@ describe('mapToDisplay', () => {
{
name: 'executing with live output',
status: 'executing',
- extraProps: { tool: baseTool, liveOutput: 'Live test output' },
+ extraProps: {
+ tool: baseTool,
+ invocation: baseInvocation,
+ liveOutput: 'Live test output',
+ },
expectedStatus: ToolCallStatus.Executing,
expectedResultDisplay: 'Live test output',
expectedName: baseTool.displayName,
@@ -928,7 +928,11 @@ describe('mapToDisplay', () => {
{
name: 'success',
status: 'success',
- extraProps: { tool: baseTool, response: baseResponse },
+ extraProps: {
+ tool: baseTool,
+ invocation: baseInvocation,
+ response: baseResponse,
+ },
expectedStatus: ToolCallStatus.Success,
expectedResultDisplay: baseResponse.resultDisplay as any,
expectedName: baseTool.displayName,
@@ -970,6 +974,7 @@ describe('mapToDisplay', () => {
status: 'cancelled',
extraProps: {
tool: baseTool,
+ invocation: baseInvocation,
response: {
...baseResponse,
resultDisplay: 'Cancelled display',
@@ -1030,12 +1035,21 @@ describe('mapToDisplay', () => {
request: { ...baseRequest, callId: 'call1' },
status: 'success',
tool: baseTool,
+ invocation: baseTool.build(baseRequest.args),
response: { ...baseResponse, callId: 'call1' },
} as ToolCall;
+ const toolForCall2 = new MockTool(
+ baseTool.name,
+ baseTool.displayName,
+ false,
+ false,
+ true,
+ );
const toolCall2: ToolCall = {
request: { ...baseRequest, callId: 'call2' },
status: 'executing',
- tool: { ...baseTool, isOutputMarkdown: true },
+ tool: toolForCall2,
+ invocation: toolForCall2.build(baseRequest.args),
liveOutput: 'markdown output',
} as ToolCall;