1. 排行榜悬浮窗关闭后小按钮消失问题 - 修复面板居中模式关闭时按钮位置错误 - 保存并恢复按钮的居中状态 2. 首页tag按钮按素材数量排序 - 添加按素材数量降序排序逻辑 3. 上传弹窗输入框浅色模式文字不可见 - 添加 text-slate-900 确保浅色模式下文字可见 - 添加 placeholder 样式确保占位符正确显示
272 lines
9.2 KiB
TypeScript
272 lines
9.2 KiB
TypeScript
/**
|
||
* 模板验证器单元测试
|
||
*
|
||
* 测试模板ID验证逻辑
|
||
*/
|
||
|
||
import {
|
||
validateTemplateId,
|
||
isBuiltinTemplate,
|
||
extractBuiltinTemplateKey,
|
||
BUILTIN_TEMPLATE_KEYS,
|
||
TemplateValidationError,
|
||
} from '../template-validator'
|
||
|
||
// Mock prisma
|
||
jest.mock('../prisma', () => ({
|
||
prisma: {
|
||
uploadTemplate: {
|
||
findUnique: jest.fn(),
|
||
findMany: jest.fn(),
|
||
},
|
||
},
|
||
}))
|
||
|
||
const { prisma } = require('../prisma')
|
||
|
||
describe('模板验证器测试', () => {
|
||
beforeEach(() => {
|
||
jest.clearAllMocks()
|
||
})
|
||
|
||
describe('validateTemplateId - 验证模板ID', () => {
|
||
test('应该接受空模板ID(向后兼容)', async () => {
|
||
const result = await validateTemplateId(null)
|
||
expect(result.valid).toBe(true)
|
||
expect(result.isBuiltin).toBe(false)
|
||
})
|
||
|
||
test('应该验证有效的内置模板', async () => {
|
||
const result = await validateTemplateId('builtin-video-material')
|
||
expect(result.valid).toBe(true)
|
||
expect(result.isBuiltin).toBe(true)
|
||
expect(result.templateKey).toBe('video-material')
|
||
})
|
||
|
||
test('应该拒绝无效的内置模板', async () => {
|
||
const result = await validateTemplateId('builtin-invalid-template')
|
||
expect(result.valid).toBe(false)
|
||
expect(result.isBuiltin).toBe(true)
|
||
expect(result.errorCode).toBe(TemplateValidationError.INVALID_BUILTIN_KEY)
|
||
})
|
||
|
||
test('应该验证存在的自定义模板', async () => {
|
||
prisma.uploadTemplate.findUnique.mockResolvedValue({
|
||
id: 'custom-id',
|
||
name: '自定义模板',
|
||
status: 'active',
|
||
})
|
||
|
||
const result = await validateTemplateId('custom-id')
|
||
expect(result.valid).toBe(true)
|
||
expect(result.isBuiltin).toBe(false)
|
||
expect(result.templateName).toBe('自定义模板')
|
||
})
|
||
|
||
test('应该拒绝不存在的自定义模板', async () => {
|
||
prisma.uploadTemplate.findUnique.mockResolvedValue(null)
|
||
|
||
const result = await validateTemplateId('non-existent-id')
|
||
expect(result.valid).toBe(false)
|
||
expect(result.errorCode).toBe(TemplateValidationError.TEMPLATE_NOT_FOUND)
|
||
})
|
||
|
||
test('应该检查模板状态', async () => {
|
||
prisma.uploadTemplate.findUnique.mockResolvedValue({
|
||
id: 'custom-id',
|
||
name: '草稿模板',
|
||
status: 'draft',
|
||
})
|
||
|
||
const result = await validateTemplateId('custom-id', { checkStatus: true })
|
||
expect(result.valid).toBe(false)
|
||
expect(result.errorCode).toBe(TemplateValidationError.TEMPLATE_NOT_ACTIVE)
|
||
})
|
||
|
||
test('应该跳过状态检查(当禁用时)', async () => {
|
||
prisma.uploadTemplate.findUnique.mockResolvedValue({
|
||
id: 'custom-id',
|
||
name: '草稿模板',
|
||
status: 'draft',
|
||
})
|
||
|
||
const result = await validateTemplateId('custom-id', { checkStatus: false })
|
||
expect(result.valid).toBe(true)
|
||
})
|
||
|
||
test('应该处理数据库错误', async () => {
|
||
prisma.uploadTemplate.findUnique.mockRejectedValue(new Error('数据库连接失败'))
|
||
|
||
const result = await validateTemplateId('custom-id')
|
||
expect(result.valid).toBe(false)
|
||
expect(result.errorCode).toBe(TemplateValidationError.DATABASE_ERROR)
|
||
})
|
||
|
||
test('应该记录验证时间', async () => {
|
||
const result = await validateTemplateId('builtin-video-material')
|
||
expect(result.validationTime).toBeDefined()
|
||
expect(typeof result.validationTime).toBe('number')
|
||
expect(result.validationTime).toBeGreaterThanOrEqual(0)
|
||
})
|
||
|
||
test('应该记录性能日志', async () => {
|
||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation()
|
||
|
||
await validateTemplateId('builtin-video-material', { logPerformance: true })
|
||
|
||
expect(consoleSpy).toHaveBeenCalled()
|
||
consoleSpy.mockRestore()
|
||
})
|
||
})
|
||
|
||
describe('isBuiltinTemplate - 检查是否为内置模板', () => {
|
||
test('应该识别内置模板', () => {
|
||
expect(isBuiltinTemplate('builtin-video-material')).toBe(true)
|
||
expect(isBuiltinTemplate('builtin-3dmax-material')).toBe(true)
|
||
})
|
||
|
||
test('应该识别非内置模板', () => {
|
||
expect(isBuiltinTemplate('custom-template-id')).toBe(false)
|
||
expect(isBuiltinTemplate('some-other-id')).toBe(false)
|
||
})
|
||
|
||
test('应该处理空值', () => {
|
||
expect(isBuiltinTemplate(null)).toBe(false)
|
||
expect(isBuiltinTemplate(undefined)).toBe(false)
|
||
expect(isBuiltinTemplate('')).toBe(false)
|
||
})
|
||
})
|
||
|
||
describe('extractBuiltinTemplateKey - 提取内置模板key', () => {
|
||
test('应该提取有效的模板key', () => {
|
||
expect(extractBuiltinTemplateKey('builtin-video-material')).toBe('video-material')
|
||
expect(extractBuiltinTemplateKey('builtin-3dmax-material')).toBe('3dmax-material')
|
||
})
|
||
|
||
test('应该拒绝无效的模板key', () => {
|
||
expect(extractBuiltinTemplateKey('builtin-invalid')).toBe(null)
|
||
})
|
||
|
||
test('应该拒绝非内置模板', () => {
|
||
expect(extractBuiltinTemplateKey('custom-template-id')).toBe(null)
|
||
})
|
||
})
|
||
|
||
describe('BUILTIN_TEMPLATE_KEYS - 内置模板列表', () => {
|
||
test('应该包含所有预期的内置模板', () => {
|
||
expect(BUILTIN_TEMPLATE_KEYS).toContain('3dmax-material')
|
||
expect(BUILTIN_TEMPLATE_KEYS).toContain('psd-material')
|
||
expect(BUILTIN_TEMPLATE_KEYS).toContain('audio-material')
|
||
expect(BUILTIN_TEMPLATE_KEYS).toContain('video-material')
|
||
expect(BUILTIN_TEMPLATE_KEYS).toContain('model-material')
|
||
expect(BUILTIN_TEMPLATE_KEYS).toContain('other-material')
|
||
})
|
||
|
||
test('应该包含正确数量的内置模板', () => {
|
||
// TypeScript的readonly数组在运行时不是真正的只读
|
||
// 这里测试数组包含预期数量的模板
|
||
expect(BUILTIN_TEMPLATE_KEYS.length).toBe(6)
|
||
})
|
||
})
|
||
|
||
describe('validateMultipleTemplateIds - 批量验证模板', () => {
|
||
test('应该批量验证多个模板ID', async () => {
|
||
prisma.uploadTemplate.findMany.mockResolvedValue([
|
||
{ id: 'custom-1', name: '自定义1', status: 'active' },
|
||
{ id: 'custom-2', name: '自定义2', status: 'active' },
|
||
])
|
||
|
||
const { validateMultipleTemplateIds } = require('../template-validator')
|
||
const results = await validateMultipleTemplateIds([
|
||
'builtin-video-material',
|
||
'custom-1',
|
||
'custom-2',
|
||
'non-existent',
|
||
])
|
||
|
||
expect(results.size).toBe(4)
|
||
expect(results.get('builtin-video-material')?.valid).toBe(true)
|
||
expect(results.get('custom-1')?.valid).toBe(true)
|
||
expect(results.get('custom-2')?.valid).toBe(true)
|
||
expect(results.get('non-existent')?.valid).toBe(false)
|
||
})
|
||
|
||
test('应该优化批量查询(减少数据库往返)', async () => {
|
||
prisma.uploadTemplate.findMany.mockResolvedValue([])
|
||
|
||
const { validateMultipleTemplateIds } = require('../template-validator')
|
||
await validateMultipleTemplateIds(['custom-1', 'custom-2', 'custom-3'])
|
||
|
||
// 应该只调用一次findMany而不是多次findUnique
|
||
expect(prisma.uploadTemplate.findMany).toHaveBeenCalledTimes(1)
|
||
})
|
||
})
|
||
|
||
describe('getTemplateWithFields - 获取模板完整信息', () => {
|
||
test('应该获取内置模板的字段', async () => {
|
||
const { getTemplateWithFields } = require('../template-validator')
|
||
const template = await getTemplateWithFields('builtin-video-material')
|
||
|
||
expect(template).toBeDefined()
|
||
expect(template.fields).toBeDefined()
|
||
expect(Array.isArray(template.fields)).toBe(true)
|
||
})
|
||
|
||
test('应该获取自定义模板的字段', async () => {
|
||
prisma.uploadTemplate.findUnique.mockResolvedValue({
|
||
id: 'custom-id',
|
||
name: '自定义模板',
|
||
fields: [
|
||
{ id: 'field-1', key: 'field1', label: '字段1' },
|
||
],
|
||
})
|
||
|
||
const { getTemplateWithFields } = require('../template-validator')
|
||
const template = await getTemplateWithFields('custom-id')
|
||
|
||
expect(template).toBeDefined()
|
||
expect(template.fields).toHaveLength(1)
|
||
})
|
||
|
||
test('应该返回null当模板不存在', async () => {
|
||
prisma.uploadTemplate.findUnique.mockResolvedValue(null)
|
||
|
||
const { getTemplateWithFields } = require('../template-validator')
|
||
const template = await getTemplateWithFields('non-existent')
|
||
|
||
expect(template).toBeNull()
|
||
})
|
||
|
||
test('应该返回null当内置模板key无效', async () => {
|
||
const { getTemplateWithFields } = require('../template-validator')
|
||
const template = await getTemplateWithFields('builtin-invalid')
|
||
|
||
expect(template).toBeNull()
|
||
})
|
||
})
|
||
|
||
describe('性能测试', () => {
|
||
test('验证应该在合理时间内完成', async () => {
|
||
const startTime = Date.now()
|
||
await validateTemplateId('builtin-video-material')
|
||
const duration = Date.now() - startTime
|
||
|
||
// 验证应该在100ms内完成
|
||
expect(duration).toBeLessThan(100)
|
||
})
|
||
|
||
test('批量验证应该比单独验证更快', async () => {
|
||
prisma.uploadTemplate.findMany.mockResolvedValue([])
|
||
|
||
const { validateMultipleTemplateIds } = require('../template-validator')
|
||
|
||
const startTime = Date.now()
|
||
await validateMultipleTemplateIds(['id1', 'id2', 'id3', 'id4', 'id5'])
|
||
const batchDuration = Date.now() - startTime
|
||
|
||
// 批量验证应该很快(因为只有一次数据库查询)
|
||
expect(batchDuration).toBeLessThan(100)
|
||
})
|
||
})
|
||
})
|