166 lines
5.2 KiB
TypeScript
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' },
|
|
})
|
|
})
|
|
})
|