test(backend): cover storage, indexer, thumbnail, and tag-filter logic
storage.rs: thumbnail worker clamping across parallelism levels, the adaptive-profile EMA transitions, and UNC-path fallback detection. indexer.rs: supported-media extension matching plus media-kind and MIME mapping. thumbnail.rs: fit_dimensions aspect-ratio math with extreme ratios and is_jpeg extension checks. ai_tag_filter.rs: padding and mixed-separator normalization edge cases.
This commit is contained in:
@@ -42,4 +42,13 @@ mod tests {
|
||||
assert!(!is_removed_ai_tag(tag), "{tag} should be kept");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removed_ai_tags_tolerate_padding_and_mixed_separators() {
|
||||
assert!(is_removed_ai_tag(" 1girl "));
|
||||
assert!(is_removed_ai_tag("1_-_girl"));
|
||||
assert!(is_removed_ai_tag("No Humans"));
|
||||
assert!(!is_removed_ai_tag(""));
|
||||
assert!(!is_removed_ai_tag(" "));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1995,3 +1995,42 @@ fn process_watcher_rename(
|
||||
Err(e) => log::error!("Watcher rename: post-update fetch error: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn supported_media_matches_known_extensions_case_insensitively() {
|
||||
for path in ["a.jpg", "b.JPEG", "c.PNG", "d.avif", "e.mp4", "f.WEBM"] {
|
||||
assert!(
|
||||
is_supported_media(Path::new(path)),
|
||||
"{path} should be supported"
|
||||
);
|
||||
}
|
||||
for path in ["notes.txt", "archive.zip", "no_extension", "clip.mkv"] {
|
||||
assert!(
|
||||
!is_supported_media(Path::new(path)),
|
||||
"{path} should be skipped"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn media_kind_splits_video_from_image_extensions() {
|
||||
for ext in ["mp4", "MOV", "m4v", "webm"] {
|
||||
assert_eq!(media_kind_for_ext(ext), "video");
|
||||
}
|
||||
for ext in ["jpg", "PNG", "webp", "avif"] {
|
||||
assert_eq!(media_kind_for_ext(ext), "image");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mime_types_map_per_extension() {
|
||||
assert_eq!(mime_for_ext("JPG"), "image/jpeg");
|
||||
assert_eq!(mime_for_ext("png"), "image/png");
|
||||
assert_eq!(mime_for_ext("mov"), "video/quicktime");
|
||||
assert_eq!(mime_for_ext("m4v"), "video/mp4");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,3 +122,53 @@ fn fallback_profile_for_path(path: &Path) -> StorageProfile {
|
||||
|
||||
StorageProfile::Balanced
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn thumbnail_workers_scale_with_parallelism_within_clamps() {
|
||||
assert_eq!(StorageProfile::Fast.thumbnail_workers(3), 2);
|
||||
assert_eq!(StorageProfile::Fast.thumbnail_workers(12), 4);
|
||||
assert_eq!(StorageProfile::Fast.thumbnail_workers(64), 4);
|
||||
assert_eq!(StorageProfile::Balanced.thumbnail_workers(4), 2);
|
||||
assert_eq!(StorageProfile::Balanced.thumbnail_workers(12), 3);
|
||||
assert_eq!(StorageProfile::Balanced.thumbnail_workers(64), 3);
|
||||
assert_eq!(StorageProfile::Conservative.thumbnail_workers(64), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adaptive_profile_tracks_scan_speed() {
|
||||
let mut adaptive = RuntimeAdaptiveProfile::new(StorageProfile::Balanced);
|
||||
assert_eq!(adaptive.profile(), StorageProfile::Balanced);
|
||||
|
||||
// 1 ms/item → fast storage.
|
||||
adaptive.observe_scan_batch(10, Duration::from_millis(10));
|
||||
assert_eq!(adaptive.profile(), StorageProfile::Fast);
|
||||
|
||||
// A very slow batch drags the EMA over the conservative threshold.
|
||||
adaptive.observe_scan_batch(1, Duration::from_millis(100));
|
||||
assert_eq!(adaptive.profile(), StorageProfile::Conservative);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adaptive_profile_ignores_empty_batches() {
|
||||
let mut adaptive = RuntimeAdaptiveProfile::new(StorageProfile::Fast);
|
||||
adaptive.observe_scan_batch(0, Duration::from_secs(10));
|
||||
assert_eq!(adaptive.profile(), StorageProfile::Fast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fallback_profile_treats_unc_paths_as_conservative() {
|
||||
assert_eq!(
|
||||
fallback_profile_for_path(Path::new("\\\\server\\share\\photos")),
|
||||
StorageProfile::Conservative
|
||||
);
|
||||
assert_eq!(
|
||||
fallback_profile_for_path(Path::new("C:\\photos")),
|
||||
StorageProfile::Balanced
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,6 +383,26 @@ fn fit_dimensions(width: u32, height: u32, max_size: u32) -> (u32, u32) {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fit_dimensions_preserves_aspect_ratio_within_max() {
|
||||
// Already small enough: unchanged.
|
||||
assert_eq!(fit_dimensions(100, 50, THUMB_SIZE), (100, 50));
|
||||
// Landscape and portrait scale to the max on their long edge.
|
||||
assert_eq!(fit_dimensions(6400, 3200, THUMB_SIZE), (320, 160));
|
||||
assert_eq!(fit_dimensions(3200, 6400, THUMB_SIZE), (160, 320));
|
||||
// Extreme ratios never collapse to zero.
|
||||
assert_eq!(fit_dimensions(1, 100_000, 320), (1, 320));
|
||||
assert_eq!(fit_dimensions(100_000, 1, 320), (320, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_jpeg_checks_extension_only() {
|
||||
assert!(is_jpeg(Path::new("photo.jpg")));
|
||||
assert!(is_jpeg(Path::new("photo.JPEG")));
|
||||
assert!(!is_jpeg(Path::new("photo.png")));
|
||||
assert!(!is_jpeg(Path::new("photo")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scale_numerator_picks_smallest_sufficient() {
|
||||
assert_eq!(scale_numerator(6000, THUMB_SIZE), 1);
|
||||
|
||||
Reference in New Issue
Block a user