package com.yunhai.flowcenter.service; import com.yunhai.flowcenter.model.FlowModels; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * 流程图服务端出图(2026-09-16 "CAS 验证码式嵌入"): * renderPng 恒出非空 PNG;布局对环/孤儿/缺失端点防御出图(外部 img 场景无错误页可跳)。 */ class DiagramImageServiceTest { private final DiagramImageService service = new DiagramImageService(new DiagramLayoutService()); private FlowModels.NodeVo node(String id, String type, String label, String sub, String state) { FlowModels.NodeVo n = new FlowModels.NodeVo(); n.setId(id); n.setType(type); n.setLabel(label); n.setSubLabel(sub); n.setState(state); return n; } private FlowModels.EdgeVo edge(String from, String to, String name) { FlowModels.EdgeVo e = new FlowModels.EdgeVo(); e.setFrom(from); e.setTo(to); e.setName(name); return e; } private FlowModels.DiagramVo vo(List nodes, List edges) { FlowModels.DiagramVo vo = new FlowModels.DiagramVo(); vo.setName("教师入职审批"); vo.setMode("auto"); vo.setNodes(nodes); vo.setEdges(edges); return vo; } @Test void render_instanceVo_producesPngWithStateColors() { List nodes = new ArrayList<>(Arrays.asList( node("n1", "START", "开始", null, null), node("n2", "APPROVAL", "审核节点", "张三、李四 · 任一人通过", null), node("n3", "APPROVAL", "科室审批", "教学处 · 3人 · 时限24h", "active"), node("n4", "END", "结束", null, null))); List edges = new ArrayList<>(Arrays.asList( edge("n1", "n2", null), edge("n2", "n3", null), edge("n3", "n4", null))); byte[] png = service.renderPng(vo(nodes, edges)); // PNG 头 89 50 4E 47 assertEquals(0x89, png[0] & 0xFF); assertEquals(0x50, png[1] & 0xFF); assertTrue(png.length > 2000, "渲染结果过小,疑似空图"); // 状态无关的纯定义图也恒出图(flowGuid 口径) for (FlowModels.NodeVo n : nodes) { n.setState(null); } assertNotEquals(0, service.renderPng(vo(nodes, edges)).length); } @Test void smoke_writeSamplePng_forVisualCheck() throws Exception { List nodes = new ArrayList<>(Arrays.asList( node("n1", "START", "开始", null, null), node("n2", "APPROVAL", "教师入职审批", "刘文娟 · 时限48h", "done"), node("n3", "APPROVAL", "科室审批", "教学处 · 云海小学001 · 任一人通过 · 时限24h", "active"), node("n4", "END", "结束", null, null))); List edges = new ArrayList<>(Arrays.asList( edge("n1", "n2", null), edge("n2", "n3", "同意"), edge("n3", "n4", null))); java.nio.file.Files.write( java.nio.file.Paths.get("target", "diagram-smoke.png"), service.renderPng(vo(nodes, edges))); } @Test void render_defensive_missingEndpointAndCycle_stillProducesImage() { // 成环 + 指向不存在端点的边:不抛,仍出图(外部 img 场景防御) List nodes = new ArrayList<>(Arrays.asList( node("n1", "START", "开始", null, null), node("n2", "APPROVAL", "审核节点", null, "done"), node("n3", "APPROVAL", "孤儿节点", null, null))); List edges = new ArrayList<>(Arrays.asList( edge("n1", "n2", null), edge("n2", "n1", null), edge("n2", "ghost", null))); assertDoesNotThrow(() -> service.renderPng(vo(nodes, edges))); } }