You've already forked FrameTour-BE
- 新增拼图元素自动填充引擎 PuzzleElementFillEngine - 支持基于规则的条件匹配和数据源解析 - 实现机位数量、机位ID等多维度条件策略 - 添加 DEVICE_IMAGE、USER_AVATAR 等数据源类型支持 - 增加景区隔离校验确保模板使用安全性 - 强化图片下载安全校验,防范 SSRF 攻击 - 支持本地文件路径解析和公网 URL 安全检查 - 完善静态值数据源策略支持 localPath 配置 - 优化生成流程中 faceId 和 scenicId 的校验逻辑 - 补充相关单元测试覆盖核心功能点
178 lines
5.7 KiB
Java
178 lines
5.7 KiB
Java
package com.ycwl.basic.puzzle.element;
|
|
|
|
import com.ycwl.basic.puzzle.element.base.BaseElement;
|
|
import com.ycwl.basic.puzzle.element.base.ElementFactory;
|
|
import com.ycwl.basic.puzzle.element.enums.ElementType;
|
|
import com.ycwl.basic.puzzle.element.impl.ImageElement;
|
|
import com.ycwl.basic.puzzle.element.impl.TextElement;
|
|
import com.ycwl.basic.puzzle.element.exception.ElementValidationException;
|
|
import com.ycwl.basic.puzzle.element.renderer.RenderContext;
|
|
import com.ycwl.basic.puzzle.entity.PuzzleElementEntity;
|
|
import com.ycwl.basic.puzzle.test.PuzzleTestDataBuilder;
|
|
import org.junit.jupiter.api.BeforeAll;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import java.awt.*;
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.IOException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
import javax.imageio.ImageIO;
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
|
|
/**
|
|
* ImageElement 单元测试
|
|
*
|
|
* @author Claude
|
|
* @since 2025-01-18
|
|
*/
|
|
class ImageElementTest {
|
|
|
|
private Graphics2D graphics;
|
|
private RenderContext context;
|
|
|
|
@BeforeAll
|
|
static void initRegistry() {
|
|
ElementFactory.clearRegistry();
|
|
ElementFactory.register(ElementType.TEXT, TextElement.class);
|
|
ElementFactory.register(ElementType.IMAGE, ImageElement.class);
|
|
}
|
|
|
|
static class TestableImageElement extends ImageElement {
|
|
boolean isSafe(String url) {
|
|
return isSafeRemoteUrl(url);
|
|
}
|
|
|
|
BufferedImage load(String path) {
|
|
return downloadImage(path);
|
|
}
|
|
}
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
BufferedImage canvas = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
|
|
graphics = canvas.createGraphics();
|
|
|
|
Map<String, String> dynamicData = new HashMap<>();
|
|
context = new RenderContext(graphics, dynamicData, 800, 600);
|
|
}
|
|
|
|
@Test
|
|
void testImageElement_Creation_Success() {
|
|
// Given
|
|
PuzzleElementEntity entity = PuzzleTestDataBuilder.createImageElement(
|
|
1L, "userAvatar", 50, 100, 100, 100, 5,
|
|
"https://example.com/avatar.jpg"
|
|
);
|
|
|
|
// When
|
|
BaseElement element = ElementFactory.create(entity);
|
|
|
|
// Then
|
|
assertNotNull(element);
|
|
assertEquals("userAvatar", element.getElementKey());
|
|
}
|
|
|
|
@Test
|
|
void testImageElement_RoundedImage_Success() {
|
|
// Given
|
|
PuzzleElementEntity entity = PuzzleTestDataBuilder.createRoundedImageElement(
|
|
1L, "userAvatar", 50, 100, 100, 100, 5,
|
|
"https://example.com/avatar.jpg", 50
|
|
);
|
|
|
|
// When
|
|
BaseElement element = ElementFactory.create(entity);
|
|
|
|
// Then
|
|
assertNotNull(element);
|
|
// 验证配置包含圆角信息
|
|
String schema = element.getConfigSchema();
|
|
assertTrue(schema.contains("borderRadius"));
|
|
}
|
|
|
|
@Test
|
|
void testImageElement_InvalidConfig_MissingDefaultImageUrl() {
|
|
// Given
|
|
PuzzleElementEntity entity = new PuzzleElementEntity();
|
|
entity.setElementType("IMAGE");
|
|
entity.setElementKey("testKey");
|
|
entity.setConfig("{\"imageFitMode\":\"FILL\"}"); // 缺少 defaultImageUrl
|
|
entity.setXPosition(0);
|
|
entity.setYPosition(0);
|
|
entity.setWidth(100);
|
|
entity.setHeight(100);
|
|
|
|
// When & Then
|
|
assertThrows(ElementValidationException.class, () -> ElementFactory.create(entity));
|
|
}
|
|
|
|
@Test
|
|
void testImageElement_InvalidConfig_InvalidBorderRadius() {
|
|
// Given
|
|
PuzzleElementEntity entity = new PuzzleElementEntity();
|
|
entity.setElementType("IMAGE");
|
|
entity.setElementKey("testKey");
|
|
entity.setConfig("{\"defaultImageUrl\":\"test.jpg\",\"borderRadius\":-1}"); // 非法圆角
|
|
entity.setXPosition(0);
|
|
entity.setYPosition(0);
|
|
entity.setWidth(100);
|
|
entity.setHeight(100);
|
|
|
|
// When & Then
|
|
assertThrows(ElementValidationException.class, () -> ElementFactory.create(entity));
|
|
}
|
|
|
|
@Test
|
|
void testImageElement_GetConfigSchema() {
|
|
// Given
|
|
PuzzleElementEntity entity = PuzzleTestDataBuilder.createImageElement(
|
|
1L, "userAvatar", 50, 100, 100, 100, 5,
|
|
"https://example.com/avatar.jpg"
|
|
);
|
|
|
|
BaseElement element = ElementFactory.create(entity);
|
|
|
|
// When
|
|
String schema = element.getConfigSchema();
|
|
|
|
// Then
|
|
assertNotNull(schema);
|
|
assertTrue(schema.contains("defaultImageUrl"));
|
|
assertTrue(schema.contains("imageFitMode"));
|
|
assertTrue(schema.contains("borderRadius"));
|
|
}
|
|
|
|
@Test
|
|
void testImageElement_SafeRemoteUrlChecks() {
|
|
TestableImageElement element = new TestableImageElement();
|
|
assertFalse(element.isSafe("http://127.0.0.1/admin.png"));
|
|
assertFalse(element.isSafe("http://localhost/private.png"));
|
|
assertFalse(element.isSafe("file:///etc/passwd"));
|
|
assertTrue(element.isSafe("https://8.8.8.8/logo.png"));
|
|
}
|
|
|
|
@Test
|
|
void testImageElement_LoadLocalImageSuccess() throws IOException {
|
|
Path temp = Files.createTempFile("puzzle-image", ".png");
|
|
try {
|
|
BufferedImage bufferedImage = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
|
|
ImageIO.write(bufferedImage, "png", temp.toFile());
|
|
|
|
TestableImageElement element = new TestableImageElement();
|
|
BufferedImage loaded = element.load(temp.toString());
|
|
|
|
assertNotNull(loaded);
|
|
assertEquals(10, loaded.getWidth());
|
|
assertEquals(10, loaded.getHeight());
|
|
} finally {
|
|
Files.deleteIfExists(temp);
|
|
}
|
|
}
|
|
}
|