flnxi-Wu/lib/__tests__/dingtalk-login-token.test.ts
2026-05-25 16:20:26 +08:00

93 lines
2.7 KiB
TypeScript

import {
consumeDingtalkLoginToken,
createDingtalkLoginToken,
} from '../dingtalk-login-token'
jest.mock('@/lib/prisma', () => ({
prisma: {
dingtalkLoginToken: {
create: jest.fn(),
},
$transaction: jest.fn(),
},
}))
const { prisma } = require('@/lib/prisma')
describe('dingtalk-login-token', () => {
beforeEach(() => {
jest.resetAllMocks()
})
it('创建票据时只保存哈希,不保存明文 token', async () => {
const token = await createDingtalkLoginToken('user-1', 'login')
expect(token.token).toEqual(expect.any(String))
expect(token.token.length).toBeGreaterThan(30)
expect(prisma.dingtalkLoginToken.create).toHaveBeenCalledWith({
data: expect.objectContaining({
userId: 'user-1',
purpose: 'login',
tokenHash: expect.any(String),
expiresAt: expect.any(Date),
}),
})
expect(prisma.dingtalkLoginToken.create.mock.calls[0][0].data.tokenHash).not.toBe(token.token)
})
it('消费票据时会标记已使用并返回用户', async () => {
const mockTx = {
dingtalkLoginToken: {
findUnique: jest.fn().mockResolvedValue({
id: 'token-1',
purpose: 'login',
usedAt: null,
expiresAt: new Date(Date.now() + 60_000),
user: {
id: 'user-1',
email: 'user@example.com',
username: '张三',
role: 'USER',
isSuperAdmin: false,
avatar: null,
isActive: true,
},
}),
updateMany: jest.fn().mockResolvedValue({ count: 1 }),
},
}
prisma.$transaction.mockImplementation((callback: any) => callback(mockTx))
const user = await consumeDingtalkLoginToken('plain-token', 'login')
expect(user).toEqual(expect.objectContaining({ id: 'user-1', email: 'user@example.com' }))
expect(mockTx.dingtalkLoginToken.updateMany).toHaveBeenCalledWith({
where: expect.objectContaining({
id: 'token-1',
purpose: 'login',
usedAt: null,
}),
data: { usedAt: expect.any(Date) },
})
})
it('拒绝过期或已使用票据', async () => {
const mockTx = {
dingtalkLoginToken: {
findUnique: jest.fn().mockResolvedValue({
id: 'token-1',
purpose: 'login',
usedAt: new Date(),
expiresAt: new Date(Date.now() + 60_000),
user: { id: 'user-1' },
}),
updateMany: jest.fn(),
},
}
prisma.$transaction.mockImplementation((callback: any) => callback(mockTx))
await expect(consumeDingtalkLoginToken('plain-token', 'login')).resolves.toBeNull()
expect(mockTx.dingtalkLoginToken.updateMany).not.toHaveBeenCalled()
})
})