86 lines
2.4 KiB
TypeScript
86 lines
2.4 KiB
TypeScript
/** @jest-environment node */
|
|
|
|
import { lookup } from 'node:dns/promises'
|
|
import { NextRequest } from 'next/server'
|
|
import { GET } from './route'
|
|
|
|
jest.mock('node:dns/promises', () => ({
|
|
lookup: jest.fn(),
|
|
}))
|
|
|
|
jest.mock('@/lib/video-preview-access', () => ({
|
|
findBlockedVideoPreviewByUrl: jest.fn(),
|
|
isVideoMediaPath: jest.fn(() => false),
|
|
}))
|
|
|
|
const mockedLookup = lookup as jest.MockedFunction<typeof lookup>
|
|
|
|
function createRequest(targetUrl: string) {
|
|
return new NextRequest(
|
|
`https://sc.jiansun.vip/api/proxy-media?url=${encodeURIComponent(targetUrl)}`,
|
|
)
|
|
}
|
|
|
|
describe('/api/proxy-media', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
mockedLookup.mockResolvedValue([
|
|
{ address: '1.1.1.1', family: 4 },
|
|
] as any)
|
|
global.fetch = jest.fn() as jest.Mock
|
|
})
|
|
|
|
test('回环地址在发起网络请求前就被拒绝', async () => {
|
|
const response = await GET(createRequest('http://127.0.0.1:9000/a.jpg'))
|
|
|
|
expect(response.status).toBe(403)
|
|
expect(global.fetch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('域名解析到内网地址时拒绝回源', async () => {
|
|
mockedLookup.mockResolvedValue([
|
|
{ address: '192.168.58.20', family: 4 },
|
|
] as any)
|
|
|
|
const response = await GET(createRequest('https://s3.1080p.hk/a.jpg'))
|
|
|
|
expect(response.status).toBe(403)
|
|
expect(global.fetch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('公网 S3 使用手动跳转和流式响应', async () => {
|
|
;(global.fetch as jest.Mock).mockResolvedValue(new Response('image-data', {
|
|
status: 200,
|
|
headers: {
|
|
'content-type': 'image/jpeg',
|
|
'content-length': '10',
|
|
},
|
|
}))
|
|
|
|
const response = await GET(createRequest('https://s3.1080p.hk/a.jpg?token=secret'))
|
|
|
|
expect(response.status).toBe(200)
|
|
expect(await response.text()).toBe('image-data')
|
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
'https://s3.1080p.hk/a.jpg?token=secret',
|
|
expect.objectContaining({
|
|
method: 'GET',
|
|
redirect: 'manual',
|
|
signal: expect.any(AbortSignal),
|
|
}),
|
|
)
|
|
})
|
|
|
|
test('每次上游跳转都会重新验证目标', async () => {
|
|
;(global.fetch as jest.Mock).mockResolvedValue(new Response(null, {
|
|
status: 302,
|
|
headers: { location: 'http://169.254.169.254/latest/meta-data' },
|
|
}))
|
|
|
|
const response = await GET(createRequest('https://s3.1080p.hk/a.jpg'))
|
|
|
|
expect(response.status).toBe(403)
|
|
expect(global.fetch).toHaveBeenCalledTimes(1)
|
|
})
|
|
})
|