flnxi-Wu/app/api/templates/__tests__/templates-route.test.ts
2026-05-25 16:48:19 +08:00

303 lines
9.5 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.

/**
* 模板列表 API 测试
*
* 测试 app/api/templates/route.ts 的核心功能
*
* 主要测试点:
* 1. 内置模板生成逻辑
* 2. 自定义模板查询(排除内置模板)
* 3. 模板合并和排序
*/
import {
BUILTIN_TEMPLATE_KEYS,
BUILTIN_TEMPLATE_NAMES,
} from '@/lib/upload-template-presets'
function createBuiltinTemplateListItem(key: string) {
return {
id: `builtin-${key}`,
name: BUILTIN_TEMPLATE_NAMES[key] || key,
isBuiltin: true,
}
}
describe('模板列表 API - 内置模板配置', () => {
describe('BUILTIN_TEMPLATE_KEYS', () => {
it('应该包含6个内置模板key', () => {
expect(BUILTIN_TEMPLATE_KEYS).toHaveLength(6)
})
it('应该包含所有预期的模板key', () => {
const expectedKeys = [
'3dmax-material',
'psd-material',
'audio-material',
'video-material',
'model-material',
'other-material',
]
expect(BUILTIN_TEMPLATE_KEYS).toEqual(expectedKeys)
})
})
describe('BUILTIN_TEMPLATE_NAMES', () => {
it('每个模板key都应该有对应的名称', () => {
for (const key of BUILTIN_TEMPLATE_KEYS) {
expect(BUILTIN_TEMPLATE_NAMES[key]).toBeDefined()
expect(typeof BUILTIN_TEMPLATE_NAMES[key]).toBe('string')
expect(BUILTIN_TEMPLATE_NAMES[key].length).toBeGreaterThan(0)
}
})
})
describe('createBuiltinTemplate', () => {
it('应该生成正确格式的内置模板对象', () => {
const template = createBuiltinTemplateListItem('3dmax-material')
expect(template).toEqual({
id: 'builtin-3dmax-material',
name: '3DMAX 素材模板',
isBuiltin: true,
})
})
it('内置模板ID应该以builtin-开头', () => {
for (const key of BUILTIN_TEMPLATE_KEYS) {
const template = createBuiltinTemplateListItem(key)
expect(template.id).toMatch(/^builtin-/)
expect(template.id).toBe(`builtin-${key}`)
}
})
it('所有内置模板应该标记isBuiltin为true', () => {
for (const key of BUILTIN_TEMPLATE_KEYS) {
const template = createBuiltinTemplateListItem(key)
expect(template.isBuiltin).toBe(true)
}
})
it('未知key应该使用key作为名称', () => {
const template = createBuiltinTemplateListItem('unknown-key')
expect(template.name).toBe('unknown-key')
})
})
})
describe('模板列表 API - 模板过滤逻辑', () => {
/**
* 测试内置模板过滤逻辑
* 验证:当 enabledBuiltinTemplates 为空数组时,应该返回所有内置模板
*/
describe('内置模板启用过滤', () => {
it('enabledBuiltinTemplates为空数组时应返回所有内置模板', () => {
const enabledBuiltinTemplates: string[] = []
const builtinTemplates = BUILTIN_TEMPLATE_KEYS
.filter(key => enabledBuiltinTemplates.length === 0 || enabledBuiltinTemplates.includes(key))
.map(key => createBuiltinTemplateListItem(key))
expect(builtinTemplates).toHaveLength(6)
})
it('enabledBuiltinTemplates有值时应只返回启用的模板', () => {
const enabledBuiltinTemplates = ['3dmax-material', 'psd-material']
const builtinTemplates = BUILTIN_TEMPLATE_KEYS
.filter(key => enabledBuiltinTemplates.length === 0 || enabledBuiltinTemplates.includes(key))
.map(key => createBuiltinTemplateListItem(key))
expect(builtinTemplates).toHaveLength(2)
expect(builtinTemplates.map(t => t.id)).toEqual([
'builtin-3dmax-material',
'builtin-psd-material',
])
})
it('enabledBuiltinTemplates只包含一个时应只返回该模板', () => {
const enabledBuiltinTemplates = ['video-material']
const builtinTemplates = BUILTIN_TEMPLATE_KEYS
.filter(key => enabledBuiltinTemplates.length === 0 || enabledBuiltinTemplates.includes(key))
.map(key => createBuiltinTemplateListItem(key))
expect(builtinTemplates).toHaveLength(1)
expect(builtinTemplates[0].id).toBe('builtin-video-material')
})
})
/**
* 测试自定义模板排除内置模板的逻辑
* 这是本次修改的核心功能
*/
describe('自定义模板排除内置模板', () => {
it('应该正确识别内置模板ID格式', () => {
const builtinIds = [
'builtin-3dmax-material',
'builtin-psd-material',
'builtin-audio-material',
]
for (const id of builtinIds) {
expect(id.startsWith('builtin-')).toBe(true)
}
})
it('自定义模板ID不应该以builtin-开头', () => {
const customIds = [
'custom-template-1',
'my-template',
'user-created-template',
]
for (const id of customIds) {
expect(id.startsWith('builtin-')).toBe(false)
}
})
it('模拟过滤逻辑:应该排除builtin-开头的模板', () => {
// 模拟数据库中的模板数据
const allDbTemplates = [
{ id: 'builtin-3dmax-material', name: '3DMAX 素材模板', status: 'active' },
{ id: 'builtin-psd-material', name: 'PSD 素材模板', status: 'active' },
{ id: 'custom-template-1', name: '自定义模板1', status: 'active' },
{ id: 'my-template', name: '我的模板', status: 'active' },
]
// 模拟 Prisma 查询的 NOT startsWith 逻辑
const customTemplates = allDbTemplates.filter(t =>
t.status === 'active' && !t.id.startsWith('builtin-')
)
expect(customTemplates).toHaveLength(2)
expect(customTemplates.map(t => t.id)).toEqual([
'custom-template-1',
'my-template',
])
})
it('当数据库中没有自定义模板时应返回空数组', () => {
const allDbTemplates = [
{ id: 'builtin-3dmax-material', name: '3DMAX 素材模板', status: 'active' },
{ id: 'builtin-psd-material', name: 'PSD 素材模板', status: 'active' },
]
const customTemplates = allDbTemplates.filter(t =>
t.status === 'active' && !t.id.startsWith('builtin-')
)
expect(customTemplates).toHaveLength(0)
})
it('应该排除非active状态的模板', () => {
const allDbTemplates = [
{ id: 'custom-template-1', name: '自定义模板1', status: 'active' },
{ id: 'custom-template-2', name: '自定义模板2', status: 'draft' },
{ id: 'custom-template-3', name: '自定义模板3', status: 'archived' },
]
const customTemplates = allDbTemplates.filter(t =>
t.status === 'active' && !t.id.startsWith('builtin-')
)
expect(customTemplates).toHaveLength(1)
expect(customTemplates[0].id).toBe('custom-template-1')
})
})
/**
* 测试模板合并逻辑
*/
describe('模板合并', () => {
it('合并后内置模板应该在前面', () => {
const builtinTemplates = [
{ id: 'builtin-3dmax-material', name: '3DMAX', isBuiltin: true },
]
const customTemplates = [
{ id: 'custom-1', name: '自定义1' },
]
const allTemplates = [
...builtinTemplates,
...customTemplates.map(t => ({ ...t, isBuiltin: false })),
]
expect(allTemplates[0].isBuiltin).toBe(true)
expect(allTemplates[1].isBuiltin).toBe(false)
})
it('自定义模板应该标记isBuiltin为false', () => {
const customTemplates = [
{ id: 'custom-1', name: '自定义1' },
{ id: 'custom-2', name: '自定义2' },
]
const merged = customTemplates.map(t => ({ ...t, isBuiltin: false }))
for (const t of merged) {
expect(t.isBuiltin).toBe(false)
}
})
it('合并后不应该有重复的模板', () => {
const builtinTemplateItems = BUILTIN_TEMPLATE_KEYS.map(key => createBuiltinTemplateListItem(key))
const customTemplates = [
{ id: 'custom-1', name: '自定义1', isBuiltin: false },
]
const allTemplates = [...builtinTemplateItems, ...customTemplates]
const ids = allTemplates.map(t => t.id)
const uniqueIds = new Set(ids)
expect(uniqueIds.size).toBe(ids.length)
})
})
})
describe('模板列表 API - 边界情况', () => {
it('空的enabledBuiltinTemplates数组应该表示全部启用', () => {
const enabledBuiltinTemplates: string[] = []
const isAllEnabled = enabledBuiltinTemplates.length === 0
expect(isAllEnabled).toBe(true)
})
it('null或undefined的enabledBuiltinTemplates应该被处理为全部启用', () => {
// 模拟从数据库获取的值
const settingsValue: string | null = null
let enabledBuiltinTemplates: string[] = []
if (settingsValue) {
try {
const parsed = JSON.parse(settingsValue)
if (Array.isArray(parsed) && parsed.length > 0) {
enabledBuiltinTemplates = parsed
}
} catch {
// 解析失败,保持空数组
}
}
// 空数组表示全部启用
const builtinTemplates = BUILTIN_TEMPLATE_KEYS
.filter(key => enabledBuiltinTemplates.length === 0 || enabledBuiltinTemplates.includes(key))
expect(builtinTemplates).toHaveLength(6)
})
it('无效的JSON应该被处理为全部启用', () => {
const invalidJson = 'not-valid-json'
let enabledBuiltinTemplates: string[] = []
try {
const parsed = JSON.parse(invalidJson)
if (Array.isArray(parsed) && parsed.length > 0) {
enabledBuiltinTemplates = parsed
}
} catch {
// 解析失败,保持空数组
}
expect(enabledBuiltinTemplates).toHaveLength(0)
})
})