mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 15:04:14 +00:00
110 lines
3.4 KiB
TypeScript
110 lines
3.4 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
import { NextRequest, NextResponse } from 'next/server'
|
|
|
|
function createCronRequest(
|
|
url = 'https://example.com/api/cron/test',
|
|
authHeader?: string,
|
|
extraHeaders: Record<string, string> = {},
|
|
): NextRequest {
|
|
const headers = new Headers(extraHeaders)
|
|
if (authHeader) {
|
|
headers.set('authorization', authHeader)
|
|
}
|
|
return new NextRequest(url, { headers })
|
|
}
|
|
|
|
describe('cron auth and coordination', () => {
|
|
beforeEach(() => {
|
|
vi.resetModules()
|
|
vi.stubEnv('REDIS_ENABLED', 'false')
|
|
vi.stubEnv('CRON_SECRET', 'top-secret-token')
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs()
|
|
})
|
|
|
|
it('denies requests when CRON_SECRET is missing', async () => {
|
|
vi.stubEnv('CRON_SECRET', '')
|
|
const { requireCronAuth } = await import('@/lib/security/cron-auth')
|
|
|
|
const response = requireCronAuth(createCronRequest())
|
|
|
|
expect(response).not.toBeNull()
|
|
expect(response?.status).toBe(503)
|
|
})
|
|
|
|
it('denies requests with missing bearer header', async () => {
|
|
const { requireCronAuth } = await import('@/lib/security/cron-auth')
|
|
|
|
const response = requireCronAuth(createCronRequest())
|
|
|
|
expect(response).not.toBeNull()
|
|
expect(response?.status).toBe(401)
|
|
})
|
|
|
|
it('allows requests with valid bearer header', async () => {
|
|
const { requireCronAuth } = await import('@/lib/security/cron-auth')
|
|
|
|
const response = requireCronAuth(
|
|
createCronRequest('https://example.com/api/cron/test', 'Bearer top-secret-token'),
|
|
)
|
|
|
|
expect(response).toBeNull()
|
|
})
|
|
|
|
it('ignores duplicate idempotency-key requests', async () => {
|
|
const { withCronExecution, resetCronCoordinationStateForTests } = await import(
|
|
'@/lib/security/cron-coordination'
|
|
)
|
|
resetCronCoordinationStateForTests()
|
|
|
|
const req1 = createCronRequest(
|
|
'https://example.com/api/cron/test?idempotencyKey=abc',
|
|
'Bearer top-secret-token',
|
|
)
|
|
const req2 = createCronRequest(
|
|
'https://example.com/api/cron/test?idempotencyKey=abc',
|
|
'Bearer top-secret-token',
|
|
)
|
|
|
|
const first = await withCronExecution(req1, { endpoint: 'test-cron' }, async () =>
|
|
NextResponse.json({ ok: true }),
|
|
)
|
|
const second = await withCronExecution(req2, { endpoint: 'test-cron' }, async () =>
|
|
NextResponse.json({ ok: true }),
|
|
)
|
|
|
|
expect(first.status).toBe(200)
|
|
expect(second.status).toBe(202)
|
|
})
|
|
|
|
it('enforces execution lock per endpoint', async () => {
|
|
const { withCronExecution, resetCronCoordinationStateForTests } = await import(
|
|
'@/lib/security/cron-coordination'
|
|
)
|
|
resetCronCoordinationStateForTests()
|
|
|
|
let releaseHandler: () => void = () => {}
|
|
const firstRun = withCronExecution(
|
|
createCronRequest('https://example.com/api/cron/test', 'Bearer top-secret-token'),
|
|
{ endpoint: 'locked-cron', lockTtlMs: 120000 },
|
|
async () =>
|
|
new Promise<NextResponse>((resolve) => {
|
|
releaseHandler = () => resolve(NextResponse.json({ ok: true }))
|
|
}),
|
|
)
|
|
|
|
const second = await withCronExecution(
|
|
createCronRequest('https://example.com/api/cron/test', 'Bearer top-secret-token'),
|
|
{ endpoint: 'locked-cron', lockTtlMs: 120000 },
|
|
async () => NextResponse.json({ ok: true }),
|
|
)
|
|
|
|
expect(second.status).toBe(423)
|
|
|
|
releaseHandler()
|
|
const first = await firstRun
|
|
expect(first.status).toBe(200)
|
|
})
|
|
})
|