A receptek függvényei második paraméterben kapják a klienst, ezért tesztben a kassza/testing mock kliensét adhatod át nekik. A mock nem hív hálózatot, ugyanazt a validációt és kerekítést futtatja, mint az éles kliens, és naplózza a hívásokat. Ez a recept a többi recept kódjához ad kész teszteket.
A mock részletes leírása a Tesztelés oldalon van. Itt azt látod, mit érdemes tesztelni egy valódi integrációban.
A tsconfigPaths() miatt a tesztekben is működnek a @/lib/... importok. A kamu kulcsra azért van szükség, mert a receptek modulszinten importálják a @/lib/kassza klienst, a createKassza() pedig már létrehozáskor ellenőrzi a kulcs meglétét.
import { type Kassza, SzamlazzError } from 'kassza'import { createMockKassza, type MockKassza } from 'kassza/testing'import { expect, test } from 'vitest'import { type FizetettRendeles, szamlazFizetettRendelest } from './szamlazas'const rendeles: FizetettRendeles = { id: '5001', fizetesiMod: 'bankkártya', vevo: { name: 'Nagy Péter', zip: '1111', city: 'Budapest', address: 'Fő utca 1.', email: 'peter@example.hu', }, tetelek: [{ name: 'Póló', quantity: 2, grossUnitPrice: 5_990, vat: 27 }],}function szamlakeszitesek(mock: MockKassza): number { return mock.calls.filter((hivas) => hivas.method === 'invoices.create').length}function idotullepesUtanElkeszul(mock: MockKassza): Kassza { return { ...mock, invoices: { ...mock.invoices, create: async (adatok) => { await mock.invoices.create(adatok) throw new SzamlazzError('A Számlázz.hu nem válaszolt.', { category: 'timeout' }) }, }, }}test('egy rendelésről csak egy számla készül', async () => { const mock = createMockKassza() const elso = await szamlazFizetettRendelest(rendeles, mock) const masodik = await szamlazFizetettRendelest(rendeles, mock) expect(masodik).toBe(elso) expect(szamlakeszitesek(mock)).toBe(1) expect(mock.invoiceRecords.get(elso)?.details.totals.grossAmount).toBe(11_980)})test('időtúllépés után a mégis elkészült számlát adja vissza', async () => { const mock = createMockKassza() const szamlaszam = await szamlazFizetettRendelest(rendeles, idotullepesUtanElkeszul(mock)) expect(mock.invoiceRecords.size).toBe(1) expect(mock.invoiceRecords.has(szamlaszam)).toBe(true)})test('hálózati hibát továbbdob, ha nem készült számla', async () => { const mock = createMockKassza() mock.failNext('invoices.create') await expect(szamlazFizetettRendelest(rendeles, mock)).rejects.toMatchObject({ category: 'network', }) expect(mock.invoiceRecords.size).toBe(0)})test('validációs hiba után nem kérdez le újra', async () => { const mock = createMockKassza() const szamlazas = szamlazFizetettRendelest({ ...rendeles, tetelek: [] }, mock) await expect(szamlazas).rejects.toMatchObject({ category: 'validation' }) expect(mock.calls.map((hivas) => hivas.method)).toEqual(['invoices.get', 'invoices.create'])})test('a kelte a budapesti idő szerinti nap', async () => { const mock = createMockKassza({ now: () => new Date('2026-12-31T23:30:00Z') }) const szamlaszam = await szamlazFizetettRendelest(rendeles, mock) expect(mock.invoiceRecords.get(szamlaszam)?.details.header.issueDate).toBe('2027-01-01')})
Az időtúllépéses teszt csomagoló klienst használ, mert a failNext() a bizonylat létrehozása előtt dob. A csomagoló előbb kiállítja a számlát, és csak utána dob hibát, ahogy egy elveszett válasz után történne.
A route handlerek modulszinten importálják a klienst, ezért itt a vi.mock() cseréli le a @/lib/kassza modult mock kliensre, a saját adatbázis-függvényt pedig egy kémfüggvényre. A teszt az IPN webhook route handlerét hívja meg egy összerakott Request-tel.