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");
|
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}"),
|
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
|
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 {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn scale_numerator_picks_smallest_sufficient() {
|
fn scale_numerator_picks_smallest_sufficient() {
|
||||||
assert_eq!(scale_numerator(6000, THUMB_SIZE), 1);
|
assert_eq!(scale_numerator(6000, THUMB_SIZE), 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user