use noyalib::cst::parse_document;
#[track_caller]
fn removes_exactly(src: &str, path: &str, want: &str) {
let mut doc = parse_document(src).expect("parse");
doc.remove(path).unwrap_or_else(|e| panic!("{path}: {e}"));
assert_eq!(doc.source(), want, "removing {path:?} from {src:?}");
let _reparsed = parse_document(doc.source()).expect("result re-parses");
}
#[track_caller]
fn no_whitespace_only_line(src: &str, path: &str) {
let mut doc = parse_document(src).expect("parse");
doc.remove(path).unwrap_or_else(|e| panic!("{path}: {e}"));
for (n, line) in doc.source().lines().enumerate() {
assert!(
line.is_empty() || !line.trim().is_empty(),
"line {} of {:?} is whitespace-only after removing {path:?}",
n + 1,
doc.source()
);
}
}
#[test]
fn wrapped_sequence_first_member_takes_its_line() {
removes_exactly(
"ports: [\n 80,\n 443,\n]\n",
"ports[0]",
"ports: [\n 443,\n]\n",
);
}
#[test]
fn wrapped_sequence_last_member_takes_its_line() {
removes_exactly(
"ports: [\n 80,\n 443,\n]\n",
"ports[1]",
"ports: [\n 80,\n]\n",
);
}
#[test]
fn wrapped_mapping_member_takes_its_line() {
removes_exactly(
"cfg: {\n a: 1,\n b: 2,\n}\n",
"cfg.a",
"cfg: {\n b: 2,\n}\n",
);
}
#[test]
fn the_reported_shapes_leave_no_whitespace_only_line() {
no_whitespace_only_line("ports: [\n 80,\n 443,\n]\n", "ports[0]");
no_whitespace_only_line("ports: [\n 80,\n 443,\n]\n", "ports[1]");
no_whitespace_only_line("cfg: {\n a: 1,\n b: 2,\n}\n", "cfg.a");
}
#[test]
fn an_opening_indicator_holds_the_line() {
removes_exactly(
"ports: [80,\n 443,\n]\n",
"ports[0]",
"ports: [\n 443,\n]\n",
);
}
#[test]
fn a_sibling_member_holds_the_line() {
removes_exactly(
"ports: [\n 80, 443,\n 8080,\n]\n",
"ports[0]",
"ports: [\n 443,\n 8080,\n]\n",
);
}
#[test]
fn a_closing_indicator_holds_the_line() {
removes_exactly(
"ports: [\n 80,\n 443]\n",
"ports[1]",
"ports: [\n 80,\n ]\n",
);
}
#[test]
fn a_trailing_comment_holds_the_line() {
removes_exactly(
"ports: [\n 80, # why\n 443,\n]\n",
"ports[0]",
"ports: [\n # why\n 443,\n]\n",
);
}
#[test]
fn single_line_collections_are_untouched() {
removes_exactly("ports: [80, 443]\n", "ports[0]", "ports: [443]\n");
removes_exactly("ports: [80, 443]\n", "ports[1]", "ports: [80]\n");
removes_exactly("cfg: {a: 1, b: 2}\n", "cfg.a", "cfg: {b: 2}\n");
removes_exactly("cfg: {a: 1, b: 2}\n", "cfg.b", "cfg: {a: 1}\n");
}
#[test]
fn a_middle_member_takes_its_line() {
removes_exactly(
"ports: [\n 80,\n 443,\n 8080,\n]\n",
"ports[1]",
"ports: [\n 80,\n 8080,\n]\n",
);
}
#[test]
fn a_last_member_without_a_trailing_comma() {
removes_exactly(
"ports: [\n 80,\n 443\n]\n",
"ports[1]",
"ports: [\n 80,\n]\n",
);
}
#[test]
fn crlf_line_endings_are_preserved() {
removes_exactly(
"ports: [\r\n 80,\r\n 443,\r\n]\r\n",
"ports[0]",
"ports: [\r\n 443,\r\n]\r\n",
);
}
#[test]
fn nested_inside_a_block_mapping() {
removes_exactly(
"a:\n b: [\n 1,\n 2,\n ]\n",
"a.b[0]",
"a:\n b: [\n 2,\n ]\n",
);
}
#[test]
fn siblings_and_the_document_survive() {
removes_exactly(
"keep: 0\nports: [\n 80,\n 443,\n]\ntail: 9\n",
"ports[0]",
"keep: 0\nports: [\n 443,\n]\ntail: 9\n",
);
}
#[test]
fn removing_every_member_one_at_a_time_stays_clean() {
let mut doc = parse_document("ports: [\n 80,\n 443,\n 8080,\n]\n").expect("parse");
doc.remove("ports[2]").expect("remove 2");
doc.remove("ports[1]").expect("remove 1");
assert_eq!(doc.source(), "ports: [\n 80,\n]\n");
for line in doc.source().lines() {
assert!(
line.is_empty() || !line.trim().is_empty(),
"{:?}",
doc.source()
);
}
}
#[test]
fn the_edit_changes_the_value_by_exactly_one_member() {
let src = "ports: [\n 80,\n 443,\n]\nother: keep\n";
let mut doc = parse_document(src).expect("parse");
doc.remove("ports[0]").expect("remove");
let after: noyalib::Value = noyalib::from_str(doc.source()).expect("after");
let expect: noyalib::Value = noyalib::from_str("ports: [443]\nother: keep\n").expect("expect");
assert_eq!(after, expect);
}
#[test]
fn a_refused_remove_leaves_the_source_untouched() {
let src = "ports: [\n 80,\n 443,\n]\n";
let mut doc = parse_document(src).expect("parse");
assert!(doc.remove("ports[9]").is_err());
assert_eq!(doc.source(), src, "source untouched after a refusal");
}
#[test]
fn the_block_path_is_unchanged_and_still_agrees() {
removes_exactly("ports:\n - 80\n - 443\n", "ports[0]", "ports:\n - 443\n");
removes_exactly("ports:\n - 80\n - 443\n", "ports[1]", "ports:\n - 80\n");
removes_exactly("cfg:\n a: 1\n b: 2\n", "cfg.a", "cfg:\n b: 2\n");
}