Files
backmanager-server/test-all.js
2026-06-22 20:48:29 +08:00

355 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// test-all.js —— 手动运行,逐接口验证全部 CRUD
// 用法: node test-all.js
require('dotenv').config()
const http = require('http')
const BASE = 'http://127.0.0.1:3000'
let token = ''
let failCount = 0
let passCount = 0
// ========== 工具函数 ==========
function req(method, path, body, useToken = true) {
return new Promise((resolve, reject) => {
const u = new URL(path, BASE)
const opts = {
hostname: u.hostname, port: u.port, path: u.pathname + u.search, method,
headers: { 'Content-Type': 'application/json' },
}
if (useToken && token) opts.headers['Authorization'] = 'Bearer ' + token
const r = http.request(opts, (res) => {
let d = ''
res.on('data', (c) => (d += c))
res.on('end', () => {
try { resolve({ status: res.statusCode, body: JSON.parse(d) }) }
catch { resolve({ status: res.statusCode, body: d.substring(0, 300) }) }
})
})
r.on('error', reject)
if (body) r.write(JSON.stringify(body))
r.end()
})
}
function check(label, res, expectStatus, expectInfo) {
const ok = res.status === expectStatus
if (ok) {
passCount++
console.log(`${label}`)
} else {
failCount++
console.log(`${label} 预期 HTTP ${expectStatus}, 实际 HTTP ${res.status}`)
console.log(` 响应: ${JSON.stringify(res.body).substring(0, 200)}`)
}
if (expectInfo) console.log(`${expectInfo}`)
}
// ========== 主流程 ==========
async function main() {
console.log('╔══════════════════════════════════════════╗')
console.log('║ 企业管理系统 — 接口全量测试 ║')
console.log('╚══════════════════════════════════════════╝')
console.log(`服务地址: ${BASE}\n`)
// ────────── 1. 登录 ──────────
console.log('━'.repeat(50))
console.log('【1】 用户登录模块')
console.log('━'.repeat(50))
let r = await req('POST', '/api/user/login', { username: 'admin', password: '123456' }, false)
check('POST /api/user/login (正确密码)', r, 200)
if (r.body?.data?.token) {
token = r.body.data.token
console.log(` ↳ 角色: ${r.body.data.userInfo.role}, 姓名: ${r.body.data.userInfo.real_name}`)
}
r = await req('POST', '/api/user/login', { username: 'admin', password: 'wrong' }, false)
check('POST /api/user/login (错误密码)', r, 400, '→ 账号或密码错误')
r = await req('POST', '/api/user/login', { username: '', password: '' }, false)
check('POST /api/user/login (空参数)', r, 400, '→ 用户名和密码必填')
// ────────── 2. 个人信息 ──────────
console.log('\n━'.repeat(50))
console.log('【2】 个人信息 & 改密')
console.log('━'.repeat(50))
r = await req('GET', '/api/user/info')
check('GET /api/user/info', r, 200)
if (r.body?.data) console.log(` ↳ 用户名: ${r.body.data.username}, 角色: ${r.body.data.role}`)
r = await req('POST', '/api/user/logout')
check('POST /api/user/logout', r, 200, '→ JWT 无状态,前端删 token 即可')
r = await req('PUT', '/api/user/password', { oldPassword: 'wrong', newPassword: 'newpwd999' })
check('PUT /api/user/password (旧密码错)', r, 400, '→ 旧密码错误')
r = await req('PUT', '/api/user/password', { oldPassword: '123456', newPassword: '123456' })
check('PUT /api/user/password (新旧相同)', r, 400, '→ 新密码不能与旧密码相同')
r = await req('PUT', '/api/user/password', { oldPassword: '123456', newPassword: '123' })
check('PUT /api/user/password (新密码太短)', r, 400, '→ 新密码至少 6 位')
// ────────── 3. 用户管理 CRUD ──────────
console.log('\n━'.repeat(50))
console.log('【3】 用户管理 CRUD管理员')
console.log('━'.repeat(50))
r = await req('GET', '/api/users?page=1&pageSize=10')
check('GET /api/users (列表)', r, 200)
if (r.body?.data) console.log(` ↳ 共 ${r.body.data.total} 个用户, 本页 ${r.body.data.list?.length}`)
const ts = Date.now()
const newUser = `tester_${ts}`
r = await req('POST', '/api/users', { username: newUser, password: 'pass123', real_name: '测试员', role: 'user' })
check('POST /api/users (创建)', r, 200)
let userId = r.body?.data?.id
if (userId) console.log(` ↳ 新建用户 ID: ${userId}, 用户名: ${newUser}`)
r = await req('POST', '/api/users', { username: 'admin', password: '123456' })
check('POST /api/users (重复用户名)', r, 409, '→ 用户名已存在')
r = await req('POST', '/api/users', { username: 'bad', password: '12', role: 'superman' })
check('POST /api/users (非法角色)', r, 400, '→ 角色只能为 admin 或 user')
if (userId) {
r = await req('GET', '/api/users/' + userId)
check('GET /api/users/:id (详情)', r, 200)
r = await req('PUT', '/api/users/' + userId, { real_name: '改名后的测试员', status: 1 })
check('PUT /api/users/:id (更新姓名)', r, 200)
if (r.body?.data) console.log(` ↳ 新姓名: ${r.body.data.real_name}`)
r = await req('PUT', '/api/users/' + userId, { password: 'newpass456' })
check('PUT /api/users/:id (管理员重置密码)', r, 200, '→ 管理员可直接改他人密码')
// 用新用户登录验证改密成功
const r2 = await req('POST', '/api/user/login', { username: newUser, password: 'newpass456' }, false)
check(' └ 新用户用新密码登录', r2, 200)
// 测试非管理员访问
const userToken = r2.body?.data?.token
if (userToken) {
const oldToken = token
token = userToken
r = await req('GET', '/api/users')
check(' └ 普通用户访问用户列表', r, 403, '→ 需要管理员权限')
token = oldToken
}
r = await req('DELETE', '/api/users/' + userId)
check('DELETE /api/users/:id (删除)', r, 200)
}
r = await req('DELETE', '/api/users/1')
check('DELETE /api/users/1 (删自己)', r, 400, '→ 不能删除自己')
r = await req('GET', '/api/users', null, false)
check('GET /api/users (无 token)', r, 401, '→ 未登录')
// ────────── 4. 客户管理 ──────────
console.log('\n━'.repeat(50))
console.log('【4】 客户管理 CRUD')
console.log('━'.repeat(50))
r = await req('GET', '/api/customers?page=1&pageSize=10')
check('GET /api/customers (列表)', r, 200)
if (r.body?.data) console.log(` ↳ 共 ${r.body.data.total}`)
r = await req('POST', '/api/customers', { name: '测试加盟商', phone: '13800001111', province: '广东', city: '东莞', district: '松山湖', address: '科技路88号', customer_type: 'VIP', email: 'test@test.com' })
check('POST /api/customers (创建VIP客户)', r, 200)
let custId = r.body?.data?.id
if (custId) console.log(` ↳ 新建客户 ID: ${custId}, 类型: ${r.body.data.customer_type}`)
r = await req('POST', '/api/customers', { name: '' })
check('POST /api/customers (缺姓名)', r, 400, '→ 客户姓名必填')
// 不传 customer_type 应为默认 Normal
r = await req('POST', '/api/customers', { name: '默认类型测试' })
check('POST /api/customers (不传类型默认Normal)', r, 200)
if (r.body?.data) console.log(` ↳ 默认类型: ${r.body.data.customer_type}`)
if (r.body?.data?.id) await req('DELETE', '/api/customers/' + r.body.data.id)
if (custId) {
r = await req('GET', '/api/customers/' + custId)
check('GET /api/customers/:id (详情)', r, 200)
r = await req('PUT', '/api/customers/' + custId, { phone: '13900002222', customer_type: 'Normal', remark: '更新备注' })
check('PUT /api/customers/:id (更新类型+电话)', r, 200)
if (r.body?.data) console.log(` ↳ 更新后类型: ${r.body.data.customer_type}, 电话: ${r.body.data.phone}`)
r = await req('GET', '/api/customers?name=测试')
check('GET /api/customers?name=测试 (按姓名搜索)', r, 200)
r = await req('GET', '/api/customers?customer_type=Normal')
check('GET /api/customers?customer_type=Normal (按类型筛选)', r, 200)
if (r.body?.data) console.log(` ↳ Normal 类型共 ${r.body.data.total}`)
r = await req('DELETE', '/api/customers/' + custId)
check('DELETE /api/customers/:id (删除)', r, 200)
}
r = await req('GET', '/api/customers/99999')
check('GET /api/customers/99999 (不存在)', r, 404, '→ 客户不存在')
// ────────── 5. 员工管理 ──────────
console.log('\n━'.repeat(50))
console.log('【5】 员工管理 CRUD')
console.log('━'.repeat(50))
r = await req('GET', '/api/employees?page=1&pageSize=10')
check('GET /api/employees (列表)', r, 200)
r = await req('POST', '/api/employees', { name: '测试员工', gender: '男', age: 30, education: '本科', department: '研发部', position: '工程师', salary: 15000, phone: '13700000001', email: 'emp@test.com' })
check('POST /api/employees (创建)', r, 200)
let empId = r.body?.data?.id
if (empId) console.log(` ↳ 新建员工 ID: ${empId}, 部门: ${r.body.data.department}`)
if (empId) {
r = await req('GET', '/api/employees/' + empId)
check('GET /api/employees/:id (详情)', r, 200)
r = await req('PUT', '/api/employees/' + empId, { salary: 18000, position: '高级工程师' })
check('PUT /api/employees/:id (更新)', r, 200)
r = await req('GET', '/api/employees?status=1')
check('GET /api/employees?status=1 (在职筛选)', r, 200)
r = await req('DELETE', '/api/employees/' + empId)
check('DELETE /api/employees/:id (删除)', r, 200)
}
// ────────── 6. 产品管理 ──────────
console.log('\n━'.repeat(50))
console.log('【6】 产品管理 CRUD')
console.log('━'.repeat(50))
r = await req('GET', '/api/products?page=1&pageSize=10')
check('GET /api/products (列表)', r, 200)
r = await req('POST', '/api/products', { name: '测试产品-X1', type: '电子产品', quantity: 500, price: 299.99, unit: '台', specification: '型号 X1-2026', supplier: '供应商A' })
check('POST /api/products (创建)', r, 200)
let prodId = r.body?.data?.id
if (prodId) console.log(` ↳ 新建产品 ID: ${prodId}, 库存: ${r.body.data.quantity}`)
if (prodId) {
r = await req('GET', '/api/products/' + prodId)
check('GET /api/products/:id (详情)', r, 200)
r = await req('PUT', '/api/products/' + prodId, { price: 259.99, quantity: 480 })
check('PUT /api/products/:id (更新价格库存)', r, 200)
r = await req('GET', '/api/products?name=测试')
check('GET /api/products?name=测试 (搜索)', r, 200)
r = await req('DELETE', '/api/products/' + prodId)
check('DELETE /api/products/:id (删除)', r, 200)
}
// ────────── 7. 合同管理 ──────────
console.log('\n━'.repeat(50))
console.log('【7】 合同管理 CRUD关联客户+员工)')
console.log('━'.repeat(50))
// 先创建客户和员工做外键
const cRes = await req('POST', '/api/customers', { name: '合同测试客户' })
const eRes = await req('POST', '/api/employees', { name: '合同业务员' })
const cId = cRes.body?.data?.id
const eId = eRes.body?.data?.id
console.log(` ↳ 先创建客户(${cId}) 和 员工(${eId}) 供合同关联`)
r = await req('POST', '/api/contracts', { customer_id: cId, contract_name: '年度供货协议', contract_no: 'HT-2026-088', amount: 500000, effective_date: '2026-06-01', expiry_date: '2027-05-31', employee_id: eId, status: '生效' })
check('POST /api/contracts (创建)', r, 200)
let conId = r.body?.data?.id
if (conId) {
console.log(` ↳ 新建合同 ID: ${conId}`)
if (r.body?.data?.customer_name) console.log(` ↳ 联查客户: ${r.body.data.customer_name}, 业务员: ${r.body.data.employee_name}`)
}
r = await req('POST', '/api/contracts', { customer_id: 99999, contract_name: '无效合同' })
check('POST /api/contracts (客户不存在)', r, 400, '→ 关联客户不存在')
if (conId) {
r = await req('GET', '/api/contracts')
check('GET /api/contracts (列表)', r, 200)
r = await req('GET', '/api/contracts/' + conId)
check('GET /api/contracts/:id (详情)', r, 200)
r = await req('PUT', '/api/contracts/' + conId, { status: '完成', remark: '已履约' })
check('PUT /api/contracts/:id (更新状态)', r, 200)
r = await req('GET', '/api/contracts?status=完成')
check('GET /api/contracts?status=完成 (状态筛选)', r, 200)
r = await req('DELETE', '/api/contracts/' + conId)
check('DELETE /api/contracts/:id (删除)', r, 200)
}
// 清理外键依赖数据
if (cId) await req('DELETE', '/api/customers/' + cId)
if (eId) await req('DELETE', '/api/employees/' + eId)
// ────────── 8. 售后管理 ──────────
console.log('\n━'.repeat(50))
console.log('【8】 售后管理 CRUD关联客户+员工)')
console.log('━'.repeat(50))
const c2 = await req('POST', '/api/customers', { name: '售后测试客户' })
const e2 = await req('POST', '/api/employees', { name: '售后处理员' })
const cId2 = c2.body?.data?.id
const eId2 = e2.body?.data?.id
console.log(` ↳ 先创建客户(${cId2}) 和 员工(${eId2})`)
r = await req('POST', '/api/after-sales', { customer_id: cId2, feedback: '设备运行异常,需技术支持', employee_id: eId2, handle_method: '更换故障模块', handle_status: '处理中', service_date: '2026-06-20' })
check('POST /api/after-sales (创建)', r, 200)
let asId = r.body?.data?.id
if (asId) {
console.log(` ↳ 新建售后 ID: ${asId}`)
if (r.body?.data?.customer_name) console.log(` ↳ 联查客户: ${r.body.data.customer_name}, 处理人: ${r.body.data.employee_name}`)
}
r = await req('POST', '/api/after-sales', { customer_id: 99999, feedback: '测试' })
check('POST /api/after-sales (客户不存在)', r, 400, '→ 关联客户不存在')
if (asId) {
r = await req('GET', '/api/after-sales')
check('GET /api/after-sales (列表)', r, 200)
r = await req('GET', '/api/after-sales/' + asId)
check('GET /api/after-sales/:id (详情)', r, 200)
r = await req('PUT', '/api/after-sales/' + asId, { handle_status: '已完成', handle_method: '远程升级固件解决' })
check('PUT /api/after-sales/:id (更新)', r, 200)
r = await req('GET', '/api/after-sales?handle_status=已完成')
check('GET /api/after-sales?handle_status=已完成 (状态筛选)', r, 200)
r = await req('DELETE', '/api/after-sales/' + asId)
check('DELETE /api/after-sales/:id (删除)', r, 200)
}
if (cId2) await req('DELETE', '/api/customers/' + cId2)
if (eId2) await req('DELETE', '/api/employees/' + eId2)
// ────────── 收尾 ──────────
console.log('\n' + '═'.repeat(50))
console.log(`测试完成: 通过 ${passCount} / 失败 ${failCount}`)
if (failCount > 0) {
console.log('⚠️ 存在失败用例,请检查上方输出')
} else {
console.log('🎉 全部接口通过!')
}
console.log('═'.repeat(50))
// 优雅退出
process.exit(failCount > 0 ? 1 : 0)
}
main().catch((e) => {
console.error('测试异常:', e.message)
console.error('请确认服务已启动: node server.js')
process.exit(1)
})