143 lines
4.8 KiB
TypeScript
143 lines
4.8 KiB
TypeScript
/** @jest-environment node */
|
|
|
|
import { NextRequest, NextResponse } from 'next/server'
|
|
|
|
import { GET, POST } from '@/app/api/dingtalk/h5-login/route'
|
|
import { loginDingtalkUserByH5AuthCode } from '@/lib/dingtalk-login'
|
|
import { authorizeDingtalkH5UserForNextAuth } from '@/lib/dingtalk-login'
|
|
import { createDingtalkPerformanceSessionToken } from '@/lib/dingtalk-performance-session'
|
|
|
|
jest.mock('@/lib/dingtalk-login', () => ({
|
|
authorizeDingtalkH5UserForNextAuth: jest.fn(),
|
|
loginDingtalkUserByH5AuthCode: jest.fn(),
|
|
}))
|
|
|
|
jest.mock('@/lib/dingtalk-performance-session', () => ({
|
|
createDingtalkPerformanceSessionToken: jest.fn(),
|
|
resolveDingtalkPerformanceSessionScope: jest.fn((redirectPath: string) => {
|
|
const url = new URL(redirectPath, 'https://performance-survey.test')
|
|
const segments = url.pathname.split('/').filter(Boolean)
|
|
const surveyId = segments[2] || ''
|
|
const targetId = url.searchParams.get('targetId')?.trim() || ''
|
|
if (segments[0] === 'performance-survey' && segments[1] === 'survey' && surveyId && targetId) {
|
|
return { kind: 'assignment', surveyId, targetId }
|
|
}
|
|
if (segments[0] === 'performance-survey' && segments[1] === 'results' && surveyId) {
|
|
return { kind: 'results', surveyId, targetId: targetId || null }
|
|
}
|
|
return null
|
|
}),
|
|
}))
|
|
|
|
jest.mock('@/lib/public-app-url', () => ({
|
|
getPublicAppOrigin: jest.fn(() => 'https://example.test'),
|
|
}))
|
|
|
|
jest.mock('@/lib/safe-redirect', () => ({
|
|
normalizeInternalRedirectPath: jest.fn((value: unknown, fallback: string) => (
|
|
typeof value === 'string' && value.startsWith('/') ? value : fallback
|
|
)),
|
|
}))
|
|
|
|
describe('DingTalk H5 public login body limit', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
jest.spyOn(console, 'error').mockImplementation(() => undefined)
|
|
})
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks()
|
|
})
|
|
|
|
test('超限 multipart 在 formData 解析前返回 413', async () => {
|
|
const formData = jest.fn()
|
|
const request = {
|
|
headers: new Headers({
|
|
'content-type': 'multipart/form-data; boundary=test',
|
|
'content-length': String(64 * 1024 + 1),
|
|
}),
|
|
formData,
|
|
} as unknown as NextRequest
|
|
|
|
const response = await POST(request)
|
|
const body = await response.json()
|
|
|
|
expect(response.status).toBe(413)
|
|
expect(body.code).toBe('REQUEST_TOO_LARGE')
|
|
expect(formData).not.toHaveBeenCalled()
|
|
expect(loginDingtalkUserByH5AuthCode).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('缺少 Content-Length 时不读取匿名请求体', async () => {
|
|
const text = jest.fn()
|
|
const request = {
|
|
headers: new Headers({ 'content-type': 'text/plain' }),
|
|
text,
|
|
} as unknown as NextRequest
|
|
|
|
const response = await POST(request)
|
|
|
|
expect(response.status).toBe(411)
|
|
expect(text).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('小型 JSON 登录请求保持原有成功链路', async () => {
|
|
const payload = JSON.stringify({ code: 'short-code' })
|
|
;(loginDingtalkUserByH5AuthCode as jest.Mock).mockResolvedValue(
|
|
NextResponse.json({ success: true }),
|
|
)
|
|
const request = new NextRequest('http://localhost/api/dingtalk/h5-login', {
|
|
method: 'POST',
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
'content-length': String(Buffer.byteLength(payload)),
|
|
},
|
|
body: payload,
|
|
})
|
|
|
|
const response = await POST(request)
|
|
|
|
expect(response.status).toBe(200)
|
|
expect(loginDingtalkUserByH5AuthCode).toHaveBeenCalledWith(
|
|
request,
|
|
'short-code',
|
|
{ appSource: 'workNotice' },
|
|
)
|
|
})
|
|
|
|
test('绩效调研免登落地页写入限定路径的会话 Cookie', async () => {
|
|
;(authorizeDingtalkH5UserForNextAuth as jest.Mock).mockResolvedValue({
|
|
id: 'user-1',
|
|
email: 'external@example.com',
|
|
username: '跨部门同事',
|
|
role: 'USER',
|
|
department: '运营中心',
|
|
isSuperAdmin: false,
|
|
})
|
|
;(createDingtalkPerformanceSessionToken as jest.Mock).mockReturnValue('signed-performance-token')
|
|
|
|
const response = await GET(new NextRequest(
|
|
'https://sc.jiansun.vip/api/dingtalk/h5-login?code=short-code&redirect=%2Fperformance-survey%2Fsurvey%2Fsurvey-1%3FtargetId%3Dtarget-1',
|
|
{
|
|
headers: {
|
|
accept: 'text/html',
|
|
'user-agent': 'DingTalk/7.0',
|
|
},
|
|
},
|
|
))
|
|
|
|
expect(response.status).toBe(200)
|
|
expect(response.headers.get('set-cookie')).toContain(
|
|
'dingtalk-performance-session=signed-performance-token',
|
|
)
|
|
expect(response.headers.get('set-cookie')).toContain('Path=/performance-survey')
|
|
expect(response.headers.get('set-cookie')).toContain('Path=/api')
|
|
expect(response.headers.get('set-cookie')).toContain('HttpOnly')
|
|
expect(authorizeDingtalkH5UserForNextAuth).toHaveBeenCalledWith('short-code', {
|
|
appSource: 'workNotice',
|
|
clientIp: null,
|
|
allowRestrictedPerformanceAccess: true,
|
|
})
|
|
})
|
|
})
|