import { Hono } from 'hono'; import { describe, expect, it } from 'vitest'; import { z } from 'zod'; import { dec, parseBody, parseListQuery } from '../../src/lib/http.js'; import { errorHandler } from '../../src/middleware/error-handler.js'; describe('parseListQuery', () => { it('extrait page/per_page/sort/filter', async () => { const app = new Hono(); app.get('/', (c) => { const q = parseListQuery(c); return c.json(q); }); const res = await app.request('/?page=2&per_page=20&sort=-name&filter[statut]=actif'); const body = (await res.json()) as { page: number; per_page: number; sort: string; filter: Record; }; expect(body.page).toBe(2); expect(body.per_page).toBe(20); expect(body.sort).toBe('-name'); expect(body.filter.statut).toBe('actif'); }); it('clamp per_page entre 1 et 200', async () => { const app = new Hono(); app.get('/', (c) => c.json(parseListQuery(c))); const r1 = await app.request('/?per_page=500'); const b1 = (await r1.json()) as { per_page: number }; expect(b1.per_page).toBe(200); const r2 = await app.request('/?per_page=0'); const b2 = (await r2.json()) as { per_page: number }; // 0 -> default fallback 50 expect(b2.per_page).toBeGreaterThanOrEqual(1); }); it('defaults page=1 per_page=50', async () => { const app = new Hono(); app.get('/', (c) => c.json(parseListQuery(c))); const res = await app.request('/'); const body = (await res.json()) as { page: number; per_page: number }; expect(body.page).toBe(1); expect(body.per_page).toBe(50); }); }); describe('dec', () => { it('toFixed 2 sur Decimal', async () => { const { Decimal } = await import('decimal.js'); expect(dec(new Decimal('40.123'))).toBe('40.12'); expect(dec(new Decimal(0))).toBe('0.00'); }); it('null/undefined -> "0"', () => { expect(dec(null)).toBe('0'); expect(dec(undefined)).toBe('0'); }); }); describe('parseBody', () => { it('valide via schema', async () => { const app = new Hono(); app.onError(errorHandler); app.post('/', async (c) => { const body = await parseBody(c, z.object({ a: z.number() })); return c.json(body); }); const res = await app.request('/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ a: 1 }), }); expect(res.status).toBe(200); }); it('400 si body pas JSON', async () => { const app = new Hono(); app.onError(errorHandler); app.post('/', async (c) => { await parseBody(c, z.object({})); return c.json({}); }); const res = await app.request('/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: 'not-json', }); expect(res.status).toBe(400); }); it('400 si schema mismatch', async () => { const app = new Hono(); app.onError(errorHandler); app.post('/', async (c) => { await parseBody(c, z.object({ a: z.number() })); return c.json({}); }); const res = await app.request('/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ a: 'not-a-number' }), }); expect(res.status).toBe(400); }); });