import { NextRequest } from 'next/server' jest.mock('@/lib/auth', () => ({ authOptions: {}, })) jest.mock('next-auth', () => ({ getServerSession: jest.fn(), })) jest.mock('@/lib/prisma', () => ({ prisma: { department: { findUnique: jest.fn(), findMany: jest.fn(), create: jest.fn(), deleteMany: jest.fn(), }, user: { findUnique: jest.fn(), findMany: jest.fn(), updateMany: jest.fn(), update: jest.fn(), }, $transaction: jest.fn(), }, })) jest.mock('@/lib/license', () => ({ canUseDepartmentIsolation: jest.fn(), planDepartmentStructureChangeWithPaidCore: jest.fn(), })) const { getServerSession } = require('next-auth') const { prisma } = require('@/lib/prisma') const { canUseDepartmentIsolation, planDepartmentStructureChangeWithPaidCore } = require('@/lib/license') import { POST, PATCH, DELETE } from '../route' describe('departments route license guard', () => { beforeEach(() => { jest.clearAllMocks() getServerSession.mockResolvedValue({ user: { id: 'admin-id', role: 'ADMIN' }, }) prisma.user.findUnique.mockResolvedValue({ isSuperAdmin: true }) prisma.department.findMany.mockResolvedValue([]) planDepartmentStructureChangeWithPaidCore.mockImplementation(async ({ action, target }: any) => { if (action === 'create') { return { valid: true, allowed: true, action, departmentOps: [{ type: 'create', data: { name: String(target.name).trim() } }], userOps: [], } } return { valid: false, allowed: false, error: '未设置测试计划', errorCode: 'TEST_PLAN_MISSING', statusCode: 500, } }) canUseDepartmentIsolation.mockResolvedValue(true) }) test('POST 普通管理员不能创建部门', async () => { prisma.user.findUnique.mockResolvedValue({ isSuperAdmin: false }) const request = new NextRequest('http://localhost/api/departments', { method: 'POST', body: JSON.stringify({ name: '设计部' }), }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(403) expect(data.error).toContain('最高权限') expect(canUseDepartmentIsolation).not.toHaveBeenCalled() expect(prisma.department.create).not.toHaveBeenCalled() }) test('POST 最高权限账号可以创建部门', async () => { prisma.department.create.mockResolvedValue({ id: 'dept-1', name: '设计部' }) const request = new NextRequest('http://localhost/api/departments', { method: 'POST', body: JSON.stringify({ name: '设计部' }), }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(201) expect(data.name).toBe('设计部') expect(planDepartmentStructureChangeWithPaidCore).toHaveBeenCalledWith({ action: 'create', target: { name: '设计部' }, departments: [], }) expect(prisma.department.create).toHaveBeenCalledWith({ data: { name: '设计部' } }) }) test('PATCH 应在许可证不支持时返回 403', async () => { canUseDepartmentIsolation.mockResolvedValue(false) const request = new NextRequest('http://localhost/api/departments', { method: 'PATCH', body: JSON.stringify({ id: 'dept-1', name: '新部门', }), }) const response = await PATCH(request) const data = await response.json() expect(response.status).toBe(403) expect(data.error).toContain('当前许可证未包含部门隔离功能') expect(prisma.department.findUnique).not.toHaveBeenCalled() }) test('PATCH 普通管理员不能重命名部门', async () => { prisma.user.findUnique.mockResolvedValue({ isSuperAdmin: false }) const request = new NextRequest('http://localhost/api/departments', { method: 'PATCH', body: JSON.stringify({ id: 'dept-1', name: '新部门', }), }) const response = await PATCH(request) const data = await response.json() expect(response.status).toBe(403) expect(data.error).toContain('最高权限') expect(canUseDepartmentIsolation).not.toHaveBeenCalled() expect(prisma.department.findUnique).not.toHaveBeenCalled() }) test('DELETE 应在许可证不支持时返回 403', async () => { canUseDepartmentIsolation.mockResolvedValue(false) const request = new NextRequest('http://localhost/api/departments?id=dept-1', { method: 'DELETE', }) const response = await DELETE(request) const data = await response.json() expect(response.status).toBe(403) expect(data.error).toContain('当前许可证未包含部门隔离功能') expect(prisma.department.findUnique).not.toHaveBeenCalled() }) test('DELETE 普通管理员不能删除部门', async () => { prisma.user.findUnique.mockResolvedValue({ isSuperAdmin: false }) const request = new NextRequest('http://localhost/api/departments?id=dept-1', { method: 'DELETE', }) const response = await DELETE(request) const data = await response.json() expect(response.status).toBe(403) expect(data.error).toContain('最高权限') expect(canUseDepartmentIsolation).not.toHaveBeenCalled() expect(prisma.department.findUnique).not.toHaveBeenCalled() }) })