import { describe, it, expect } from 'vitest'; import { Hono } from 'hono'; import { searchRouter } from '../src/routes/search.ts'; import type { CardsDb } from '../src/db/connection.ts'; function buildApp() { const stub = { select: () => ({ from: () => ({ innerJoin: () => ({ where: () => ({ orderBy: () => ({ limit: () => [] }) }), }), }), }), }; const app = new Hono(); app.route('/api/v1/search', searchRouter({ db: stub as unknown as CardsDb })); return { app }; } describe('searchRouter — Auth-Gate', () => { it('GET ohne X-User-Id ist 401', async () => { const { app } = buildApp(); const res = await app.request('/api/v1/search?q=foo'); expect(res.status).toBe(401); }); }); describe('searchRouter — Query-Validation', () => { it('GET ohne q ist 422', async () => { const { app } = buildApp(); const res = await app.request('/api/v1/search', { headers: { 'X-User-Id': 'u-1' }, }); expect(res.status).toBe(422); }); it('GET mit q über 500 Zeichen ist 422', async () => { const { app } = buildApp(); const long = 'x'.repeat(501); const res = await app.request(`/api/v1/search?q=${encodeURIComponent(long)}`, { headers: { 'X-User-Id': 'u-1' }, }); expect(res.status).toBe(422); }); it('GET mit gültigem q liefert SearchResultEnvelope', async () => { const { app } = buildApp(); const res = await app.request('/api/v1/search?q=Konfuzius', { headers: { 'X-User-Id': 'u-1' }, }); expect(res.status).toBe(200); const body = (await res.json()) as { envelope_version: string; query: string; app: string; results: unknown[]; partial: boolean; }; expect(body.envelope_version).toBe('0.1'); expect(body.query).toBe('Konfuzius'); expect(body.app).toBe('cards'); expect(Array.isArray(body.results)).toBe(true); expect(body.partial).toBe(false); }); });