patch_test.rs
#[cfg(test)]
mod patch_tests {
use super::*;
const DOC: &str = "\
Title\n\
=====\n\
\n\
[testmark]:# (one/input)\n\
```text\n\
hello\n\
```\n\
\n\
prose between\n\
\n\
[testmark]:# (one/output)\n\
```\n\
old\n\
```\n\
\n\
[testmark]:# (two/output)\n\
```\n\
also old\n\
```\n";
#[test]
fn empty_accumulator_applies_as_identity() {
let doc = Document::parse(DOC).expect("parses");
let acc = PatchAccumulator::new();
assert!(acc.is_empty());
assert_eq!(doc.patched_all(&acc).unwrap(), DOC);
}
#[test]
fn same_body_applies_as_identity() {
let doc = Document::parse(DOC).expect("parses");
let mut acc = PatchAccumulator::new();
acc.put("one/output", "old\n");
assert_eq!(doc.patched_all(&acc).unwrap(), DOC);
}
#[test]
fn multiple_patches_apply_in_one_pass() {
let doc = Document::parse(DOC).expect("parses");
let mut acc = PatchAccumulator::new();
// Deliberately recorded in reverse document order:
// application order comes from the document, not from insertion.
acc.put("two/output", "fresh two\n");
acc.put("one/output", "fresh one\nwith two lines\n");
let text = doc.patched_all(&acc).unwrap();
let redoc = Document::parse(&text).expect("reparses");
assert_eq!(redoc.hunk("one/input").unwrap().body().unwrap(), "hello\n");
assert_eq!(
redoc.hunk("one/output").unwrap().body().unwrap(),
"fresh one\nwith two lines\n"
);
assert_eq!(redoc.hunk("two/output").unwrap().body().unwrap(), "fresh two\n");
assert!(text.contains("prose between"));
// Untouched hunks keep their block_tag; patched hunks keep theirs too.
assert_eq!(redoc.hunk("one/input").unwrap().block_tag().unwrap(), "text");
}
#[test]
fn multi_patch_agrees_with_sequential_single_patches() {
let doc = Document::parse(DOC).expect("parses");
let mut acc = PatchAccumulator::new();
acc.put("one/output", "a\n");
acc.put("two/output", "b\n");
let all_at_once = doc.patched_all(&acc).unwrap();
let step_one = doc.patched("one/output", "a\n").unwrap();
let step_two = Document::parse(&step_one)
.expect("reparses")
.patched("two/output", "b\n")
.unwrap();
assert_eq!(all_at_once, step_two);
}
#[test]
fn put_replaces_earlier_value() {
let mut acc = PatchAccumulator::new();
acc.put("a", "first\n");
acc.put("a", "second\n");
assert_eq!(acc.get("a"), Some("second\n"));
let doc = Document::parse("[testmark]:# (a)\n```\nx\n```\n").expect("parses");
let text = doc.patched_all(&acc).unwrap();
let redoc = Document::parse(&text).expect("reparses");
assert_eq!(redoc.hunk("a").unwrap().body().unwrap(), "second\n");
}
#[test]
fn patch_can_empty_a_body() {
let doc = Document::parse(DOC).expect("parses");
let mut acc = PatchAccumulator::new();
acc.put("one/output", "");
let text = doc.patched_all(&acc).unwrap();
let redoc = Document::parse(&text).expect("reparses");
assert_eq!(redoc.hunk("one/output").unwrap().body().unwrap(), "");
}
#[test]
fn unknown_hunk_is_an_error() {
let doc = Document::parse(DOC).expect("parses");
let mut acc = PatchAccumulator::new();
acc.put("no/such/hunk", "x\n");
assert_eq!(
doc.patched_all(&acc).unwrap_err(),
PatchError::NoSuchHunk {
name: "no/such/hunk".into(),
}
);
}
#[test]
fn table_hunk_is_an_error() {
let doc = Document::parse("[testmark]:# (t)\n| a |\n| --- |\n| 1 |\n").expect("parses");
let mut acc = PatchAccumulator::new();
acc.put("t", "x\n");
assert_eq!(
doc.patched_all(&acc).unwrap_err(),
PatchError::NotACodeHunk {
name: "t".into(),
}
);
}
}
crates/testmark/src/patch_test.rs · 3.5 KiB · 9049dd4f1c