use std::fs;
use std::process::Command;
#[test]
fn test_init_command_creates_and_loads_config() {
let temp_dir = tempfile::tempdir().unwrap();
let base_path = temp_dir.path();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["init"])
.output()
.unwrap();
assert!(output.status.success(), "Init command failed");
assert!(base_path.join(".rumdl.toml").exists(), "Config file not created");
fs::write(
base_path.join("test.md"),
"# Heading level 1\n### Heading level 3 (skipping level 2)\n",
)
.unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "test.md"])
.output()
.unwrap();
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
let combined_output = format!("{stdout}\n{stderr}");
assert!(
combined_output.contains("MD022") || combined_output.contains("MD041"),
"Should detect at least one of: MD022 (blanks around headings) or MD041 (first line heading)"
);
}
#[test]
fn test_utilities_via_complex_markdown() {
let temp_dir = tempfile::tempdir().unwrap();
let base_path = temp_dir.path();
let complex_md = r#"
# Heading 1
## Heading 2
Text with *emphasis* and **strong emphasis**.
### Heading 3
- List item 1
- Nested item 1.1
- Deeply nested 1.1.1
- Nested item 1.2
- List item 2
- Nested item 2.1
- Deeply nested 2.1.1
1. Ordered item 1
1. Nested ordered 1.1
1. Deeply nested 1.1.1
2. Nested ordered 1.2
2. Ordered item 2
> Blockquote text
> multiple lines
>
> With blank line
```js
console.log('Code block');
```
Text with <span>inline HTML</span>.

[Empty link]()
This line is very long and exceeds the usual line length limits by a significant margin which should trigger line length rules.
Indented code block instead of fenced code block that should be detected by the code block utilities.
Heading level jump coming:
# Heading 1
#### Heading 4 (skipping level 2 and 3)
___
"#;
fs::write(base_path.join("complex.md"), complex_md).unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "complex.md", "--verbose"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined_output = format!("{stdout}\n{stderr}");
assert!(
combined_output.contains("MD001")
|| combined_output.contains("MD013")
|| combined_output.contains("MD042")
|| combined_output.contains("MD045")
|| combined_output.contains("MD046"),
"Should detect at least one of: MD001 (heading jump), MD013 (line length), MD042 (empty link), MD045 (no alt text), or MD046 (code block style)"
);
let _fix_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "complex.md", "--fix"])
.output()
.unwrap();
let fixed_content = fs::read_to_string(base_path.join("complex.md")).unwrap();
assert!(complex_md != fixed_content, "File should be modified by fix operation");
}
#[test]
fn test_multiple_rule_groups() {
let temp_dir = tempfile::tempdir().unwrap();
let base_path = temp_dir.path();
let test_content = r#"
# Heading 1
# Heading 1 Duplicate
## heading 2 (inconsistent style)
- First list item
- Second item with extra spaces
- Third list item with trailing whitespace
- Fourth item with hard tab
Unnecessarily long line that exceeds the default line length limit and should trigger a warning if enabled.
Multiple blank lines above this one.
"#;
fs::write(base_path.join("test.md"), test_content).unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "test.md", "--verbose"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined_output = format!("{stdout}\n{stderr}");
assert!(
combined_output.contains("MD003")
|| combined_output.contains("MD009")
|| combined_output.contains("MD010")
|| combined_output.contains("MD012")
|| combined_output.contains("MD013")
|| combined_output.contains("MD024")
|| combined_output.contains("MD030"),
"Should detect at least one of: MD003 (heading style), MD009 (trailing spaces), MD010 (tabs), MD012 (multiple blanks), MD013 (line length), MD024 (duplicate heading), or MD030 (list marker spaces)"
);
}
#[test]
fn test_emphasis_and_heading_rules() {
let temp_dir = tempfile::tempdir().unwrap();
let base_path = temp_dir.path();
let test_content = r#"
*This is an emphasized line that should be detected as a heading by MD036*
**Another emphasized line that should be detected**
# Indented heading (MD023)
## Missing blank line above (MD022)
Text immediately below heading (MD022)
# First Heading Level 1
### Heading Level 3 (Skipping level 2, MD001)
**This is not a heading, because it's not on a line by itself**
"#;
fs::write(base_path.join("test.md"), test_content).unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "test.md", "--verbose"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined_output = format!("{stdout}\n{stderr}");
assert!(
combined_output.contains("MD001")
|| combined_output.contains("MD022")
|| combined_output.contains("MD023")
|| combined_output.contains("MD036"),
"Should detect at least one of: MD001 (heading increment), MD022 (blanks around headings), MD023 (heading start left), or MD036 (emphasis as heading)"
);
let fix_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "test.md", "--fix", "--verbose"])
.output()
.unwrap();
println!("Fix command stdout: {}", String::from_utf8_lossy(&fix_output.stdout));
println!("Fix command stderr: {}", String::from_utf8_lossy(&fix_output.stderr));
let fixed_content = fs::read_to_string(base_path.join("test.md")).unwrap();
println!("Original content:\n{test_content}");
println!("Fixed content:\n{fixed_content}");
assert!(
fixed_content != test_content,
"File should be modified by fix operation"
);
}
#[test]
fn test_url_and_link_rules() {
let temp_dir = tempfile::tempdir().unwrap();
let base_path = temp_dir.path();
let test_content = r#"
# Links and URLs Test
Bare URL: https://example.com
[Link with space at end](https://example.com )
[Empty link]()

[Undefined reference][undefined]
Visit http://example.com for more information.
"#;
fs::write(base_path.join("test.md"), test_content).unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "test.md", "--verbose"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined_output = format!("{stdout}\n{stderr}");
println!("Check command output:\n{combined_output}");
assert!(
combined_output.contains("MD034")
|| combined_output.contains("MD039")
|| combined_output.contains("MD042")
|| combined_output.contains("MD045")
|| combined_output.contains("MD052"),
"Should detect at least one of: MD034 (bare URL), MD039 (space in links), MD042 (empty link), MD045 (no alt text), or MD052 (undefined reference)"
);
let fix_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "test.md", "--fix", "--verbose"])
.output()
.unwrap();
println!("Fix command stdout: {}", String::from_utf8_lossy(&fix_output.stdout));
println!("Fix command stderr: {}", String::from_utf8_lossy(&fix_output.stderr));
let fixed_content = fs::read_to_string(base_path.join("test.md")).unwrap();
println!("Original content:\n{test_content}");
println!("Fixed content:\n{fixed_content}");
assert!(
fixed_content != test_content,
"File should be modified by fix operation"
);
}
#[test]
fn test_profiling_features() {
let temp_dir = tempfile::tempdir().unwrap();
let base_path = temp_dir.path();
fs::write(base_path.join("test.md"), "# Test Heading\n\nSimple content.\n").unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "test.md", "--verbose"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Rules:") || stdout.contains("MD") || stdout.contains("Success:"),
"Should show rules or summary information"
);
}
#[test]
fn test_low_coverage_rules() {
let temp_dir = tempfile::tempdir().unwrap();
let base_path = temp_dir.path();
let test_content = r#"
# Heading with Trailing Punctuation!
> This is a blockquote
> with multiple lines.
> This is another blockquote
>
> with a blank line.
- List item 1
- List item 2 with extra space after marker
- List item 3
1. Ordered item 1
1. Ordered item 2 (not using incremental numbers)
1. Ordered item 3
| Column 1 | Column 2 |
|-|-|
| Row 1 | Data 1 |
| Row 2 | Data 2 |
| Row 3 | Data 3 With extra column | Extra |
"#;
fs::write(base_path.join("test.md"), test_content).unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "test.md", "--verbose"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined_output = format!("{stdout}\n{stderr}");
assert!(
combined_output.contains("MD026")
|| combined_output.contains("MD028")
|| combined_output.contains("MD029")
|| combined_output.contains("MD030")
|| combined_output.contains("MD056"),
"Should detect at least one of: MD026 (trailing punctuation), MD028 (blank in blockquote), MD029 (ordered list prefix), MD030 (list marker space), or MD056 (table columns)"
);
let _fix_output = Command::new(env!("CARGO_BIN_EXE_rumdl"))
.current_dir(base_path)
.args(["check", "test.md", "--fix", "--verbose"])
.output()
.unwrap();
let fixed_content = fs::read_to_string(base_path.join("test.md")).unwrap();
assert!(
fixed_content != test_content,
"File should be modified by fix operation"
);
}