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