Shumiland/tests/api/tickets-tariffs.test.ts
Vadym Samoilenko cca4ea1d55
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: implement full frontend — all sections, components, Figma Code Connect
- All 8 home page sections: Hero, Locations slider, WhyParents accordion,
  Birthday pricing cards, Video, Gallery, Reviews slider, News
- UI components: NavLink, BtnPrimary, BtnGradient, BtnDetails, AccordionItem
- Layout: sticky Header (NavLink + BtnPrimary), Footer with logo
- Figma Code Connect: 5 components published (.figma.tsx + figma.config.json)
- Public assets: all Figma images and SVGs exported
- Pages: /kvytky, /lokatsii, /blog, /dni-narodzhennia, /grupovi-vidviduvannia
- Tests: Vitest unit/api suites, Playwright e2e screenshots
- Payload CMS: blocks, collections, seed data updates
- Hero negative-margin to extend behind sticky header
- Custom Tailwind breakpoints: lg=1440px, xl=1920px
- Fix ESLint config: drop FlatCompat, use eslint-config-next flat export
- Add tsconfig.tsbuildinfo, test-results/, agentdb.rvf* to .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:40:56 +01:00

200 lines
6.1 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('treats null sort as 0 for ordering purposes', async () => {
mockGetTariffs.mockResolvedValueOnce([
{ id: 1, name: 'A', price: 100 },
{ id: 2, name: 'B', price: 200 },
])
mockPayloadFind.mockResolvedValueOnce({
docs: [
{ ezy_id: 2, display_name: 'B', sort: null },
{ ezy_id: 1, display_name: 'A', sort: 1 },
],
})
const res = await GET()
const { tariffs } = await res.json()
expect(tariffs[0].id).toBe(2) // null → 0, sorts before 1
expect(tariffs[1].id).toBe(1)
})
it('falls back to ezy name when display_name is null', async () => {
mockGetTariffs.mockResolvedValueOnce([{ id: 1, name: 'Ezy Name', price: 100 }])
mockPayloadFind.mockResolvedValueOnce({
docs: [{ ezy_id: 1, display_name: null, sort: 0 }],
})
const res = await GET()
const { tariffs } = await res.json()
expect(tariffs[0].name).toBe('Ezy Name')
})
it('includes categoryTag, description, image, and icon in merged response', async () => {
mockGetTariffs.mockResolvedValueOnce([{ id: 1, name: 'Adult', price: 250 }])
mockPayloadFind.mockResolvedValueOnce({
docs: [
{
ezy_id: 1,
display_name: 'Adult UA',
category_tag: 'adult',
description: 'Full-day pass',
image: { url: '/img/adult.jpg' },
icon: '🎿',
sort: 0,
},
],
})
const res = await GET()
const { tariffs } = await res.json()
expect(tariffs[0].categoryTag).toBe('adult')
expect(tariffs[0].description).toBe('Full-day pass')
expect(tariffs[0].image).toEqual({ url: '/img/adult.jpg' })
expect(tariffs[0].icon).toBe('🎿')
})
it('fallback response uses last_synced_price and includes stale:true', async () => {
mockGetTariffs.mockRejectedValueOnce(new Error('ezy down'))
mockPayloadFind.mockResolvedValueOnce({
docs: [
{
ezy_id: 3,
display_name: 'Weekend',
last_synced_name: 'Weekend pass',
last_synced_price: 199,
category_tag: 'weekend',
description: null,
image: null,
icon: null,
sort: 0,
},
],
})
const res = await GET()
const { tariffs } = await res.json()
expect(tariffs[0].price).toBe(199)
expect(tariffs[0].stale).toBe(true)
expect(tariffs[0].id).toBe(3)
})
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)
})
})