mdt_cli 0.9.0

Write docs once, sync everywhere — markdown templates that eliminate doc drift
Documentation
mod common;

use mdt_core::MdtResult;
use mdt_core::check_project;
use mdt_core::compute_updates;
use mdt_core::project::scan_project_with_config;
use rstest::rstest;

fn load_fixture(name: &str) -> (tempfile::TempDir, mdt_core::project::ProjectContext) {
	let tmp = tempfile::tempdir().unwrap_or_else(|e| panic!("tempdir: {e}"));
	common::copy_fixture(name, tmp.path());
	let ctx = scan_project_with_config(tmp.path()).unwrap_or_else(|e| panic!("scan {name}: {e}"));
	(tmp, ctx)
}

#[rstest]
#[case("lenient_whitespace_only")]
#[case("lenient_extra_blank_lines")]
#[case("lenient_trailing_whitespace")]
#[case("lenient_mixed_blank_counts")]
#[case("lenient_trailing_newline_diff")]
fn lenient_whitespace_only_differences_pass(#[case] fixture: &str) -> MdtResult<()> {
	let (_tmp, ctx) = load_fixture(fixture);
	let result = check_project(&ctx)?;

	assert!(result.is_ok(), "{fixture} should be treated as up to date");
	insta::allow_duplicates! {
		insta::assert_debug_snapshot!(result.stale, @r#"[]"#);
	}

	Ok(())
}

#[rstest]
#[case("lenient_word_change", "lenient_word_change_is_stale")]
#[case("lenient_added_line", "lenient_added_line_is_stale")]
#[case("lenient_removed_line", "lenient_removed_line_is_stale")]
#[case(
	"lenient_completely_different",
	"lenient_completely_different_is_stale"
)]
#[case("lenient_code_block_change", "lenient_code_block_change_is_stale")]
#[case("lenient_inline_change", "lenient_inline_change_is_stale")]
#[case(
	"lenient_mixed_stale_clean",
	"lenient_mixed_stale_clean_detects_only_content_change"
)]
fn lenient_content_changes_are_snapshotted(
	#[case] fixture: &str,
	#[case] snapshot_name: &str,
) -> MdtResult<()> {
	let (tmp, ctx) = load_fixture(fixture);
	let result = check_project(&ctx)?;

	assert!(!result.is_ok(), "{fixture} should be stale");
	common::with_redacted_temp_dir(tmp.path(), || {
		insta::assert_debug_snapshot!(snapshot_name, &result.stale);
	});

	Ok(())
}

#[test]
fn lenient_update_writes_exact_source_bytes() -> MdtResult<()> {
	let (tmp, ctx) = load_fixture("lenient_word_change");
	let updates = compute_updates(&ctx)?;

	insta::assert_debug_snapshot!(updates.updated_count, @r#"1"#);

	let content = updates
		.updated_files
		.get(&tmp.path().join("readme.md"))
		.unwrap_or_else(|| panic!("expected updated readme"));
	insta::assert_snapshot!(
		"lenient_update_writes_exact_source_bytes__readme_md",
		content
	);

	Ok(())
}

#[rstest]
#[case("strict_identical")]
fn strict_identical_content_passes(#[case] fixture: &str) -> MdtResult<()> {
	let (_tmp, ctx) = load_fixture(fixture);
	let result = check_project(&ctx)?;

	assert!(result.is_ok(), "{fixture} should be byte-identical");
	insta::allow_duplicates! {
		insta::assert_debug_snapshot!(result.stale, @r#"[]"#);
	}

	Ok(())
}

#[rstest]
#[case("strict_extra_blank_lines", "strict_extra_blank_lines_is_stale")]
#[case("strict_trailing_whitespace", "strict_trailing_whitespace_is_stale")]
#[case("strict_single_extra_newline", "strict_single_extra_newline_is_stale")]
#[case("strict_content_change", "strict_content_change_is_stale")]
#[case("strict_multiple_blocks", "strict_multiple_blocks_all_detected")]
fn strict_differences_are_snapshotted(
	#[case] fixture: &str,
	#[case] snapshot_name: &str,
) -> MdtResult<()> {
	let (tmp, ctx) = load_fixture(fixture);
	let result = check_project(&ctx)?;

	assert!(!result.is_ok(), "{fixture} should be stale");
	common::with_redacted_temp_dir(tmp.path(), || {
		insta::assert_debug_snapshot!(snapshot_name, &result.stale);
	});

	Ok(())
}