Skip to content

Commit 1d6bc66

Browse files
authored
test(read-file): unskipping read-file tests (#53)
* test(read-file): unskipping read-file tests * test: adding sequence
1 parent b2b4ee4 commit 1d6bc66

4 files changed

Lines changed: 263 additions & 241 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{
2+
"fixtures": [
3+
{
4+
"match": {
5+
"sequenceIndex": 0,
6+
"userMessage": "READ_FILE_SIMPLE_SMOKE"
7+
},
8+
"response": {
9+
"toolCalls": [
10+
{
11+
"name": "read_file",
12+
"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}}",
13+
"id": "call_read_file_simple_001"
14+
}
15+
]
16+
}
17+
},
18+
{
19+
"match": {
20+
"sequenceIndex": 0,
21+
"userMessage": "READ_FILE_MULTILINE_SMOKE"
22+
},
23+
"response": {
24+
"toolCalls": [
25+
{
26+
"name": "read_file",
27+
"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}}",
28+
"id": "call_read_file_multiline_001"
29+
}
30+
]
31+
}
32+
},
33+
{
34+
"match": {
35+
"sequenceIndex": 0,
36+
"userMessage": "READ_FILE_SLICE_SMOKE"
37+
},
38+
"response": {
39+
"toolCalls": [
40+
{
41+
"name": "read_file",
42+
"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}}",
43+
"id": "call_read_file_slice_001"
44+
}
45+
]
46+
}
47+
},
48+
{
49+
"match": {
50+
"sequenceIndex": 0,
51+
"userMessage": "READ_FILE_MISSING_SMOKE"
52+
},
53+
"response": {
54+
"toolCalls": [
55+
{
56+
"name": "read_file",
57+
"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}}",
58+
"id": "call_read_file_missing_001"
59+
}
60+
]
61+
}
62+
},
63+
{
64+
"match": {
65+
"sequenceIndex": 0,
66+
"userMessage": "READ_FILE_XML_SMOKE"
67+
},
68+
"response": {
69+
"toolCalls": [
70+
{
71+
"name": "read_file",
72+
"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}}",
73+
"id": "call_read_file_xml_001"
74+
}
75+
]
76+
}
77+
},
78+
{
79+
"match": {
80+
"sequenceIndex": 0,
81+
"userMessage": "READ_FILE_MULTIPLE_SMOKE"
82+
},
83+
"response": {
84+
"toolCalls": [
85+
{
86+
"name": "read_file",
87+
"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}}",
88+
"id": "call_read_file_multiple_simple_001"
89+
},
90+
{
91+
"name": "read_file",
92+
"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}}",
93+
"id": "call_read_file_multiple_multiline_001"
94+
}
95+
]
96+
}
97+
},
98+
{
99+
"match": {
100+
"sequenceIndex": 0,
101+
"userMessage": "READ_FILE_LARGE_SMOKE"
102+
},
103+
"response": {
104+
"toolCalls": [
105+
{
106+
"name": "read_file",
107+
"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}}",
108+
"id": "call_read_file_large_001"
109+
}
110+
]
111+
}
112+
}
113+
]
114+
}
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)