use rumdl_lib::lint_context::LintContext;
use rumdl_lib::rule::Rule;
use rumdl_lib::rules::MD055TablePipeStyle;
#[test]
fn test_name() {
let rule = MD055TablePipeStyle::default();
assert_eq!(rule.name(), "MD055");
}
#[test]
fn test_consistent_pipe_styles() {
let rule = MD055TablePipeStyle::default();
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 0);
let content = r#"
Header 1 | Header 2
-------- | --------
Cell 1 | Cell 2
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 0);
}
#[test]
fn test_inconsistent_pipe_styles() {
let rule = MD055TablePipeStyle::default();
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
Cell 1 | Cell 2
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].line, 4);
assert!(result[0].message.contains("Table pipe style"));
}
#[test]
fn test_leading_and_trailing_style() {
let rule = MD055TablePipeStyle::new("leading_and_trailing".to_string());
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 0);
let content = r#"
Header 1 | Header 2
-------- | --------
Cell 1 | Cell 2
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 3); }
#[test]
fn test_no_leading_or_trailing_style() {
let rule = MD055TablePipeStyle::new("no_leading_or_trailing".to_string());
let content = r#"
Header 1 | Header 2
-------- | --------
Cell 1 | Cell 2
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 0);
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 3); }
#[test]
fn test_leading_only_style() {
let rule = MD055TablePipeStyle::new("leading_only".to_string());
let content = r#"
| Header 1 | Header 2
| -------- | --------
| Cell 1 | Cell 2
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
0,
"Content with leading pipes only should not trigger warnings with leading_only style"
);
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
3,
"Content with both leading and trailing pipes should be flagged when style is leading_only"
);
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed = rule.fix(&ctx).unwrap();
let fixed_ctx = LintContext::new(&fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&fixed_ctx).unwrap();
assert_eq!(result.len(), 0);
}
#[test]
fn test_trailing_only_style() {
let rule = MD055TablePipeStyle::new("trailing_only".to_string());
let content = r#"
Header 1 | Header 2 |
-------- | -------- |
Cell 1 | Cell 2 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
0,
"Content with trailing pipes only should not trigger warnings with trailing_only style"
);
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
3,
"Content with both leading and trailing pipes should be flagged when style is trailing_only"
);
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed = rule.fix(&ctx).unwrap();
let fixed_ctx = LintContext::new(&fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&fixed_ctx).unwrap();
assert_eq!(result.len(), 0);
}
#[test]
fn test_code_blocks_ignored() {
let rule = MD055TablePipeStyle::default();
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |
```markdown
| This is a table in a code block |
Header with inconsistent style | that should be ignored
```
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 0);
}
#[test]
fn test_fix() {
let rule = MD055TablePipeStyle::new("leading_and_trailing".to_string());
let content = r#"
Header 1 | Header 2
-------- | --------
Cell 1 | Cell 2
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed = rule.fix(&ctx).unwrap();
let fixed_ctx = LintContext::new(&fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&fixed_ctx).unwrap();
assert_eq!(result.len(), 0, "Fixed content should have no warnings");
let rule = MD055TablePipeStyle::new("no_leading_or_trailing".to_string());
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed = rule.fix(&ctx).unwrap();
let fixed_ctx = LintContext::new(&fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&fixed_ctx).unwrap();
assert_eq!(result.len(), 0, "Fixed content should have no warnings");
let rule = MD055TablePipeStyle::new("leading_only".to_string());
let content = r#"
Header 1 | Header 2 |
-------- | -------- |
Cell 1 | Cell 2 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed = rule.fix(&ctx).unwrap();
let fixed_ctx = LintContext::new(&fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&fixed_ctx).unwrap();
assert_eq!(result.len(), 0, "Fixed content should have no warnings");
let rule = MD055TablePipeStyle::new("trailing_only".to_string());
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed = rule.fix(&ctx).unwrap();
let fixed_ctx = LintContext::new(&fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&fixed_ctx).unwrap();
assert_eq!(result.len(), 0, "Fixed content should have no warnings");
}
#[test]
fn test_false_positives_not_flagged() {
let rule = MD055TablePipeStyle::default();
let content = r#"
# MD055 - Table pipe style
## Description
This rule is triggered when table rows in a Markdown file have inconsistent pipe styles.
In Markdown tables, you can include or omit leading and trailing pipe characters (`|`). This rule enforces a consistent style for these pipes.
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
0,
"Regular text mentioning pipe characters should not be flagged"
);
let content = r#"
The `style` parameter can have the following values:
- `consistent` (default): All table rows should use the same style as the first table row
- `leading*and*trailing`: All table rows must have both leading and trailing pipes (`| cell | cell |`)
- `leading*only`: All table rows must have leading pipes and no trailing pipes (`| cell | cell`)
- `trailing*only`: All table rows must have trailing pipes and no leading pipes (`cell | cell |`)
- `no*leading*or*trailing`: All table rows must have neither leading nor trailing pipes (`cell | cell`)
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
0,
"Bullet points with pipe characters should not be flagged"
);
let content = r#"
You can use pipes in inline code like `| cell | cell |` without issues.
Also backticks with pipes: ``| some | code |`` should be ignored.
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
0,
"Inline code with pipe characters should not be flagged"
);
let content = r#"
This is a line with | some | pipes | but no table structure.
Another line | with | pipes | that | doesn't | form | a | table.
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
0,
"Lines with pipes but no table structure should not be flagged"
);
let content = r#"
| This | looks | like | a | table | row |
But there's no delimiter row, so it's not a table.
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
0,
"Single line with pipes (no delimiter row) should not be flagged"
);
}
#[test]
fn test_actual_tables_are_still_flagged() {
let rule = MD055TablePipeStyle::default();
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
Cell 1 | Cell 2
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
1,
"Actual tables with inconsistent styles should still be flagged"
);
assert_eq!(result[0].line, 4, "The inconsistent row should be flagged");
}
#[test]
fn test_table_detection_requires_delimiter_row() {
let rule = MD055TablePipeStyle::default();
let content = r#"
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |
| Cell 3 | Cell 4 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 0, "Properly formatted table should not be flagged");
let content = r#"
| Header 1 | Header 2 |
| Cell 1 | Cell 2 |
| Cell 3 | Cell 4 |
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(
result.len(),
0,
"Lines without delimiter row should not be considered a table"
);
}
#[test]
fn test_mixed_content_with_tables() {
let rule = MD055TablePipeStyle::default();
let content = r#"
# Document with mixed content
This line mentions pipe characters (`|`) in regular text.
- Bullet point with pipes: `| cell | cell |`
- Another bullet: uses pipes (`|`) in description
Now here's an actual table:
| Header 1 | Header 2 |
| -------- | -------- |
Cell 1 | Cell 2
And more text with | pipes | that | aren't | tables.
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 1, "Only the actual table row should be flagged");
assert_eq!(result[0].line, 13, "The inconsistent table row should be flagged");
assert!(
result[0].message.contains("Table pipe style"),
"Should be a table pipe style warning"
);
}
#[test]
fn test_fix_does_not_corrupt_non_tables() {
let rule = MD055TablePipeStyle::new("leading_and_trailing".to_string());
let content = r#"
# MD055 - Table pipe style
In Markdown tables, you can include or omit leading and trailing pipe characters (`|`).
- `leading*and*trailing`: All table rows must have both leading and trailing pipes (`| cell | cell |`)
- `no*leading*or*trailing`: All table rows must have neither leading nor trailing pipes (`cell | cell`)
This line has | some | pipes | but | isn't | a | table.
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed = rule.fix(&ctx).unwrap();
assert_eq!(
content.trim(),
fixed.trim(),
"Non-table content should not be modified by fix"
);
let fixed_ctx = LintContext::new(&fixed, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&fixed_ctx).unwrap();
assert_eq!(result.len(), 0, "Fixed content should have no warnings");
}
#[test]
fn test_md055_continuation_table_preserves_indent() {
let rule = MD055TablePipeStyle::new("leading_and_trailing".to_string());
let content = "- Item\n h1 | h2\n ---|---\n d1 | d2";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed = rule.fix(&ctx).unwrap();
let lines: Vec<&str> = fixed.lines().collect();
assert_eq!(lines[0], "- Item", "List text unchanged");
for (idx, line) in lines.iter().enumerate().skip(1) {
assert!(
line.starts_with(" "),
"Line {idx} must preserve 2-space indent, got: {line:?}",
);
}
assert!(
lines[1].trim().starts_with('|') && lines[1].trim().ends_with('|'),
"Header should have leading/trailing pipes after fix, got: {:?}",
lines[1]
);
}
#[test]
fn test_md055_continuation_table_idempotent() {
let rule = MD055TablePipeStyle::new("leading_and_trailing".to_string());
let content = "- Item\n | h1 | h2 |\n |---|---|\n | d1 | d2 |";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed1 = rule.fix(&ctx).unwrap();
let ctx2 = LintContext::new(&fixed1, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed2 = rule.fix(&ctx2).unwrap();
assert_eq!(fixed1, fixed2, "MD055 fix must be idempotent for continuation tables");
}
#[test]
fn test_md055_nested_list_continuation_table_at_parent_level() {
let rule = MD055TablePipeStyle::new("leading_and_trailing".to_string());
let content = "- Parent\n - Child\n\n | h1 | h2 |\n |---|---|\n | d1 | d2 |";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed = rule.fix(&ctx).unwrap();
let lines: Vec<&str> = fixed.lines().collect();
assert!(
lines[3].starts_with(" "),
"Table header at parent level must keep indent, got: {:?}",
lines[3]
);
assert!(
lines[4].starts_with(" "),
"Table delimiter at parent level must keep indent, got: {:?}",
lines[4]
);
}