use anyhow::Result;
use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
use tempfile::TempDir;
use wsb::{Args, RenameEngine};
use wsb::refac::cli::{OutputFormat, ProgressMode};
mod test_utils {
use super::*;
pub fn create_test_files_for_diff(root: &Path) -> Result<()> {
File::create(root.join("simple_test.txt"))?
.write_all(b"Line 1 with no changes\nLine 2 with old pattern\nLine 3 with no changes\nLine 4 with old content\nLine 5 final line")?;
File::create(root.join("multi_occurrence.txt"))?
.write_all(b"First old pattern here\nNormal line\nSecond old pattern here\nAnother normal line\nThird old pattern at end")?;
File::create(root.join("old_filename.txt"))?
.write_all(b"This file has no content changes\nJust the filename will change")?;
File::create(root.join("old_both.txt"))?
.write_all(b"This file has old pattern in content\nAnd the filename also changes")?;
Ok(())
}
}
#[test]
fn test_diff_preview_basic_functionality() -> Result<()> {
let temp_dir = TempDir::new()?;
let root = temp_dir.path();
test_utils::create_test_files_for_diff(root)?;
let args = Args {
root_dir: root.to_path_buf(),
pattern: Some("old".to_string()),
substitute: Some("new".to_string()),
patterns_file: None,
format: OutputFormat::Human,
assume_yes: false, verbose: true,
progress: ProgressMode::Never, threads: 1,
max_depth: 0,
files_only: false,
dirs_only: false,
names_only: false,
content_only: false,
include_patterns: vec![],
exclude_patterns: vec![],
ignore_case: false,
use_regex: false,
follow_symlinks: false,
include_hidden: false,
backup: false,
binary_names: false,
};
let engine = RenameEngine::new(args)?;
let simple_content = fs::read_to_string(root.join("simple_test.txt"))?;
assert!(simple_content.contains("old pattern"));
assert!(simple_content.contains("old content"));
let multi_content = fs::read_to_string(root.join("multi_occurrence.txt"))?;
assert_eq!(multi_content.matches("old pattern").count(), 3);
let both_content = fs::read_to_string(root.join("old_both.txt"))?;
assert!(both_content.contains("old pattern"));
Ok(())
}
#[test]
fn test_diff_preview_line_counting() -> Result<()> {
let temp_dir = TempDir::new()?;
let root = temp_dir.path();
let test_content = "Line 1: Normal content\nLine 2: Contains old pattern here\nLine 3: More normal content\nLine 4: Another old pattern\nLine 5: Final content";
File::create(root.join("line_test.txt"))?
.write_all(test_content.as_bytes())?;
let args = Args {
root_dir: root.to_path_buf(),
pattern: Some("old pattern".to_string()),
substitute: Some("new pattern".to_string()),
patterns_file: None,
format: OutputFormat::Human,
assume_yes: false,
verbose: true,
progress: ProgressMode::Never,
threads: 1,
max_depth: 0,
files_only: false,
dirs_only: false,
names_only: false,
content_only: true, include_patterns: vec![],
exclude_patterns: vec![],
ignore_case: false,
use_regex: false,
follow_symlinks: false,
include_hidden: false,
backup: false,
binary_names: false,
};
let engine = RenameEngine::new(args)?;
let content = fs::read_to_string(root.join("line_test.txt"))?;
assert_eq!(content.matches("old pattern").count(), 2);
let lines: Vec<&str> = content.lines().collect();
assert_eq!(lines.len(), 5);
assert!(lines[1].contains("old pattern"));
assert!(lines[3].contains("old pattern"));
Ok(())
}
#[test]
fn test_diff_preview_with_regex_patterns() -> Result<()> {
let temp_dir = TempDir::new()?;
let root = temp_dir.path();
File::create(root.join("regex_test.txt"))?
.write_all(b"Version 1.2.3 is old\nVersion 4.5.6 is current\nVersion 7.8.9 will be new")?;
let args = Args {
root_dir: root.to_path_buf(),
pattern: Some(r"\d+\.\d+\.\d+".to_string()),
substitute: Some("X.Y.Z".to_string()),
patterns_file: None,
files_only: false,
dirs_only: false,
names_only: false,
content_only: true,
format: OutputFormat::Human,
assume_yes: false,
verbose: true,
progress: ProgressMode::Never,
threads: 1,
max_depth: 0,
include_patterns: vec![],
exclude_patterns: vec![],
ignore_case: false,
use_regex: true, follow_symlinks: false,
include_hidden: false,
backup: false,
binary_names: false,
};
let engine = RenameEngine::new(args)?;
let content = fs::read_to_string(root.join("regex_test.txt"))?;
let regex = regex::Regex::new(r"\d+\.\d+\.\d+")?;
let matches: Vec<_> = regex.find_iter(&content).collect();
assert_eq!(matches.len(), 3);
Ok(())
}
#[test]
fn test_diff_preview_no_changes_scenario() -> Result<()> {
let temp_dir = TempDir::new()?;
let root = temp_dir.path();
File::create(root.join("no_match.txt"))?
.write_all(b"This file contains no matching patterns\nNothing to change here\nAll content stays the same")?;
let args = Args {
root_dir: root.to_path_buf(),
pattern: Some("nonexistent".to_string()),
substitute: Some("replacement".to_string()),
patterns_file: None,
files_only: false,
dirs_only: false,
names_only: false,
content_only: false,
format: OutputFormat::Human,
assume_yes: false,
verbose: true,
progress: ProgressMode::Never,
threads: 1,
max_depth: 0,
include_patterns: vec![],
exclude_patterns: vec![],
ignore_case: false,
use_regex: false,
follow_symlinks: false,
include_hidden: false,
backup: false,
binary_names: false,
};
let engine = RenameEngine::new(args)?;
let content = fs::read_to_string(root.join("no_match.txt"))?;
assert_eq!(content.matches("nonexistent").count(), 0);
Ok(())
}
#[test]
fn test_diff_preview_context_lines() -> Result<()> {
let temp_dir = TempDir::new()?;
let root = temp_dir.path();
let lines = vec![
"Context line 1",
"Context line 2",
"Line with old pattern", "Context line 4",
"Context line 5",
"Context line 6",
"Another old pattern line", "Context line 8",
"Context line 9",
];
let content = lines.join("\n");
File::create(root.join("context_test.txt"))?
.write_all(content.as_bytes())?;
let args = Args {
root_dir: root.to_path_buf(),
pattern: Some("old pattern".to_string()),
substitute: Some("new pattern".to_string()),
patterns_file: None,
files_only: false,
dirs_only: false,
names_only: false,
content_only: true,
format: OutputFormat::Human,
assume_yes: false,
verbose: true,
progress: ProgressMode::Never,
threads: 1,
max_depth: 0,
include_patterns: vec![],
exclude_patterns: vec![],
ignore_case: false,
use_regex: false,
follow_symlinks: false,
include_hidden: false,
backup: false,
binary_names: false,
};
let engine = RenameEngine::new(args)?;
let file_content = fs::read_to_string(root.join("context_test.txt"))?;
let file_lines: Vec<&str> = file_content.lines().collect();
assert_eq!(file_lines.len(), 9);
assert!(file_lines[2].contains("old pattern")); assert!(file_lines[6].contains("old pattern"));
assert_eq!(file_lines[0], "Context line 1"); assert_eq!(file_lines[1], "Context line 2"); assert_eq!(file_lines[3], "Context line 4"); assert_eq!(file_lines[4], "Context line 5");
Ok(())
}
#[test]
fn test_diff_preview_multiple_files_limit() -> Result<()> {
let temp_dir = TempDir::new()?;
let root = temp_dir.path();
for i in 1..=7 {
File::create(root.join(format!("test_file_{}.txt", i)))?
.write_all(format!("File {} with old pattern content", i).as_bytes())?;
}
let args = Args {
root_dir: root.to_path_buf(),
pattern: Some("old pattern".to_string()),
substitute: Some("new pattern".to_string()),
patterns_file: None,
files_only: false,
dirs_only: false,
names_only: false,
content_only: true,
format: OutputFormat::Human,
assume_yes: false,
verbose: true,
progress: ProgressMode::Never,
threads: 1,
max_depth: 0,
include_patterns: vec![],
exclude_patterns: vec![],
ignore_case: false,
use_regex: false,
follow_symlinks: false,
include_hidden: false,
backup: false,
binary_names: false,
};
let engine = RenameEngine::new(args)?;
for i in 1..=7 {
let content = fs::read_to_string(root.join(format!("test_file_{}.txt", i)))?;
assert!(content.contains("old pattern"));
}
Ok(())
}