1. 排行榜悬浮窗关闭后小按钮消失问题 - 修复面板居中模式关闭时按钮位置错误 - 保存并恢复按钮的居中状态 2. 首页tag按钮按素材数量排序 - 添加按素材数量降序排序逻辑 3. 上传弹窗输入框浅色模式文字不可见 - 添加 text-slate-900 确保浅色模式下文字可见 - 添加 placeholder 样式确保占位符正确显示
244 lines
6.8 KiB
TypeScript
244 lines
6.8 KiB
TypeScript
/**
|
|
* 启动检查模块测试
|
|
* 测试 lib/startup-check.ts 的核心功能
|
|
*/
|
|
|
|
import {
|
|
runStartupCheck,
|
|
getStartupCheckStatus,
|
|
resetStartupCheck,
|
|
} from '../startup-check'
|
|
import { ensureBuiltinTemplates } from '../system-integrity'
|
|
|
|
// Mock system-integrity 模块
|
|
jest.mock('../system-integrity', () => ({
|
|
ensureBuiltinTemplates: jest.fn(),
|
|
}))
|
|
|
|
const mockEnsureBuiltinTemplates = ensureBuiltinTemplates as jest.MockedFunction<
|
|
typeof ensureBuiltinTemplates
|
|
>
|
|
|
|
describe('启动检查模块', () => {
|
|
beforeEach(() => {
|
|
// 每个测试前重置状态
|
|
resetStartupCheck()
|
|
jest.clearAllMocks()
|
|
// 静默 console 输出
|
|
jest.spyOn(console, 'log').mockImplementation(() => {})
|
|
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
})
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks()
|
|
})
|
|
|
|
describe('runStartupCheck', () => {
|
|
it('首次调用时应执行检查并返回结果', async () => {
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: ['builtin-3dmax-material'],
|
|
errors: [],
|
|
})
|
|
|
|
const result = await runStartupCheck()
|
|
|
|
expect(result).not.toBeNull()
|
|
expect(result?.success).toBe(true)
|
|
expect(result?.templatesCreated).toEqual(['builtin-3dmax-material'])
|
|
expect(result?.errors).toHaveLength(0)
|
|
expect(mockEnsureBuiltinTemplates).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('重复调用时应返回缓存结果而不重新执行', async () => {
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: ['builtin-psd-material'],
|
|
errors: [],
|
|
})
|
|
|
|
// 第一次调用
|
|
const result1 = await runStartupCheck()
|
|
// 第二次调用
|
|
const result2 = await runStartupCheck()
|
|
|
|
expect(result1).toEqual(result2)
|
|
expect(mockEnsureBuiltinTemplates).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('当没有创建模板时应返回空数组', async () => {
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: [],
|
|
errors: [],
|
|
})
|
|
|
|
const result = await runStartupCheck()
|
|
|
|
expect(result?.success).toBe(true)
|
|
expect(result?.templatesCreated).toHaveLength(0)
|
|
})
|
|
|
|
it('当有错误时应设置success为false', async () => {
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: [],
|
|
errors: ['模板创建失败'],
|
|
})
|
|
|
|
const result = await runStartupCheck()
|
|
|
|
expect(result?.success).toBe(false)
|
|
expect(result?.errors).toContain('模板创建失败')
|
|
})
|
|
|
|
it('当ensureBuiltinTemplates抛出异常时应捕获并记录错误', async () => {
|
|
mockEnsureBuiltinTemplates.mockRejectedValue(new Error('数据库连接失败'))
|
|
|
|
const result = await runStartupCheck()
|
|
|
|
expect(result?.success).toBe(false)
|
|
expect(result?.templatesCreated).toHaveLength(0)
|
|
expect(result?.errors).toContain('数据库连接失败')
|
|
})
|
|
|
|
it('当抛出非Error对象时应正确处理', async () => {
|
|
mockEnsureBuiltinTemplates.mockRejectedValue('字符串错误')
|
|
|
|
const result = await runStartupCheck()
|
|
|
|
expect(result?.success).toBe(false)
|
|
expect(result?.errors).toContain('字符串错误')
|
|
})
|
|
|
|
it('应该记录创建的模板数量', async () => {
|
|
const consoleSpy = jest.spyOn(console, 'log')
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: ['builtin-3dmax-material', 'builtin-psd-material'],
|
|
errors: [],
|
|
})
|
|
|
|
await runStartupCheck()
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('自动创建了 2 个内置模板'),
|
|
expect.any(Array)
|
|
)
|
|
})
|
|
|
|
it('应该记录错误信息', async () => {
|
|
const warnSpy = jest.spyOn(console, 'warn')
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: [],
|
|
errors: ['错误1', '错误2'],
|
|
})
|
|
|
|
await runStartupCheck()
|
|
|
|
expect(warnSpy).toHaveBeenCalledWith(
|
|
expect.stringContaining('启动检查有错误'),
|
|
expect.any(Array)
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('getStartupCheckStatus', () => {
|
|
it('初始状态应该是未执行', () => {
|
|
const status = getStartupCheckStatus()
|
|
|
|
expect(status.executed).toBe(false)
|
|
expect(status.result).toBeNull()
|
|
})
|
|
|
|
it('执行后应该返回正确的状态', async () => {
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: [],
|
|
errors: [],
|
|
})
|
|
|
|
await runStartupCheck()
|
|
const status = getStartupCheckStatus()
|
|
|
|
expect(status.executed).toBe(true)
|
|
expect(status.result).not.toBeNull()
|
|
expect(status.result?.success).toBe(true)
|
|
})
|
|
|
|
it('即使检查失败也应该标记为已执行', async () => {
|
|
mockEnsureBuiltinTemplates.mockRejectedValue(new Error('失败'))
|
|
|
|
await runStartupCheck()
|
|
const status = getStartupCheckStatus()
|
|
|
|
expect(status.executed).toBe(true)
|
|
expect(status.result?.success).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('resetStartupCheck', () => {
|
|
it('应该重置执行状态', async () => {
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: [],
|
|
errors: [],
|
|
})
|
|
|
|
await runStartupCheck()
|
|
expect(getStartupCheckStatus().executed).toBe(true)
|
|
|
|
resetStartupCheck()
|
|
expect(getStartupCheckStatus().executed).toBe(false)
|
|
expect(getStartupCheckStatus().result).toBeNull()
|
|
})
|
|
|
|
it('重置后再次调用runStartupCheck应该重新执行', async () => {
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: ['template-1'],
|
|
errors: [],
|
|
})
|
|
|
|
await runStartupCheck()
|
|
resetStartupCheck()
|
|
|
|
mockEnsureBuiltinTemplates.mockResolvedValue({
|
|
checked: true,
|
|
created: ['template-2'],
|
|
errors: [],
|
|
})
|
|
|
|
const result = await runStartupCheck()
|
|
|
|
expect(mockEnsureBuiltinTemplates).toHaveBeenCalledTimes(2)
|
|
expect(result?.templatesCreated).toEqual(['template-2'])
|
|
})
|
|
})
|
|
|
|
describe('幂等性测试', () => {
|
|
it('多次并发调用应该只执行一次', async () => {
|
|
mockEnsureBuiltinTemplates.mockImplementation(async () => {
|
|
// 模拟异步操作
|
|
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
return { checked: true, created: [], errors: [] }
|
|
})
|
|
|
|
// 并发调用
|
|
const results = await Promise.all([
|
|
runStartupCheck(),
|
|
runStartupCheck(),
|
|
runStartupCheck(),
|
|
])
|
|
|
|
// 所有结果应该相同
|
|
expect(results[0]).toEqual(results[1])
|
|
expect(results[1]).toEqual(results[2])
|
|
// 但由于第一次调用还没完成,可能会执行多次
|
|
// 这是当前实现的已知行为
|
|
})
|
|
})
|
|
})
|