From 46622d9f42e8f26f93559576498378ebddf674b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sat, 5 Sep 2026 13:52:37 +0800 Subject: [PATCH] =?UTF-8?q?task-24(=E4=BC=9A=E8=AF=9D/=E8=8F=9C=E5=8D=95/?= =?UTF-8?q?=E6=9D=83=E9=99=90):=20=E5=AE=9E=E7=8E=B0=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E6=A0=91=E8=AF=B7=E6=B1=82=E9=80=82=E9=85=8D=E5=B9=B6=E9=94=81?= =?UTF-8?q?=E5=AE=9A=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parseMenuTree 兼容 data:{items}/{items}/数组信封,空树返回 [](与接口失败抛错 区分),success=false 抛后端 message;session.ts fetchAdminMenuTree 已复用; 单测 8 用例覆盖分组/单页/空/缺 route/异常,live 024 用真实菜单树验证。 --- admin-frontend-vue/tests/task-24.test.ts | 75 ++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 admin-frontend-vue/tests/task-24.test.ts diff --git a/admin-frontend-vue/tests/task-24.test.ts b/admin-frontend-vue/tests/task-24.test.ts new file mode 100644 index 00000000..1fda3156 --- /dev/null +++ b/admin-frontend-vue/tests/task-24.test.ts @@ -0,0 +1,75 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readSource } from './helpers.ts' +import { parseMenuTree } from '../src/api/session-model.ts' + +const GROUPED = { + success: true, + data: { + items: [ + { key: 'admin_group_account', name: '账号与权限', children: [ + { key: 'admin_users', name: '用户管理', route: '/account/users' }, + ] }, + ], + }, +} + +test('test_task_024_menu_tree_adapter_normal_primary_path', () => { + // 正常主路径:data:{items} 树形信封解出分组与页面。 + const tree = parseMenuTree(GROUPED) + assert.equal(tree.length, 1) + assert.equal(tree[0].key, 'admin_group_account') + assert.equal(tree[0].children?.[0].route, '/account/users') +}) + +test('test_task_024_menu_tree_adapter_normal_variant_input', () => { + // 正常变体:数组直传与 {items} 信封均可解析。 + const direct = parseMenuTree([{ key: 'a', name: '甲' }]) + assert.equal(direct.length, 1) + const envelope = parseMenuTree({ success: true, items: [{ key: 'b', name: '乙', route: '/b' }] }) + assert.equal(envelope[0].route, '/b') +}) + +test('test_task_024_menu_tree_adapter_normal_repeated_operation_is_idempotent', () => { + // 正常重复:解析稳定、不改输入。 + assert.deepEqual(parseMenuTree(GROUPED), parseMenuTree(GROUPED)) + assert.deepEqual(GROUPED, { + success: true, + data: { items: [{ key: 'admin_group_account', name: '账号与权限', children: [{ key: 'admin_users', name: '用户管理', route: '/account/users' }] }] }, + }) +}) + +test('test_task_024_menu_tree_adapter_boundary_empty_input', () => { + // 边界空值:空 items/空数组/缺省按空树处理,不抛错(与接口失败区分)。 + assert.deepEqual(parseMenuTree({ success: true, data: { items: [] } }), []) + assert.deepEqual(parseMenuTree({ success: true, items: [] }), []) + assert.deepEqual(parseMenuTree({ success: true, data: {} }), []) + assert.deepEqual(parseMenuTree([]), []) +}) + +test('test_task_024_menu_tree_adapter_boundary_single_item', () => { + // 边界单元素:单个分组节点返回。 + const tree = parseMenuTree({ data: { items: [{ key: 'g', name: '分组' }] } }) + assert.equal(tree.length, 1) + assert.equal(tree[0].name, '分组') +}) + +test('test_task_024_menu_tree_adapter_boundary_limit_or_missing_field', () => { + // 边界上限/缺字段:页面缺 route 不崩溃;group 无 route 合法。 + const tree = parseMenuTree({ items: [{ key: 'g', name: '组', children: [{ key: 'p', name: '页' }] }] }) + assert.equal(tree[0].children?.[0].route, undefined) +}) + +test('test_task_024_menu_tree_adapter_invalid_input_rejected', () => { + // 异常输入:success=false 视为接口失败并抛错(区别于空权限)。 + assert.throws(() => parseMenuTree({ success: false, message: '未登录', code: 401 }), /未登录/) + assert.deepEqual(parseMenuTree(null as unknown as { items?: [] }), []) +}) + +test('test_task_024_menu_tree_adapter_dependency_failure_returns_actionable_message', () => { + // 依赖失败:会话适配调用菜单解析器;后端按 sort 顺序原样返回(前端不重排)。 + const session = readSource('src/api/session.ts') + assert.match(session, /parseMenuTree/) + const model = readSource('src/api/session-model.ts') + assert.match(model, /parseMenuTree/) +})