129 lines
4.4 KiB
TypeScript
129 lines
4.4 KiB
TypeScript
/** @jest-environment node */
|
|
|
|
import { getServerSession } from 'next-auth'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { GET } from '@/app/api/materials/facets/route'
|
|
import {
|
|
buildMaterialWhereFromSearchParams,
|
|
createMaterialQueryContext,
|
|
} from '@/lib/material-query'
|
|
|
|
jest.mock('next-auth', () => ({ getServerSession: jest.fn() }))
|
|
jest.mock('@/lib/auth', () => ({ authOptions: {} }))
|
|
jest.mock('@/lib/prisma', () => ({
|
|
prisma: {
|
|
material: {
|
|
count: jest.fn(),
|
|
findMany: jest.fn(),
|
|
groupBy: jest.fn(),
|
|
},
|
|
},
|
|
}))
|
|
jest.mock('@/lib/material-query', () => {
|
|
class MaterialQueryError extends Error {
|
|
status: number
|
|
code: string
|
|
|
|
constructor(status: number, code: string, message: string) {
|
|
super(message)
|
|
this.status = status
|
|
this.code = code
|
|
}
|
|
}
|
|
|
|
return {
|
|
buildMaterialWhereFromSearchParams: jest.fn(),
|
|
createMaterialQueryContext: jest.fn(),
|
|
MaterialQueryError,
|
|
normalizeMaterialTypeForUi: (value: string) => value,
|
|
normalizeSelectableMaterialTypes: (values: string[]) => values,
|
|
}
|
|
})
|
|
|
|
describe('materials facets route', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
;(getServerSession as jest.Mock).mockResolvedValue({
|
|
user: { id: 'user-1', department: '品牌创意部', role: 'USER' },
|
|
})
|
|
;(createMaterialQueryContext as jest.Mock).mockResolvedValue({
|
|
canAccessDeleted: false,
|
|
expandedViewableTypes: ['任务-详情'],
|
|
isSuperAdmin: false,
|
|
})
|
|
;(buildMaterialWhereFromSearchParams as jest.Mock).mockResolvedValue({
|
|
empty: false,
|
|
where: { deletedAt: null, materialType: '任务-详情' },
|
|
})
|
|
})
|
|
|
|
test('一次请求只解析一次权限上下文,把统计查询降为 7 条且并发限制为 3', async () => {
|
|
let activeQueries = 0
|
|
let maxActiveQueries = 0
|
|
const runQuery = async <T,>(result: T): Promise<T> => {
|
|
activeQueries += 1
|
|
maxActiveQueries = Math.max(maxActiveQueries, activeQueries)
|
|
await new Promise((resolve) => setTimeout(resolve, 1))
|
|
activeQueries -= 1
|
|
return result
|
|
}
|
|
|
|
;(prisma.material.count as jest.Mock).mockImplementation(() => runQuery(0))
|
|
;(prisma.material.findMany as jest.Mock).mockImplementation(() => runQuery([]))
|
|
;(prisma.material.groupBy as jest.Mock).mockImplementation(() => runQuery([]))
|
|
|
|
const response = await GET({
|
|
nextUrl: {
|
|
searchParams: new URLSearchParams('materialType=任务-详情'),
|
|
},
|
|
} as any)
|
|
|
|
expect(response.status).toBe(200)
|
|
expect(createMaterialQueryContext).toHaveBeenCalledTimes(1)
|
|
expect(buildMaterialWhereFromSearchParams).toHaveBeenCalledTimes(7)
|
|
expect(prisma.material.count).toHaveBeenCalledTimes(1)
|
|
expect(prisma.material.groupBy).toHaveBeenCalledTimes(5)
|
|
expect(prisma.material.findMany).toHaveBeenCalledTimes(1)
|
|
expect(maxActiveQueries).toBeLessThanOrEqual(3)
|
|
|
|
const context = (createMaterialQueryContext as jest.Mock).mock.results[0].value
|
|
const resolvedContext = await context
|
|
for (const call of (buildMaterialWhereFromSearchParams as jest.Mock).mock.calls) {
|
|
expect(call[2]).toEqual(expect.objectContaining({ context: resolvedContext }))
|
|
}
|
|
})
|
|
|
|
test('各维度总数直接复用分组计数并保留空值素材', async () => {
|
|
;(prisma.material.count as jest.Mock).mockResolvedValue(9)
|
|
;(prisma.material.groupBy as jest.Mock)
|
|
.mockResolvedValueOnce([
|
|
{ categoryName: '客厅', _count: { _all: 2 } },
|
|
{ categoryName: null, _count: { _all: 1 } },
|
|
])
|
|
.mockResolvedValueOnce([{ sceneName: '室内', _count: { _all: 3 } }])
|
|
.mockResolvedValueOnce([{ platformName: '站内', _count: { _all: 4 } }])
|
|
.mockResolvedValueOnce([{ productCategoryName: '桌类', _count: { _all: 5 } }])
|
|
.mockResolvedValueOnce([{ materialType: '任务-详情', _count: { _all: 6 } }])
|
|
;(prisma.material.findMany as jest.Mock).mockResolvedValue([
|
|
{ createdAt: new Date('2026-07-01T00:00:00.000Z') },
|
|
{ createdAt: new Date('2026-07-02T00:00:00.000Z') },
|
|
])
|
|
|
|
const response = await GET({
|
|
nextUrl: { searchParams: new URLSearchParams() },
|
|
} as any)
|
|
const body = await response.json()
|
|
|
|
expect(body.total).toBe(9)
|
|
expect(body.categories).toEqual([{ id: '客厅', name: '客厅', count: 2 }])
|
|
expect(body.totals).toEqual({
|
|
materialType: 6,
|
|
category: 3,
|
|
scene: 3,
|
|
platform: 4,
|
|
productCategory: 5,
|
|
month: 2,
|
|
})
|
|
})
|
|
})
|