From 40905cbd04c2e332cd4bc2b9e0c5b3e1da9cccfa Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期一, 30 三月 2026 08:17:32 +0800
Subject: [PATCH] feat: complete rsf-design phase 1 integration

---
 rsf-design/tests/system-manage-contract.test.mjs |  129 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 128 insertions(+), 1 deletions(-)

diff --git a/rsf-design/tests/system-manage-contract.test.mjs b/rsf-design/tests/system-manage-contract.test.mjs
index c9879e9..f542a6b 100644
--- a/rsf-design/tests/system-manage-contract.test.mjs
+++ b/rsf-design/tests/system-manage-contract.test.mjs
@@ -1,20 +1,147 @@
 import assert from 'node:assert/strict'
+import { register } from 'node:module'
 import test from 'node:test'
-import { buildUserListParams, buildRoleListParams } from '../src/api/system-manage.js'
+
+const menuTreeFixture = [
+  {
+    id: 1,
+    parentId: 0,
+    name: 'menu.user',
+    route: 'user',
+    type: 0,
+    component: 'user',
+    authority: 'system:user',
+    icon: 'People',
+    sort: 1,
+    status: 1,
+    memo: 'User menu',
+    children: [
+      {
+        id: 2,
+        parentId: 1,
+        name: 'Query User',
+        type: 1,
+        authority: 'system:user:list',
+        icon: 'Search',
+        sort: 1,
+        status: 0,
+        memo: 'Query action'
+      }
+    ]
+  }
+]
+
+register(
+  `data:text/javascript,export function resolve(specifier, context, nextResolve){ if(specifier===\'@/utils/http\'){ return { shortCircuit:true, url:\'data:text/javascript,export default { get(){ return Promise.resolve({}) }, post(config){ if(config?.url===\\'/menu/list\\'){ globalThis.__lastMenuListConfig = config; return Promise.resolve(${JSON.stringify(menuTreeFixture.flatMap((node) => [node, ...node.children]))}) } if(config?.url===\\'/menu/tree\\'){ globalThis.__lastMenuTreeConfig = config; return Promise.resolve(${JSON.stringify(menuTreeFixture)}) } return Promise.resolve(config) } }\' } } return nextResolve(specifier, context) }`,
+  import.meta.url
+)
+
+const {
+  buildUserListParams,
+  buildRoleListParams,
+  fetchDeleteMenu,
+  fetchGetMenuList,
+  fetchGetMenuTree,
+  fetchResetUserPassword,
+  fetchGetRoleScopeList,
+  fetchGetRoleScopeTree,
+  fetchSaveMenu,
+  fetchUpdateMenu
+} = await import('../src/api/system-manage.js')
 
 test('buildUserListParams matches the rsf-admin paging contract', () => {
+  assert.equal(typeof buildUserListParams, 'function')
+  assert.equal(typeof buildRoleListParams, 'function')
+
   assert.deepEqual(
     buildUserListParams({
       current: 2,
       pageSize: 20,
       username: 'root',
+      email: 'root@example.com',
       deptId: 3
     }),
     {
       current: 2,
       pageSize: 20,
       username: 'root',
+      email: 'root@example.com',
       deptId: 3
     }
   )
 })
+
+test('fetchResetUserPassword requires an admin update payload', () => {
+  assert.throws(() => fetchResetUserPassword(1), /object payload/i)
+  assert.throws(() => fetchResetUserPassword({ id: 1 }), /password/i)
+
+  return fetchResetUserPassword({ id: 1, newPassword: 'secret' }).then((config) => {
+    assert.equal(config.params.password, 'secret')
+  })
+})
+
+test('fetchGetMenuList folds button nodes into authList', async () => {
+  const menuList = await fetchGetMenuList()
+
+  assert.equal(menuList.length, 1)
+  assert.equal(menuList[0].id, '1')
+  assert.equal(menuList[0].parentId, '0')
+  assert.equal(menuList[0].route, 'user')
+  assert.equal(menuList[0].authority, 'system:user')
+  assert.equal(menuList[0].status, 1)
+  assert.equal(menuList[0].memo, 'User menu')
+  assert.equal(menuList[0].meta.title, 'menus.user')
+  assert.equal(menuList[0].component, 'user')
+  assert.equal(menuList[0].meta.sort, 1)
+  assert.equal(menuList[0].meta.isEnable, true)
+  assert.deepEqual(menuList[0].meta.authList, [
+    {
+      id: '2',
+      parentId: '1',
+      parentName: '',
+      name: 'Query User',
+      title: 'Query User',
+      route: '',
+      component: '',
+      authMark: 'system:user:list',
+      authority: 'system:user:list',
+      icon: 'Search',
+      sort: 1,
+      status: 0,
+      memo: 'Query action',
+      type: 1
+    }
+  ])
+  assert.equal(menuList[0].children.length, 0)
+  assert.deepEqual(globalThis.__lastMenuListConfig?.params, {})
+})
+
+test('menu tree helpers always send an explicit empty body for Spring Map request bodies', async () => {
+  await fetchGetMenuTree()
+  assert.deepEqual(globalThis.__lastMenuTreeConfig?.params, {})
+
+  await fetchGetRoleScopeTree('menu')
+  assert.equal(globalThis.__lastMenuTreeConfig?.url, '/menu/tree')
+  assert.deepEqual(globalThis.__lastMenuTreeConfig?.params, {})
+})
+
+test('menu CRUD helpers target the real rsf-server endpoints', async () => {
+  assert.equal(typeof fetchSaveMenu, 'function')
+  assert.equal(typeof fetchUpdateMenu, 'function')
+  assert.equal(typeof fetchDeleteMenu, 'function')
+
+  const saveConfig = await fetchSaveMenu({ name: '鑿滃崟' })
+  const updateConfig = await fetchUpdateMenu({ id: 1, name: '鑿滃崟' })
+  const deleteConfig = await fetchDeleteMenu('1,2')
+
+  assert.equal(saveConfig.url, '/menu/save')
+  assert.deepEqual(saveConfig.params, { name: '鑿滃崟' })
+  assert.equal(updateConfig.url, '/menu/update')
+  assert.deepEqual(updateConfig.params, { id: 1, name: '鑿滃崟' })
+  assert.equal(deleteConfig.url, '/menu/remove/1,2')
+})
+
+test('scope resolvers fail fast on invalid scope types', () => {
+  assert.throws(() => fetchGetRoleScopeList('invalid', 1), /Unsupported scope type/i)
+  assert.throws(() => fetchGetRoleScopeTree('invalid', {}), /Unsupported scope type/i)
+})

--
Gitblit v1.9.1