ci: 统一测试入口 scripts/ci.mjs(协议/用户管理/单测/双设备/实时链路/构建/依赖审计)+ Gitea Actions 工作流
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
// 用户管理审计:角色 / 列表 / 新建 / 重置密码 / 删除 / 越权防护
|
||||
const BASE = process.env.SYNC_BASE || 'http://127.0.0.1:3780'
|
||||
let pass = 0, fail = 0
|
||||
function check(name, ok, detail) {
|
||||
if (ok) { pass++; console.log(' PASS ' + name) }
|
||||
else { fail++; console.log(' FAIL ' + name + (detail ? ' -> ' + String(JSON.stringify(detail)).slice(0, 160) : '')) }
|
||||
}
|
||||
async function req(path, method, body, token) {
|
||||
const r = await fetch(BASE + path, {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: 'Bearer ' + token } : {}) },
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
})
|
||||
return { status: r.status, data: await r.json().catch(() => ({})) }
|
||||
}
|
||||
async function main() {
|
||||
const ax = await req('/api/auth/register', 'POST', { username: 'xionglin', password: 'admin123' })
|
||||
check('xionglin 注册成功', ax.status === 200, ax.data)
|
||||
const nu = await req('/api/auth/register', 'POST', { username: 'normal_u', password: 'normal123' })
|
||||
check('普通用户注册成功', nu.status === 200, nu.data)
|
||||
const listNoAuth = await req('/api/admin/users', 'GET', null, nu.data.token)
|
||||
check('非管理员访问用户列表 403', listNoAuth.status === 403, listNoAuth.status)
|
||||
const badList = await req('/api/admin/users', 'GET', null)
|
||||
check('无 token 访问用户列表 403', badList.status === 403, badList.status)
|
||||
const list = await req('/api/admin/users', 'GET', null, ax.data.token)
|
||||
check('管理员可见用户列表', list.status === 200 && ((list.data.users || []).some(u => u.username === 'normal_u') && (list.data.users.some(u => u.username === 'xionglin' && u.role === 'admin'))), list.data)
|
||||
check('列表返回 me 字段', !!list.data.me)
|
||||
const create = await req('/api/admin/users/create', 'POST', { username: 'target_u', password: 'pass111' }, ax.data.token)
|
||||
check('管理员新建用户', create.status === 200 && !!create.data.userId, create.data)
|
||||
const dupCreate = await req('/api/admin/users/create', 'POST', { username: 'target_u', password: 'pass222' }, ax.data.token)
|
||||
check('重复用户名 409', dupCreate.status === 409, dupCreate.status)
|
||||
const loginTarget = await req('/api/auth/login', 'POST', { username: 'target_u', password: 'pass111' })
|
||||
check('新用户可用初始密码登录', loginTarget.status === 200, loginTarget.status)
|
||||
const reset = await req('/api/admin/users/password', 'POST', { userId: create.data.userId, newPassword: 'pass999' }, ax.data.token)
|
||||
check('管理员重置密码 200', reset.status === 200, reset.data)
|
||||
const oldLogin = await req('/api/auth/login', 'POST', { username: 'target_u', password: 'pass111' })
|
||||
check('旧密码失效 401', oldLogin.status === 401, oldLogin.status)
|
||||
const newLogin = await req('/api/auth/login', 'POST', { username: 'target_u', password: 'pass999' })
|
||||
check('重置后新密码可登录', newLogin.status === 200, newLogin.status)
|
||||
const selfDel = await req('/api/admin/users/delete', 'POST', { userId: ax.data.userId }, ax.data.token)
|
||||
check('不能删除自己 400', selfDel.status === 400, selfDel.status)
|
||||
const del = await req('/api/admin/users/delete', 'POST', { userId: create.data.userId }, ax.data.token)
|
||||
check('删除用户 200', del.status === 200, del.data)
|
||||
const goneLogin = await req('/api/auth/login', 'POST', { username: 'target_u', password: 'pass999' })
|
||||
check('删除后登录被拒 401', goneLogin.status === 401, goneLogin.status)
|
||||
const list2 = await req('/api/admin/users', 'GET', null, ax.data.token)
|
||||
check('列表不再含已删用户', !((list2.data.users || []).some(u => u.username === 'target_u')))
|
||||
|
||||
console.log('==== 用户管理审计: PASS ' + pass + ' / FAIL ' + fail + ' ====')
|
||||
process.exit(fail > 0 ? 1 : 0)
|
||||
}
|
||||
main().catch(e => { console.error('FAILED:', e && (e.stack || e.message)); process.exit(1) })
|
||||
Reference in New Issue
Block a user