use rumdl_lib::lint_context::LintContext;
use rumdl_lib::rule::Rule;
use rumdl_lib::rules::MD036NoEmphasisAsHeading;
use rumdl_lib::rules::md036_no_emphasis_only_first::{HeadingStyle, MD036Config};
#[test]
fn test_md036_for_emphasis_only_lines() {
let content = "Normal text\n\n**This should be a heading**\n\nMore text";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md036 = MD036NoEmphasisAsHeading::new(".,;:!?".to_string());
let warnings = md036.check(&ctx).unwrap();
assert_eq!(warnings.len(), 1, "MD036 should detect emphasis-only line");
assert!(warnings[0].message.contains("This should be a heading"));
assert!(
warnings[0].fix.is_none(),
"MD036 should not provide automatic fixes by default"
);
let fixed_md036 = md036.fix(&ctx).unwrap();
assert_eq!(fixed_md036, content, "Content should remain unchanged");
assert!(fixed_md036.contains("**This should be a heading**"));
assert!(!fixed_md036.contains("## This should be a heading"));
}
#[test]
fn test_md036_with_fix_enabled() {
let content = "Normal text\n\n**This should be a heading**\n\nMore text";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md036 = MD036NoEmphasisAsHeading::new_with_fix(
".,;:!?".to_string(),
true, HeadingStyle::Atx,
2, );
let warnings = md036.check(&ctx).unwrap();
assert_eq!(warnings.len(), 1, "MD036 should detect emphasis-only line");
assert!(warnings[0].message.contains("This should be a heading"));
assert!(
warnings[0].fix.is_some(),
"MD036 should provide automatic fixes when enabled"
);
let fix = warnings[0].fix.as_ref().unwrap();
assert!(
fix.replacement.contains("## This should be a heading"),
"Fix should convert to ATX heading"
);
let fixed_md036 = md036.fix(&ctx).unwrap();
assert!(
fixed_md036.contains("## This should be a heading"),
"Emphasis should be converted to heading"
);
assert!(
!fixed_md036.contains("**This should be a heading**"),
"Original emphasis should be removed"
);
}
#[test]
fn test_md036_fix_with_different_heading_levels() {
let content = "**Title**";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md036_l1 = MD036NoEmphasisAsHeading::new_with_fix(".,;:!?".to_string(), true, HeadingStyle::Atx, 1);
let fixed_l1 = md036_l1.fix(&ctx).unwrap();
assert_eq!(fixed_l1, "# Title");
let md036_l3 = MD036NoEmphasisAsHeading::new_with_fix(".,;:!?".to_string(), true, HeadingStyle::Atx, 3);
let fixed_l3 = md036_l3.fix(&ctx).unwrap();
assert_eq!(fixed_l3, "### Title");
let md036_l6 = MD036NoEmphasisAsHeading::new_with_fix(".,;:!?".to_string(), true, HeadingStyle::Atx, 6);
let fixed_l6 = md036_l6.fix(&ctx).unwrap();
assert_eq!(fixed_l6, "###### Title");
}
#[test]
fn test_md036_fix_is_idempotent() {
let content = "**Section Title**\n\nBody text.";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md036 = MD036NoEmphasisAsHeading::new_with_fix(".,;:!?".to_string(), true, HeadingStyle::Atx, 2);
let fixed1 = md036.fix(&ctx).unwrap();
assert_eq!(fixed1, "## Section Title\n\nBody text.");
let ctx2 = LintContext::new(&fixed1, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed2 = md036.fix(&ctx2).unwrap();
assert_eq!(fixed2, fixed1, "Fix should be idempotent");
}
#[test]
fn test_md036_invalid_heading_level_defaults_to_2() {
let content = "**Title**";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md036_l0 = MD036NoEmphasisAsHeading::new_with_fix(".,;:!?".to_string(), true, HeadingStyle::Atx, 0);
let fixed_l0 = md036_l0.fix(&ctx).unwrap();
assert_eq!(fixed_l0, "## Title", "Level 0 should default to level 2");
let md036_l7 = MD036NoEmphasisAsHeading::new_with_fix(".,;:!?".to_string(), true, HeadingStyle::Atx, 7);
let fixed_l7 = md036_l7.fix(&ctx).unwrap();
assert_eq!(fixed_l7, "## Title", "Level 7 should default to level 2");
let md036_l255 = MD036NoEmphasisAsHeading::new_with_fix(".,;:!?".to_string(), true, HeadingStyle::Atx, 255);
let fixed_l255 = md036_l255.fix(&ctx).unwrap();
assert_eq!(fixed_l255, "## Title", "Level 255 should default to level 2");
}
#[test]
fn test_md036_config_serde_validation() {
for level in 1..=6 {
let toml_str = format!("heading-level = {level}");
let config: Result<MD036Config, _> = toml::from_str(&toml_str);
assert!(
config.is_ok(),
"Level {level} should be valid, got error: {:?}",
config.err()
);
assert_eq!(config.unwrap().heading_level.get(), level);
}
for level in [0, 7, 10, 100, 255] {
let toml_str = format!("heading-level = {level}");
let config: Result<MD036Config, _> = toml::from_str(&toml_str);
assert!(
config.is_err(),
"Level {level} should be rejected during deserialization"
);
let err = config.unwrap_err().to_string();
assert!(
err.contains("must be between 1 and 6"),
"Error for level {level} should mention valid range, got: {err}"
);
}
}