flnxi-Wu/lib/__tests__/system-init.test.ts
2026-05-25 16:10:18 +08:00

166 lines
5.2 KiB
TypeScript

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<typeof ensureBuiltinTemplates>
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' },
})
})
})