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