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 let sendOrderAlert: (order: { email: string items: Array<{ tariffId: string; count: number }> amount: number }) => Promise 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') }) })