use rumdl_lib::config::{Config, MarkdownFlavor};
use rumdl_lib::fix_coordinator::FixCoordinator;
use rumdl_lib::rule::Rule;
use rumdl_lib::rules::{
CodeBlockStyle, ListItemSpacingStyle, MD036NoEmphasisAsHeading, MD046CodeBlockStyle, MD076ListItemSpacing,
all_rules,
};
fn fmt_through_coordinator(content: &str, rules: Vec<Box<dyn Rule>>) -> String {
let coordinator = FixCoordinator::new();
let config = Config::default();
let mut buf = content.to_string();
coordinator
.apply_fixes_iterative(&rules, &[], &mut buf, &config, 100, None)
.expect("fix coordinator must not error");
buf
}
#[test]
fn md036_fmt_promotes_emphasis_to_heading_with_default_config() {
let before = "# Heading\n\n**Looks like a heading**\n\nSome content.\n";
let expected = "# Heading\n\n## Looks like a heading\n\nSome content.\n";
let rule = MD036NoEmphasisAsHeading::from_config(&Config::default());
let after = fmt_through_coordinator(before, vec![rule]);
assert_eq!(after, expected);
}
#[test]
fn md076_fmt_normalizes_to_consistent_tight_list() {
let before = "- a\n- b\n- c\n\n- d\n";
let expected = "- a\n- b\n- c\n- d\n";
let rule: Box<dyn Rule> = Box::new(MD076ListItemSpacing::new(ListItemSpacingStyle::Consistent));
let after = fmt_through_coordinator(before, vec![rule]);
assert_eq!(after, expected);
}
#[test]
fn md076_fmt_tie_prefers_tight() {
let before = "- item one\n- item two\n\n- item three\n";
let expected = "- item one\n- item two\n- item three\n";
let rule: Box<dyn Rule> = Box::new(MD076ListItemSpacing::new(ListItemSpacingStyle::Consistent));
let after = fmt_through_coordinator(before, vec![rule]);
assert_eq!(after, expected);
}
#[test]
fn md076_fmt_removes_unexpected_blank_under_tight_style() {
let before = "- alpha\n- beta\n\n- gamma\n- delta\n";
let expected = "- alpha\n- beta\n- gamma\n- delta\n";
let rule: Box<dyn Rule> = Box::new(MD076ListItemSpacing::new(ListItemSpacingStyle::Tight));
let after = fmt_through_coordinator(before, vec![rule]);
assert_eq!(after, expected);
}
#[test]
fn md076_fmt_inserts_missing_blank_under_loose_style() {
let before = "- alpha\n\n- beta\n- gamma\n";
let expected = "- alpha\n\n- beta\n\n- gamma\n";
let rule: Box<dyn Rule> = Box::new(MD076ListItemSpacing::new(ListItemSpacingStyle::Loose));
let after = fmt_through_coordinator(before, vec![rule]);
assert_eq!(after, expected);
}
#[test]
fn md046_fmt_converts_indented_to_fenced() {
let before = "Intro paragraph.\n\n indented code\n more code\n\nTrailing paragraph.\n";
let expected = "Intro paragraph.\n\n```\nindented code\nmore code\n```\n\nTrailing paragraph.\n";
let rule: Box<dyn Rule> = Box::new(MD046CodeBlockStyle::new(CodeBlockStyle::Fenced));
let after = fmt_through_coordinator(before, vec![rule]);
assert_eq!(after, expected);
}
#[test]
fn md046_fmt_converts_list_internal_indented_to_fenced() {
let before = "- A list item\n\n indented code block here\n more code\n";
let expected = "- A list item\n\n ```\n indented code block here\n more code\n ```\n";
let rule: Box<dyn Rule> = Box::new(MD046CodeBlockStyle::new(CodeBlockStyle::Fenced));
let after = fmt_through_coordinator(before, vec![rule]);
assert_eq!(after, expected);
}
#[test]
fn md046_fmt_converts_fenced_to_indented() {
let before = "Intro.\n\n```\nfenced code\nmore code\n```\n\nTrailing.\n";
let expected = "Intro.\n\n fenced code\n more code\n\nTrailing.\n";
let rule: Box<dyn Rule> = Box::new(MD046CodeBlockStyle::new(CodeBlockStyle::Indented));
let after = fmt_through_coordinator(before, vec![rule]);
assert_eq!(after, expected);
}
#[test]
fn md036_default_config_attaches_inline_fix_to_warnings() {
use rumdl_lib::lint_context::LintContext;
let content = "# Title\n\n**Promote me**\n\nBody.\n";
let rule = MD036NoEmphasisAsHeading::from_config(&Config::default());
let ctx = LintContext::new(content, MarkdownFlavor::Standard, None);
let warnings = rule.check(&ctx).expect("check must succeed");
assert_eq!(warnings.len(), 1, "expected one MD036 warning");
assert!(
warnings[0].fix.is_some(),
"MD036 warning must carry an inline Fix when fix capability is FullyFixable; got {:?}",
warnings[0]
);
}
#[test]
fn md046_consistent_no_warning_implies_no_fix_for_list_internal_indented_block() {
use rumdl_lib::lint_context::LintContext;
let content = "- A list item\n\n indented code block here\n more code\n";
let ctx = LintContext::new(content, MarkdownFlavor::Standard, None);
let rule: Box<dyn Rule> = Box::new(MD046CodeBlockStyle::new(CodeBlockStyle::Consistent));
assert_eq!(
rule.check(&ctx).unwrap().len(),
0,
"Consistent + lone list-internal indented block must be self-consistent (zero warnings)"
);
let after = fmt_through_coordinator(content, vec![rule]);
assert_eq!(
after, content,
"Consistent + lone indented block must leave the document unchanged"
);
let fenced_rule: Box<dyn Rule> = Box::new(MD046CodeBlockStyle::new(CodeBlockStyle::Fenced));
let after_fenced = fmt_through_coordinator(content, vec![fenced_rule]);
assert!(
after_fenced.contains("```"),
"Explicit Fenced must convert the indented block. Got:\n{after_fenced}"
);
}
#[test]
fn md046_full_rule_set_does_not_warn_without_fixing_under_default_config() {
use rumdl_lib::lint_context::LintContext;
let before = "- A list item\n\n indented code block here\n more code\n";
let config = Config::default();
let rules = all_rules(&config);
let after = fmt_through_coordinator(before, rules);
let ctx_after = LintContext::new(&after, MarkdownFlavor::Standard, None);
let md046 = MD046CodeBlockStyle::new(CodeBlockStyle::Consistent);
let warnings = md046.check(&ctx_after).unwrap();
assert_eq!(
warnings.len(),
0,
"After fmt, MD046 must report zero warnings on the result. Output:\n{after}\n\
Warnings: {warnings:?}"
);
}