use super::*;
use agent_client_protocol::schema::v1::Diff;
fn diff(old_text: Option<&str>, new_text: &str) -> Diff {
let mut diff = Diff::new("src/main.rs", new_text);
diff.old_text = old_text.map(ToOwned::to_owned);
diff
}
#[test]
fn compacting_an_edit_costs_the_edit_rather_than_the_file() {
let old_text = (0..2_000)
.map(|line| format!("line {line}\n"))
.collect::<String>();
let new_text = old_text.replace("line 900\n", "line 900 changed\n");
let mut compacted = diff(Some(&old_text), &new_text);
let original = serde_json::to_vec(&compacted).unwrap().len();
assert!(compact_diff(&mut compacted));
let stored = serde_json::to_vec(&compacted).unwrap().len();
assert!(
stored * 20 < original,
"storing a one-line edit to a {original}-byte diff still cost {stored} bytes"
);
assert_eq!(compacted.old_text, None);
assert_eq!(compacted.new_text, "");
let patch = patch_of(&compacted);
assert_eq!((patch.insertions, patch.deletions), (1, 1));
assert!(patch.text.contains("-line 900\n"));
assert!(patch.text.contains("+line 900 changed\n"));
assert!(!patch.text.contains("line 100\n"), "kept distant context");
assert!(
patch
.text
.starts_with("--- a/src/main.rs\n+++ b/src/main.rs\n")
);
assert!(!patch.created);
assert!(!patch.truncated);
}
#[test]
fn the_stat_is_the_same_before_and_after_compaction() {
let mut compacted = diff(Some("alpha\nbeta\ngamma\n"), "alpha\ndelta\ngamma\nomega\n");
let before = patch_of(&compacted);
compact_diff(&mut compacted);
assert_eq!(patch_of(&compacted), before);
assert_eq!((before.insertions, before.deletions), (2, 1));
}
#[test]
fn a_created_file_records_that_it_was_created() {
let mut created = diff(None, "alpha\nbeta\n");
assert!(compact_diff(&mut created));
let patch = patch_of(&created);
assert!(patch.created);
assert_eq!((patch.insertions, patch.deletions), (2, 0));
assert!(patch.text.starts_with("--- /dev/null\n+++ b/src/main.rs\n"));
assert!(patch.text.contains("+alpha\n"));
}
#[test]
fn an_enormous_edit_keeps_whole_hunks_and_says_it_dropped_the_rest() {
let old_text = (0..20_000)
.map(|line| format!("line {line}\n"))
.collect::<String>();
let new_text = old_text.replace("line ", "changed ");
let mut huge = diff(Some(&old_text), &new_text);
assert!(compact_diff(&mut huge));
let patch = patch_of(&huge);
assert!(patch.truncated);
assert!(patch.text.len() <= DIFF_PATCH_BYTES + 64);
assert!(patch.text.ends_with("[mj dropped the remaining hunks]\n"));
assert_eq!(patch.insertions, 20_000);
assert_eq!(patch.deletions, 20_000);
for line in patch
.text
.lines()
.skip(2)
.take_while(|line| !line.starts_with('['))
{
assert!(
line.starts_with(['@', '+', '-', ' ']),
"not a patch line: {line:?}"
);
}
}
#[test]
fn compaction_is_idempotent_and_leaves_other_metadata_alone() {
let mut compacted = diff(Some("alpha\n"), "beta\n");
compacted.meta = Some(
[("agent.note".to_owned(), serde_json::json!("keep me"))]
.into_iter()
.collect(),
);
assert!(compact_diff(&mut compacted));
let once = compacted.clone();
assert!(!compact_diff(&mut compacted));
assert_eq!(compacted, once);
assert_eq!(
compacted.meta.as_ref().unwrap().get("agent.note"),
Some(&serde_json::json!("keep me"))
);
}