gongxiangp2/lib/__tests__/materials-share-security.test.ts

106 lines
2.9 KiB
TypeScript

/** @jest-environment node */
import fs from 'fs'
import path from 'path'
import { NextRequest } from 'next/server'
import { getServerSession } from 'next-auth'
import { GET } from '@/app/api/materials/share/route'
import { prisma } from '@/lib/prisma'
jest.mock('@/lib/prisma', () => ({
prisma: {
material: {
findFirst: jest.fn(),
},
},
}))
jest.mock('next-auth', () => ({
getServerSession: jest.fn(),
}))
jest.mock('@/lib/auth', () => ({
authOptions: {},
}))
describe('materials share security', () => {
beforeEach(() => {
jest.clearAllMocks()
;(getServerSession as jest.Mock).mockResolvedValue(null)
;(prisma.material.findFirst as jest.Mock).mockResolvedValue(null)
})
test('公开分享只查询未删除素材', async () => {
const response = await GET(
new NextRequest('http://localhost/api/materials/share?id=material-deleted'),
)
const body = await response.json()
expect(response.status).toBe(404)
expect(body.error).toBe('素材不存在')
expect(prisma.material.findFirst).toHaveBeenCalledWith(
expect.objectContaining({
where: {
id: 'material-deleted',
deletedAt: null,
},
}),
)
})
test('公开分享响应应包含钉钉审批编号', async () => {
;(prisma.material.findFirst as jest.Mock).mockResolvedValueOnce({
id: 'material-1',
previewUrl: '',
productModel: 'SPU-001',
reportTitle: null,
categoryName: '旗舰店',
sceneName: '客厅',
platformName: '',
productCategoryName: '',
materialType: '图片',
isAiGenerated: false,
taskDetailSourceType: null,
authorName: '张三',
zipFileName: '',
zipFilePath: '',
zipFileSize: BigInt(0),
downloadCount: 0,
likeCount: 0,
displayImages: [],
createdAt: new Date('2026-07-31T00:00:00.000Z'),
userId: 'user-1',
videoSourceUrl: null,
videoPreview540Url: null,
videoPreviewClipUrl: null,
videoCoverUrl: null,
videoDuration: null,
videoProcessingStatus: null,
taskNo: 'TASK202607310001',
dingtalkApprovalNo: '202508211445000157693',
taskStatus: 'completed',
taskFetchedAt: null,
isFullSet: false,
})
const response = await GET(
new NextRequest('http://localhost/api/materials/share?id=material-1'),
)
const body = await response.json()
expect(response.status).toBe(200)
expect(body.taskNo).toBe('TASK202607310001')
expect(body.dingtalkApprovalNo).toBe('202508211445000157693')
})
test('鉴权下载响应禁止共享缓存', () => {
const routeSource = fs.readFileSync(
path.join(process.cwd(), 'app/api/materials/download/route.ts'),
'utf8',
)
expect(routeSource).toContain("'Cache-Control': 'private, no-store, max-age=0'")
expect(routeSource).not.toContain("'Cache-Control': 'public, max-age=86400, must-revalidate'")
})
})