192 lines
13 KiB
JavaScript
192 lines
13 KiB
JavaScript
function id(value) { return value == null ? '' : String(value) }
|
|
|
|
export async function run(context) {
|
|
const module = 'system'
|
|
const suffix = context.marker.replace(/\D/g, '').slice(-12)
|
|
|
|
const permissionsResponse = await context.request('GET', '/api/platform/v1/iam/permissions')
|
|
const permissions = context.apiSuccess(module, '查询菜单权限资源', permissionsResponse)
|
|
context.assert(module, '权限资源返回数组', permissionsResponse, Array.isArray(permissions),
|
|
`count=${Array.isArray(permissions) ? permissions.length : 'n/a'}`)
|
|
|
|
const permissionPayload = {
|
|
type: 'BUTTON',
|
|
code: `platform:acceptance:${suffix}`,
|
|
name: `Admin验收权限-${suffix}`,
|
|
parentId: null,
|
|
routePath: null,
|
|
httpMethod: null,
|
|
sortNo: 9999,
|
|
}
|
|
const createPermissionResponse = await context.request('POST', '/api/platform/v1/iam/permissions', permissionPayload)
|
|
const permission = context.apiSuccess(module, '创建自定义权限资源', createPermissionResponse)
|
|
const permissionId = id(permission?.id)
|
|
context.assert(module, '权限创建字段正确', createPermissionResponse,
|
|
permissionId && permission?.permissionCode === permissionPayload.code && permission?.builtIn === false,
|
|
`permissionId=${permissionId || 'missing'}`)
|
|
if (!permissionId) throw new Error('权限资源创建失败,系统模块无法继续')
|
|
context.createdResources.systemPermission = { id: permissionId, code: permissionPayload.code }
|
|
|
|
const additionalPermissionIds = []
|
|
for (const definition of [
|
|
{ type: 'MENU', routePath: `/acceptance-${suffix}`, httpMethod: null },
|
|
{ type: 'PAGE', routePath: `/acceptance-${suffix}/detail`, httpMethod: null },
|
|
{ type: 'API', routePath: `/api/platform/v1/acceptance/${suffix}`, httpMethod: 'GET' },
|
|
]) {
|
|
const payload = {
|
|
type: definition.type,
|
|
code: `platform:acceptance:${definition.type.toLowerCase()}:${suffix}`,
|
|
name: `Admin验收${definition.type}-${suffix}`,
|
|
parentId: null,
|
|
routePath: definition.routePath,
|
|
httpMethod: definition.httpMethod,
|
|
sortNo: 9999,
|
|
}
|
|
const response = await context.request('POST', '/api/platform/v1/iam/permissions', payload)
|
|
const created = context.apiSuccess(module, `创建 ${definition.type} 权限资源`, response)
|
|
const createdId = id(created?.id)
|
|
context.assert(module, `${definition.type} 类型与端点字段正确回显`, response,
|
|
createdId && created?.permissionType === definition.type && created?.routePath === definition.routePath
|
|
&& (created?.httpMethod ?? null) === definition.httpMethod,
|
|
`permissionId=${createdId || 'missing'}, type=${created?.permissionType || 'missing'}, method=${created?.httpMethod ?? 'null'}`)
|
|
if (createdId) additionalPermissionIds.push(createdId)
|
|
}
|
|
|
|
const updatePermissionResponse = await context.request('PUT', `/api/platform/v1/iam/permissions/${permissionId}`, {
|
|
name: `${permissionPayload.name}-已编辑`, parentId: null, routePath: null, httpMethod: null, sortNo: 9998,
|
|
})
|
|
const updatedPermission = context.apiSuccess(module, '编辑自定义权限资源', updatePermissionResponse)
|
|
context.assert(module, '权限编辑正确回显', updatePermissionResponse,
|
|
updatedPermission?.permissionName === `${permissionPayload.name}-已编辑` && Number(updatedPermission?.sortNo) === 9998,
|
|
`permissionId=${permissionId}`)
|
|
for (const status of ['DISABLED', 'ACTIVE']) {
|
|
const response = await context.request('PUT', `/api/platform/v1/iam/permissions/${permissionId}/status`, { status })
|
|
const changed = context.apiSuccess(module, `${status === 'ACTIVE' ? '启用' : '停用'}自定义权限`, response)
|
|
context.assert(module, `权限状态回显 ${status}`, response, changed?.status === status, `status=${changed?.status ?? 'missing'}`)
|
|
}
|
|
|
|
const rolePayload = { roleCode: `ADMIN_ACCEPT_${suffix}`, roleName: `Admin验收角色-${suffix}`, description: context.marker }
|
|
const createRoleResponse = await context.request('POST', '/api/platform/v1/iam/roles', rolePayload)
|
|
const role = context.apiSuccess(module, '创建平台自定义角色', createRoleResponse)
|
|
const roleId = id(role?.id)
|
|
context.assert(module, '角色创建字段正确', createRoleResponse,
|
|
roleId && role?.roleCode === rolePayload.roleCode && role?.roleType === 'CUSTOM', `roleId=${roleId || 'missing'}`)
|
|
if (!roleId) throw new Error('角色创建失败,系统模块无法继续')
|
|
context.createdResources.systemRole = { id: roleId, code: rolePayload.roleCode, finalStatus: 'DISABLED', retainedBecauseAssigned: true }
|
|
|
|
const editRoleResponse = await context.request('PUT', `/api/platform/v1/iam/roles/${roleId}`, {
|
|
roleName: `${rolePayload.roleName}-已编辑`, description: `${context.marker}-EDITED`,
|
|
})
|
|
const editedRole = context.apiSuccess(module, '编辑平台自定义角色', editRoleResponse)
|
|
context.assert(module, '角色编辑正确回显', editRoleResponse,
|
|
editedRole?.roleName === `${rolePayload.roleName}-已编辑`, `roleId=${roleId}`)
|
|
const grantResponse = await context.request('PUT', '/api/platform/v1/iam/roles/permissions', { roleId, permissionIds: [permissionId] })
|
|
context.apiSuccess(module, '角色全量保存权限', grantResponse)
|
|
const rolesResponse = await context.request('GET', '/api/platform/v1/iam/roles')
|
|
const roles = context.apiSuccess(module, '回查平台角色列表', rolesResponse)
|
|
const listedRole = roles?.find?.((item) => id(item.id) === roleId)
|
|
context.assert(module, '角色权限 ID 正确回显', rolesResponse,
|
|
listedRole && listedRole.permissionIds?.map(id).includes(permissionId),
|
|
`permissionIds=${listedRole?.permissionIds?.map(id).join(',') || 'missing'}`)
|
|
for (const status of ['DISABLED', 'ACTIVE']) {
|
|
const response = await context.request('PUT', `/api/platform/v1/iam/roles/${roleId}/status`, { status })
|
|
const changed = context.apiSuccess(module, `${status === 'ACTIVE' ? '启用' : '停用'}自定义角色`, response)
|
|
context.assert(module, `角色状态回显 ${status}`, response, changed?.status === status, `status=${changed?.status ?? 'missing'}`)
|
|
}
|
|
|
|
const optionsResponse = await context.request('GET', '/api/platform/v1/iam/users/options')
|
|
const options = context.apiSuccess(module, '查询内部用户可选平台角色', optionsResponse)
|
|
context.assert(module, '用户选项包含平台与测试角色', optionsResponse,
|
|
options?.tenants?.some((item) => id(item.id) === '0') && options?.roles?.some((item) => id(item.id) === roleId),
|
|
`tenants=${options?.tenants?.length ?? 'n/a'}, roles=${options?.roles?.length ?? 'n/a'}`)
|
|
|
|
const phone = await context.findAvailablePhone(module)
|
|
const username = `admin_accept_${suffix}`
|
|
const initialPassword = `Accept${suffix}A1`
|
|
const resetPassword = `Reset${suffix}B2`
|
|
const userPayload = {
|
|
username, displayName: `Admin验收用户-${suffix}`, phone,
|
|
email: `${username}@example.com`, tenantId: '0', roleIds: [roleId],
|
|
password: initialPassword, status: 'ACTIVE',
|
|
}
|
|
const createUserResponse = await context.request('POST', '/api/platform/v1/iam/users', userPayload)
|
|
const user = context.apiSuccess(module, '创建平台内部用户', createUserResponse)
|
|
const membershipId = id(user?.membershipId)
|
|
context.assert(module, '内部用户账号、平台范围和角色正确', createUserResponse,
|
|
membershipId && user?.username === username && id(user?.tenantId) === '0' && user?.roleIds?.map(id).includes(roleId),
|
|
`membershipId=${membershipId || 'missing'}`)
|
|
if (!membershipId) throw new Error('平台内部用户创建失败,系统模块无法继续')
|
|
context.createdResources.systemUser = { membershipId, username, finalStatus: 'DISABLED' }
|
|
|
|
const pageResponse = await context.request('GET', `/api/platform/v1/iam/users?page=1&size=20&keyword=${username}`)
|
|
const page = context.apiSuccess(module, '按账号查询平台内部用户', pageResponse)
|
|
const listedUser = page?.records?.find((item) => id(item.membershipId) === membershipId)
|
|
const missingUserFields = listedUser ? requiredUserFields().filter((field) => !Object.hasOwn(listedUser, field)) : requiredUserFields()
|
|
context.assert(module, '用户列表包含新账号且字段完整', pageResponse,
|
|
listedUser && missingUserFields.length === 0
|
|
&& (!Object.hasOwn(listedUser, 'lastLoginAt') || typeof listedUser.lastLoginAt === 'string'),
|
|
`records=${page?.records?.length ?? 'n/a'}, missing=${missingUserFields.join(',') || 'none'}, lastLoginAt=${listedUser?.lastLoginAt ?? 'omitted'}`)
|
|
|
|
const updateUserPayload = {
|
|
displayName: `${userPayload.displayName}-已编辑`, phone,
|
|
email: `edited-${userPayload.email}`, tenantId: '0', roleIds: [roleId],
|
|
}
|
|
const updateUserResponse = await context.request('PUT', `/api/platform/v1/iam/users/${membershipId}`, updateUserPayload)
|
|
const updatedUser = context.apiSuccess(module, '编辑平台内部用户', updateUserResponse)
|
|
context.assert(module, '用户编辑正确回显', updateUserResponse,
|
|
updatedUser?.displayName === updateUserPayload.displayName && updatedUser?.email === updateUserPayload.email,
|
|
`membershipId=${membershipId}`)
|
|
|
|
const loginResponse = await context.request('POST', '/api/auth/login', { loginId: username, password: initialPassword }, '')
|
|
const loginPassed = loginResponse.http === 200 && String(loginResponse.body?.code) === '200' && Boolean(loginResponse.body?.data?.accessToken)
|
|
context.addStep(module, '新建内部用户可使用初始密码登录', loginResponse, loginPassed, `username=${username}`)
|
|
|
|
const resetResponse = await context.request('PUT', `/api/platform/v1/iam/users/${membershipId}/password`, { newPassword: resetPassword })
|
|
context.apiSuccess(module, '重置平台内部用户密码', resetResponse)
|
|
const resetLoginResponse = await context.request('POST', '/api/auth/login', { loginId: username, password: resetPassword }, '')
|
|
const resetLoginPassed = resetLoginResponse.http === 200 && String(resetLoginResponse.body?.code) === '200' && Boolean(resetLoginResponse.body?.data?.accessToken)
|
|
context.addStep(module, '内部用户使用重置密码登录', resetLoginResponse, resetLoginPassed, `username=${username}`)
|
|
if (context.options.modules.includes('phase4-cross-region-events')) {
|
|
const restrictedOverseasToken = resetLoginResponse.body?.data?.accessToken || ''
|
|
context.assert(module, '受限角色登录同时签发统一Token', resetLoginResponse,
|
|
Boolean(restrictedOverseasToken), `tokenPresent=${Boolean(restrictedOverseasToken)}`,
|
|
{ scenario: 'canonical-overseas-permission-denial', deploymentBlocker: true })
|
|
if (restrictedOverseasToken) {
|
|
const denied = await context.request('GET', '/api/platform/v1/last-leg/orders?page=1&size=1', undefined, restrictedOverseasToken, {})
|
|
context.assert(module, '缺少canonical海外订单权限必须403', denied,
|
|
(denied.http === 403 || ['403', 'A0403'].includes(String(denied.body?.code)))
|
|
&& Boolean(denied.requestId) && Boolean(denied.traceId),
|
|
`HTTP=${denied.http}, code=${denied.body?.code ?? 'missing'}, requestId=${denied.requestId || 'missing'}, traceId=${denied.traceId || 'missing'}`,
|
|
{ scenario: 'canonical-overseas-permission-denial', deploymentBlocker: true })
|
|
}
|
|
}
|
|
|
|
const disableUserResponse = await context.request('PUT', `/api/platform/v1/iam/users/${membershipId}/status`, { status: 'DISABLED' })
|
|
const disabledUser = context.apiSuccess(module, '停用平台内部用户', disableUserResponse)
|
|
context.assert(module, '内部用户停用状态正确', disableUserResponse, disabledUser?.status === 'DISABLED', `status=${disabledUser?.status ?? 'missing'}`)
|
|
const disabledLoginResponse = await context.request('POST', '/api/auth/login', { loginId: username, password: resetPassword }, '')
|
|
context.addStep(module, '停用用户禁止登录', disabledLoginResponse,
|
|
disabledLoginResponse.http === 200 && String(disabledLoginResponse.body?.code) !== '200',
|
|
`HTTP=${disabledLoginResponse.http}, code=${disabledLoginResponse.body?.code ?? 'missing'}`)
|
|
|
|
const clearPermissionResponse = await context.request('PUT', '/api/platform/v1/iam/roles/permissions', { roleId, permissionIds: [] })
|
|
context.apiSuccess(module, '清空测试角色权限', clearPermissionResponse)
|
|
const deleteRoleResponse = await context.request('DELETE', `/api/platform/v1/iam/roles/${roleId}`)
|
|
context.expectConflict(module, '已分配成员的角色禁止删除', deleteRoleResponse, 'system-role-protection')
|
|
const finalDisableRoleResponse = await context.request('PUT', `/api/platform/v1/iam/roles/${roleId}/status`, { status: 'DISABLED' })
|
|
const finalRole = context.apiSuccess(module, '清理时停用测试角色', finalDisableRoleResponse)
|
|
context.assert(module, '保留测试角色处于停用状态', finalDisableRoleResponse, finalRole?.status === 'DISABLED',
|
|
`status=${finalRole?.status ?? 'missing'}`)
|
|
const deletePermissionResponse = await context.request('DELETE', `/api/platform/v1/iam/permissions/${permissionId}`)
|
|
context.apiSuccess(module, '删除临时权限资源', deletePermissionResponse)
|
|
for (const additionalPermissionId of additionalPermissionIds) {
|
|
const response = await context.request('DELETE', `/api/platform/v1/iam/permissions/${additionalPermissionId}`)
|
|
context.apiSuccess(module, '删除四类型验收权限资源', response, `permissionId=${additionalPermissionId}`)
|
|
}
|
|
}
|
|
|
|
function requiredUserFields() {
|
|
return ['membershipId', 'userId', 'authUserId', 'username', 'displayName', 'phone', 'email',
|
|
'tenantId', 'tenantName', 'memberType', 'status', 'roleIds', 'roleNames', 'protectedAccount']
|
|
}
|