feat(recipe): compress hero image to 400px preview on save
Adds Thumbnailator-based ImageCompressor that resizes uploaded images to a 400px-wide JPEG preview stored in hero_image_preview. The recipe list uses the preview instead of the full image URL. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package com.recipeapp.recipe;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
class ImageCompressorTest {
|
||||
|
||||
private final ImageCompressor compressor = new ImageCompressor();
|
||||
|
||||
@Test
|
||||
void compressToPreview_returnsJpegDataUri() throws Exception {
|
||||
String dataUri = makePngDataUri(800, 600);
|
||||
String result = compressor.compressToPreview(dataUri);
|
||||
assertThat(result).startsWith("data:image/jpeg;base64,");
|
||||
}
|
||||
|
||||
@Test
|
||||
void compressToPreview_outputIsDecodableJpeg() throws Exception {
|
||||
String dataUri = makePngDataUri(800, 600);
|
||||
String result = compressor.compressToPreview(dataUri);
|
||||
|
||||
String base64 = result.substring("data:image/jpeg;base64,".length());
|
||||
byte[] bytes = Base64.getDecoder().decode(base64);
|
||||
BufferedImage img = ImageIO.read(new ByteArrayInputStream(bytes));
|
||||
|
||||
assertThat(img).isNotNull();
|
||||
assertThat(img.getWidth()).isLessThanOrEqualTo(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
void compressToPreview_preservesAspectRatio() throws Exception {
|
||||
String dataUri = makePngDataUri(800, 400); // 2:1 ratio
|
||||
String result = compressor.compressToPreview(dataUri);
|
||||
|
||||
String base64 = result.substring("data:image/jpeg;base64,".length());
|
||||
BufferedImage img = ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(base64)));
|
||||
|
||||
assertThat(img).isNotNull();
|
||||
double ratio = (double) img.getWidth() / img.getHeight();
|
||||
assertThat(ratio).isCloseTo(2.0, within(0.1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void compressToPreview_doesNotUpscaleSmallImages() throws Exception {
|
||||
String dataUri = makePngDataUri(200, 150); // smaller than 400px
|
||||
String result = compressor.compressToPreview(dataUri);
|
||||
|
||||
String base64 = result.substring("data:image/jpeg;base64,".length());
|
||||
BufferedImage img = ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(base64)));
|
||||
|
||||
assertThat(img).isNotNull();
|
||||
assertThat(img.getWidth()).isLessThanOrEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void compressToPreview_returnsNullForNull() {
|
||||
assertThat(compressor.compressToPreview(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void compressToPreview_returnsNullForBlankString() {
|
||||
assertThat(compressor.compressToPreview(" ")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void compressToPreview_returnsNullForNonDataUri() {
|
||||
assertThat(compressor.compressToPreview("https://example.com/image.jpg")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void compressToPreview_returnsNullForInvalidBase64() {
|
||||
assertThat(compressor.compressToPreview("data:image/jpeg;base64,!!!not-valid!!!")).isNull();
|
||||
}
|
||||
|
||||
// ── helpers ──
|
||||
|
||||
private String makePngDataUri(int width, int height) throws Exception {
|
||||
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = img.createGraphics();
|
||||
// draw gradient so PNG and JPEG both have non-trivial content
|
||||
for (int x = 0; x < width; x++) {
|
||||
g.setColor(new Color(x * 255 / width, (x * 128 / width + height / 2) % 256, 128));
|
||||
g.drawLine(x, 0, x, height);
|
||||
}
|
||||
g.dispose();
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ImageIO.write(img, "png", bos);
|
||||
return "data:image/png;base64," + Base64.getEncoder().encodeToString(bos.toByteArray());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user