86 lines
2.8 KiB
TypeScript

/** @jest-environment node */
import { POST } from '@/app/api/materials/upload/refresh/route'
import { getAuthenticatedUploadUserId } from '@/lib/upload-auth'
import {
refreshOwnedUploadSession,
UploadSessionStoreUnavailableError,
} from '@/lib/upload-sessions'
jest.mock('@/lib/upload-auth', () => ({
getAuthenticatedUploadUserId: jest.fn(),
}))
jest.mock('@/lib/upload-sessions', () => {
class MockUploadSessionStoreUnavailableError extends Error {
constructor(message = '上传会话服务暂时不可用') {
super(message)
this.name = 'UploadSessionStoreUnavailableError'
}
}
return {
isSafeUploadId: (uploadId: string) => /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(uploadId),
refreshOwnedUploadSession: jest.fn(),
UploadSessionStoreUnavailableError: MockUploadSessionStoreUnavailableError,
}
})
function createRequest(uploadId: unknown) {
return {
json: jest.fn().mockResolvedValue({ uploadId }),
} as any
}
describe('/api/materials/upload/refresh', () => {
beforeEach(() => {
jest.clearAllMocks()
;(getAuthenticatedUploadUserId as jest.Mock).mockResolvedValue('user-1')
})
test('Redis 暂时不可用时返回 503 与可重试提示', async () => {
;(refreshOwnedUploadSession as jest.Mock).mockRejectedValue(
new UploadSessionStoreUnavailableError(),
)
const response = await POST(createRequest('upload-active'))
expect(response.status).toBe(503)
expect(response.headers.get('retry-after')).toBe('3')
await expect(response.json()).resolves.toEqual({
error: '上传会话服务暂时不可用,已保留上传进度,请稍后重试',
code: 'UPLOAD_SESSION_STORE_UNAVAILABLE',
})
})
test.each(['missing', 'forbidden'])('%s 只返回确认不存在的 404 语义', async (result) => {
;(refreshOwnedUploadSession as jest.Mock).mockResolvedValue(result)
const response = await POST(createRequest('upload-missing'))
expect(response.status).toBe(404)
await expect(response.json()).resolves.toEqual({ error: '会话不存在或已过期' })
})
test('合法且属于当前用户的会话刷新成功', async () => {
;(refreshOwnedUploadSession as jest.Mock).mockResolvedValue('refreshed')
const response = await POST(createRequest('upload-owned'))
expect(response.status).toBe(200)
await expect(response.json()).resolves.toEqual({
success: true,
uploadId: 'upload-owned',
ttl: 4 * 60 * 60,
})
expect(refreshOwnedUploadSession).toHaveBeenCalledWith('upload-owned', 'user-1')
})
test('非法 uploadId 在访问会话存储前被拒绝', async () => {
const response = await POST(createRequest('../outside'))
expect(response.status).toBe(400)
expect(refreshOwnedUploadSession).not.toHaveBeenCalled()
})
})