93 lines
2.7 KiB
TypeScript
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()
|
|
})
|
|
})
|