Skip to content

Commit 9a05469

Browse files
committed
FetchInstructionsHandler.test
1 parent 2118e4f commit 9a05469

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { FetchInstructionsHandler } from "../FetchInstructionsHandler"
2+
import { Cline } from "../../../Cline"
3+
import { ToolUse } from "../../../assistant-message"
4+
import { fetchInstructionsTool } from "../../../tools/fetchInstructionsTool" // Import the function to mock
5+
import { telemetryService } from "../../../../services/telemetry/TelemetryService"
6+
7+
// --- Mocks ---
8+
jest.mock("../../../Cline")
9+
const MockCline = Cline as jest.MockedClass<typeof Cline>
10+
11+
// Mock the underlying tool function
12+
jest.mock("../../../tools/fetchInstructionsTool")
13+
const mockFetchInstructionsTool = fetchInstructionsTool as jest.Mock
14+
15+
jest.mock("../../../../services/telemetry/TelemetryService", () => ({
16+
telemetryService: {
17+
captureToolUsage: jest.fn(),
18+
},
19+
}))
20+
21+
describe("FetchInstructionsHandler", () => {
22+
let mockClineInstance: jest.MockedObject<Cline>
23+
let mockToolUse: ToolUse
24+
25+
beforeEach(() => {
26+
jest.clearAllMocks()
27+
28+
mockClineInstance = {
29+
cwd: "/workspace",
30+
consecutiveMistakeCount: 0,
31+
taskId: "test-task-id",
32+
ask: jest.fn(() => Promise.resolve({})), // Default ask response
33+
say: jest.fn(() => Promise.resolve()),
34+
pushToolResult: jest.fn(() => Promise.resolve()), // Mocked, but fetchInstructionsTool should call it
35+
handleErrorHelper: jest.fn(() => Promise.resolve()),
36+
sayAndCreateMissingParamError: jest.fn((tool, param) => Promise.resolve(`Missing ${param}`)),
37+
askApprovalHelper: jest.fn(() => Promise.resolve(true)), // Mocked, but fetchInstructionsTool uses it
38+
providerRef: { deref: () => ({ getState: () => Promise.resolve({}) }) },
39+
emit: jest.fn(),
40+
getTokenUsage: jest.fn(() => ({})),
41+
removeClosingTag: jest.fn((tag, value) => value), // Simple mock
42+
} as unknown as jest.MockedObject<Cline>
43+
44+
mockToolUse = {
45+
type: "tool_use",
46+
name: "fetch_instructions",
47+
params: {
48+
task: "create_mcp_server",
49+
},
50+
partial: false,
51+
}
52+
})
53+
54+
// --- Test validateParams ---
55+
test("validateParams should throw if task is missing", () => {
56+
delete mockToolUse.params.task
57+
const handler = new FetchInstructionsHandler(mockClineInstance, mockToolUse)
58+
expect(() => handler.validateParams()).toThrow("Missing required parameter 'task'")
59+
})
60+
61+
test("validateParams should not throw if task is present", () => {
62+
const handler = new FetchInstructionsHandler(mockClineInstance, mockToolUse)
63+
expect(() => handler.validateParams()).not.toThrow()
64+
})
65+
66+
// --- Test handlePartial ---
67+
test("handlePartial should call ask with tool info", async () => {
68+
mockToolUse.partial = true
69+
const handler = new FetchInstructionsHandler(mockClineInstance, mockToolUse)
70+
await handler.handle()
71+
expect(mockClineInstance.ask).toHaveBeenCalledWith(
72+
"tool",
73+
JSON.stringify({
74+
tool: "fetchInstructions",
75+
task: mockToolUse.params.task,
76+
}),
77+
true,
78+
)
79+
})
80+
81+
// --- Test handleComplete ---
82+
test("handleComplete should call fetchInstructionsTool with correct arguments", async () => {
83+
const handler = new FetchInstructionsHandler(mockClineInstance, mockToolUse)
84+
await handler.handle()
85+
86+
// Verify fetchInstructionsTool was called
87+
expect(mockFetchInstructionsTool).toHaveBeenCalledTimes(1)
88+
89+
// Verify the arguments passed to fetchInstructionsTool
90+
const callArgs = mockFetchInstructionsTool.mock.calls[0]
91+
expect(callArgs[0]).toBe(mockClineInstance) // First arg: Cline instance
92+
expect(callArgs[1]).toBe(mockToolUse) // Second arg: ToolUse block
93+
94+
// Verify the helper functions passed (check they are functions)
95+
expect(typeof callArgs[2]).toBe("function") // askApprovalHelper wrapper
96+
expect(typeof callArgs[3]).toBe("function") // handleErrorHelper wrapper
97+
expect(typeof callArgs[4]).toBe("function") // pushToolResult wrapper
98+
99+
// Optionally, test if the wrappers call the underlying Cline methods when invoked
100+
// Example for pushToolResult wrapper:
101+
const pushToolResultWrapper = callArgs[4]
102+
await pushToolResultWrapper("Test Result")
103+
expect(mockClineInstance.pushToolResult).toHaveBeenCalledWith(mockToolUse, "Test Result")
104+
105+
// Verify telemetry was called
106+
expect(telemetryService.captureToolUsage).toHaveBeenCalledWith(mockClineInstance.taskId, "fetch_instructions")
107+
})
108+
109+
test("handleComplete should call handleErrorHelper if fetchInstructionsTool throws", async () => {
110+
const fetchError = new Error("Fetch failed")
111+
mockFetchInstructionsTool.mockRejectedValue(fetchError) // Make the mocked function throw
112+
113+
const handler = new FetchInstructionsHandler(mockClineInstance, mockToolUse)
114+
await handler.handle()
115+
116+
expect(mockFetchInstructionsTool).toHaveBeenCalledTimes(1)
117+
expect(mockClineInstance.handleErrorHelper).toHaveBeenCalledWith(
118+
mockToolUse,
119+
"fetching instructions",
120+
fetchError,
121+
)
122+
expect(mockClineInstance.pushToolResult).not.toHaveBeenCalled() // Error helper should handle result
123+
expect(telemetryService.captureToolUsage).not.toHaveBeenCalled() // Should not be called on error
124+
})
125+
})

0 commit comments

Comments
 (0)