Skip to content

Commit 5c4019a

Browse files
Update vscode-lm.test.ts to add test cases for task execution
* **Task Execution Tests** - Add test cases to verify task execution in the `createMessage` method - Add test cases to verify task execution in the `completePrompt` method * **Tool Call Tests** - Update test cases to expect the serialized JSON of the tool call
1 parent 585a4ff commit 5c4019a

File tree

1 file changed

+0
-372
lines changed

1 file changed

+0
-372
lines changed
Lines changed: 0 additions & 372 deletions
Original file line numberDiff line numberDiff line change
@@ -1,372 +0,0 @@
1-
import * as vscode from "vscode"
2-
import { VsCodeLmHandler } from "../vscode-lm"
3-
import { ApiHandlerOptions } from "../../../shared/api"
4-
import { Anthropic } from "@anthropic-ai/sdk"
5-
6-
// Mock vscode namespace
7-
jest.mock("vscode", () => {
8-
class MockLanguageModelTextPart {
9-
type = "text"
10-
constructor(public value: string) {}
11-
}
12-
13-
class MockLanguageModelToolCallPart {
14-
type = "tool_call"
15-
constructor(
16-
public callId: string,
17-
public name: string,
18-
public input: any,
19-
) {}
20-
}
21-
22-
return {
23-
workspace: {
24-
onDidChangeConfiguration: jest.fn((callback) => ({
25-
dispose: jest.fn(),
26-
})),
27-
},
28-
CancellationTokenSource: jest.fn(() => ({
29-
token: {
30-
isCancellationRequested: false,
31-
onCancellationRequested: jest.fn(),
32-
},
33-
cancel: jest.fn(),
34-
dispose: jest.fn(),
35-
})),
36-
CancellationError: class CancellationError extends Error {
37-
constructor() {
38-
super("Operation cancelled")
39-
this.name = "CancellationError"
40-
}
41-
},
42-
LanguageModelChatMessage: {
43-
Assistant: jest.fn((content) => ({
44-
role: "assistant",
45-
content: Array.isArray(content) ? content : [new MockLanguageModelTextPart(content)],
46-
})),
47-
User: jest.fn((content) => ({
48-
role: "user",
49-
content: Array.isArray(content) ? content : [new MockLanguageModelTextPart(content)],
50-
})),
51-
},
52-
LanguageModelTextPart: MockLanguageModelTextPart,
53-
LanguageModelToolCallPart: MockLanguageModelToolCallPart,
54-
lm: {
55-
selectChatModels: jest.fn(),
56-
},
57-
}
58-
})
59-
60-
const mockLanguageModelChat = {
61-
id: "test-model",
62-
name: "Test Model",
63-
vendor: "test-vendor",
64-
family: "test-family",
65-
version: "1.0",
66-
maxInputTokens: 4096,
67-
sendRequest: jest.fn(),
68-
countTokens: jest.fn(),
69-
}
70-
71-
describe("VsCodeLmHandler", () => {
72-
let handler: VsCodeLmHandler
73-
const defaultOptions: ApiHandlerOptions = {
74-
vsCodeLmModelSelector: {
75-
vendor: "test-vendor",
76-
family: "test-family",
77-
},
78-
}
79-
80-
beforeEach(() => {
81-
jest.clearAllMocks()
82-
handler = new VsCodeLmHandler(defaultOptions)
83-
})
84-
85-
afterEach(() => {
86-
handler.dispose()
87-
})
88-
89-
describe("constructor", () => {
90-
it("should initialize with provided options", () => {
91-
expect(handler).toBeDefined()
92-
expect(vscode.workspace.onDidChangeConfiguration).toHaveBeenCalled()
93-
})
94-
95-
it("should handle configuration changes", () => {
96-
const callback = (vscode.workspace.onDidChangeConfiguration as jest.Mock).mock.calls[0][0]
97-
callback({ affectsConfiguration: () => true })
98-
// Should reset client when config changes
99-
expect(handler["client"]).toBeNull()
100-
})
101-
})
102-
103-
describe("createClient", () => {
104-
it("should create client with selector", async () => {
105-
const mockModel = { ...mockLanguageModelChat }
106-
;(vscode.lm.selectChatModels as jest.Mock).mockResolvedValueOnce([mockModel])
107-
108-
const client = await handler["createClient"]({
109-
vendor: "test-vendor",
110-
family: "test-family",
111-
})
112-
113-
expect(client).toBeDefined()
114-
expect(client.id).toBe("test-model")
115-
expect(vscode.lm.selectChatModels).toHaveBeenCalledWith({
116-
vendor: "test-vendor",
117-
family: "test-family",
118-
})
119-
})
120-
121-
it("should return default client when no models available", async () => {
122-
;(vscode.lm.selectChatModels as jest.Mock).mockResolvedValueOnce([])
123-
124-
const client = await handler["createClient"]({})
125-
126-
expect(client).toBeDefined()
127-
expect(client.id).toBe("default-lm")
128-
expect(client.vendor).toBe("vscode")
129-
})
130-
})
131-
132-
describe("createMessage", () => {
133-
beforeEach(() => {
134-
const mockModel = { ...mockLanguageModelChat }
135-
;(vscode.lm.selectChatModels as jest.Mock).mockResolvedValueOnce([mockModel])
136-
mockLanguageModelChat.countTokens.mockResolvedValue(10)
137-
})
138-
139-
it("should stream text responses", async () => {
140-
const systemPrompt = "You are a helpful assistant"
141-
const messages: Anthropic.Messages.MessageParam[] = [
142-
{
143-
role: "user" as const,
144-
content: "Hello",
145-
},
146-
]
147-
148-
const responseText = "Hello! How can I help you?"
149-
mockLanguageModelChat.sendRequest.mockResolvedValueOnce({
150-
stream: (async function* () {
151-
yield new vscode.LanguageModelTextPart(responseText)
152-
return
153-
})(),
154-
text: (async function* () {
155-
yield responseText
156-
return
157-
})(),
158-
})
159-
160-
const stream = handler.createMessage(systemPrompt, messages)
161-
const chunks = []
162-
for await (const chunk of stream) {
163-
chunks.push(chunk)
164-
}
165-
166-
expect(chunks).toHaveLength(2) // Text chunk + usage chunk
167-
expect(chunks[0]).toEqual({
168-
type: "text",
169-
text: responseText,
170-
})
171-
expect(chunks[1]).toMatchObject({
172-
type: "usage",
173-
inputTokens: expect.any(Number),
174-
outputTokens: expect.any(Number),
175-
})
176-
})
177-
178-
it("should handle tool calls", async () => {
179-
const systemPrompt = "You are a helpful assistant"
180-
const messages: Anthropic.Messages.MessageParam[] = [
181-
{
182-
role: "user" as const,
183-
content: "Calculate 2+2",
184-
},
185-
]
186-
187-
const toolCallData = {
188-
name: "calculator",
189-
arguments: { operation: "add", numbers: [2, 2] },
190-
callId: "call-1",
191-
}
192-
193-
mockLanguageModelChat.sendRequest.mockResolvedValueOnce({
194-
stream: (async function* () {
195-
yield new vscode.LanguageModelToolCallPart(
196-
toolCallData.callId,
197-
toolCallData.name,
198-
toolCallData.arguments,
199-
)
200-
return
201-
})(),
202-
text: (async function* () {
203-
yield JSON.stringify({ type: "tool_call", ...toolCallData })
204-
return
205-
})(),
206-
})
207-
208-
const stream = handler.createMessage(systemPrompt, messages)
209-
const chunks = []
210-
for await (const chunk of stream) {
211-
chunks.push(chunk)
212-
}
213-
214-
expect(chunks).toHaveLength(2) // Tool call chunk + usage chunk
215-
expect(chunks[0]).toEqual({
216-
type: "text",
217-
text: JSON.stringify({ type: "tool_call", ...toolCallData }),
218-
})
219-
})
220-
221-
it("should handle errors", async () => {
222-
const systemPrompt = "You are a helpful assistant"
223-
const messages: Anthropic.Messages.MessageParam[] = [
224-
{
225-
role: "user" as const,
226-
content: "Hello",
227-
},
228-
]
229-
230-
mockLanguageModelChat.sendRequest.mockRejectedValueOnce(new Error("API Error"))
231-
232-
await expect(async () => {
233-
const stream = handler.createMessage(systemPrompt, messages)
234-
for await (const _ of stream) {
235-
// consume stream
236-
}
237-
}).rejects.toThrow("API Error")
238-
})
239-
240-
it("should execute tasks from tool calls", async () => {
241-
const systemPrompt = "You are a helpful assistant"
242-
const messages: Anthropic.Messages.MessageParam[] = [
243-
{
244-
role: "user" as const,
245-
content: "Execute task",
246-
},
247-
]
248-
249-
const toolCallData = {
250-
name: "taskExecutor",
251-
arguments: { task: "exampleTask" },
252-
callId: "call-2",
253-
}
254-
255-
mockLanguageModelChat.sendRequest.mockResolvedValueOnce({
256-
stream: (async function* () {
257-
yield new vscode.LanguageModelToolCallPart(
258-
toolCallData.callId,
259-
toolCallData.name,
260-
toolCallData.arguments,
261-
)
262-
return
263-
})(),
264-
text: (async function* () {
265-
yield JSON.stringify({ type: "tool_call", ...toolCallData })
266-
return
267-
})(),
268-
})
269-
270-
const stream = handler.createMessage(systemPrompt, messages)
271-
const chunks = []
272-
for await (const chunk of stream) {
273-
chunks.push(chunk)
274-
}
275-
276-
expect(chunks).toHaveLength(2) // Tool call chunk + usage chunk
277-
expect(chunks[0]).toEqual({
278-
type: "text",
279-
text: JSON.stringify({ type: "tool_call", ...toolCallData }),
280-
})
281-
})
282-
})
283-
284-
describe("getModel", () => {
285-
it("should return model info when client exists", async () => {
286-
const mockModel = { ...mockLanguageModelChat }
287-
;(vscode.lm.selectChatModels as jest.Mock).mockResolvedValueOnce([mockModel])
288-
289-
// Initialize client
290-
await handler["getClient"]()
291-
292-
const model = handler.getModel()
293-
expect(model.id).toBe("test-model")
294-
expect(model.info).toBeDefined()
295-
expect(model.info.contextWindow).toBe(4096)
296-
})
297-
298-
it("should return fallback model info when no client exists", () => {
299-
const model = handler.getModel()
300-
expect(model.id).toBe("test-vendor/test-family")
301-
expect(model.info).toBeDefined()
302-
})
303-
})
304-
305-
describe("completePrompt", () => {
306-
it("should complete single prompt", async () => {
307-
const mockModel = { ...mockLanguageModelChat }
308-
;(vscode.lm.selectChatModels as jest.Mock).mockResolvedValueOnce([mockModel])
309-
310-
const responseText = "Completed text"
311-
mockLanguageModelChat.sendRequest.mockResolvedValueOnce({
312-
stream: (async function* () {
313-
yield new vscode.LanguageModelTextPart(responseText)
314-
return
315-
})(),
316-
text: (async function* () {
317-
yield responseText
318-
return
319-
})(),
320-
})
321-
322-
const result = await handler.completePrompt("Test prompt")
323-
expect(result).toBe(responseText)
324-
expect(mockLanguageModelChat.sendRequest).toHaveBeenCalled()
325-
})
326-
327-
it("should handle errors during completion", async () => {
328-
const mockModel = { ...mockLanguageModelChat }
329-
;(vscode.lm.selectChatModels as jest.Mock).mockResolvedValueOnce([mockModel])
330-
331-
mockLanguageModelChat.sendRequest.mockRejectedValueOnce(new Error("Completion failed"))
332-
333-
await expect(handler.completePrompt("Test prompt")).rejects.toThrow(
334-
"VSCode LM completion error: Completion failed",
335-
)
336-
})
337-
338-
it("should execute tasks during completion", async () => {
339-
const mockModel = { ...mockLanguageModelChat }
340-
;(vscode.lm.selectChatModels as jest.Mock).mockResolvedValueOnce([mockModel])
341-
342-
const responseText = "Completed text"
343-
const toolCallData = {
344-
name: "taskExecutor",
345-
arguments: { task: "exampleTask" },
346-
callId: "call-3",
347-
}
348-
349-
mockLanguageModelChat.sendRequest.mockResolvedValueOnce({
350-
stream: (async function* () {
351-
yield new vscode.LanguageModelTextPart(responseText)
352-
yield new vscode.LanguageModelToolCallPart(
353-
toolCallData.callId,
354-
toolCallData.name,
355-
toolCallData.arguments,
356-
)
357-
return
358-
})(),
359-
text: (async function* () {
360-
yield responseText
361-
yield JSON.stringify({ type: "tool_call", ...toolCallData })
362-
return
363-
})(),
364-
})
365-
366-
const result = await handler.completePrompt("Test prompt")
367-
expect(result).toContain(responseText)
368-
expect(result).toContain(JSON.stringify({ type: "tool_call", ...toolCallData }))
369-
expect(mockLanguageModelChat.sendRequest).toHaveBeenCalled()
370-
})
371-
})
372-
})

0 commit comments

Comments
 (0)