Shumiland/tests/api/revalidate.test.ts
Vadym Samoilenko 9b41fa447a
Some checks are pending
CI / Type Check (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Unit Tests (push) Waiting to run
Deploy / Build & Push Image (push) Waiting to run
Deploy / Deploy to VPS (push) Blocked by required conditions
feat: complete backend B1-B7 — Payload CMS, ezy payments, leads, deploy
- B1: Next.js 15 + Payload CMS 3.0 + Postgres 16, ESLint, Prettier, Husky, Vitest
- B2: 9 collections, 6 globals, 12 Page Builder blocks, access control, slugify/revalidate hooks
- B3: ezy.com.ua payments, Binotel HMAC webhook, leads API, Telegram bot, Resend email, rate limiting
- B4: Tariffs collection with ezy API sync (cron + manual), dynamic pricing source-of-truth
- B5: 13 test files covering unit libs and all API routes
- B6: Dockerfile multi-stage, docker-compose.prod.yml, nginx.conf SSL, GitHub Actions CI/CD, health endpoint
- B7: docs/admin-guide-ua.md (marketer guide), docs/deploy.md (VPS instructions), README quickstart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:14:54 +01:00

102 lines
3.3 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { NextRequest } from 'next/server'
const mockRevalidatePath = vi.fn()
vi.mock('next/cache', () => ({
revalidatePath: mockRevalidatePath,
revalidateTag: vi.fn(),
}))
const SECRET = 'test-revalidate-secret' // matches vitest.setup.ts
function makeRequest(body: unknown, token = SECRET): NextRequest {
return new NextRequest('http://localhost/api/revalidate', {
method: 'POST',
body: typeof body === 'string' ? body : JSON.stringify(body),
headers: {
'content-type': 'application/json',
authorization: `Bearer ${token}`,
},
})
}
let POST: (req: NextRequest) => Promise<Response>
beforeEach(async () => {
vi.resetModules()
mockRevalidatePath.mockReset()
vi.stubEnv('REVALIDATE_SECRET', SECRET)
const mod = await import('@/app/api/revalidate/route')
POST = mod.POST
})
describe('POST /api/revalidate', () => {
it('returns 401 when authorization header is missing', async () => {
const req = new NextRequest('http://localhost/api/revalidate', {
method: 'POST',
body: JSON.stringify({ slug: 'home' }),
headers: { 'content-type': 'application/json' },
})
const res = await POST(req)
expect(res.status).toBe(401)
})
it('returns 401 for wrong token', async () => {
const res = await POST(makeRequest({ slug: 'home' }, 'wrong-token'))
expect(res.status).toBe(401)
})
it('returns 401 when REVALIDATE_SECRET env is empty (no bypass)', async () => {
vi.stubEnv('REVALIDATE_SECRET', '')
vi.resetModules()
const mod = await import('@/app/api/revalidate/route')
const res = await mod.POST(makeRequest({ slug: 'home' }, ''))
expect(res.status).toBe(401)
})
it('returns 400 for invalid JSON body', async () => {
const req = new NextRequest('http://localhost/api/revalidate', {
method: 'POST',
body: 'not-json',
headers: {
'content-type': 'application/json',
authorization: `Bearer ${SECRET}`,
},
})
const res = await POST(req)
expect(res.status).toBe(400)
})
it('revalidates a specific path when slug is provided', async () => {
const res = await POST(makeRequest({ slug: 'about-us' }))
expect(res.status).toBe(200)
expect(mockRevalidatePath).toHaveBeenCalledWith('/about-us')
expect((await res.json()).revalidated).toBe(true)
})
it('revalidates layout when global is provided', async () => {
const res = await POST(makeRequest({ global: 'header' }))
expect(res.status).toBe(200)
expect(mockRevalidatePath).toHaveBeenCalledWith('/', 'layout')
})
it('revalidates layout when collection is provided', async () => {
const res = await POST(makeRequest({ collection: 'posts' }))
expect(res.status).toBe(200)
expect(mockRevalidatePath).toHaveBeenCalledWith('/', 'layout')
})
it('returns revalidated:true and calls no revalidation when body has none of slug/collection/global', async () => {
const res = await POST(makeRequest({}))
expect(res.status).toBe(200)
expect(mockRevalidatePath).not.toHaveBeenCalled()
})
it('returns 500 when revalidatePath throws', async () => {
mockRevalidatePath.mockImplementationOnce(() => {
throw new Error('cache error')
})
const res = await POST(makeRequest({ slug: 'boom' }))
expect(res.status).toBe(500)
})
})