use snippy::extractor::markdown::MarkdownExtractor;
use snippy::extractor::{BlockType, Extractor};
use tracing::debug;
#[tokio::test]
async fn test_markdown_extractor_simple_filename_as_comment() {
let extractor = MarkdownExtractor::new();
let content = r#"
```rust
// filename: path/to/file.rs
fn main() {
println!("Hello, world!");
}
```
"#;
let expected_content = r#"fn main() {
println!("Hello, world!");
}
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 block, got {}", blocks.len());
assert_eq!(blocks[0].filename, "path/to/file.rs", "Filename mismatch");
assert_eq!(
blocks[0].block_type,
BlockType::FullContent,
"Block type mismatch"
);
assert!(
!blocks[0].content.contains("path/to/file.rs"),
"Content should not contain the filename"
);
assert_eq!(blocks[0].content, expected_content);
debug!("Test passed for MarkdownExtractor simple filename as comment.");
}
#[tokio::test]
async fn test_markdown_extractor_simple_filename_as_heading() {
let extractor = MarkdownExtractor::new();
let content = r#"
### `path/to/file.rs`
```rust
fn main() {
println!("Hello, world!");
}
```
"#;
let expected_content = r#"fn main() {
println!("Hello, world!");
}
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 block, got {}", blocks.len());
assert_eq!(blocks[0].filename, "path/to/file.rs", "Filename mismatch");
assert_eq!(
blocks[0].block_type,
BlockType::FullContent,
"Block type mismatch"
);
assert!(
!blocks[0].content.contains("path/to/file.rs"),
"Content should not contain the filename"
);
assert_eq!(blocks[0].content, expected_content);
debug!("Test passed for MarkdownExtractor simple filename as heading.");
}
#[tokio::test]
async fn test_markdown_extractor_simple_diff_block() {
let extractor = MarkdownExtractor::new();
let content = r#"
```diff
--- a/test.rs
+++ b/test.rs
@@ -1,3 +1,3 @@
-println!("Hello, world!");
+println!("Hello, Rust!");
```
"#;
let expected_content = r#"--- a/test.rs
+++ b/test.rs
@@ -1,3 +1,3 @@
-println!("Hello, world!");
+println!("Hello, Rust!");
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 block, got {}", blocks.len());
assert_eq!(blocks[0].filename, "test.rs", "Filename mismatch");
assert_eq!(
blocks[0].block_type,
BlockType::UnifiedDiff,
"Block type mismatch"
);
assert_eq!(blocks[0].content, expected_content);
debug!("Test passed for MarkdownExtractor simple diff block.");
}
#[tokio::test]
async fn test_markdown_extractor_multiple_blocks() {
let extractor = MarkdownExtractor::new();
let content = r#"
```rust
// filename: test1.rs
fn main() {
println!("Hello, world 1!");
}
```
```rust
// filename: test2.rs
fn main() {
println!("Hello, world 2!");
}
```
```diff
--- a/test.rs
+++ b/test.rs
@@ -1,3 +1,3 @@
-println!("Hello, world!");
+println!("Hello, Rust!");
```
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 3, "Expected 3 blocks, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "test1.rs",
"Filename mismatch for block 0"
);
assert_eq!(
blocks[0].block_type,
BlockType::FullContent,
"Block type mismatch for block 0"
);
assert_eq!(
blocks[1].filename, "test2.rs",
"Filename mismatch for block 1"
);
assert_eq!(
blocks[1].block_type,
BlockType::FullContent,
"Block type mismatch for block 1"
);
assert_eq!(
blocks[2].filename, "test.rs",
"Filename mismatch for block 2"
);
assert_eq!(
blocks[2].block_type,
BlockType::UnifiedDiff,
"Block type mismatch for block 2"
);
debug!("Test passed for MarkdownExtractor multiple blocks extraction.");
}
#[tokio::test]
async fn test_markdown_extractor_no_filename() {
let extractor = MarkdownExtractor::new();
let content = r#"
```rust
fn main() {
println!("Hello, world!");
}
```
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 0, "Expected 0 blocks, got {}", blocks.len());
debug!("Test passed for MarkdownExtractor no filename extraction.");
}
#[tokio::test]
async fn test_markdown_extractor_with_backticks_in_content() {
let extractor = MarkdownExtractor::new();
let content = r#"
```rust
// filename: test_backticks.rs
fn main() {
println!("Hello, world!");
println!("This is a code block with backticks: ```code```");
}
```
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 block, got {}", blocks.len());
assert_eq!(blocks[0].filename, "test_backticks.rs", "Filename mismatch");
assert_eq!(
blocks[0].block_type,
BlockType::FullContent,
"Block type mismatch"
);
assert!(
blocks[0].content.contains("```code```"),
"Content missing '```code```'"
);
debug!("Test passed for MarkdownExtractor with backticks in content.");
}
#[tokio::test]
async fn test_markdown_extractor_mixed_blocks_with_backticks() {
let extractor = MarkdownExtractor::new();
let content = r#"
```rust
// filename: test1.rs
fn main() {
println!("Hello, world!");
}
```
```diff
--- a/test.rs
+++ b/test.rs
@@ -1,3 +1,3 @@
-println!("Hello, world!");
+println!("Hello, Rust!");
// Comment with backticks: ```comment```
```
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 2, "Expected 2 blocks, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "test1.rs",
"Filename mismatch for block 0"
);
assert_eq!(
blocks[0].block_type,
BlockType::FullContent,
"Block type mismatch for block 0"
);
assert_eq!(
blocks[1].filename, "test.rs",
"Filename mismatch for block 1"
);
assert_eq!(
blocks[1].block_type,
BlockType::UnifiedDiff,
"Block type mismatch for block 1"
);
assert!(
blocks[1].content.contains("```comment```"),
"Content missing '```comment```'"
);
debug!("Test passed for MarkdownExtractor mixed blocks with backticks.");
}
#[tokio::test]
async fn test_markdown_extractor_blocks_with_different_languages() {
let extractor = MarkdownExtractor::new();
let content = r#"
```rust
// filename: test_rust.rs
fn main() {
println!("Hello, Rust!");
}
```
```python
# filename: test_python.py
def main():
print("Hello, Python!")
```
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 2, "Expected 2 blocks, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "test_rust.rs",
"Filename mismatch for block 0"
);
assert_eq!(
blocks[0].block_type,
BlockType::FullContent,
"Block type mismatch for block 0"
);
assert_eq!(
blocks[1].filename, "test_python.py",
"Filename mismatch for block 1"
);
assert_eq!(
blocks[1].block_type,
BlockType::FullContent,
"Block type mismatch for block 1"
);
debug!("Test passed for MarkdownExtractor blocks with different languages.");
}
#[tokio::test]
async fn test_markdown_extractor_non_standard_file_extensions() {
let extractor = MarkdownExtractor::new();
let content = r#"
```rust
// filename: test.customext
fn main() {
println!("Hello, custom extension!");
}
```
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 block, got {}", blocks.len());
assert_eq!(blocks[0].filename, "test.customext", "Filename mismatch");
assert_eq!(
blocks[0].block_type,
BlockType::FullContent,
"Block type mismatch"
);
debug!("Test passed for MarkdownExtractor non-standard file extensions.");
}
#[tokio::test]
async fn test_search_replace_block_without_filename() {
let extractor = MarkdownExtractor::new();
let content = r#"
```replace
<<<<<<< SEARCH
old_function();
=======
new_function();
>>>>>>> REPLACE
```
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 0, "Expected 0 blocks, got {}", blocks.len());
debug!("Test passed for MarkdownExtractor search-replace block without filename.");
}
#[tokio::test]
async fn test_search_replace_block_with_filename_comment() {
let extractor = MarkdownExtractor::new();
let content = r#"
```replace
// filename: test_search_replace.rs
<<<<<<< SEARCH
old_function();
=======
new_function();
>>>>>>> REPLACE
```
"#;
let expected_content = r#"<<<<<<< SEARCH
old_function();
=======
new_function();
>>>>>>> REPLACE
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 block, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "test_search_replace.rs",
"Filename mismatch"
);
assert_eq!(
blocks[0].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch"
);
assert_eq!(blocks[0].content, expected_content);
debug!("Test passed for MarkdownExtractor search-replace block with filename comment.");
}
#[tokio::test]
async fn test_search_replace_block_with_filename_heading() {
let extractor = MarkdownExtractor::new();
let content = r#"
### `test_search_replace_heading.rs`
```replace
<<<<<<< SEARCH
old_function();
=======
new_function();
>>>>>>> REPLACE
```
"#;
let expected_content = r#"<<<<<<< SEARCH
old_function();
=======
new_function();
>>>>>>> REPLACE
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 block, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "test_search_replace_heading.rs",
"Filename mismatch"
);
assert_eq!(
blocks[0].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch"
);
assert_eq!(blocks[0].content, expected_content);
debug!("Test passed for MarkdownExtractor search-replace block with filename heading.");
}
#[tokio::test]
async fn test_search_replace_block_with_additional_delimiters() {
let extractor = MarkdownExtractor::new();
let content = r#"
### `test_additional_delimiters.rs`
```replace
<<<<<<< SEARCH
old_function();
=======
new_function();
>>>>>>> REPLACE
additional_code();
```
"#;
let expected_content = r#"<<<<<<< SEARCH
old_function();
=======
new_function();
>>>>>>> REPLACE
additional_code();
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 block, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "test_additional_delimiters.rs",
"Filename mismatch"
);
assert_eq!(
blocks[0].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch"
);
assert_eq!(blocks[0].content, expected_content);
debug!("Test passed for MarkdownExtractor search-replace block with additional delimiters.");
}
#[tokio::test]
async fn test_search_replace_multiple_blocks_in_one_code_block() {
let extractor = MarkdownExtractor::new();
let content = r#"
### `test_multiple_blocks.rs`
```replace
<<<<<<< SEARCH
old_function_1();
=======
new_function_1();
>>>>>>> REPLACE
<<<<<<< SEARCH
old_function_2();
=======
new_function_2();
>>>>>>> REPLACE
```
"#;
let expected_content = r#"<<<<<<< SEARCH
old_function_1();
=======
new_function_1();
>>>>>>> REPLACE
<<<<<<< SEARCH
old_function_2();
=======
new_function_2();
>>>>>>> REPLACE
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 block, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "test_multiple_blocks.rs",
"Filename mismatch"
);
assert_eq!(
blocks[0].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch"
);
assert_eq!(blocks[0].content, expected_content);
debug!("Test passed for MarkdownExtractor search-replace block with multiple blocks in one code block.");
}
#[tokio::test]
async fn test_search_replace_block_with_different_number_of_delimiters() {
let extractor = MarkdownExtractor::new();
let content = r#"
### `test_incorrect_syntax.rs`
```replace
<<<<< SEARCH
old_function();
====
new_function();
>>>> REPLACE
```
"#;
let expected_content = r#"<<<<< SEARCH
old_function();
====
new_function();
>>>> REPLACE
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 1, "Expected 1 blocks, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "test_incorrect_syntax.rs",
"Filename mismatch for block 0"
);
assert_eq!(
blocks[0].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch for block 0"
);
assert_eq!(blocks[0].content, expected_content);
}
#[tokio::test]
async fn test_mixed_search_replace_blocks_with_headings_and_comments() {
let extractor = MarkdownExtractor::new();
let content = r#"
### `path/tofile/first.rs`
```replace
// filename: first.rs
<<<<<<< SEARCH
fn first() {
println!("This is the first block");
}
=======
fn first_updated() {
println!("This is the first block updated");
}
>>>>>>> REPLACE
```
```replace
// filename: second.rs
<<<<<<< SEARCH
fn second() {
println!("This is the second block");
}
=======
fn second_updated() {
println!("This is the second block updated");
}
>>>>>>> REPLACE
```
### `test_no_filename.rs`
```replace
<<<<<<< SEARCH
fn no_filename() {
println!("No filename specified in comment or heading");
}
=======
fn no_filename_updated() {
println!("Updated block with no filename specified in comment or heading");
}
>>>>>>> REPLACE
```
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 3, "Expected 3 blocks, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "first.rs",
"Filename mismatch for block 0"
);
assert_eq!(
blocks[0].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch for block 0"
);
assert_eq!(
blocks[1].filename, "second.rs",
"Filename mismatch for block 1"
);
assert_eq!(
blocks[1].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch for block 1"
);
assert_eq!(
blocks[2].filename, "test_no_filename.rs",
"Filename mismatch for block 2"
);
assert_eq!(
blocks[2].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch for block 2"
);
debug!(
"Test passed for MarkdownExtractor mixed search-replace blocks with headings and comments."
);
}
#[tokio::test]
async fn test_mixed_search_replace_blocks_with_and_without_headings() {
let extractor = MarkdownExtractor::new();
let content = r#"
### `path/tofile/first.rs`
```replace
// filename: first.rs
<<<<<<< SEARCH
fn first() {
println!("This is the first block");
}
=======
fn first_updated() {
println!("This is the first block updated");
}
>>>>>>> REPLACE
```
```replace
// filename: second.rs
<<<<<<< SEARCH
fn second() {
println!("This is the second block");
}
=======
fn second_updated() {
println!("This is the second block updated");
}
>>>>>>> REPLACE
```
### `path/tofile/third.rs`
```replace
<<<<<<< SEARCH
fn third() {
println!("This is the third block");
}
=======
fn third_updated() {
println!("This is the third block updated");
}
>>>>>>> REPLACE
```
"#;
let blocks = extractor
.extract(content)
.unwrap_or_else(|e| panic!("Failed to extract content: {:?}", e));
assert_eq!(blocks.len(), 3, "Expected 3 blocks, got {}", blocks.len());
assert_eq!(
blocks[0].filename, "first.rs",
"Filename mismatch for block 0"
);
assert_eq!(
blocks[0].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch for block 0"
);
assert_eq!(
blocks[1].filename, "second.rs",
"Filename mismatch for block 1"
);
assert_eq!(
blocks[1].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch for block 1"
);
assert_eq!(
blocks[2].filename, "path/tofile/third.rs",
"Filename mismatch for block 2"
);
assert_eq!(
blocks[2].block_type,
BlockType::SearchReplaceBlock,
"Block type mismatch for block 2"
);
debug!(
"Test passed for MarkdownExtractor mixed search-replace blocks with and without headings."
);
}