use rumdl_lib::lint_context::LintContext;
use rumdl_lib::rule::Rule;
use rumdl_lib::rules::{
MD004UnorderedListStyle, MD005ListIndent, MD007ULIndent, MD009TrailingSpaces, MD010NoHardTabs,
MD012NoMultipleBlanks, MD022BlanksAroundHeadings, MD023HeadingStartLeft, MD028NoBlanksBlockquote,
MD030ListMarkerSpace, MD031BlanksAroundFences, MD032BlanksAroundLists, MD047SingleTrailingNewline,
MD064NoMultipleConsecutiveSpaces,
};
#[test]
fn test_md009_md010_tabs_and_spaces() {
let md009 = MD009TrailingSpaces::default();
let md010 = MD010NoHardTabs::default();
let content = "Text\t \n\tIndented \n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed_tabs = md010.fix(&ctx).unwrap();
let ctx_after_tabs = LintContext::new(&fixed_tabs, rumdl_lib::config::MarkdownFlavor::Standard, None);
let final_fixed = md009.fix(&ctx_after_tabs).unwrap();
let final_ctx = LintContext::new(&final_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md009_check = md009.check(&final_ctx).unwrap();
let md010_check = md010.check(&final_ctx).unwrap();
assert_eq!(md009_check.len(), 0, "No trailing spaces after fixes");
assert_eq!(md010_check.len(), 0, "No tabs after fixes");
}
#[test]
fn test_md004_md030_list_style_and_spacing() {
let md004 = MD004UnorderedListStyle::default();
let md030 = MD030ListMarkerSpace::default();
let content = "* Item 1\n- Item 2\n+ Item 3";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md004_result = md004.check(&ctx).unwrap();
assert!(!md004_result.is_empty(), "Should detect inconsistent list markers");
let md030_result = md030.check(&ctx).unwrap();
assert!(!md030_result.is_empty(), "Should detect spacing issues");
let fixed_md004 = md004.fix(&ctx).unwrap();
let ctx_after_md004 = LintContext::new(&fixed_md004, rumdl_lib::config::MarkdownFlavor::Standard, None);
let final_fixed = md030.fix(&ctx_after_md004).unwrap();
let final_ctx = LintContext::new(&final_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
assert_eq!(md004.check(&final_ctx).unwrap().len(), 0);
assert_eq!(md030.check(&final_ctx).unwrap().len(), 0);
}
#[test]
fn test_md005_md007_list_indentation_conflict() {
let md005 = MD005ListIndent::default();
let md007 = MD007ULIndent::default();
let content = "* Item 1\n * Nested 1\n * Double nested";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md005_issues = md005.check(&ctx).unwrap();
let _md007_issues = md007.check(&ctx).unwrap();
if !md005_issues.is_empty() {
let fixed_md005 = md005.fix(&ctx).unwrap();
let ctx_after_md005 = LintContext::new(&fixed_md005, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md007_after = md007.check(&ctx_after_md005).unwrap();
if !md007_after.is_empty() {
let final_fixed = md007.fix(&ctx_after_md005).unwrap();
let final_ctx = LintContext::new(&final_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let final_md005 = md005.check(&final_ctx).unwrap();
let final_md007 = md007.check(&final_ctx).unwrap();
assert!(
final_md005.is_empty() || final_md007.is_empty(),
"Rules should converge to a stable state"
);
}
}
}
#[test]
fn test_md012_md022_blank_lines_around_headings() {
let md012 = MD012NoMultipleBlanks::default();
let md022 = MD022BlanksAroundHeadings::default();
let content = "Text\n# Heading\nMore text\n\n\n## Another heading\n\n\nText";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let _md012_issues = md012.check(&ctx).unwrap();
let md022_issues = md022.check(&ctx).unwrap();
if !md022_issues.is_empty() {
let fixed_md022 = md022.fix(&ctx).unwrap();
let ctx_after_md022 = LintContext::new(&fixed_md022, rumdl_lib::config::MarkdownFlavor::Standard, None);
let final_fixed = md012.fix(&ctx_after_md022).unwrap();
let final_ctx = LintContext::new(&final_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
assert_eq!(md012.check(&final_ctx).unwrap().len(), 0, "No multiple blank lines");
assert_eq!(md022.check(&final_ctx).unwrap().len(), 0, "Proper heading spacing");
}
}
#[test]
fn test_md023_md009_heading_indentation_and_trailing_spaces() {
let md023 = MD023HeadingStartLeft;
let md009 = MD009TrailingSpaces::default();
let content = " # Heading \n ## Another heading ";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed_md023 = md023.fix(&ctx).unwrap();
let ctx_after_md023 = LintContext::new(&fixed_md023, rumdl_lib::config::MarkdownFlavor::Standard, None);
let final_fixed = md009.fix(&ctx_after_md023).unwrap();
let final_ctx = LintContext::new(&final_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
assert_eq!(md023.check(&final_ctx).unwrap().len(), 0);
assert_eq!(md009.check(&final_ctx).unwrap().len(), 0);
assert!(!final_fixed.contains(" #"), "No leading spaces before headings");
}
#[test]
fn test_md031_md032_fence_and_list_blank_lines() {
let md031 = MD031BlanksAroundFences::default();
let md032 = MD032BlanksAroundLists::default();
let content = "* Item 1\n```\ncode\n```\n* Item 2\n\n* Item 3";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md031_issues = md031.check(&ctx).unwrap();
let _md032_issues = md032.check(&ctx).unwrap();
if !md031_issues.is_empty() {
let fixed_md031 = md031.fix(&ctx).unwrap();
let ctx_after_md031 = LintContext::new(&fixed_md031, rumdl_lib::config::MarkdownFlavor::Standard, None);
if !md032.check(&ctx_after_md031).unwrap().is_empty() {
let final_fixed = md032.fix(&ctx_after_md031).unwrap();
let final_ctx = LintContext::new(&final_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
assert_eq!(md031.check(&final_ctx).unwrap().len(), 0, "Fences have proper spacing");
}
}
}
#[test]
fn test_md047_with_other_rules() {
let md047 = MD047SingleTrailingNewline;
let md009 = MD009TrailingSpaces::default();
let content = "Text with trailing spaces ";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed_md009 = md009.fix(&ctx).unwrap();
let ctx_after_md009 = LintContext::new(&fixed_md009, rumdl_lib::config::MarkdownFlavor::Standard, None);
let final_fixed = md047.fix(&ctx_after_md009).unwrap();
assert!(
final_fixed.ends_with('\n') && !final_fixed.ends_with("\n\n"),
"Single trailing newline"
);
assert!(!final_fixed.lines().any(|l| l.ends_with(' ')), "No trailing spaces");
}
#[test]
fn test_blockquote_list_combination() {
let md004 = MD004UnorderedListStyle::default();
let md009 = MD009TrailingSpaces::default();
let md028 = MD028NoBlanksBlockquote;
let content = "> * Item 1 \n>\n> - Item 2 \n> + Item 3";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed_md028 = md028.fix(&ctx).unwrap();
let ctx_after_md028 = LintContext::new(&fixed_md028, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed_md004 = md004.fix(&ctx_after_md028).unwrap();
let ctx_after_md004 = LintContext::new(&fixed_md004, rumdl_lib::config::MarkdownFlavor::Standard, None);
let final_fixed = md009.fix(&ctx_after_md004).unwrap();
let final_ctx = LintContext::new(&final_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
assert_eq!(md028.check(&final_ctx).unwrap().len(), 0, "MD028 satisfied");
assert_eq!(md004.check(&final_ctx).unwrap().len(), 0, "MD004 satisfied");
assert_eq!(md009.check(&final_ctx).unwrap().len(), 0, "MD009 satisfied");
}
#[test]
fn test_md030_md064_no_conflict_in_blockquoted_list() {
let md030 = MD030ListMarkerSpace::new(1, 1, 2, 2);
let md064 = MD064NoMultipleConsecutiveSpaces::new();
let content = "# Title\n\n> 1. Hello.\n> This is a list item.\n> 2. This is another list item\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md030_warnings = md030.check(&ctx).unwrap();
assert!(
md030_warnings.is_empty(),
"MD030 should be satisfied with 2-space markers"
);
let md064_warnings = md064.check(&ctx).unwrap();
assert!(
md064_warnings.is_empty(),
"MD064 should not flag spaces controlled by MD030"
);
let fixed = md064.fix(&ctx).unwrap();
assert_eq!(
fixed, content,
"MD064 fix should not modify content that MD030 controls"
);
let ctx_after = LintContext::new(&fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md030_recheck = md030.check(&ctx_after).unwrap();
assert!(
md030_recheck.is_empty(),
"MD030 should still be satisfied after MD064 fix"
);
}