Files
pixstrip/pixstrip-core/tests/rename_tests.rs
lashman b432cc7431 Fix 26 bugs, edge cases, and consistency issues from fifth audit pass
Critical: undo toast now trashes only batch output files (not entire dir),
JPEG scanline write errors propagated, selective metadata write result returned.

High: zero-dimension guards in ResizeConfig/fit_within, negative aspect ratio
rejection, FM integration toggle infinite recursion guard, saturating counter
arithmetic in executor.

Medium: PNG compression level passed to oxipng, pct mode updates job_config,
external file loading updates step indicator, CLI undo removes history entries,
watch config write failures reported, fast-copy path reads image dimensions for
rename templates, discovery excludes unprocessable formats (heic/svg/ico/jxl),
CLI warns on invalid algorithm/overwrite values, resolve_collision trailing dot
fix, generation guards on all preview threads to cancel stale results, default
DPI aligned to 0, watermark text width uses char count not byte length.

Low: binary path escaped in Nautilus extension, file dialog filter aligned with
discovery, reset_wizard clears preset_mode and output_dir.
2026-03-07 19:47:23 +02:00

281 lines
6.6 KiB
Rust

use pixstrip_core::operations::rename::{
apply_template, apply_regex_replace, apply_space_replacement,
apply_special_chars, apply_case_conversion, resolve_collision,
};
#[test]
fn template_basic_variables() {
let result = apply_template(
"{name}_{counter:3}.{ext}",
"sunset",
"jpg",
1,
None,
);
assert_eq!(result, "sunset_001.jpg");
}
#[test]
fn template_with_prefix() {
let result = apply_template(
"blog_{name}.{ext}",
"photo",
"webp",
1,
None,
);
assert_eq!(result, "blog_photo.webp");
}
#[test]
fn template_counter_padding() {
let result = apply_template(
"{name}_{counter:4}.{ext}",
"img",
"png",
42,
None,
);
assert_eq!(result, "img_0042.png");
}
#[test]
fn template_counter_no_padding() {
let result = apply_template(
"{name}_{counter}.{ext}",
"img",
"png",
42,
None,
);
assert_eq!(result, "img_42.png");
}
#[test]
fn template_width_height() {
let result = apply_template(
"{name}_{width}x{height}.{ext}",
"photo",
"jpg",
1,
Some((1920, 1080)),
);
assert_eq!(result, "photo_1920x1080.jpg");
}
#[test]
fn collision_adds_suffix() {
let dir = tempfile::tempdir().unwrap();
let base = dir.path().join("photo.jpg");
std::fs::write(&base, b"exists").unwrap();
let resolved = resolve_collision(&base);
assert_eq!(
resolved.file_name().unwrap().to_str().unwrap(),
"photo_1.jpg"
);
}
#[test]
fn collision_increments() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("photo.jpg"), b"exists").unwrap();
std::fs::write(dir.path().join("photo_1.jpg"), b"exists").unwrap();
let resolved = resolve_collision(&dir.path().join("photo.jpg"));
assert_eq!(
resolved.file_name().unwrap().to_str().unwrap(),
"photo_2.jpg"
);
}
#[test]
fn no_collision_returns_same() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("unique.jpg");
let resolved = resolve_collision(&path);
assert_eq!(resolved, path);
}
// --- Regex replace tests ---
#[test]
fn regex_replace_basic() {
let result = apply_regex_replace("hello_world", "_", "-");
assert_eq!(result, "hello-world");
}
#[test]
fn regex_replace_pattern() {
let result = apply_regex_replace("IMG_20260307_001", r"\d{8}", "DATE");
assert_eq!(result, "IMG_DATE_001");
}
#[test]
fn regex_replace_invalid_pattern_returns_original() {
let result = apply_regex_replace("hello", "[invalid", "x");
assert_eq!(result, "hello");
}
#[test]
fn regex_replace_empty_find_returns_original() {
let result = apply_regex_replace("hello", "", "x");
assert_eq!(result, "hello");
}
// --- Space replacement tests ---
#[test]
fn space_replacement_none() {
assert_eq!(apply_space_replacement("hello world", 0), "hello world");
}
#[test]
fn space_replacement_underscore() {
assert_eq!(apply_space_replacement("hello world", 1), "hello_world");
}
#[test]
fn space_replacement_hyphen() {
assert_eq!(apply_space_replacement("hello world", 2), "hello-world");
}
#[test]
fn space_replacement_dot() {
assert_eq!(apply_space_replacement("hello world", 3), "hello.world");
}
#[test]
fn space_replacement_camelcase() {
assert_eq!(apply_space_replacement("hello world", 4), "helloWorld");
}
#[test]
fn space_replacement_remove() {
assert_eq!(apply_space_replacement("hello world", 5), "helloworld");
}
// --- Special chars tests ---
#[test]
fn special_chars_keep_all() {
assert_eq!(apply_special_chars("file<name>.txt", 0), "file<name>.txt");
}
#[test]
fn special_chars_filesystem_safe() {
assert_eq!(apply_special_chars("file<name>", 1), "filename");
}
#[test]
fn special_chars_web_safe() {
assert_eq!(apply_special_chars("file name!@#", 2), "filename");
}
#[test]
fn special_chars_alphanumeric_only() {
assert_eq!(apply_special_chars("file-name_123", 5), "filename123");
}
// --- Case conversion tests ---
#[test]
fn case_conversion_none() {
assert_eq!(apply_case_conversion("Hello World", 0), "Hello World");
}
#[test]
fn case_conversion_lowercase() {
assert_eq!(apply_case_conversion("Hello World", 1), "hello world");
}
#[test]
fn case_conversion_uppercase() {
assert_eq!(apply_case_conversion("Hello World", 2), "HELLO WORLD");
}
#[test]
fn case_conversion_title_case() {
assert_eq!(apply_case_conversion("hello world", 3), "Hello World");
}
#[test]
fn case_conversion_title_preserves_separators() {
assert_eq!(apply_case_conversion("hello-world_foo", 3), "Hello-World_Foo");
}
// --- RenameConfig::apply_simple edge cases ---
use pixstrip_core::operations::RenameConfig;
fn default_rename_config() -> RenameConfig {
RenameConfig {
prefix: String::new(),
suffix: String::new(),
counter_start: 1,
counter_padding: 3,
counter_enabled: false,
counter_position: 3,
template: None,
case_mode: 0,
replace_spaces: 0,
special_chars: 0,
regex_find: String::new(),
regex_replace: String::new(),
}
}
#[test]
fn apply_simple_no_changes() {
let cfg = default_rename_config();
assert_eq!(cfg.apply_simple("photo", "jpg", 1), "photo.jpg");
}
#[test]
fn apply_simple_with_prefix_suffix() {
let mut cfg = default_rename_config();
cfg.prefix = "web_".into();
cfg.suffix = "_final".into();
assert_eq!(cfg.apply_simple("photo", "jpg", 1), "web_photo_final.jpg");
}
#[test]
fn apply_simple_counter_after_suffix() {
let mut cfg = default_rename_config();
cfg.counter_enabled = true;
cfg.counter_position = 3;
assert_eq!(cfg.apply_simple("photo", "jpg", 1), "photo_001.jpg");
}
#[test]
fn apply_simple_counter_replaces_name() {
let mut cfg = default_rename_config();
cfg.counter_enabled = true;
cfg.counter_position = 4;
assert_eq!(cfg.apply_simple("photo", "jpg", 1), "001.jpg");
}
#[test]
fn apply_simple_empty_name() {
let cfg = default_rename_config();
assert_eq!(cfg.apply_simple("", "png", 1), ".png");
}
#[test]
fn apply_simple_case_lowercase() {
let mut cfg = default_rename_config();
cfg.case_mode = 1;
assert_eq!(cfg.apply_simple("MyPhoto", "JPG", 1), "myphoto.JPG");
}
#[test]
fn apply_simple_large_counter_padding_capped() {
let mut cfg = default_rename_config();
cfg.counter_enabled = true;
cfg.counter_padding = 100; // should be capped to 10
cfg.counter_position = 3;
let result = cfg.apply_simple("photo", "jpg", 1);
// Counter portion should be at most 10 digits
assert!(result.len() <= 22); // "photo_" + 10 digits + ".jpg"
}