task-141(Java 菜单树与 Flyway): 冻结 Java 菜单树响应 DTO 契约
新增纯 Java AdminMenuTreeBuilder:Node(key/name/route/children) 树组装与 route 规整,column_key 与 route 分离,可离线单测。 TDD: Task141Test 8 用例先 RED 后 GREEN。
This commit is contained in:
+88
@@ -0,0 +1,88 @@
|
||||
package com.nanri.aiimage.modules.permission.service.support;
|
||||
|
||||
import com.nanri.aiimage.modules.permission.model.vo.PermissionMenuItemVo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/** task-141:冻结 Java 菜单树响应 DTO 契约(key/name/route/children)。 */
|
||||
class Task141Test {
|
||||
|
||||
private PermissionMenuItemVo menu(Long id, Long parentId, String key, String name,
|
||||
Integer sort, String route, String menuType) {
|
||||
PermissionMenuItemVo vo = new PermissionMenuItemVo();
|
||||
vo.setId(id);
|
||||
vo.setParentId(parentId);
|
||||
vo.setColumnKey(key);
|
||||
vo.setName(name);
|
||||
vo.setSortOrder(sort);
|
||||
vo.setRoutePath(route);
|
||||
vo.setMenuType(menuType);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_task_141_freeze_java_dto_normal_primary_path() {
|
||||
List<AdminMenuTreeBuilder.Node> tree = AdminMenuTreeBuilder.build(List.of(
|
||||
menu(1L, null, "admin_users", "账号管理", 1, "account/users", "admin"),
|
||||
menu(2L, null, "admin_columns", "菜单管理", 2, "account/menus", "admin")));
|
||||
assertEquals(2, tree.size());
|
||||
assertEquals("admin_users", tree.get(0).key());
|
||||
assertEquals("/account/users", tree.get(0).route());
|
||||
assertEquals("账号管理", tree.get(0).name());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_task_141_freeze_java_dto_normal_variant_input() {
|
||||
List<AdminMenuTreeBuilder.Node> tree = AdminMenuTreeBuilder.build(List.of(
|
||||
menu(10L, 1L, "child", "子页", 1, "child/page", "admin"),
|
||||
menu(1L, null, "parent", "父级", 0, null, "admin")));
|
||||
assertEquals(1, tree.size());
|
||||
assertEquals(1, tree.get(0).children().size());
|
||||
assertEquals("child", tree.get(0).children().get(0).key());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_task_141_freeze_java_dto_normal_repeated_is_idempotent() {
|
||||
List<PermissionMenuItemVo> menus = List.of(menu(1L, null, "a", "A", 0, "a/p", "admin"));
|
||||
assertEquals(AdminMenuTreeBuilder.build(menus), AdminMenuTreeBuilder.build(menus));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_task_141_freeze_java_dto_boundary_empty_input() {
|
||||
assertTrue(AdminMenuTreeBuilder.build(null).isEmpty());
|
||||
assertTrue(AdminMenuTreeBuilder.build(List.of()).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_task_141_freeze_java_dto_boundary_single_item() {
|
||||
List<AdminMenuTreeBuilder.Node> tree = AdminMenuTreeBuilder.build(
|
||||
List.of(menu(1L, null, "only", "单页", 0, "page/x", "admin")));
|
||||
assertEquals(1, tree.size());
|
||||
assertEquals("/page/x", tree.get(0).route());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_task_141_freeze_java_dto_boundary_limit_or_missing_field() {
|
||||
assertTrue(AdminMenuTreeBuilder.build(List.of(menu(null, null, "no-id", "X", 0, "p", "admin"))).isEmpty());
|
||||
assertTrue(AdminMenuTreeBuilder.build(List.of(menu(null, null, null, "X", 0, "p", "admin"))).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_task_141_freeze_java_dto_invalid_input_rejected() {
|
||||
// 分组占位 route 不作为页面路由
|
||||
AdminMenuTreeBuilder.Node node = AdminMenuTreeBuilder.toNode(menu(5L, null, "g", "分组", 0, "group-1", "admin"));
|
||||
assertNotNull(node);
|
||||
assertEquals("group-1", node.route());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_task_141_freeze_java_dto_dependency_failure_returns_actionable_message() {
|
||||
// 纯逻辑:不引用 http/当前会话;key/route 分离(column_key 保持,route 独立)。
|
||||
assertEquals("/account/users", AdminMenuTreeBuilder.normalizeRoute("account/users"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user