#[cfg(test)]
mod check_tests {
	use super::*;

	const DOC: &str = "\
[testmark]:# (a/expect)\n\
```\n\
alpha\n\
```\n\
\n\
[testmark]:# (b/expect)\n\
```\n\
beta\n\
```\n\
\n\
[testmark]:# (tbl)\n\
| h |\n\
| --- |\n\
| 1 |\n";

	fn checker(regen: bool) -> Checker {
		Checker::new(Document::parse(DOC).expect("parses"), regen)
	}

	#[test]
	fn normal_mode_match_is_ok() {
		let mut chk = checker(false);
		assert_eq!(chk.check("a/expect", "alpha\n"), Ok(()));
		assert!(chk.patches.is_empty());
	}

	#[test]
	fn normal_mode_mismatch_is_error_with_regen_hint() {
		let mut chk = checker(false);
		let err = chk.check("a/expect", "omega\n").unwrap_err();
		assert!(matches!(err, CheckError::Mismatch { .. }));
		let msg = err.to_string();
		assert!(msg.contains("mismatch in a/expect"));
		assert!(msg.contains("alpha"));
		assert!(msg.contains("omega"));
		assert!(msg.contains("TESTMARK_REGEN=1"));
		assert!(chk.patches.is_empty());
	}

	#[test]
	fn regen_mode_mismatch_records_a_patch() {
		let mut chk = checker(true);
		assert_eq!(chk.check("a/expect", "omega\n"), Ok(()));
		assert_eq!(chk.patches.get("a/expect"), Some("omega\n"));
		let text = chk.doc.patched_all(&chk.patches).unwrap();
		let redoc = Document::parse(&text).expect("reparses");
		assert_eq!(redoc.hunk("a/expect").unwrap().body().unwrap(), "omega\n");
		assert_eq!(redoc.hunk("b/expect").unwrap().body().unwrap(), "beta\n");
	}

	#[test]
	fn regen_mode_match_records_nothing() {
		let mut chk = checker(true);
		assert_eq!(chk.check("a/expect", "alpha\n"), Ok(()));
		assert!(chk.patches.is_empty());
	}

	#[test]
	fn absent_hunk_is_error_in_both_modes() {
		for regen in [false, true] {
			let mut chk = checker(regen);
			assert_eq!(
				chk.check("no/such", "x\n"),
				Err(CheckError::NoSuchHunk {
					name: "no/such".into(),
				})
			);
		}
	}

	#[test]
	fn table_hunk_is_error() {
		let mut chk = checker(false);
		assert_eq!(
			chk.check("tbl", "x\n"),
			Err(CheckError::NotACodeHunk {
				name: "tbl".into(),
			})
		);
	}

	#[test]
	fn recheck_with_same_value_is_ok() {
		for regen in [false, true] {
			let mut chk = checker(regen);
			assert_eq!(chk.check("a/expect", "alpha\n"), Ok(()));
			assert_eq!(chk.check("a/expect", "alpha\n"), Ok(()));
		}
	}

	#[test]
	fn recheck_with_different_value_is_conflict() {
		// Conflicts must surface in both modes, and regardless of whether the
		// first check matched the document (i.e. whether a patch was recorded).
		for regen in [false, true] {
			for first in ["alpha\n", "omega\n"] {
				let mut chk = checker(regen);
				let _ = chk.check("a/expect", first);
				let err = chk.check("a/expect", "something else\n").unwrap_err();
				assert!(
					matches!(err, CheckError::Conflict { .. }),
					"regen={regen} first={first:?}: got {err:?}"
				);
			}
		}
	}

	#[test]
	fn write_back_roundtrip() {
		let dir = std::env::temp_dir().join(format!("testmark-check-test-{}", std::process::id()));
		std::fs::create_dir_all(&dir).expect("tmp dir");
		let path = dir.join("fixture.md");
		std::fs::write(&path, DOC).expect("seed fixture");

		let mut chk = Checker::new(crate::read_file(&path).expect("reads"), true);
		chk.check("b/expect", "gamma\n").expect("regen records");
		write_back(&path, &chk).expect("writes");

		let redoc = crate::read_file(&path).expect("re-reads");
		assert_eq!(redoc.hunk("b/expect").unwrap().body().unwrap(), "gamma\n");
		assert_eq!(redoc.hunk("a/expect").unwrap().body().unwrap(), "alpha\n");

		std::fs::remove_dir_all(&dir).expect("cleanup");
	}

	#[test]
	fn write_back_without_patches_is_a_no_op() {
		// Point at a path that does not exist: a no-op must not try to write it.
		let chk = checker(false);
		let path = std::path::Path::new("/nonexistent/testmark-no-op");
		assert!(write_back(path, &chk).is_ok());
	}
}
