-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathbuiltin_tools.rs
More file actions
258 lines (230 loc) · 9.75 KB
/
Copy pathbuiltin_tools.rs
File metadata and controls
258 lines (230 loc) · 9.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
use std::time::Duration;
use github_copilot_sdk::MessageOptions;
use super::support::{assistant_message_content, with_e2e_context};
/// Built-in tool tests spawn a real CLI subprocess and execute actual shell /
/// file tools. Under concurrent Windows CI load (e2e runs 4-wide on a 4-vCPU
/// runner) this agent loop can briefly exceed the 60s `send_and_wait` default,
/// so give it extra headroom while still failing fast on a genuine hang.
const SEND_TIMEOUT: Duration = Duration::from_secs(120);
fn message(prompt: &str) -> MessageOptions {
MessageOptions::from(prompt).with_wait_timeout(SEND_TIMEOUT)
}
#[tokio::test]
async fn should_capture_exit_code_in_output() {
with_e2e_context(
"builtin_tools",
"should_capture_exit_code_in_output",
|ctx| {
Box::pin(async move {
ctx.set_default_copilot_user();
let client = ctx.start_client().await;
let session = client
.create_session(ctx.approve_all_session_config())
.await
.expect("create session");
let msg = session
.send_and_wait(message(
"Run 'echo hello && echo world'. Tell me the exact output.",
))
.await
.expect("send")
.expect("assistant message");
let content = assistant_message_content(&msg);
assert!(content.contains("hello"));
assert!(content.contains("world"));
session.disconnect().await.expect("disconnect session");
client.stop().await.expect("stop client");
})
},
)
.await;
}
#[tokio::test]
async fn should_capture_stderr_output() {
with_e2e_context("builtin_tools", "should_capture_stderr_output", |ctx| {
Box::pin(async move {
if cfg!(windows) {
return;
}
ctx.set_default_copilot_user();
let client = ctx.start_client().await;
let session = client
.create_session(ctx.approve_all_session_config())
.await
.expect("create session");
let msg = session
.send_and_wait(message("Run 'echo error_msg >&2; sleep 0.5; echo ok' and tell me what stderr said. Reply with just the stderr content."))
.await
.expect("send")
.expect("assistant message");
assert!(assistant_message_content(&msg).contains("error_msg"));
session.disconnect().await.expect("disconnect session");
client.stop().await.expect("stop client");
})
})
.await;
}
#[tokio::test]
async fn should_read_file_with_line_range() {
with_e2e_context("builtin_tools", "should_read_file_with_line_range", |ctx| {
Box::pin(async move {
ctx.set_default_copilot_user();
std::fs::write(ctx.work_dir().join("lines.txt"), "line1\nline2\nline3\nline4\nline5\n")
.expect("write lines file");
let client = ctx.start_client().await;
let session = client
.create_session(ctx.approve_all_session_config())
.await
.expect("create session");
let msg = session
.send_and_wait(message("Read lines 2 through 4 of the file 'lines.txt' in this directory. Tell me what those lines contain."))
.await
.expect("send")
.expect("assistant message");
let content = assistant_message_content(&msg);
assert!(content.contains("line2"));
assert!(content.contains("line4"));
session.disconnect().await.expect("disconnect session");
client.stop().await.expect("stop client");
})
})
.await;
}
#[tokio::test]
async fn should_handle_nonexistent_file_gracefully() {
with_e2e_context(
"builtin_tools",
"should_handle_nonexistent_file_gracefully",
|ctx| {
Box::pin(async move {
ctx.set_default_copilot_user();
let client = ctx.start_client().await;
let session = client
.create_session(ctx.approve_all_session_config())
.await
.expect("create session");
let msg = session
.send_and_wait(message("Try to read the file 'does_not_exist.txt'. If it doesn't exist, say 'FILE_NOT_FOUND'."))
.await
.expect("send")
.expect("assistant message");
let content = assistant_message_content(&msg).to_uppercase();
assert!(
content.contains("NOT FOUND")
|| content.contains("NOT EXIST")
|| content.contains("NO SUCH")
|| content.contains("FILE_NOT_FOUND")
|| content.contains("DOES NOT EXIST")
|| content.contains("ERROR"),
"expected missing-file response, got: {content}"
);
session.disconnect().await.expect("disconnect session");
client.stop().await.expect("stop client");
})
},
)
.await;
}
#[tokio::test]
async fn should_edit_a_file_successfully() {
with_e2e_context("builtin_tools", "should_edit_a_file_successfully", |ctx| {
Box::pin(async move {
ctx.set_default_copilot_user();
std::fs::write(ctx.work_dir().join("edit_me.txt"), "Hello World\nGoodbye World\n")
.expect("write edit file");
let client = ctx.start_client().await;
let session = client
.create_session(ctx.approve_all_session_config())
.await
.expect("create session");
let msg = session
.send_and_wait(message("Edit the file 'edit_me.txt': replace 'Hello World' with 'Hi Universe'. Then read it back and tell me its contents."))
.await
.expect("send")
.expect("assistant message");
assert!(assistant_message_content(&msg).contains("Hi Universe"));
session.disconnect().await.expect("disconnect session");
client.stop().await.expect("stop client");
})
})
.await;
}
#[tokio::test]
async fn should_create_a_new_file() {
with_e2e_context("builtin_tools", "should_create_a_new_file", |ctx| {
Box::pin(async move {
ctx.set_default_copilot_user();
let client = ctx.start_client().await;
let session = client
.create_session(ctx.approve_all_session_config())
.await
.expect("create session");
let msg = session
.send_and_wait(message("Create a file called 'new_file.txt' with the content 'Created by test'. Then read it back to confirm."))
.await
.expect("send")
.expect("assistant message");
assert!(assistant_message_content(&msg).contains("Created by test"));
session.disconnect().await.expect("disconnect session");
client.stop().await.expect("stop client");
})
})
.await;
}
#[tokio::test]
async fn should_search_for_patterns_in_files() {
with_e2e_context(
"builtin_tools",
"should_search_for_patterns_in_files",
|ctx| {
Box::pin(async move {
ctx.set_default_copilot_user();
std::fs::write(ctx.work_dir().join("data.txt"), "apple\nbanana\napricot\ncherry\n")
.expect("write data file");
let client = ctx.start_client().await;
let session = client
.create_session(ctx.approve_all_session_config())
.await
.expect("create session");
let msg = session
.send_and_wait(message("Search for lines starting with 'ap' in the file 'data.txt'. Tell me which lines matched."))
.await
.expect("send")
.expect("assistant message");
let content = assistant_message_content(&msg);
assert!(content.contains("apple"));
assert!(content.contains("apricot"));
session.disconnect().await.expect("disconnect session");
client.stop().await.expect("stop client");
})
},
)
.await;
}
#[tokio::test]
async fn should_find_files_by_pattern() {
with_e2e_context("builtin_tools", "should_find_files_by_pattern", |ctx| {
Box::pin(async move {
ctx.set_default_copilot_user();
let src = ctx.work_dir().join("src");
std::fs::create_dir(&src).expect("create src directory");
std::fs::write(src.join("index.ts"), "export const index = 1;")
.expect("write index.ts");
std::fs::write(ctx.work_dir().join("README.md"), "# Readme").expect("write readme");
let client = ctx.start_client().await;
let session = client
.create_session(ctx.approve_all_session_config())
.await
.expect("create session");
let msg = session
.send_and_wait(message("Find all .ts files in this directory (recursively). List the filenames you found."))
.await
.expect("send")
.expect("assistant message");
assert!(assistant_message_content(&msg).contains("index.ts"));
session.disconnect().await.expect("disconnect session");
client.stop().await.expect("stop client");
})
})
.await;
}