/** * 管理员功能完整性测试脚本 * 测试范围: * 1. 用户管理 - 用户列表、搜索、封禁/解封、删除、修改存储权限、查看用户文件 * 2. 系统设置 - SMTP邮件配置、存储配置、注册开关、主题设置 * 3. 分享管理 - 查看所有分享、删除分享 * 4. 系统监控 - 健康检查、存储统计、操作日志 * 5. 安全检查 - 管理员权限验证、敏感操作确认 */ const http = require('http'); const BASE_URL = 'http://localhost:40001'; let adminToken = ''; let testUserId = null; let testShareId = null; // 测试结果收集 const testResults = { passed: [], failed: [], warnings: [] }; // 辅助函数:发送HTTP请求 function request(method, path, data = null, token = null) { return new Promise((resolve, reject) => { const url = new URL(path, BASE_URL); const options = { hostname: url.hostname, port: url.port, path: url.pathname + url.search, method: method, headers: { 'Content-Type': 'application/json' } }; if (token) { options.headers['Authorization'] = `Bearer ${token}`; } const req = http.request(options, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { try { const json = JSON.parse(body); resolve({ status: res.statusCode, data: json }); } catch (e) { resolve({ status: res.statusCode, data: body }); } }); }); req.on('error', reject); if (data) { req.write(JSON.stringify(data)); } req.end(); }); } // 测试函数包装器 async function test(name, fn) { try { await fn(); testResults.passed.push(name); console.log(`[PASS] ${name}`); } catch (error) { testResults.failed.push({ name, error: error.message }); console.log(`[FAIL] ${name}: ${error.message}`); } } // 警告记录 function warn(message) { testResults.warnings.push(message); console.log(`[WARN] ${message}`); } // 断言函数 function assert(condition, message) { if (!condition) { throw new Error(message); } } // ============ 测试用例 ============ // 1. 安全检查:未认证访问应被拒绝 async function testUnauthorizedAccess() { const res = await request('GET', '/api/admin/users'); assert(res.status === 401, `未认证访问应返回401,实际返回: ${res.status}`); } // 2. 管理员登录 async function testAdminLogin() { const res = await request('POST', '/api/login', { username: 'admin', password: 'admin123', captcha: '' // 开发环境可能不需要验证码 }); // 登录可能因为验证码失败,这是预期的 if (res.status === 400 && res.data.message && res.data.message.includes('验证码')) { warn('登录需要验证码,跳过登录测试,使用模拟token'); // 使用JWT库生成一个测试token(需要知道JWT_SECRET) // 或者直接查询数据库 return; } if (res.data.success) { adminToken = res.data.token; console.log(' - 获取到管理员token'); } else { throw new Error(`登录失败: ${res.data.message}`); } } // 3. 用户列表获取 async function testGetUsers() { if (!adminToken) { warn('无admin token,跳过用户列表测试'); return; } const res = await request('GET', '/api/admin/users', null, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); assert(Array.isArray(res.data.users), 'users应为数组'); // 记录测试用户ID if (res.data.users.length > 1) { const nonAdminUser = res.data.users.find(u => !u.is_admin); if (nonAdminUser) { testUserId = nonAdminUser.id; } } } // 4. 系统设置获取 async function testGetSettings() { if (!adminToken) { warn('无admin token,跳过系统设置测试'); return; } const res = await request('GET', '/api/admin/settings', null, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); assert(res.data.settings !== undefined, '应包含settings对象'); assert(res.data.settings.smtp !== undefined, '应包含smtp配置'); assert(res.data.settings.global_theme !== undefined, '应包含全局主题设置'); } // 5. 更新系统设置 async function testUpdateSettings() { if (!adminToken) { warn('无admin token,跳过更新系统设置测试'); return; } const res = await request('POST', '/api/admin/settings', { global_theme: 'dark', max_upload_size: 10737418240 }, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); } // 6. 健康检查 async function testHealthCheck() { if (!adminToken) { warn('无admin token,跳过健康检查测试'); return; } const res = await request('GET', '/api/admin/health-check', null, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); assert(res.data.checks !== undefined, '应包含checks数组'); assert(res.data.overallStatus !== undefined, '应包含overallStatus'); assert(res.data.summary !== undefined, '应包含summary'); // 检查各项检测项目 const checkNames = res.data.checks.map(c => c.name); assert(checkNames.includes('JWT密钥'), '应包含JWT密钥检查'); assert(checkNames.includes('数据库连接'), '应包含数据库连接检查'); assert(checkNames.includes('存储目录'), '应包含存储目录检查'); } // 7. 存储统计 async function testStorageStats() { if (!adminToken) { warn('无admin token,跳过存储统计测试'); return; } const res = await request('GET', '/api/admin/storage-stats', null, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); assert(res.data.stats !== undefined, '应包含stats对象'); assert(typeof res.data.stats.totalDisk === 'number', 'totalDisk应为数字'); } // 8. 系统日志获取 async function testGetLogs() { if (!adminToken) { warn('无admin token,跳过系统日志测试'); return; } const res = await request('GET', '/api/admin/logs?page=1&pageSize=10', null, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); assert(Array.isArray(res.data.logs), 'logs应为数组'); assert(typeof res.data.total === 'number', 'total应为数字'); } // 9. 日志统计 async function testLogStats() { if (!adminToken) { warn('无admin token,跳过日志统计测试'); return; } const res = await request('GET', '/api/admin/logs/stats', null, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); assert(res.data.stats !== undefined, '应包含stats对象'); } // 10. 分享列表获取 async function testGetShares() { if (!adminToken) { warn('无admin token,跳过分享列表测试'); return; } const res = await request('GET', '/api/admin/shares', null, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); assert(Array.isArray(res.data.shares), 'shares应为数组'); // 记录测试分享ID if (res.data.shares.length > 0) { testShareId = res.data.shares[0].id; } } // 11. 安全检查:普通用户不能访问管理员API async function testNonAdminAccess() { // 使用一个无效的token模拟普通用户 const fakeToken = 'invalid-token'; const res = await request('GET', '/api/admin/users', null, fakeToken); assert(res.status === 401, `无效token应返回401,实际: ${res.status}`); } // 12. 安全检查:不能封禁自己 async function testCannotBanSelf() { if (!adminToken) { warn('无admin token,跳过封禁自己测试'); return; } // 获取当前管理员ID const usersRes = await request('GET', '/api/admin/users', null, adminToken); const adminUser = usersRes.data.users.find(u => u.is_admin); if (!adminUser) { warn('未找到管理员用户'); return; } const res = await request('POST', `/api/admin/users/${adminUser.id}/ban`, { banned: true }, adminToken); assert(res.status === 400, `封禁自己应返回400,实际: ${res.status}`); assert(res.data.message.includes('不能封禁自己'), '应提示不能封禁自己'); } // 13. 安全检查:不能删除自己 async function testCannotDeleteSelf() { if (!adminToken) { warn('无admin token,跳过删除自己测试'); return; } const usersRes = await request('GET', '/api/admin/users', null, adminToken); const adminUser = usersRes.data.users.find(u => u.is_admin); if (!adminUser) { warn('未找到管理员用户'); return; } const res = await request('DELETE', `/api/admin/users/${adminUser.id}`, null, adminToken); assert(res.status === 400, `删除自己应返回400,实际: ${res.status}`); assert(res.data.message.includes('不能删除自己'), '应提示不能删除自己'); } // 14. 参数验证:无效用户ID async function testInvalidUserId() { if (!adminToken) { warn('无admin token,跳过无效用户ID测试'); return; } const res = await request('POST', '/api/admin/users/invalid/ban', { banned: true }, adminToken); assert(res.status === 400, `无效用户ID应返回400,实际: ${res.status}`); } // 15. 参数验证:无效分享ID async function testInvalidShareId() { if (!adminToken) { warn('无admin token,跳过无效分享ID测试'); return; } const res = await request('DELETE', '/api/admin/shares/invalid', null, adminToken); assert(res.status === 400, `无效分享ID应返回400,实际: ${res.status}`); } // 16. 存储权限设置 async function testSetStoragePermission() { if (!adminToken || !testUserId) { warn('无admin token或测试用户,跳过存储权限测试'); return; } const res = await request('POST', `/api/admin/users/${testUserId}/storage-permission`, { storage_permission: 'local_only', local_storage_quota: 2147483648 // 2GB }, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); } // 17. 参数验证:无效的存储权限值 async function testInvalidStoragePermission() { if (!adminToken || !testUserId) { warn('无admin token或测试用户,跳过无效存储权限测试'); return; } const res = await request('POST', `/api/admin/users/${testUserId}/storage-permission`, { storage_permission: 'invalid_permission' }, adminToken); assert(res.status === 400, `无效存储权限应返回400,实际: ${res.status}`); } // 18. 主题设置验证 async function testInvalidTheme() { if (!adminToken) { warn('无admin token,跳过无效主题测试'); return; } const res = await request('POST', '/api/admin/settings', { global_theme: 'invalid_theme' }, adminToken); assert(res.status === 400, `无效主题应返回400,实际: ${res.status}`); } // 19. 日志清理测试 async function testLogCleanup() { if (!adminToken) { warn('无admin token,跳过日志清理测试'); return; } const res = await request('POST', '/api/admin/logs/cleanup', { keepDays: 90 }, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); assert(typeof res.data.deletedCount === 'number', 'deletedCount应为数字'); } // 20. SMTP测试(预期失败因为未配置) async function testSmtpTest() { if (!adminToken) { warn('无admin token,跳过SMTP测试'); return; } const res = await request('POST', '/api/admin/settings/test-smtp', { to: 'test@example.com' }, adminToken); // SMTP未配置时应返回400 if (res.status === 400 && res.data.message && res.data.message.includes('SMTP未配置')) { console.log(' - SMTP未配置,这是预期的'); return; } // 如果SMTP已配置,可能成功或失败 assert(res.status === 200 || res.status === 500, `应返回200或500,实际: ${res.status}`); } // 21. 上传工具检查 async function testCheckUploadTool() { if (!adminToken) { warn('无admin token,跳过上传工具检查测试'); return; } const res = await request('GET', '/api/admin/check-upload-tool', null, adminToken); assert(res.status === 200, `应返回200,实际: ${res.status}`); assert(res.data.success === true, '应返回success: true'); assert(typeof res.data.exists === 'boolean', 'exists应为布尔值'); } // 22. 用户文件查看 - 无效用户ID验证 async function testInvalidUserIdForFiles() { if (!adminToken) { warn('无admin token,跳过用户文件查看无效ID测试'); return; } const res = await request('GET', '/api/admin/users/invalid/files', null, adminToken); assert(res.status === 400, `无效用户ID应返回400,实际: ${res.status}`); } // 23. 删除用户 - 无效用户ID验证 async function testInvalidUserIdForDelete() { if (!adminToken) { warn('无admin token,跳过删除用户无效ID测试'); return; } const res = await request('DELETE', '/api/admin/users/invalid', null, adminToken); assert(res.status === 400, `无效用户ID应返回400,实际: ${res.status}`); } // 24. 存储权限设置 - 无效用户ID验证 async function testInvalidUserIdForPermission() { if (!adminToken) { warn('无admin token,跳过存储权限无效ID测试'); return; } const res = await request('POST', '/api/admin/users/invalid/storage-permission', { storage_permission: 'local_only' }, adminToken); assert(res.status === 400, `无效用户ID应返回400,实际: ${res.status}`); } // 主测试函数 async function runTests() { console.log('========================================'); console.log('管理员功能完整性测试'); console.log('========================================\n'); // 先尝试直接使用数据库获取token try { const jwt = require('jsonwebtoken'); const { UserDB } = require('./database'); require('dotenv').config(); const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production'; const adminUser = UserDB.findByUsername('admin'); if (adminUser) { adminToken = jwt.sign( { id: adminUser.id, username: adminUser.username, is_admin: adminUser.is_admin, type: 'access' }, JWT_SECRET, { expiresIn: '2h' } ); console.log('[INFO] 已通过数据库直接生成管理员token\n'); } } catch (e) { console.log('[INFO] 无法直接生成token,将尝试登录: ' + e.message + '\n'); } // 安全检查测试 console.log('\n--- 安全检查 ---'); await test('未认证访问应被拒绝', testUnauthorizedAccess); await test('无效token应被拒绝', testNonAdminAccess); // 如果还没有token,尝试登录 if (!adminToken) { await test('管理员登录', testAdminLogin); } // 用户管理测试 console.log('\n--- 用户管理 ---'); await test('获取用户列表', testGetUsers); await test('不能封禁自己', testCannotBanSelf); await test('不能删除自己', testCannotDeleteSelf); await test('无效用户ID验证', testInvalidUserId); await test('设置存储权限', testSetStoragePermission); await test('无效存储权限验证', testInvalidStoragePermission); // 系统设置测试 console.log('\n--- 系统设置 ---'); await test('获取系统设置', testGetSettings); await test('更新系统设置', testUpdateSettings); await test('无效主题验证', testInvalidTheme); await test('SMTP测试', testSmtpTest); // 分享管理测试 console.log('\n--- 分享管理 ---'); await test('获取分享列表', testGetShares); await test('无效分享ID验证', testInvalidShareId); // 系统监控测试 console.log('\n--- 系统监控 ---'); await test('健康检查', testHealthCheck); await test('存储统计', testStorageStats); await test('获取系统日志', testGetLogs); await test('日志统计', testLogStats); await test('日志清理', testLogCleanup); // 其他功能测试 console.log('\n--- 其他功能 ---'); await test('上传工具检查', testCheckUploadTool); // 参数验证增强测试 console.log('\n--- 参数验证增强 ---'); await test('用户文件查看无效ID验证', testInvalidUserIdForFiles); await test('删除用户无效ID验证', testInvalidUserIdForDelete); await test('存储权限设置无效ID验证', testInvalidUserIdForPermission); // 输出测试结果 console.log('\n========================================'); console.log('测试结果汇总'); console.log('========================================'); console.log(`通过: ${testResults.passed.length}`); console.log(`失败: ${testResults.failed.length}`); console.log(`警告: ${testResults.warnings.length}`); if (testResults.failed.length > 0) { console.log('\n失败的测试:'); testResults.failed.forEach(f => { console.log(` - ${f.name}: ${f.error}`); }); } if (testResults.warnings.length > 0) { console.log('\n警告:'); testResults.warnings.forEach(w => { console.log(` - ${w}`); }); } console.log('\n========================================'); // 返回退出码 process.exit(testResults.failed.length > 0 ? 1 : 0); } // 运行测试 runTests().catch(err => { console.error('测试执行错误:', err); process.exit(1); });