testmark-rs

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