61 lines
3.5 KiB
TypeScript
61 lines
3.5 KiB
TypeScript
// 管理端用户面板两处缺陷的回归锁定:
|
|
// (1) getUserRole 的 role 摘要不带 permissions(线上实测),projectCreateSources 不得因此抛异常;
|
|
// 展示来源时用已加载角色列表里同一 roleId 的条目补齐权限,不新增请求。
|
|
// (2) 一次性登录密码按服务端严格解码要求发送 JSON 对象体。
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { renderToStaticMarkup } from 'react-dom/server'
|
|
import { api, type RoleRecord } from '../src/api'
|
|
import { auth } from '../src/auth'
|
|
import { ProjectCreateSourceNote, roleWithPermissions } from '../src/components/AccountUsersPanel'
|
|
import { projectCreateSources } from '../src/rolePermissions'
|
|
|
|
const listed: RoleRecord = {
|
|
roleId: 'role-pm', name: '项目经理', description: '研发', enabled: true, roleVersion: 3, bindingCount: 1,
|
|
permissions: [
|
|
{ actionId: 'project.create', allowed: true, capAllowed: true, permissionVersion: 2 },
|
|
{ actionId: 'document.read', allowed: true, capAllowed: true, permissionVersion: 1 },
|
|
],
|
|
}
|
|
/** getUserRole 的真实响应形状:有角色字段、没有 permissions。 */
|
|
const summary = { roleId: 'role-pm', name: '项目经理', description: '研发', enabled: true, roleVersion: 3, bindingCount: 1 }
|
|
|
|
afterEach(() => vi.restoreAllMocks())
|
|
|
|
describe('角色摘要缺少 permissions 时的创建能力来源', () => {
|
|
it('把缺失的 permissions 当作空集,不抛异常也不虚构角色来源', () => {
|
|
expect(projectCreateSources({ name: '项目经理' }, [])).toEqual([])
|
|
expect(projectCreateSources({ name: '项目经理' }, ['project.create'])).toEqual(['明确系统授权'])
|
|
expect(projectCreateSources({ ...summary, permissions: undefined }, [])).toEqual([])
|
|
expect(renderToStaticMarkup(<ProjectCreateSourceNote role={{ name: '项目经理' }} grants={[]} />))
|
|
.toContain('被加入项目后由成员关系提供基础协作')
|
|
})
|
|
|
|
it('用角色列表里同一 roleId 的权限补齐来源', () => {
|
|
const resolved = roleWithPermissions(summary, [listed])
|
|
expect(resolved?.permissions?.some(item => item.actionId === 'project.create')).toBe(true)
|
|
expect(renderToStaticMarkup(<ProjectCreateSourceNote role={resolved} grants={[]} />))
|
|
.toContain('创建项目来源:职位角色「项目经理」')
|
|
})
|
|
|
|
it('列表里没有对应条目时按无角色默认权限处理,角色自带权限时不被列表覆盖', () => {
|
|
expect(projectCreateSources(roleWithPermissions(summary, []), [])).toEqual([])
|
|
const denied = { ...listed, roleId: 'role-other', permissions: [{ actionId: 'project.create', allowed: false, capAllowed: false, permissionVersion: 1 }] }
|
|
expect(projectCreateSources(roleWithPermissions({ ...summary, permissions: denied.permissions }, [denied]), [])).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('管理员一次性登录密码请求体', () => {
|
|
it('发送 POST + JSON 对象体,避免服务端 400 EOF', async () => {
|
|
const issued = { owner: 'alice', id: 'otp_123', oneTimePassword: 'otp-0001', mustChangePassword: true }
|
|
const request = vi.spyOn(auth, 'fetch').mockResolvedValue(new Response(JSON.stringify(issued)))
|
|
|
|
const out = await api.adminIssueOneTimePassword('alice')
|
|
|
|
const [url, options] = request.mock.calls[0]
|
|
expect(url).toBe('/api/admin/users/alice/one-time-password')
|
|
expect(options?.method).toBe('POST')
|
|
expect(options?.headers).toEqual({ 'Content-Type': 'application/json' })
|
|
expect(JSON.parse(String(options?.body))).toEqual({})
|
|
expect(out).toEqual(issued)
|
|
})
|
|
}) |