sal/text/tests/text_replacement_tests.rs
Mahmoud-Emad a7a7353aa1
Some checks failed
Rhai Tests / Run Rhai Tests (push) Has been cancelled
feat: Add sal-text crate
- Add a new crate `sal-text` for text manipulation utilities.
- Integrate `sal-text` into the main `sal` crate.
- Remove the previous `text` module from `sal`.  This improves
  organization and allows for independent development of the
  `sal-text` library.
2025-06-19 14:43:27 +03:00

302 lines
9.0 KiB
Rust

//! Unit tests for text replacement functionality
//!
//! These tests validate the TextReplacer and TextReplacerBuilder including:
//! - Literal string replacement
//! - Regex pattern replacement
//! - Multiple chained replacements
//! - File operations (read, write, in-place)
//! - Error handling and edge cases
use sal_text::{TextReplacer, TextReplacerBuilder};
use std::fs;
use tempfile::NamedTempFile;
#[test]
fn test_text_replacer_literal_single() {
let replacer = TextReplacer::builder()
.pattern("hello")
.replacement("hi")
.regex(false)
.build()
.expect("Failed to build replacer");
let result = replacer.replace("hello world, hello universe");
assert_eq!(result, "hi world, hi universe");
}
#[test]
fn test_text_replacer_regex_single() {
let replacer = TextReplacer::builder()
.pattern(r"\d+")
.replacement("NUMBER")
.regex(true)
.build()
.expect("Failed to build replacer");
let result = replacer.replace("There are 123 items and 456 more");
assert_eq!(result, "There are NUMBER items and NUMBER more");
}
#[test]
fn test_text_replacer_multiple_operations() {
let replacer = TextReplacer::builder()
.pattern(r"\d+")
.replacement("NUMBER")
.regex(true)
.and()
.pattern("world")
.replacement("universe")
.regex(false)
.build()
.expect("Failed to build replacer");
let result = replacer.replace("Hello world, there are 123 items");
assert_eq!(result, "Hello universe, there are NUMBER items");
}
#[test]
fn test_text_replacer_chained_operations() {
let replacer = TextReplacer::builder()
.pattern("cat")
.replacement("dog")
.regex(false)
.and()
.pattern("dog")
.replacement("animal")
.regex(false)
.build()
.expect("Failed to build replacer");
// Operations are applied in sequence, so "cat" -> "dog" -> "animal"
let result = replacer.replace("The cat sat on the mat");
assert_eq!(result, "The animal sat on the mat");
}
#[test]
fn test_text_replacer_regex_capture_groups() {
let replacer = TextReplacer::builder()
.pattern(r"(\d{4})-(\d{2})-(\d{2})")
.replacement("$3/$2/$1")
.regex(true)
.build()
.expect("Failed to build replacer");
let result = replacer.replace("Date: 2023-12-01");
assert_eq!(result, "Date: 01/12/2023");
}
#[test]
fn test_text_replacer_case_sensitive() {
let replacer = TextReplacer::builder()
.pattern("Hello")
.replacement("Hi")
.regex(false)
.build()
.expect("Failed to build replacer");
let result = replacer.replace("Hello world, hello universe");
assert_eq!(result, "Hi world, hello universe");
}
#[test]
fn test_text_replacer_regex_case_insensitive() {
let replacer = TextReplacer::builder()
.pattern(r"(?i)hello")
.replacement("Hi")
.regex(true)
.build()
.expect("Failed to build replacer");
let result = replacer.replace("Hello world, HELLO universe");
assert_eq!(result, "Hi world, Hi universe");
}
#[test]
fn test_text_replacer_empty_input() {
let replacer = TextReplacer::builder()
.pattern("test")
.replacement("replacement")
.regex(false)
.build()
.expect("Failed to build replacer");
let result = replacer.replace("");
assert_eq!(result, "");
}
#[test]
fn test_text_replacer_no_matches() {
let replacer = TextReplacer::builder()
.pattern("xyz")
.replacement("abc")
.regex(false)
.build()
.expect("Failed to build replacer");
let input = "Hello world";
let result = replacer.replace(input);
assert_eq!(result, input);
}
#[test]
fn test_text_replacer_file_operations() {
// Create a temporary file with test content
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
let test_content = "Hello world, there are 123 items";
fs::write(temp_file.path(), test_content).expect("Failed to write to temp file");
let replacer = TextReplacer::builder()
.pattern(r"\d+")
.replacement("NUMBER")
.regex(true)
.and()
.pattern("world")
.replacement("universe")
.regex(false)
.build()
.expect("Failed to build replacer");
// Test replace_file
let result = replacer.replace_file(temp_file.path()).expect("Failed to replace file content");
assert_eq!(result, "Hello universe, there are NUMBER items");
// Verify original file is unchanged
let original_content = fs::read_to_string(temp_file.path()).expect("Failed to read original file");
assert_eq!(original_content, test_content);
}
#[test]
fn test_text_replacer_file_in_place() {
// Create a temporary file with test content
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
let test_content = "Hello world, there are 123 items";
fs::write(temp_file.path(), test_content).expect("Failed to write to temp file");
let replacer = TextReplacer::builder()
.pattern("world")
.replacement("universe")
.regex(false)
.build()
.expect("Failed to build replacer");
// Test replace_file_in_place
replacer.replace_file_in_place(temp_file.path()).expect("Failed to replace file in place");
// Verify file content was changed
let new_content = fs::read_to_string(temp_file.path()).expect("Failed to read modified file");
assert_eq!(new_content, "Hello universe, there are 123 items");
}
#[test]
fn test_text_replacer_file_to_file() {
// Create source file
let mut source_file = NamedTempFile::new().expect("Failed to create source file");
let test_content = "Hello world, there are 123 items";
fs::write(source_file.path(), test_content).expect("Failed to write to source file");
// Create destination file
let dest_file = NamedTempFile::new().expect("Failed to create dest file");
let replacer = TextReplacer::builder()
.pattern(r"\d+")
.replacement("NUMBER")
.regex(true)
.build()
.expect("Failed to build replacer");
// Test replace_file_to
replacer.replace_file_to(source_file.path(), dest_file.path())
.expect("Failed to replace file to destination");
// Verify source file is unchanged
let source_content = fs::read_to_string(source_file.path()).expect("Failed to read source file");
assert_eq!(source_content, test_content);
// Verify destination file has replaced content
let dest_content = fs::read_to_string(dest_file.path()).expect("Failed to read dest file");
assert_eq!(dest_content, "Hello world, there are NUMBER items");
}
#[test]
fn test_text_replacer_invalid_regex() {
let result = TextReplacer::builder()
.pattern("[invalid regex")
.replacement("test")
.regex(true)
.build();
assert!(result.is_err());
}
#[test]
fn test_text_replacer_builder_default_regex_false() {
let replacer = TextReplacer::builder()
.pattern(r"\d+")
.replacement("NUMBER")
.build()
.expect("Failed to build replacer");
// Should treat as literal since regex defaults to false
let result = replacer.replace(r"Match \d+ pattern");
assert_eq!(result, "Match NUMBER pattern");
}
#[test]
fn test_text_replacer_complex_regex() {
let replacer = TextReplacer::builder()
.pattern(r"(\w+)@(\w+\.\w+)")
.replacement("EMAIL_ADDRESS")
.regex(true)
.build()
.expect("Failed to build replacer");
let result = replacer.replace("Contact john@example.com or jane@test.org");
assert_eq!(result, "Contact EMAIL_ADDRESS or EMAIL_ADDRESS");
}
#[test]
fn test_text_replacer_multiline_text() {
let replacer = TextReplacer::builder()
.pattern(r"^\s*//.*$")
.replacement("")
.regex(true)
.build()
.expect("Failed to build replacer");
let input = "function test() {\n // This is a comment\n return true;\n // Another comment\n}";
let result = replacer.replace(input);
// Note: This test depends on how the regex engine handles multiline mode
// The actual behavior might need adjustment based on regex flags
assert!(result.contains("function test()"));
assert!(result.contains("return true;"));
}
#[test]
fn test_text_replacer_unicode_text() {
let replacer = TextReplacer::builder()
.pattern("café")
.replacement("coffee")
.regex(false)
.build()
.expect("Failed to build replacer");
let result = replacer.replace("I love café in the morning");
assert_eq!(result, "I love coffee in the morning");
}
#[test]
fn test_text_replacer_large_text() {
let large_text = "word ".repeat(10000);
let replacer = TextReplacer::builder()
.pattern("word")
.replacement("term")
.regex(false)
.build()
.expect("Failed to build replacer");
let result = replacer.replace(&large_text);
assert_eq!(result, "term ".repeat(10000));
}