flnxi-Wu/lib/__tests__/template-validator.test.ts
WuYongJie 63ec6b7dfb fix: 修复排行榜按钮消失、tag排序和输入框浅色模式问题
1. 排行榜悬浮窗关闭后小按钮消失问题
   - 修复面板居中模式关闭时按钮位置错误
   - 保存并恢复按钮的居中状态

2. 首页tag按钮按素材数量排序
   - 添加按素材数量降序排序逻辑

3. 上传弹窗输入框浅色模式文字不可见
   - 添加 text-slate-900 确保浅色模式下文字可见
   - 添加 placeholder 样式确保占位符正确显示
2025-12-14 21:41:29 +08:00

272 lines
9.2 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.

/**
* 模板验证器单元测试
*
* 测试模板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)
})
})
})