172 lines
5.4 KiB
TypeScript
172 lines
5.4 KiB
TypeScript
import { NextRequest } from 'next/server'
|
|
|
|
jest.mock('@/lib/auth', () => ({
|
|
authOptions: {},
|
|
}))
|
|
|
|
jest.mock('next-auth', () => ({
|
|
getServerSession: jest.fn(),
|
|
}))
|
|
|
|
jest.mock('@/lib/prisma', () => ({
|
|
prisma: {
|
|
user: {
|
|
findUnique: jest.fn(),
|
|
},
|
|
invitation: {
|
|
count: jest.fn(),
|
|
findMany: jest.fn(),
|
|
create: jest.fn(),
|
|
update: jest.fn(),
|
|
delete: jest.fn(),
|
|
},
|
|
},
|
|
}))
|
|
|
|
jest.mock('@/lib/config', () => ({
|
|
resolveBaseUrl: jest.fn(() => 'http://localhost:3000'),
|
|
}))
|
|
|
|
const { getServerSession } = require('next-auth')
|
|
const { prisma } = require('@/lib/prisma')
|
|
const { resolveBaseUrl } = require('@/lib/config')
|
|
|
|
import { GET, POST } from '../route'
|
|
import { PATCH, DELETE } from '../[id]/route'
|
|
|
|
function jsonRequest(url: string, body: unknown, method = 'POST') {
|
|
return new NextRequest(url, {
|
|
method,
|
|
body: JSON.stringify(body),
|
|
})
|
|
}
|
|
|
|
describe('邀请链接管理权限', () => {
|
|
beforeEach(() => {
|
|
jest.resetAllMocks()
|
|
resolveBaseUrl.mockReturnValue('http://localhost:3000')
|
|
getServerSession.mockResolvedValue({
|
|
user: { id: 'admin-1', role: 'ADMIN', isSuperAdmin: false },
|
|
})
|
|
prisma.user.findUnique.mockResolvedValue({ isSuperAdmin: false })
|
|
})
|
|
|
|
it('普通管理员不能查看或创建邀请链接', async () => {
|
|
const listResponse = await GET(new NextRequest('http://localhost/api/invitations'))
|
|
const createResponse = await POST(
|
|
jsonRequest('http://localhost/api/invitations', { maxUses: 1, expiresInDays: null })
|
|
)
|
|
|
|
expect(listResponse.status).toBe(403)
|
|
expect(createResponse.status).toBe(403)
|
|
expect(prisma.invitation.findMany).not.toHaveBeenCalled()
|
|
expect(prisma.invitation.create).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('旧会话带最高权限标记但数据库已撤销时不能创建邀请链接', async () => {
|
|
getServerSession.mockResolvedValue({
|
|
user: { id: 'admin-1', role: 'ADMIN', isSuperAdmin: true },
|
|
})
|
|
prisma.user.findUnique.mockResolvedValue({ isSuperAdmin: false })
|
|
|
|
const response = await POST(
|
|
jsonRequest('http://localhost/api/invitations', { maxUses: 1, expiresInDays: null })
|
|
)
|
|
|
|
expect(response.status).toBe(403)
|
|
expect(prisma.invitation.create).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('最高权限账号可以创建并查看邀请链接', async () => {
|
|
getServerSession.mockResolvedValue({
|
|
user: { id: 'super-1', role: 'ADMIN', isSuperAdmin: true },
|
|
})
|
|
prisma.user.findUnique.mockResolvedValue({ isSuperAdmin: true })
|
|
prisma.invitation.create.mockResolvedValue({
|
|
id: 'invite-1',
|
|
code: 'ABCD1234',
|
|
createdBy: 'super-1',
|
|
maxUses: 3,
|
|
currentUses: 0,
|
|
expiresAt: null,
|
|
isActive: true,
|
|
createdAt: new Date('2026-05-28T00:00:00.000Z'),
|
|
})
|
|
prisma.invitation.count.mockResolvedValue(1)
|
|
prisma.invitation.findMany.mockResolvedValue([
|
|
{
|
|
id: 'invite-1',
|
|
code: 'ABCD1234',
|
|
createdBy: 'super-1',
|
|
maxUses: 3,
|
|
currentUses: 0,
|
|
expiresAt: null,
|
|
isActive: true,
|
|
createdAt: new Date('2026-05-28T00:00:00.000Z'),
|
|
},
|
|
])
|
|
|
|
const createResponse = await POST(
|
|
jsonRequest('http://localhost/api/invitations', { maxUses: 3, expiresInDays: null })
|
|
)
|
|
const listResponse = await GET(new NextRequest('http://localhost/api/invitations'))
|
|
const listData = await listResponse.json()
|
|
|
|
expect(createResponse.status).toBe(200)
|
|
expect(listResponse.status).toBe(200)
|
|
expect(prisma.invitation.create).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
data: expect.objectContaining({
|
|
createdBy: 'super-1',
|
|
maxUses: 3,
|
|
}),
|
|
})
|
|
)
|
|
expect(listData.invitations[0].inviteUrl).toBe('http://localhost:3000/register?invite=ABCD1234')
|
|
})
|
|
|
|
it('普通管理员不能启停或删除邀请链接', async () => {
|
|
const patchResponse = await PATCH(
|
|
jsonRequest('http://localhost/api/invitations/invite-1', { isActive: false }, 'PATCH'),
|
|
{ params: { id: 'invite-1' } }
|
|
)
|
|
const deleteResponse = await DELETE(
|
|
new NextRequest('http://localhost/api/invitations/invite-1', { method: 'DELETE' }),
|
|
{ params: { id: 'invite-1' } }
|
|
)
|
|
|
|
expect(patchResponse.status).toBe(403)
|
|
expect(deleteResponse.status).toBe(403)
|
|
expect(prisma.invitation.update).not.toHaveBeenCalled()
|
|
expect(prisma.invitation.delete).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('最高权限账号可以启停和删除邀请链接', async () => {
|
|
getServerSession.mockResolvedValue({
|
|
user: { id: 'super-1', role: 'ADMIN', isSuperAdmin: true },
|
|
})
|
|
prisma.user.findUnique.mockResolvedValue({ isSuperAdmin: true })
|
|
prisma.invitation.update.mockResolvedValue({ id: 'invite-1', isActive: false })
|
|
prisma.invitation.delete.mockResolvedValue({ id: 'invite-1' })
|
|
|
|
const patchResponse = await PATCH(
|
|
jsonRequest('http://localhost/api/invitations/invite-1', { isActive: false }, 'PATCH'),
|
|
{ params: { id: 'invite-1' } }
|
|
)
|
|
const deleteResponse = await DELETE(
|
|
new NextRequest('http://localhost/api/invitations/invite-1', { method: 'DELETE' }),
|
|
{ params: { id: 'invite-1' } }
|
|
)
|
|
|
|
expect(patchResponse.status).toBe(200)
|
|
expect(deleteResponse.status).toBe(200)
|
|
expect(prisma.invitation.update).toHaveBeenCalledWith({
|
|
where: { id: 'invite-1' },
|
|
data: { isActive: false },
|
|
})
|
|
expect(prisma.invitation.delete).toHaveBeenCalledWith({
|
|
where: { id: 'invite-1' },
|
|
})
|
|
})
|
|
})
|