gongxiangp2/lib/__tests__/sku-route-authorization.test.ts

151 lines
5.2 KiB
TypeScript

/** @jest-environment node */
import { getServerSession } from 'next-auth'
import { prisma } from '@/lib/prisma'
import { canDownloadSkuMaterial, canManageSkuMaterial } from '@/app/api/materials/[id]/skus/authorization'
import { POST as createSku } from '@/app/api/materials/[id]/skus/route'
import {
DELETE as deleteSku,
GET as getSku,
PUT as updateSku,
} from '@/app/api/materials/[id]/skus/[skuId]/route'
import { POST as uploadSku } from '@/app/api/materials/[id]/skus/upload/route'
import { PUT as reorderSkus } from '@/app/api/materials/[id]/skus/reorder/route'
import { GET as downloadSku } from '@/app/api/materials/[id]/skus/[skuId]/download/route'
import { POST as batchDownloadSkus } from '@/app/api/materials/[id]/skus/batch-download/route'
jest.mock('next-auth', () => ({ getServerSession: jest.fn() }))
jest.mock('@/lib/auth', () => ({ authOptions: {} }))
jest.mock('@/lib/material-log', () => ({ createMaterialLog: jest.fn() }))
jest.mock('@/lib/material-download-log', () => ({ ensureMaterialDownloadLogsTable: jest.fn() }))
jest.mock('@/lib/file-cleanup', () => ({
deleteFileIfUnreferenced: jest.fn(),
deleteLocalFile: jest.fn(),
}))
jest.mock('@/app/api/materials/[id]/skus/authorization', () => ({
canManageSkuMaterial: jest.fn(),
canDownloadSkuMaterial: jest.fn(),
}))
jest.mock('@/lib/prisma', () => ({
prisma: {
material: {
findUnique: jest.fn(),
findFirst: jest.fn(),
update: jest.fn(),
},
materialSku: {
findFirst: jest.fn(),
findMany: jest.fn(),
aggregate: jest.fn(),
create: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
},
materialAsset: {
findMany: jest.fn(),
},
user: {
findUnique: jest.fn(),
findMany: jest.fn(),
},
materialDownloadLog: {
create: jest.fn(),
},
$transaction: jest.fn(),
},
}))
describe('SKU routes object authorization', () => {
const session = { user: { id: 'user-2', role: 'USER', department: '设计部' } }
const material = { id: 'material-1', userId: 'user-1', materialType: '标准模型', productModel: 'SPU-1' }
beforeEach(() => {
jest.clearAllMocks()
;(getServerSession as jest.Mock).mockResolvedValue(session)
;(canManageSkuMaterial as jest.Mock).mockResolvedValue(false)
;(canDownloadSkuMaterial as jest.Mock).mockResolvedValue(false)
;(prisma.user.findUnique as jest.Mock).mockResolvedValue({ canDownload: true })
})
test.each([
['创建', async () => {
;(prisma.material.findUnique as jest.Mock).mockResolvedValue(material)
return createSku({ json: jest.fn() } as any, { params: { id: 'material-1' } })
}],
['上传', async () => {
;(prisma.material.findUnique as jest.Mock).mockResolvedValue(material)
return uploadSku({ formData: jest.fn() } as any, { params: { id: 'material-1' } })
}],
['排序', async () => {
;(prisma.material.findUnique as jest.Mock).mockResolvedValue(material)
return reorderSkus({ json: jest.fn() } as any, { params: { id: 'material-1' } })
}],
])('非所有者不得%s SKU', async (_label, invoke) => {
const response = await invoke()
expect(response.status).toBe(403)
expect(prisma.materialSku.create).not.toHaveBeenCalled()
expect(prisma.materialSku.update).not.toHaveBeenCalled()
})
test.each([
['更新', updateSku],
['删除', deleteSku],
])('非所有者不得%s目标 SKU', async (_label, handler) => {
;(prisma.materialSku.findFirst as jest.Mock).mockResolvedValue({
id: 'sku-1',
materialId: 'material-1',
material: { userId: 'user-1' },
})
const response = await handler({ json: jest.fn() } as any, {
params: { id: 'material-1', skuId: 'sku-1' },
})
expect(response.status).toBe(403)
expect(prisma.materialSku.update).not.toHaveBeenCalled()
expect(prisma.materialSku.delete).not.toHaveBeenCalled()
})
test('SKU 详情查询必须同时绑定 materialId 与 skuId', async () => {
;(prisma.materialSku.findFirst as jest.Mock).mockResolvedValue({
id: 'sku-1',
materialId: 'material-1',
skuName: 'SKU-1',
previewUrl: '',
fileName: null,
filePath: '',
fileSize: BigInt(0),
sortOrder: 0,
whiteBackgroundUploadedBy: null,
})
const response = await getSku({} as any, {
params: { id: 'material-1', skuId: 'sku-1' },
})
expect(response.status).toBe(200)
expect(prisma.materialSku.findFirst).toHaveBeenCalledWith(expect.objectContaining({
where: { id: 'sku-1', materialId: 'material-1', deletedAt: null },
}))
})
test('无素材下载权限时单个和批量下载均在读文件前拒绝', async () => {
;(prisma.material.findFirst as jest.Mock).mockResolvedValue(material)
const singleResponse = await downloadSku(
{ url: 'http://localhost/api/materials/material-1/skus/sku-1/download' } as any,
{ params: { id: 'material-1', skuId: 'sku-1' } },
)
const batchResponse = await batchDownloadSkus(
{ json: jest.fn().mockResolvedValue({ skuIds: ['sku-1'] }) } as any,
{ params: { id: 'material-1' } },
)
expect(singleResponse.status).toBe(403)
expect(batchResponse.status).toBe(403)
expect(prisma.materialSku.findMany).not.toHaveBeenCalled()
})
})