Files
FrameTour-BE/src/test/java/com/ycwl/basic/puzzle/element/ImageElementTest.java
Jerry Yan cfb3625ac0 feat(puzzle): 实现智能自动填充引擎和安全增强
- 新增拼图元素自动填充引擎 PuzzleElementFillEngine
- 支持基于规则的条件匹配和数据源解析
- 实现机位数量、机位ID等多维度条件策略
- 添加 DEVICE_IMAGE、USER_AVATAR 等数据源类型支持
- 增加景区隔离校验确保模板使用安全性
- 强化图片下载安全校验,防范 SSRF 攻击
- 支持本地文件路径解析和公网 URL 安全检查
- 完善静态值数据源策略支持 localPath 配置
- 优化生成流程中 faceId 和 scenicId 的校验逻辑
- 补充相关单元测试覆盖核心功能点
2025-11-19 17:28:41 +08:00

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);
}
}
}