Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions crates/agent/src/tools/edit_file_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,15 @@ mod tests {
// Edit 2 appears — edit 1 is now complete and should be applied
sender.send_partial(json!({
"path": "root/file.txt",
"edits": [
{"old_text": "aaa", "new_text": "AAA"},
{"old_text": "ccc"}
]
}));
cx.run_until_parked();
sender.send_partial(json!({
"path": "root/file.txt",
"mode": "edit",
"edits": [
{"old_text": "aaa", "new_text": "AAA"},
{"old_text": "ccc", "new_text": "CCC"}
Expand All @@ -774,6 +783,16 @@ mod tests {
// Edit 3 appears — edit 2 is now complete and should be applied
sender.send_partial(json!({
"path": "root/file.txt",
"edits": [
{"old_text": "aaa", "new_text": "AAA"},
{"old_text": "ccc", "new_text": "CCC"},
{"old_text": "eee"}
]
}));
cx.run_until_parked();
sender.send_partial(json!({
"path": "root/file.txt",
"mode": "edit",
"edits": [
{"old_text": "aaa", "new_text": "AAA"},
{"old_text": "ccc", "new_text": "CCC"},
Expand Down Expand Up @@ -909,6 +928,12 @@ mod tests {
}));
cx.run_until_parked();

sender.send_partial(json!({
"path": "root/file.txt",
"edits": [{"old_text": "hello world"}]
}));
cx.run_until_parked();

sender.send_partial(json!({
"path": "root/file.txt",
"edits": [{"old_text": "hello world", "new_text": "goodbye world"}]
Expand Down Expand Up @@ -2135,6 +2160,99 @@ mod tests {
assert_eq!(new_text, "new_content");
}

#[gpui::test]
async fn test_streaming_edit_file_tool_new_and_old_text_appear_together(
cx: &mut TestAppContext,
) {
let (tool, _project, _action_log, _fs, _thread) =
setup_test(cx, json!({"file.txt": "old_content"})).await;
let (mut sender, input) = ToolInput::<EditFileToolInput>::test();
let (event_stream, _receiver) = ToolCallEventStream::test();
let task = cx.update(|cx| tool.clone().run(input, event_stream, cx));

sender.send_partial(json!({
"mode": "edit",
"path": "root/file.txt"
}));
cx.run_until_parked();

sender.send_partial(json!({
"mode": "edit",
"path": "root/file.txt",
"edits": [{"new_text": "new_content", "old_text": "old"}]
}));
cx.run_until_parked();

sender.send_partial(json!({
"mode": "edit",
"path": "root/file.txt",
"edits": [{"new_text": "new_content", "old_text": "old_content"}]
}));
cx.run_until_parked();

sender.send_full(json!({
"mode": "edit",
"path": "root/file.txt",
"edits": [{"new_text": "new_content", "old_text": "old_content"}]
}));
cx.run_until_parked();

let result = task.await;
let EditFileToolOutput::Success { new_text, .. } = result.unwrap() else {
panic!("expected success");
};
assert_eq!(new_text, "new_content");
}

#[gpui::test]
async fn test_streaming_edit_file_tool_new_text_before_old_text(cx: &mut TestAppContext) {
let (tool, _project, _action_log, _fs, _thread) =
setup_test(cx, json!({"file.txt": "old_content"})).await;
let (mut sender, input) = ToolInput::<EditFileToolInput>::test();
let (event_stream, _receiver) = ToolCallEventStream::test();
let task = cx.update(|cx| tool.clone().run(input, event_stream, cx));

sender.send_partial(json!({
"mode": "edit",
"path": "root/file.txt"
}));
cx.run_until_parked();

sender.send_partial(json!({
"mode": "edit",
"path": "root/file.txt",
"edits": [{"new_text": "new_content"}]
}));
cx.run_until_parked();

sender.send_partial(json!({
"mode": "edit",
"path": "root/file.txt",
"edits": [{"new_text": "new_content", "old_text": ""}]
}));
cx.run_until_parked();

sender.send_partial(json!({
"mode": "edit",
"path": "root/file.txt",
"edits": [{"new_text": "new_content", "old_text": "old"}]
}));
cx.run_until_parked();

sender.send_full(json!({
"mode": "edit",
"path": "root/file.txt",
"edits": [{"new_text": "new_content", "old_text": "old_content"}]
}));
cx.run_until_parked();

let result = task.await;
let EditFileToolOutput::Success { new_text, .. } = result.unwrap() else {
panic!("expected success");
};
assert_eq!(new_text, "new_content");
}

#[gpui::test]
async fn test_streaming_edit_partial_last_line(cx: &mut TestAppContext) {
let file_content = indoc::indoc! {r#"
Expand Down
Loading
Loading