this repo has no description
1use bspds::image::{ImageProcessor, ImageError, OutputFormat, THUMB_SIZE_FEED, THUMB_SIZE_FULL, DEFAULT_MAX_FILE_SIZE};
2use image::{DynamicImage, ImageFormat};
3use std::io::Cursor;
4fn create_test_png(width: u32, height: u32) -> Vec<u8> {
5 let img = DynamicImage::new_rgb8(width, height);
6 let mut buf = Vec::new();
7 img.write_to(&mut Cursor::new(&mut buf), ImageFormat::Png).unwrap();
8 buf
9}
10fn create_test_jpeg(width: u32, height: u32) -> Vec<u8> {
11 let img = DynamicImage::new_rgb8(width, height);
12 let mut buf = Vec::new();
13 img.write_to(&mut Cursor::new(&mut buf), ImageFormat::Jpeg).unwrap();
14 buf
15}
16fn create_test_gif(width: u32, height: u32) -> Vec<u8> {
17 let img = DynamicImage::new_rgb8(width, height);
18 let mut buf = Vec::new();
19 img.write_to(&mut Cursor::new(&mut buf), ImageFormat::Gif).unwrap();
20 buf
21}
22fn create_test_webp(width: u32, height: u32) -> Vec<u8> {
23 let img = DynamicImage::new_rgb8(width, height);
24 let mut buf = Vec::new();
25 img.write_to(&mut Cursor::new(&mut buf), ImageFormat::WebP).unwrap();
26 buf
27}
28#[test]
29fn test_process_png() {
30 let processor = ImageProcessor::new();
31 let data = create_test_png(500, 500);
32 let result = processor.process(&data, "image/png").unwrap();
33 assert_eq!(result.original.width, 500);
34 assert_eq!(result.original.height, 500);
35}
36#[test]
37fn test_process_jpeg() {
38 let processor = ImageProcessor::new();
39 let data = create_test_jpeg(400, 300);
40 let result = processor.process(&data, "image/jpeg").unwrap();
41 assert_eq!(result.original.width, 400);
42 assert_eq!(result.original.height, 300);
43}
44#[test]
45fn test_process_gif() {
46 let processor = ImageProcessor::new();
47 let data = create_test_gif(200, 200);
48 let result = processor.process(&data, "image/gif").unwrap();
49 assert_eq!(result.original.width, 200);
50 assert_eq!(result.original.height, 200);
51}
52#[test]
53fn test_process_webp() {
54 let processor = ImageProcessor::new();
55 let data = create_test_webp(300, 200);
56 let result = processor.process(&data, "image/webp").unwrap();
57 assert_eq!(result.original.width, 300);
58 assert_eq!(result.original.height, 200);
59}
60#[test]
61fn test_thumbnail_feed_size() {
62 let processor = ImageProcessor::new();
63 let data = create_test_png(800, 600);
64 let result = processor.process(&data, "image/png").unwrap();
65 let thumb = result.thumbnail_feed.expect("Should generate feed thumbnail for large image");
66 assert!(thumb.width <= THUMB_SIZE_FEED);
67 assert!(thumb.height <= THUMB_SIZE_FEED);
68}
69#[test]
70fn test_thumbnail_full_size() {
71 let processor = ImageProcessor::new();
72 let data = create_test_png(2000, 1500);
73 let result = processor.process(&data, "image/png").unwrap();
74 let thumb = result.thumbnail_full.expect("Should generate full thumbnail for large image");
75 assert!(thumb.width <= THUMB_SIZE_FULL);
76 assert!(thumb.height <= THUMB_SIZE_FULL);
77}
78#[test]
79fn test_no_thumbnail_small_image() {
80 let processor = ImageProcessor::new();
81 let data = create_test_png(100, 100);
82 let result = processor.process(&data, "image/png").unwrap();
83 assert!(result.thumbnail_feed.is_none(), "Small image should not get feed thumbnail");
84 assert!(result.thumbnail_full.is_none(), "Small image should not get full thumbnail");
85}
86#[test]
87fn test_webp_conversion() {
88 let processor = ImageProcessor::new().with_output_format(OutputFormat::WebP);
89 let data = create_test_png(300, 300);
90 let result = processor.process(&data, "image/png").unwrap();
91 assert_eq!(result.original.mime_type, "image/webp");
92}
93#[test]
94fn test_jpeg_output_format() {
95 let processor = ImageProcessor::new().with_output_format(OutputFormat::Jpeg);
96 let data = create_test_png(300, 300);
97 let result = processor.process(&data, "image/png").unwrap();
98 assert_eq!(result.original.mime_type, "image/jpeg");
99}
100#[test]
101fn test_png_output_format() {
102 let processor = ImageProcessor::new().with_output_format(OutputFormat::Png);
103 let data = create_test_jpeg(300, 300);
104 let result = processor.process(&data, "image/jpeg").unwrap();
105 assert_eq!(result.original.mime_type, "image/png");
106}
107#[test]
108fn test_max_dimension_enforced() {
109 let processor = ImageProcessor::new().with_max_dimension(1000);
110 let data = create_test_png(2000, 2000);
111 let result = processor.process(&data, "image/png");
112 assert!(matches!(result, Err(ImageError::TooLarge { .. })));
113 if let Err(ImageError::TooLarge { width, height, max_dimension }) = result {
114 assert_eq!(width, 2000);
115 assert_eq!(height, 2000);
116 assert_eq!(max_dimension, 1000);
117 }
118}
119#[test]
120fn test_file_size_limit() {
121 let processor = ImageProcessor::new().with_max_file_size(100);
122 let data = create_test_png(500, 500);
123 let result = processor.process(&data, "image/png");
124 assert!(matches!(result, Err(ImageError::FileTooLarge { .. })));
125 if let Err(ImageError::FileTooLarge { size, max_size }) = result {
126 assert!(size > 100);
127 assert_eq!(max_size, 100);
128 }
129}
130#[test]
131fn test_default_max_file_size() {
132 assert_eq!(DEFAULT_MAX_FILE_SIZE, 10 * 1024 * 1024);
133}
134#[test]
135fn test_unsupported_format_rejected() {
136 let processor = ImageProcessor::new();
137 let data = b"this is not an image";
138 let result = processor.process(data, "application/octet-stream");
139 assert!(matches!(result, Err(ImageError::UnsupportedFormat(_))));
140}
141#[test]
142fn test_corrupted_image_handling() {
143 let processor = ImageProcessor::new();
144 let data = b"\x89PNG\r\n\x1a\ncorrupted data here";
145 let result = processor.process(data, "image/png");
146 assert!(matches!(result, Err(ImageError::DecodeError(_))));
147}
148#[test]
149fn test_aspect_ratio_preserved_landscape() {
150 let processor = ImageProcessor::new();
151 let data = create_test_png(1600, 800);
152 let result = processor.process(&data, "image/png").unwrap();
153 let thumb = result.thumbnail_full.expect("Should have thumbnail");
154 let original_ratio = 1600.0 / 800.0;
155 let thumb_ratio = thumb.width as f64 / thumb.height as f64;
156 assert!((original_ratio - thumb_ratio).abs() < 0.1, "Aspect ratio should be preserved");
157}
158#[test]
159fn test_aspect_ratio_preserved_portrait() {
160 let processor = ImageProcessor::new();
161 let data = create_test_png(800, 1600);
162 let result = processor.process(&data, "image/png").unwrap();
163 let thumb = result.thumbnail_full.expect("Should have thumbnail");
164 let original_ratio = 800.0 / 1600.0;
165 let thumb_ratio = thumb.width as f64 / thumb.height as f64;
166 assert!((original_ratio - thumb_ratio).abs() < 0.1, "Aspect ratio should be preserved");
167}
168#[test]
169fn test_mime_type_detection_auto() {
170 let processor = ImageProcessor::new();
171 let data = create_test_png(100, 100);
172 let result = processor.process(&data, "application/octet-stream");
173 assert!(result.is_ok(), "Should detect PNG format from data");
174}
175#[test]
176fn test_is_supported_mime_type() {
177 assert!(ImageProcessor::is_supported_mime_type("image/jpeg"));
178 assert!(ImageProcessor::is_supported_mime_type("image/jpg"));
179 assert!(ImageProcessor::is_supported_mime_type("image/png"));
180 assert!(ImageProcessor::is_supported_mime_type("image/gif"));
181 assert!(ImageProcessor::is_supported_mime_type("image/webp"));
182 assert!(ImageProcessor::is_supported_mime_type("IMAGE/PNG"));
183 assert!(ImageProcessor::is_supported_mime_type("Image/Jpeg"));
184 assert!(!ImageProcessor::is_supported_mime_type("image/bmp"));
185 assert!(!ImageProcessor::is_supported_mime_type("image/tiff"));
186 assert!(!ImageProcessor::is_supported_mime_type("text/plain"));
187 assert!(!ImageProcessor::is_supported_mime_type("application/json"));
188}
189#[test]
190fn test_strip_exif() {
191 let data = create_test_jpeg(100, 100);
192 let result = ImageProcessor::strip_exif(&data);
193 assert!(result.is_ok());
194 let stripped = result.unwrap();
195 assert!(!stripped.is_empty());
196}
197#[test]
198fn test_with_thumbnails_disabled() {
199 let processor = ImageProcessor::new().with_thumbnails(false);
200 let data = create_test_png(2000, 2000);
201 let result = processor.process(&data, "image/png").unwrap();
202 assert!(result.thumbnail_feed.is_none(), "Thumbnails should be disabled");
203 assert!(result.thumbnail_full.is_none(), "Thumbnails should be disabled");
204}
205#[test]
206fn test_builder_chaining() {
207 let processor = ImageProcessor::new()
208 .with_max_dimension(2048)
209 .with_max_file_size(5 * 1024 * 1024)
210 .with_output_format(OutputFormat::Jpeg)
211 .with_thumbnails(true);
212 let data = create_test_png(500, 500);
213 let result = processor.process(&data, "image/png").unwrap();
214 assert_eq!(result.original.mime_type, "image/jpeg");
215}
216#[test]
217fn test_processed_image_fields() {
218 let processor = ImageProcessor::new();
219 let data = create_test_png(500, 500);
220 let result = processor.process(&data, "image/png").unwrap();
221 assert!(!result.original.data.is_empty());
222 assert!(!result.original.mime_type.is_empty());
223 assert!(result.original.width > 0);
224 assert!(result.original.height > 0);
225}
226#[test]
227fn test_only_feed_thumbnail_for_medium_images() {
228 let processor = ImageProcessor::new();
229 let data = create_test_png(500, 500);
230 let result = processor.process(&data, "image/png").unwrap();
231 assert!(result.thumbnail_feed.is_some(), "Should have feed thumbnail");
232 assert!(result.thumbnail_full.is_none(), "Should NOT have full thumbnail for 500px image");
233}
234#[test]
235fn test_both_thumbnails_for_large_images() {
236 let processor = ImageProcessor::new();
237 let data = create_test_png(2000, 2000);
238 let result = processor.process(&data, "image/png").unwrap();
239 assert!(result.thumbnail_feed.is_some(), "Should have feed thumbnail");
240 assert!(result.thumbnail_full.is_some(), "Should have full thumbnail for 2000px image");
241}
242#[test]
243fn test_exact_threshold_boundary_feed() {
244 let processor = ImageProcessor::new();
245 let at_threshold = create_test_png(THUMB_SIZE_FEED, THUMB_SIZE_FEED);
246 let result = processor.process(&at_threshold, "image/png").unwrap();
247 assert!(result.thumbnail_feed.is_none(), "Exact threshold should not generate thumbnail");
248 let above_threshold = create_test_png(THUMB_SIZE_FEED + 1, THUMB_SIZE_FEED + 1);
249 let result = processor.process(&above_threshold, "image/png").unwrap();
250 assert!(result.thumbnail_feed.is_some(), "Above threshold should generate thumbnail");
251}
252#[test]
253fn test_exact_threshold_boundary_full() {
254 let processor = ImageProcessor::new();
255 let at_threshold = create_test_png(THUMB_SIZE_FULL, THUMB_SIZE_FULL);
256 let result = processor.process(&at_threshold, "image/png").unwrap();
257 assert!(result.thumbnail_full.is_none(), "Exact threshold should not generate thumbnail");
258 let above_threshold = create_test_png(THUMB_SIZE_FULL + 1, THUMB_SIZE_FULL + 1);
259 let result = processor.process(&above_threshold, "image/png").unwrap();
260 assert!(result.thumbnail_full.is_some(), "Above threshold should generate thumbnail");
261}