use rumdl_lib::lint_context::LintContext;
use rumdl_lib::rule::Rule;
use rumdl_lib::rules::{MD004UnorderedListStyle, MD005ListIndent, MD007ULIndent, MD029OrderedListPrefix};
#[test]
fn test_mixed_unordered_list_style_and_indentation() {
let md004 = MD004UnorderedListStyle::default(); let md005 = MD005ListIndent::default(); let md007 = MD007ULIndent::default();
let content = "\
* First item with asterisk
+ Second item with plus (MD004 violation)
* Wrong indent 1 space (MD005, MD007 violations)
* Third item back to asterisk";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md004_result = md004.check(&ctx).unwrap();
let md005_result = md005.check(&ctx).unwrap();
let md007_result = md007.check(&ctx).unwrap();
assert!(!md004_result.is_empty(), "MD004 should detect mixed markers");
assert!(!md005_result.is_empty(), "MD005 should detect wrong indentation");
assert!(!md007_result.is_empty(), "MD007 should detect wrong nested indentation");
let md004_fixed = md004.fix(&ctx).unwrap();
let ctx_after_md004 = LintContext::new(&md004_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md004_recheck = md004.check(&ctx_after_md004).unwrap();
assert!(md004_recheck.is_empty(), "MD004 issues should be fixed");
let md005_after_md004 = md005.check(&ctx_after_md004).unwrap();
let md007_after_md004 = md007.check(&ctx_after_md004).unwrap();
assert!(
!md005_after_md004.is_empty(),
"MD005 issues should remain after MD004 fix"
);
assert!(
!md007_after_md004.is_empty(),
"MD007 issues should remain after MD004 fix"
);
}
#[test]
fn test_ordered_list_style_and_indentation() {
let md029 = MD029OrderedListPrefix::default(); let md005 = MD005ListIndent::default();
let content = "\
1. First item
3. Wrong number (MD029 violation)
2. Wrong indent and number (MD005, MD029 violations)
4. Fourth item";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md029_result = md029.check(&ctx).unwrap();
let md005_result = md005.check(&ctx).unwrap();
assert!(!md029_result.is_empty(), "MD029 should detect wrong numbering");
assert!(!md005_result.is_empty(), "MD005 should detect wrong indentation");
let md029_fixed = md029.fix(&ctx).unwrap();
let ctx_after_md029 = LintContext::new(&md029_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md029_recheck = md029.check(&ctx_after_md029).unwrap();
assert!(md029_recheck.is_empty(), "MD029 issues should be fixed");
let md005_after_md029 = md005.check(&ctx_after_md029).unwrap();
assert!(
!md005_after_md029.is_empty(),
"MD005 issues should remain after MD029 fix"
);
}
#[test]
fn test_complex_nested_mixed_lists() {
let md004 = MD004UnorderedListStyle::default();
let md005 = MD005ListIndent::default();
let md007 = MD007ULIndent::default();
let md029 = MD029OrderedListPrefix::default();
let content = "\
* Unordered list item
1. Ordered nested item
2. Another ordered nested item
4. Wrong number in same list (MD029 violation)
+ Mixed marker style (MD004 violation)
* Bullet at 2 spaces
* Wrong indent 3 spaces (MD005 - inconsistent with sibling, MD007 violation)
* Back to proper unordered";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md004_result = md004.check(&ctx).unwrap();
let md005_result = md005.check(&ctx).unwrap();
let md007_result = md007.check(&ctx).unwrap();
let md029_result = md029.check(&ctx).unwrap();
assert!(!md004_result.is_empty(), "MD004 should detect mixed markers");
assert!(
!md005_result.is_empty(),
"MD005 should detect inconsistent bullet indentation"
);
assert!(!md007_result.is_empty(), "MD007 should detect wrong nested indentation");
assert!(!md029_result.is_empty(), "MD029 should detect wrong numbering");
let step1 = md004.fix(&ctx).unwrap();
let ctx1 = LintContext::new(&step1, rumdl_lib::config::MarkdownFlavor::Standard, None);
let step2 = md005.fix(&ctx1).unwrap();
let ctx2 = LintContext::new(&step2, rumdl_lib::config::MarkdownFlavor::Standard, None);
let step3 = md007.fix(&ctx2).unwrap();
let ctx3 = LintContext::new(&step3, rumdl_lib::config::MarkdownFlavor::Standard, None);
let step4 = md029.fix(&ctx3).unwrap();
let ctx_final = LintContext::new(&step4, rumdl_lib::config::MarkdownFlavor::Standard, None);
assert!(
md004.check(&ctx_final).unwrap().is_empty(),
"MD004 should pass after all fixes"
);
assert!(
md005.check(&ctx_final).unwrap().is_empty(),
"MD005 should pass after all fixes"
);
assert!(
md007.check(&ctx_final).unwrap().is_empty(),
"MD007 should pass after all fixes"
);
assert!(
md029.check(&ctx_final).unwrap().is_empty(),
"MD029 should pass after all fixes"
);
}
#[test]
fn test_deep_nesting_with_multiple_list_types() {
let md005 = MD005ListIndent::default();
let md007 = MD007ULIndent::default();
let md029 = MD029OrderedListPrefix::default();
let content = "\
1. Top level ordered
* Second level unordered
1. Third level ordered
* Fourth level unordered
1. Fifth level ordered (independent numbering)
* Back to second level
2. Second top level";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md005_result = md005.check(&ctx).unwrap();
let md007_result = md007.check(&ctx).unwrap();
let md029_result = md029.check(&ctx).unwrap();
assert!(md005_result.is_empty(), "MD005 should pass with proper indentation");
assert!(md007_result.is_empty(), "MD007 should pass with proper nesting");
assert!(
md029_result.is_empty(),
"MD029 should pass with correct sequential numbering"
);
}
#[test]
fn test_list_rules_with_code_blocks() {
let md005 = MD005ListIndent::default();
let md029 = MD029OrderedListPrefix::default();
let content = "\
1. First item with code:
```rust
fn example() {
// This indentation should not affect list rules
println!(\"Hello\");
}
```
2. Second item continues sequence
```python
def another():
pass # Wrong Python indentation, but shouldn't affect MD005
```
3. Third item";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md005_result = md005.check(&ctx).unwrap();
let md029_result = md029.check(&ctx).unwrap();
assert!(md005_result.is_empty(), "MD005 should ignore code block indentation");
assert!(
md029_result.is_empty(),
"MD029 should maintain sequence across code blocks"
);
}
#[test]
fn test_list_rules_with_blockquotes() {
let md005 = MD005ListIndent::default();
let md029 = MD029OrderedListPrefix::default();
let content = "\
> This is a blockquote with lists:
> 1. First quoted item
> 1. Nested quoted item
> 2. Another nested item
> 2. Second quoted item
>
> * Unordered list in quote
> * Nested unordered in quote";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md005_result = md005.check(&ctx).unwrap();
let md029_result = md029.check(&ctx).unwrap();
assert!(
md005_result.is_empty(),
"MD005 should handle blockquote lists correctly"
);
assert!(
md029_result.is_empty(),
"MD029 should handle blockquote lists correctly"
);
}
#[test]
fn test_list_continuation_across_rules() {
let md004 = MD004UnorderedListStyle::default();
let md005 = MD005ListIndent::default();
let content = "\
* First item
Some continuation text
+ Second item with wrong marker
More continuation text
* Nested item
Nested continuation";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md004_result = md004.check(&ctx).unwrap();
let md005_result = md005.check(&ctx).unwrap();
assert!(!md004_result.is_empty(), "MD004 should detect marker inconsistency");
assert!(
md005_result.is_empty(),
"MD005 should accept proper continuation indentation"
);
let md004_fixed = md004.fix(&ctx).unwrap();
let ctx_fixed = LintContext::new(&md004_fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md005_after_fix = md005.check(&ctx_fixed).unwrap();
assert!(md005_after_fix.is_empty(), "MD005 should still pass after MD004 fix");
assert!(
md004_fixed.contains("Some continuation text"),
"Continuation text should be preserved"
);
assert!(
md004_fixed.contains("More continuation text"),
"All continuation text should be preserved"
);
assert!(
md004_fixed.contains("Nested continuation"),
"Nested continuation should be preserved"
);
}
#[test]
fn test_empty_lines_between_list_items() {
let md004 = MD004UnorderedListStyle::default();
let md005 = MD005ListIndent::default();
let md029 = MD029OrderedListPrefix::default();
let content = "\
1. First item
2. Second item with empty line above
3. Third item
* Nested unordered with empty line above
* Another nested item
4. Fourth item";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let md004_result = md004.check(&ctx).unwrap();
let md005_result = md005.check(&ctx).unwrap();
let md029_result = md029.check(&ctx).unwrap();
assert!(md004_result.is_empty(), "MD004 should handle empty lines correctly");
assert!(md005_result.is_empty(), "MD005 should handle empty lines correctly");
assert!(
md029_result.is_empty(),
"MD029 should pass - nested list is part of item 3's content"
);
}
#[test]
#[ignore] fn test_performance_with_large_mixed_lists() {
let md004 = MD004UnorderedListStyle::default();
let md005 = MD005ListIndent::default();
let md007 = MD007ULIndent::default();
let md029 = MD029OrderedListPrefix::default();
let mut content = String::new();
for i in 1..=50 {
content.push_str(&format!("{i}. Ordered item {i}\n"));
if i % 3 == 0 {
let indent = if i < 10 { " " } else { " " };
content.push_str(&format!(
"{indent}This item has extended content with proper indentation.\n"
));
content.push_str(&format!("{indent}It includes multiple lines to test performance.\n"));
}
}
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let start = std::time::Instant::now();
let md004_result = md004.check(&ctx).unwrap();
let md005_result = md005.check(&ctx).unwrap();
let md007_result = md007.check(&ctx).unwrap();
let md029_result = md029.check(&ctx).unwrap();
let duration = start.elapsed();
assert!(
duration.as_millis() < 1000,
"List rules should be fast on large structures"
);
assert!(md004_result.is_empty(), "MD004 should pass on large correct list");
assert!(md005_result.is_empty(), "MD005 should pass on large correct list");
assert!(md007_result.is_empty(), "MD007 should pass on large correct list");
assert!(md029_result.is_empty(), "MD029 should pass on large correct list");
}