61 lines
3.2 KiB
TypeScript
61 lines
3.2 KiB
TypeScript
/** @jest-environment node */
|
|
import { NextRequest } from 'next/server'
|
|
import { getServerSession } from 'next-auth'
|
|
import { POST } from '@/app/api/admin/materials/batch-delete/route'
|
|
import { enqueueMaterialPurge, MaterialPurgeError } from '@/lib/material-purge'
|
|
import { readFile } from 'fs/promises'
|
|
import path from 'path'
|
|
|
|
jest.mock('next-auth', () => ({ getServerSession: jest.fn() }))
|
|
jest.mock('@/lib/auth', () => ({ authOptions: {} }))
|
|
jest.mock('@/lib/prisma', () => ({ prisma: {} }))
|
|
jest.mock('@/lib/material-log', () => ({ createMaterialLog: jest.fn() }))
|
|
jest.mock('@/lib/department-permission', () => ({ isSystemAdmin: (role: string) => ['ADMIN', 'SYSTEM_ADMIN'].includes(role) }))
|
|
jest.mock('@/lib/material-purge', () => ({
|
|
enqueueMaterialPurge: jest.fn(), ensureMaterialPurgeWorkerStarted: jest.fn(),
|
|
MaterialPurgeError: class extends Error { constructor(message: string, readonly status = 409) { super(message) } },
|
|
}))
|
|
|
|
const id = '11111111-1111-4111-8111-111111111111'
|
|
const request = (body: unknown) => new NextRequest('http://localhost/api/admin/materials/batch-delete', {
|
|
method: 'POST', body: JSON.stringify(body), headers: { 'Content-Type': 'application/json' },
|
|
})
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
;(getServerSession as jest.Mock).mockResolvedValue({ user: { id: 'admin', role: 'ADMIN' } })
|
|
})
|
|
|
|
test.each([null, { user: { id: 'user', role: 'USER' } }, { user: { id: 'department', role: 'DEPARTMENT_ADMIN' } }])('永久删除拒绝非系统管理员', async session => {
|
|
;(getServerSession as jest.Mock).mockResolvedValue(session)
|
|
expect((await POST(request({ ids: [id], permanent: true }))).status).toBe(403)
|
|
expect(enqueueMaterialPurge).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('只在任务可靠提交后返回 202 和真实任务,不宣称文件已经清理', async () => {
|
|
const jobs = [{ id: 'job', status: 'queued' }]
|
|
;(enqueueMaterialPurge as jest.Mock).mockResolvedValue(jobs)
|
|
const result = await POST(request({ ids: [id], permanent: true }))
|
|
expect(result.status).toBe(202)
|
|
expect(await result.json()).toEqual({ success: true, jobs })
|
|
expect(enqueueMaterialPurge).toHaveBeenCalledWith([id], 'admin')
|
|
})
|
|
|
|
test.each([{ ids: [id], permanent: 'true' }, { ids: ['invalid'], permanent: true }, { ids: Array(101).fill(id), permanent: true }])('拒绝错误删除方式和超限批量输入', async body => {
|
|
expect((await POST(request(body))).status).toBe(400)
|
|
expect(enqueueMaterialPurge).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('批量条件冲突返回可恢复的上下文错误', async () => {
|
|
;(enqueueMaterialPurge as jest.Mock).mockRejectedValue(new MaterialPurgeError('只能彻底删除回收站中的素材'))
|
|
const result = await POST(request({ ids: [id], permanent: true }))
|
|
expect(result.status).toBe(409)
|
|
expect(await result.json()).toEqual({ error: '只能彻底删除回收站中的素材' })
|
|
})
|
|
|
|
test.each(['app/api/materials/route.ts', 'app/api/admin/materials/batch-delete/route.ts'])('%s 的请求路径不直接做物理删除', async file => {
|
|
const source = await readFile(path.join(process.cwd(), file), 'utf8')
|
|
expect(source).toContain('enqueueMaterialPurge(')
|
|
expect(source).not.toMatch(/deleteFiles\(|deleteMaterialRelatedRecords\(|prisma\.material\.delete\(/)
|
|
})
|