package com.yunhai.agent; import com.yunhai.agent.tool.FormField; import com.yunhai.agent.tool.ToolDef; import com.yunhai.agent.tool.ToolParam; import com.yunhai.agent.tool.defs.LeaveTools; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * 表单字段「控件形态」的回归断言(纯单元,不依赖 Spring)。 * *
背景(2026-10-10 截图实测):请假表单里「请假类型」渲染成了普通输入框, * 占位文字是「请假类型编号,来自 leave.types」。根因有两层: *
这正是 2026-10-10 反馈「请假类型怎么变成数字框了,还是 1 这种」的修复点 —— * 光有选项来源不够,还得有可用的默认选项,控件才立得住。 */ @Test void fallbackOptionsKeepSelectWidgetWhenDictUnavailable() { ToolParam p = ToolParam.required("leaveType", "integer", "请假类型") .label("请假类型") .fallbackOptions(new String[]{"1", "2"}, new String[]{"病假", "事假及其他"}) .optionsFrom("leave.types"); FormField f = p.toFormField(); assertEquals("select", f.getWidget(), "有兜底选项时必须渲染成下拉框"); assertEquals(2, f.getOptions().size()); assertEquals("1", f.getOptions().get(0).get("value")); assertEquals("病假", f.getOptions().get(0).get("label")); assertEquals("事假及其他", f.getOptions().get(1).get("label")); assertTrue(f.isOptionsFallback(), "必须标记为兜底,前端才能提示「自定义项没取到」"); assertEquals("leave.types", f.getOptionsFrom(), "实时字典来源要保留,取到后覆盖兜底"); } /** ⑤ 权威枚举优先于兜底:两者都配时兜底不生效,也不应被误标为兜底 */ @Test void explicitEnumWinsOverFallback() { ToolParam p = ToolParam.required("status", "integer", "审批结论") .options(new String[]{"1"}, new String[]{"通过"}) .fallbackOptions(new String[]{"9"}, new String[]{"兜底项"}); FormField f = p.toFormField(); assertEquals(1, f.getOptions().size()); assertEquals("通过", f.getOptions().get(0).get("label")); assertFalse(f.isOptionsFallback()); } /** ⑥ 真实工具定义回归:请假类型必须是带中文选项的下拉框,不能退回数字框 */ @Test void leaveSubmitTypeIsASelectInRealToolDefinition() { ToolDef def = LeaveTools.defs().stream() .filter(d -> "leave.submit".equals(d.getName())) .findFirst() .orElseThrow(() -> new AssertionError("leave.submit 工具定义丢失")); ToolParam leaveType = def.getParams().stream() .filter(p -> "leaveType".equals(p.getName())) .findFirst() .orElseThrow(() -> new AssertionError("leaveType 参数丢失")); FormField f = leaveType.toFormField(); assertEquals("select", f.getWidget()); assertEquals(2, f.getOptions().size()); assertEquals("病假", f.getOptions().get(0).get("label")); assertEquals("事假及其他", f.getOptions().get(1).get("label")); } }