Skip to content

Commit 9f39044

Browse files
committed
test(read-file): unskipping read-file tests
1 parent 6c5d4e1 commit 9f39044

4 files changed

Lines changed: 256 additions & 241 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
{
2+
"fixtures": [
3+
{
4+
"match": {
5+
"userMessage": "READ_FILE_SIMPLE_SMOKE"
6+
},
7+
"response": {
8+
"toolCalls": [
9+
{
10+
"name": "read_file",
11+
"arguments": "{\"path\":\"simple-read-file-smoke.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":2000,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":2000}}",
12+
"id": "call_read_file_simple_001"
13+
}
14+
]
15+
}
16+
},
17+
{
18+
"match": {
19+
"userMessage": "READ_FILE_MULTILINE_SMOKE"
20+
},
21+
"response": {
22+
"toolCalls": [
23+
{
24+
"name": "read_file",
25+
"arguments": "{\"path\":\"multiline-read-file.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":20,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":20}}",
26+
"id": "call_read_file_multiline_001"
27+
}
28+
]
29+
}
30+
},
31+
{
32+
"match": {
33+
"userMessage": "READ_FILE_SLICE_SMOKE"
34+
},
35+
"response": {
36+
"toolCalls": [
37+
{
38+
"name": "read_file",
39+
"arguments": "{\"path\":\"multiline-read-file.txt\",\"mode\":\"slice\",\"offset\":2,\"limit\":3,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":2000}}",
40+
"id": "call_read_file_slice_001"
41+
}
42+
]
43+
}
44+
},
45+
{
46+
"match": {
47+
"userMessage": "READ_FILE_MISSING_SMOKE"
48+
},
49+
"response": {
50+
"toolCalls": [
51+
{
52+
"name": "read_file",
53+
"arguments": "{\"path\":\"non-existent-read-file.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":2000,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":2000}}",
54+
"id": "call_read_file_missing_001"
55+
}
56+
]
57+
}
58+
},
59+
{
60+
"match": {
61+
"userMessage": "READ_FILE_XML_SMOKE"
62+
},
63+
"response": {
64+
"toolCalls": [
65+
{
66+
"name": "read_file",
67+
"arguments": "{\"path\":\"xml-content-read-file.xml\",\"mode\":\"slice\",\"offset\":1,\"limit\":50,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":50}}",
68+
"id": "call_read_file_xml_001"
69+
}
70+
]
71+
}
72+
},
73+
{
74+
"match": {
75+
"userMessage": "READ_FILE_MULTIPLE_SMOKE"
76+
},
77+
"response": {
78+
"toolCalls": [
79+
{
80+
"name": "read_file",
81+
"arguments": "{\"path\":\"simple-read-file-smoke.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":20,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":20}}",
82+
"id": "call_read_file_multiple_simple_001"
83+
},
84+
{
85+
"name": "read_file",
86+
"arguments": "{\"path\":\"multiline-read-file.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":20,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":20}}",
87+
"id": "call_read_file_multiple_multiline_001"
88+
}
89+
]
90+
}
91+
},
92+
{
93+
"match": {
94+
"userMessage": "READ_FILE_LARGE_SMOKE"
95+
},
96+
"response": {
97+
"toolCalls": [
98+
{
99+
"name": "read_file",
100+
"arguments": "{\"path\":\"large-read-file.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":1000,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":2000}}",
101+
"id": "call_read_file_large_001"
102+
}
103+
]
104+
}
105+
}
106+
]
107+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { LLMock } from "@copilotkit/aimock"
2+
import type { ChatCompletionRequest, ChatMessage } from "@copilotkit/aimock"
3+
4+
type ToolResultExpectation = { toolCallId: string; expected: string[] }
5+
6+
type ReadFileResultFixture = {
7+
toolCallId: string
8+
expected: string[] | ToolResultExpectation[]
9+
result: string
10+
id: string
11+
}
12+
13+
function isToolResultExpectation(value: unknown): value is ToolResultExpectation {
14+
return typeof value === "object" && value !== null && "toolCallId" in value && "expected" in value
15+
}
16+
17+
function toolResultContains(req: ChatCompletionRequest, toolCallId: string, expected: string[]) {
18+
const messages = Array.isArray(req?.messages) ? req.messages : []
19+
const toolMessage = messages.find(
20+
(message: ChatMessage) => message?.role === "tool" && message.tool_call_id === toolCallId,
21+
)
22+
23+
const content = toolMessage?.content
24+
if (typeof content !== "string") {
25+
return false
26+
}
27+
28+
return expected.every((text) => content.includes(text))
29+
}
30+
31+
function toolResultsContain(req: ChatCompletionRequest, expectations: ToolResultExpectation[]) {
32+
return expectations.every(({ toolCallId, expected }) => toolResultContains(req, toolCallId, expected))
33+
}
34+
35+
export function addReadFileResultFixtures(mock: InstanceType<typeof LLMock>) {
36+
const fixtures: ReadFileResultFixture[] = [
37+
{
38+
toolCallId: "call_read_file_simple_001",
39+
expected: ["File: simple-read-file-smoke.txt", "1 | Hello, World!"],
40+
result: 'The file [`simple-read-file-smoke.txt`](simple-read-file-smoke.txt) contains the text: "Hello, World!"',
41+
id: "call_read_file_simple_002",
42+
},
43+
{
44+
toolCallId: "call_read_file_multiline_001",
45+
expected: ["File: multiline-read-file.txt", "1 | Line 1", "5 | Line 5"],
46+
result: "The file [`multiline-read-file.txt`](multiline-read-file.txt) contains 5 lines: Line 1, Line 2, Line 3, Line 4, and Line 5.",
47+
id: "call_read_file_multiline_002",
48+
},
49+
{
50+
toolCallId: "call_read_file_slice_001",
51+
expected: ["File: multiline-read-file.txt", "2 | Line 2", "3 | Line 3", "4 | Line 4"],
52+
result: "The three lines read from [`multiline-read-file.txt`](multiline-read-file.txt) starting at offset 2 are:\n\n- Line 2\n- Line 3\n- Line 4",
53+
id: "call_read_file_slice_002",
54+
},
55+
{
56+
toolCallId: "call_read_file_missing_001",
57+
expected: ["non-existent-read-file.txt", "ENOENT", "no such file or directory"],
58+
result: "Attempting to read [`non-existent-read-file.txt`](non-existent-read-file.txt) resulted in an error: the file does not exist. This error was handled appropriately and no file contents were returned.",
59+
id: "call_read_file_missing_002",
60+
},
61+
{
62+
toolCallId: "call_read_file_xml_001",
63+
expected: ["File: xml-content-read-file.xml", "<child>Test content</child>", "<data>Some data</data>"],
64+
result: "The XML file [`xml-content-read-file.xml`](xml-content-read-file.xml) contains the following elements:\n- `<root>` (root element)\n- `<child>` (child of root)\n- `<data>` (child of root)\n\nThe structure is:\n```xml\n<root>\n <child>Test content</child>\n <data>Some data</data>\n</root>\n```",
65+
id: "call_read_file_xml_002",
66+
},
67+
{
68+
toolCallId: "call_read_file_multiple_multiline_001",
69+
expected: [
70+
{
71+
toolCallId: "call_read_file_multiple_simple_001",
72+
expected: ["File: simple-read-file-smoke.txt", "1 | Hello, World!"],
73+
},
74+
{
75+
toolCallId: "call_read_file_multiple_multiline_001",
76+
expected: ["File: multiline-read-file.txt", "1 | Line 1", "5 | Line 5"],
77+
},
78+
],
79+
result: "Contents of [`simple-read-file-smoke.txt`](simple-read-file-smoke.txt):\n\n```\nHello, World!\n```\n\nContents of [`multiline-read-file.txt`](multiline-read-file.txt):\n\n```\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5\n```",
80+
id: "call_read_file_multiple_002",
81+
},
82+
{
83+
toolCallId: "call_read_file_large_001",
84+
expected: [
85+
"File: large-read-file.txt",
86+
"1 | Line 1: This is a test line with some content",
87+
"100 | Line 100: This is a test line with some content",
88+
],
89+
result: "The file [`large-read-file.txt`](large-read-file.txt) contains 100 lines, each following the pattern: `Line N: This is a test line with some content`, where `N` is the line number (from 1 to 100). The structure is consistent throughout the file, with only the line number changing on each line.",
90+
id: "call_read_file_large_002",
91+
},
92+
]
93+
94+
for (const fixture of fixtures) {
95+
mock.addFixture({
96+
match: {
97+
toolCallId: fixture.toolCallId,
98+
predicate: (req) =>
99+
isToolResultExpectation(fixture.expected[0])
100+
? toolResultsContain(req, fixture.expected as ToolResultExpectation[])
101+
: toolResultContains(req, fixture.toolCallId, fixture.expected as string[]),
102+
},
103+
response: {
104+
toolCalls: [
105+
{
106+
name: "attempt_completion",
107+
arguments: JSON.stringify({ result: fixture.result }),
108+
id: fixture.id,
109+
},
110+
],
111+
},
112+
})
113+
}
114+
}

apps/vscode-e2e/src/runTest.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import * as fs from "fs/promises"
55
import { runTests } from "@vscode/test-electron"
66
import { LLMock } from "@copilotkit/aimock"
77

8+
import { addReadFileResultFixtures } from "./fixtures/read-file"
9+
810
function getCliFlagValue(flag: string) {
911
return process.argv.find((arg, index) => process.argv[index - 1] === flag)
1012
}
@@ -75,6 +77,8 @@ async function main() {
7577
mock.loadFixtureDir(fixturesDir)
7678

7779
if (!isRecord) {
80+
addReadFileResultFixtures(mock)
81+
7882
// The modes test (switch_mode → ask) triggers a second API call whose last
7983
// user message starts with <environment_details> directly — no <user_message>
8084
// wrapper. JSON fixtures use substring matching so a bare "<environment_details>"

0 commit comments

Comments
 (0)