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