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:
2026-07-05 21:19:50 +01:00
parent 782cf0ea08
commit ca5c500e18
4 changed files with 118 additions and 0 deletions
+9
View File
@@ -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(" "));
}
}
+39
View File
@@ -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");
}
}
+50
View File
@@ -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
);
}
}
+20
View File
@@ -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);