Shumiland/tests/api/tickets-tariffs.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

122 lines
3.8 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
const mockGetTariffs = vi.fn()
const mockPayloadFind = vi.fn()
const mockGetPayload = vi.fn(() => ({ find: mockPayloadFind }))
vi.mock('@/lib/ezy', () => ({ getTariffs: mockGetTariffs }))
vi.mock('payload', () => ({ getPayload: mockGetPayload }))
vi.mock('@payload-config', () => ({ default: {} }))
const ezyTariffs = [
{ id: 1, name: 'Дорослий', price: 250 },
{ id: 2, name: 'Дитячий', price: 150 },
]
const dbTariffs = [
{ ezy_id: 1, display_name: 'Дорослий (UA)', category_tag: 'adult', sort: 0 },
{ ezy_id: 2, display_name: 'Дитячий (UA)', category_tag: 'child', sort: 1 },
]
let GET: () => Promise<Response>
beforeEach(async () => {
vi.resetModules()
mockGetTariffs.mockReset()
mockPayloadFind.mockReset()
const mod = await import('@/app/api/tickets/tariffs/route')
GET = mod.GET
})
describe('GET /api/tickets/tariffs', () => {
it('returns merged tariffs sorted by sort field', async () => {
mockGetTariffs.mockResolvedValueOnce(ezyTariffs)
mockPayloadFind.mockResolvedValueOnce({ docs: dbTariffs })
const res = await GET()
expect(res.status).toBe(200)
const { tariffs } = await res.json()
expect(tariffs).toHaveLength(2)
expect(tariffs[0].id).toBe(1)
expect(tariffs[0].name).toBe('Дорослий (UA)')
expect(tariffs[0].price).toBe(250)
})
it('filters out ezy tariffs with no matching DB record', async () => {
mockGetTariffs.mockResolvedValueOnce([...ezyTariffs, { id: 99, name: 'Unknown', price: 0 }])
mockPayloadFind.mockResolvedValueOnce({ docs: dbTariffs })
const res = await GET()
const { tariffs } = await res.json()
expect(tariffs).toHaveLength(2)
expect(tariffs.map((t: { id: number }) => t.id)).not.toContain(99)
})
it('uses db display_name over ezy name', async () => {
mockGetTariffs.mockResolvedValueOnce([{ id: 1, name: 'EZY Name', price: 300 }])
mockPayloadFind.mockResolvedValueOnce({
docs: [{ ezy_id: 1, display_name: 'DB Name', sort: 0 }],
})
const res = await GET()
const { tariffs } = await res.json()
expect(tariffs[0].name).toBe('DB Name')
})
it('falls back to DB tariffs when ezy throws, with warning', async () => {
mockGetTariffs.mockRejectedValueOnce(new Error('ezy down'))
mockPayloadFind.mockResolvedValueOnce({
docs: [
{
ezy_id: 1,
display_name: 'Adult',
last_synced_price: 250,
last_synced_name: 'Adult',
sort: 0,
},
],
})
const res = await GET()
expect(res.status).toBe(200)
const json = await res.json()
expect(json.warning).toMatch(/outdated/i)
expect(json.tariffs[0].stale).toBe(true)
})
it('returns 503 when ezy fails and DB is empty', async () => {
mockGetTariffs.mockRejectedValueOnce(new Error('ezy down'))
mockPayloadFind.mockResolvedValueOnce({ docs: [] })
const res = await GET()
expect(res.status).toBe(503)
})
it('returns 503 when both ezy and DB fail', async () => {
mockGetTariffs.mockRejectedValueOnce(new Error('ezy down'))
mockPayloadFind.mockRejectedValueOnce(new Error('DB down'))
const res = await GET()
expect(res.status).toBe(503)
const json = await res.json()
expect(json.error).toBeDefined()
})
it('returns tariffs sorted in ascending sort order', async () => {
mockGetTariffs.mockResolvedValueOnce([
{ id: 2, name: 'B', price: 100 },
{ id: 1, name: 'A', price: 200 },
])
mockPayloadFind.mockResolvedValueOnce({
docs: [
{ ezy_id: 1, display_name: 'A', sort: 0 },
{ ezy_id: 2, display_name: 'B', sort: 5 },
],
})
const res = await GET()
const { tariffs } = await res.json()
expect(tariffs[0].id).toBe(1)
expect(tariffs[1].id).toBe(2)
})
})