jest.mock('@/lib/prisma', () => ({ prisma: { systemConfig: { findUnique: jest.fn(), upsert: jest.fn(), delete: jest.fn(), }, $transaction: jest.fn(), }, })) jest.mock('bcryptjs', () => ({ hash: jest.fn(async (value: string) => `hashed:${value}`), })) jest.mock('@/lib/system-integrity', () => ({ BUILTIN_TEMPLATE_KEYS: ['3dmax-material', 'psd-material'], ensureBuiltinTemplates: jest.fn(), })) import { prisma } from '@/lib/prisma' import bcrypt from 'bcryptjs' import { ensureBuiltinTemplates } from '@/lib/system-integrity' import { clearInitialAdminPasswordRequirement, initializeSystem, isInitialAdminPasswordChangeRequired, } from '../system-init' const mockEnsureBuiltinTemplates = ensureBuiltinTemplates as jest.MockedFunction describe('system-init', () => { beforeEach(() => { jest.clearAllMocks() delete process.env.INIT_ADMIN_EMAIL delete process.env.INIT_ADMIN_PASSWORD mockEnsureBuiltinTemplates.mockResolvedValue({ created: ['builtin-3dmax-material'], errors: [], } as never) }) it('未配置初始密码时应生成一次性密码并要求首次改密', async () => { const tx = { user: { findFirst: jest.fn() .mockResolvedValueOnce(null) .mockResolvedValueOnce(null), update: jest.fn(), create: jest.fn().mockResolvedValue({ id: 'admin-1', email: 'admin@local.flnxi', username: 'admin', role: 'ADMIN', isSuperAdmin: true, }), }, department: { findFirst: jest.fn().mockResolvedValue(null), create: jest.fn().mockResolvedValue({ name: '默认部门' }), }, category: { findFirst: jest.fn().mockResolvedValue(null), create: jest.fn().mockResolvedValue({ name: '默认店铺' }), }, scene: { findFirst: jest.fn().mockResolvedValue(null), create: jest.fn().mockResolvedValue({ name: '默认场景' }), }, } ;(prisma.$transaction as jest.Mock).mockImplementation(async (callback) => callback(tx)) ;(prisma.systemConfig.upsert as jest.Mock).mockResolvedValue({}) const result = await initializeSystem() expect(result.initialized).toBe(true) expect(result.admin?.created).toBe(true) expect(result.admin?.mustChangePassword).toBe(true) expect(result.admin?.email).toBe('admin@local.flnxi') expect(result.admin?.passwordSource).toBe('generated') expect(result.admin?.initialPassword).toBeTruthy() expect(prisma.systemConfig.upsert).toHaveBeenCalledWith( expect.objectContaining({ where: { key: 'initial_admin_state' }, }) ) }) it('使用环境变量初始密码时不应把明文密码回传给接口层', async () => { process.env.INIT_ADMIN_PASSWORD = 'Env-Password-Should-Not-Return' const tx = { user: { findFirst: jest.fn() .mockResolvedValueOnce(null) .mockResolvedValueOnce(null), update: jest.fn(), create: jest.fn().mockResolvedValue({ id: 'admin-1', email: 'admin@local.flnxi', username: 'admin', role: 'ADMIN', isSuperAdmin: true, }), }, department: { findFirst: jest.fn().mockResolvedValue(null), create: jest.fn().mockResolvedValue({ name: '默认部门' }), }, category: { findFirst: jest.fn().mockResolvedValue(null), create: jest.fn().mockResolvedValue({ name: '默认店铺' }), }, scene: { findFirst: jest.fn().mockResolvedValue(null), create: jest.fn().mockResolvedValue({ name: '默认场景' }), }, } ;(prisma.$transaction as jest.Mock).mockImplementation(async (callback) => callback(tx)) ;(prisma.systemConfig.upsert as jest.Mock).mockResolvedValue({}) const result = await initializeSystem() expect(bcrypt.hash).toHaveBeenCalledWith('Env-Password-Should-Not-Return', 10) expect(result.admin?.created).toBe(true) expect(result.admin?.passwordSource).toBe('environment') expect(result.admin?.initialPassword).toBeUndefined() }) it('应根据 initial_admin_state 判断是否需要首次改密', async () => { ;(prisma.systemConfig.findUnique as jest.Mock).mockResolvedValue({ key: 'initial_admin_state', value: JSON.stringify({ userId: 'user-1', email: 'admin@local.flnxi', mustChangePassword: true, createdAt: new Date().toISOString(), }), }) await expect(isInitialAdminPasswordChangeRequired('user-1')).resolves.toBe(true) await expect(isInitialAdminPasswordChangeRequired('user-2')).resolves.toBe(false) }) it('首次改密完成后应清除 initial_admin_state', async () => { ;(prisma.systemConfig.findUnique as jest.Mock).mockResolvedValue({ key: 'initial_admin_state', value: JSON.stringify({ userId: 'user-1', email: 'admin@local.flnxi', mustChangePassword: true, createdAt: new Date().toISOString(), }), }) ;(prisma.systemConfig.delete as jest.Mock).mockResolvedValue({}) await clearInitialAdminPasswordRequirement('user-1') expect(prisma.systemConfig.delete).toHaveBeenCalledWith({ where: { key: 'initial_admin_state' }, }) }) })