flnxi-Wu/__tests__/middleware-cors.test.ts
2026-05-25 17:12:11 +08:00

130 lines
3.6 KiB
TypeScript

jest.mock('next/server', () => {
class MockNextResponse extends Response {
static next() {
return new MockNextResponse(null, { status: 200 })
}
static redirect(url: URL | string) {
return new MockNextResponse(null, {
status: 307,
headers: { Location: url.toString() },
})
}
}
return { NextResponse: MockNextResponse }
})
jest.mock('next-auth/jwt', () => ({
getToken: jest.fn(),
}))
function createRequest(url: string, options: { method?: string; origin?: string } = {}) {
const parsed = new URL(url)
const headers = new Headers()
if (options.origin) {
headers.set('origin', options.origin)
}
return {
method: options.method || 'GET',
url,
headers,
nextUrl: {
pathname: parsed.pathname,
origin: parsed.origin,
},
} as any
}
describe('middleware CORS', () => {
const originalEnv = process.env
beforeEach(() => {
jest.resetModules()
process.env = {
...originalEnv,
ALLOWED_ORIGINS: 'https://portal.example.com',
APP_PUBLIC_URL: 'https://app.example.com',
NEXTAUTH_URL: 'https://app.example.com',
}
})
afterEach(() => {
process.env = originalEnv
})
it('允许白名单来源携带凭据访问 API', async () => {
const { middleware } = await import('../middleware')
const response = await middleware(createRequest('https://app.example.com/api/materials', {
origin: 'https://portal.example.com',
}))
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('https://portal.example.com')
expect(response.headers.get('Access-Control-Allow-Credentials')).toBe('true')
expect(response.headers.get('Vary')).toBe('Origin')
})
it('拒绝陌生来源的预检请求', async () => {
const { middleware } = await import('../middleware')
const response = await middleware(createRequest('https://app.example.com/api/materials', {
method: 'OPTIONS',
origin: 'https://evil.example.com',
}))
expect(response.status).toBe(403)
expect(response.headers.get('Access-Control-Allow-Origin')).toBeNull()
})
it('陌生来源的普通 API 响应不返回 CORS 凭据头', async () => {
const { middleware } = await import('../middleware')
const response = await middleware(createRequest('https://app.example.com/api/materials', {
origin: 'https://evil.example.com',
}))
expect(response.status).toBe(200)
expect(response.headers.get('Access-Control-Allow-Origin')).toBeNull()
expect(response.headers.get('Access-Control-Allow-Credentials')).toBeNull()
})
})
describe('middleware admin pages', () => {
const originalEnv = process.env
beforeEach(() => {
jest.resetModules()
process.env = {
...originalEnv,
NEXTAUTH_SECRET: 'test-secret',
}
})
afterEach(() => {
process.env = originalEnv
})
it('普通用户访问监控页会被带回首页', async () => {
const { getToken } = require('next-auth/jwt')
getToken.mockResolvedValue({ role: 'USER' })
const { middleware } = await import('../middleware')
const response = await middleware(createRequest('https://app.example.com/admin/monitor'))
expect(response.status).toBe(307)
expect(response.headers.get('Location')).toBe('https://app.example.com/')
})
it('管理员可以访问监控页', async () => {
const { getToken } = require('next-auth/jwt')
getToken.mockResolvedValue({ role: 'ADMIN' })
const { middleware } = await import('../middleware')
const response = await middleware(createRequest('https://app.example.com/admin/monitor'))
expect(response.status).toBe(200)
})
})