gongxiangp2/scripts/test-shadow-system.ts
chenpei ea9076df45 feat: 实现统一的下载错误处理系统并修复产品全案数据库Schema
- 创建统一的下载错误处理模块 (lib/download-errors.ts)
- 实现13种错误类型分类和中文错误消息
- 后端统一使用 createDownloadError 创建标准化错误响应
- 前端统一使用 parseDownloadError 解析错误并显示
- 修复正式系统数据库Schema,添加产品全案相关字段和表
- 执行数据库迁移:添加 task_no, task_status, task_fetched_at 字段
- 创建 material_assets, api_keys, api_call_logs 表
2026-01-05 17:52:42 +08:00

389 lines
9.9 KiB
TypeScript
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.

/**
* 影子系统功能测试脚本
*
* 测试功能:
* 1. 用户登录
* 2. 用户角色切换(管理员/普通用户)
* 3. 用户状态管理
*
* 使用方法:
* npx tsx scripts/test-shadow-system.ts
*/
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// 测试配置
const SHADOW_SYSTEM_URL = process.env.SHADOW_SYSTEM_URL || 'http://192.168.58.20:5320';
const TEST_USER_EMAIL = process.env.TEST_USER_EMAIL || '';
const TEST_USER_PASSWORD = process.env.TEST_USER_PASSWORD || '';
interface TestResult {
name: string;
passed: boolean;
error?: string;
details?: any;
}
const results: TestResult[] = [];
async function test(name: string, testFn: () => Promise<any>): Promise<void> {
try {
console.log(`\n🧪 测试: ${name}`);
const details = await testFn();
results.push({ name, passed: true, details });
console.log(` ✅ 通过`);
} catch (error: any) {
results.push({ name, passed: false, error: error.message });
console.log(` ❌ 失败: ${error.message}`);
}
}
async function main() {
console.log('========================================');
console.log('影子系统功能测试');
console.log('========================================');
console.log(`测试目标: ${SHADOW_SYSTEM_URL}`);
console.log(`数据库: ${process.env.DATABASE_URL?.split('@')[1] || '未设置'}`);
console.log('');
// 测试1: 检查数据库连接
await test('数据库连接', async () => {
await prisma.$connect();
const count = await prisma.user.count();
return { totalUsers: count };
});
// 测试2: 检查用户状态字段
await test('用户状态字段检查', async () => {
const users = await prisma.user.findMany({
take: 5,
select: {
id: true,
email: true,
username: true,
status: true,
isActive: true,
role: true,
lastLoginAt: true,
loginCount: true,
},
});
const hasStatus = users.every(u => u.status !== null && u.status !== undefined);
const hasIsActive = users.every(u => u.isActive !== null && u.isActive !== undefined);
if (!hasStatus || !hasIsActive) {
throw new Error('用户状态字段缺失');
}
return {
sampleUsers: users.map(u => ({
email: u.email,
status: u.status,
isActive: u.isActive,
role: u.role,
hasLastLoginAt: u.lastLoginAt !== null,
loginCount: u.loginCount,
})),
};
});
// 测试3: 检查部门外键关联
await test('部门外键关联检查', async () => {
const usersWithDept = await prisma.user.findMany({
where: {
department: { not: '' },
},
take: 5,
select: {
id: true,
email: true,
department: true,
departmentId: true,
departmentRef: {
select: {
id: true,
name: true,
},
},
},
});
const allHaveDepartmentId = usersWithDept.every(u => u.departmentId !== null);
const allHaveDepartmentRef = usersWithDept.every(u => u.departmentRef !== null);
const namesMatch = usersWithDept.every(
u => !u.departmentRef || u.department === u.departmentRef.name
);
if (!allHaveDepartmentId) {
throw new Error('部分用户缺少 departmentId');
}
if (!allHaveDepartmentRef) {
throw new Error('部分用户缺少 departmentRef 关联');
}
if (!namesMatch) {
throw new Error('部门名称与关联不匹配');
}
return {
sampleUsers: usersWithDept.map(u => ({
email: u.email,
department: u.department,
departmentId: u.departmentId,
departmentRefName: u.departmentRef?.name,
})),
};
});
// 测试4: 查找测试用户(管理员和普通用户)
await test('查找测试用户', async () => {
const adminUser = await prisma.user.findFirst({
where: {
role: 'ADMIN',
status: 'active',
},
select: {
id: true,
email: true,
username: true,
role: true,
status: true,
},
});
const normalUser = await prisma.user.findFirst({
where: {
role: 'USER',
status: 'active',
},
select: {
id: true,
email: true,
username: true,
role: true,
status: true,
},
});
if (!adminUser || !normalUser) {
throw new Error('未找到足够的测试用户(需要至少1个管理员和1个普通用户)');
}
return {
adminUser: {
id: adminUser.id,
email: adminUser.email,
username: adminUser.username,
role: adminUser.role,
status: adminUser.status,
},
normalUser: {
id: normalUser.id,
email: normalUser.email,
username: normalUser.username,
role: normalUser.role,
status: normalUser.status,
},
};
});
// 测试5: 测试角色切换(模拟)
await test('角色切换功能(数据验证)', async () => {
// 找到一个普通用户
const user = await prisma.user.findFirst({
where: {
role: 'USER',
status: 'active',
},
});
if (!user) {
throw new Error('未找到可测试的普通用户');
}
const originalRole = user.role;
// 验证可以切换到ADMIN
await prisma.user.update({
where: { id: user.id },
data: { role: 'ADMIN' },
});
const updatedUser = await prisma.user.findUnique({
where: { id: user.id },
select: { role: true },
});
if (updatedUser?.role !== 'ADMIN') {
throw new Error('角色切换失败');
}
// 恢复原角色
await prisma.user.update({
where: { id: user.id },
data: { role: originalRole },
});
return {
testUserId: user.id,
originalRole,
switchedToAdmin: true,
restored: true,
};
});
// 测试6: 测试状态切换(模拟)
await test('状态切换功能(数据验证)', async () => {
const user = await prisma.user.findFirst({
where: {
status: 'active',
},
});
if (!user) {
throw new Error('未找到可测试的用户');
}
const originalStatus = user.status;
const originalIsActive = user.isActive;
// 测试切换到suspended
await prisma.user.update({
where: { id: user.id },
data: {
status: 'suspended',
isActive: false,
},
});
const suspendedUser = await prisma.user.findUnique({
where: { id: user.id },
select: { status: true, isActive: true },
});
if (suspendedUser?.status !== 'suspended' || suspendedUser?.isActive !== false) {
throw new Error('状态切换失败');
}
// 测试切换回active
await prisma.user.update({
where: { id: user.id },
data: {
status: 'active',
isActive: true,
},
});
const activeUser = await prisma.user.findUnique({
where: { id: user.id },
select: { status: true, isActive: true },
});
if (activeUser?.status !== 'active' || activeUser?.isActive !== true) {
throw new Error('状态恢复失败');
}
return {
testUserId: user.id,
originalStatus,
switchedToSuspended: true,
restoredToActive: true,
};
});
// 测试7: 验证双写逻辑(status 和 isActive 的一致性)
await test('双写逻辑验证(status 和 isActive 一致性)', async () => {
const inconsistencies = await prisma.user.findMany({
where: {
OR: [
{ status: 'active', isActive: false },
{ status: { not: 'active' }, isActive: true },
],
},
take: 10,
select: {
id: true,
email: true,
status: true,
isActive: true,
},
});
if (inconsistencies.length > 0) {
throw new Error(
`发现 ${inconsistencies.length} 个用户 status 和 isActive 不一致`
);
}
return {
checked: true,
inconsistencies: 0,
};
});
// 测试8: 统计信息
await test('数据统计', async () => {
const totalUsers = await prisma.user.count();
const activeUsers = await prisma.user.count({ where: { status: 'active' } });
const pendingUsers = await prisma.user.count({ where: { status: 'pending' } });
const suspendedUsers = await prisma.user.count({ where: { status: 'suspended' } });
const archivedUsers = await prisma.user.count({ where: { status: 'archived' } });
const adminUsers = await prisma.user.count({ where: { role: 'ADMIN' } });
const normalUsers = await prisma.user.count({ where: { role: 'USER' } });
const usersWithDept = await prisma.user.count({ where: { departmentId: { not: null } } });
return {
totalUsers,
byStatus: {
active: activeUsers,
pending: pendingUsers,
suspended: suspendedUsers,
archived: archivedUsers,
},
byRole: {
admin: adminUsers,
user: normalUsers,
},
usersWithDepartmentId: usersWithDept,
};
});
// 输出测试结果
console.log('\n========================================');
console.log('测试结果汇总');
console.log('========================================');
const passed = results.filter(r => r.passed).length;
const failed = results.filter(r => !r.passed).length;
results.forEach(result => {
const icon = result.passed ? '✅' : '❌';
console.log(`${icon} ${result.name}`);
if (!result.passed && result.error) {
console.log(` 错误: ${result.error}`);
}
});
console.log('\n========================================');
console.log(`总计: ${results.length} 个测试`);
console.log(`✅ 通过: ${passed}`);
console.log(`❌ 失败: ${failed}`);
console.log('========================================\n');
if (failed > 0) {
process.exit(1);
}
}
main()
.catch((error) => {
console.error('测试执行失败:', error);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});