Shumiland/tests/unit/lib/telegram.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

129 lines
4.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, expect, vi, beforeEach } from 'vitest'
const mockFetch = vi.fn()
vi.stubGlobal('fetch', mockFetch)
let sendLeadAlert: (lead: {
name: string
phone: string
email?: string
formSource: string
utmSource?: string
}) => Promise<void>
let sendOrderAlert: (order: {
email: string
items: Array<{ tariffId: string; count: number }>
amount: number
}) => Promise<void>
beforeEach(async () => {
vi.resetModules()
mockFetch.mockReset()
vi.stubEnv('TELEGRAM_BOT_TOKEN', 'test-bot-token')
vi.stubEnv('TELEGRAM_CHAT_ID', '123456789')
const mod = await import('@/lib/telegram')
sendLeadAlert = mod.sendLeadAlert
sendOrderAlert = mod.sendOrderAlert
})
function telegramOk(): Response {
return { ok: true, status: 200 } as Response
}
function telegramFail(): Response {
return { ok: false, status: 400 } as Response
}
describe('sendLeadAlert', () => {
it('sends a Telegram message with all fields', async () => {
mockFetch.mockResolvedValueOnce(telegramOk())
await sendLeadAlert({
name: 'Іван',
phone: '+380501234567',
email: 'ivan@example.com',
formSource: 'hero-form',
utmSource: 'google',
})
expect(mockFetch).toHaveBeenCalledOnce()
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]
const body = JSON.parse(init.body as string)
expect(body.text).toContain('Іван')
expect(body.text).toContain('+380501234567')
expect(body.text).toContain('ivan@example.com')
expect(body.text).toContain('google')
expect(body.chat_id).toBe('123456789')
})
it('omits email and utmSource lines when not provided', async () => {
mockFetch.mockResolvedValueOnce(telegramOk())
await sendLeadAlert({ name: 'Ганна', phone: '+380671234567', formSource: 'footer-form' })
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]
const body = JSON.parse(init.body as string)
expect(body.text).not.toContain('📧')
expect(body.text).not.toContain('🔗')
})
it('skips sending when BOT_TOKEN is not configured', async () => {
vi.stubEnv('TELEGRAM_BOT_TOKEN', '')
vi.resetModules()
const mod = await import('@/lib/telegram')
await mod.sendLeadAlert({ name: 'X', phone: '123', formSource: 'test' })
expect(mockFetch).not.toHaveBeenCalled()
})
it('skips sending when CHAT_ID is not configured', async () => {
vi.stubEnv('TELEGRAM_CHAT_ID', '')
vi.resetModules()
const mod = await import('@/lib/telegram')
await mod.sendLeadAlert({ name: 'X', phone: '123', formSource: 'test' })
expect(mockFetch).not.toHaveBeenCalled()
})
it('does not throw when Telegram API returns non-ok status', async () => {
mockFetch.mockResolvedValueOnce(telegramFail())
await expect(
sendLeadAlert({ name: 'X', phone: '123', formSource: 'test' })
).resolves.not.toThrow()
})
it('does not throw when fetch rejects (network error)', async () => {
mockFetch.mockRejectedValueOnce(new Error('network error'))
await expect(
sendLeadAlert({ name: 'X', phone: '123', formSource: 'test' })
).resolves.not.toThrow()
})
})
describe('sendOrderAlert', () => {
it('sends a Telegram message with order details', async () => {
mockFetch.mockResolvedValueOnce(telegramOk())
await sendOrderAlert({
email: 'buyer@example.com',
items: [{ tariffId: 'adult', count: 2 }],
amount: 500,
})
expect(mockFetch).toHaveBeenCalledOnce()
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]
const body = JSON.parse(init.body as string)
expect(body.text).toContain('buyer@example.com')
expect(body.text).toContain('adult')
expect(body.text).toContain('500')
})
it('formats multiple items in the message', async () => {
mockFetch.mockResolvedValueOnce(telegramOk())
await sendOrderAlert({
email: 'b@b.com',
items: [
{ tariffId: 'adult', count: 1 },
{ tariffId: 'child', count: 3 },
],
amount: 700,
})
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]
const body = JSON.parse(init.body as string)
expect(body.text).toContain('adult × 1')
expect(body.text).toContain('child × 3')
})
})