import { readdir } from 'node:fs/promises'; import type { FastifyInstance, LightMyRequestResponse } from 'fastify'; import FormData from 'form-data'; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { buildApp } from '../src/app.js'; import { login, makeConfig, makeTempDir, removeDir, SESSION_COOKIE, startStubIdp, type StubIdp, TINY_PNG, } from './helpers.js'; let idp: StubIdp; let session: string; let app: FastifyInstance; let dirA: string; let dirB: string; beforeAll(async () => { idp = await startStubIdp(); }); afterAll(async () => { await idp.close(); }); beforeEach(async () => { dirA = await makeTempDir(); dirB = await makeTempDir(); app = await buildApp( makeConfig({ targets: [ { name: 'a', label: 'Dossier A', dir: dirA }, { name: 'b', label: 'Dossier B', dir: dirB }, ], maxFileSizeBytes: 2000, oidc: { issuer: idp.issuer, clientId: idp.clientId, clientSecret: idp.clientSecret, scopes: ['openid', 'profile', 'email'], }, }), { logger: false }, ); await app.ready(); session = await login(app, idp); }); afterEach(async () => { await app.close(); await removeDir(dirA); await removeDir(dirB); }); const postUpload = ( form: FormData, opts: { cookie?: string; target?: string } = {}, ): Promise => app.inject({ method: 'POST', url: opts.target !== undefined ? `/upload?target=${encodeURIComponent(opts.target)}` : '/upload', payload: form, headers: form.getHeaders(), ...(opts.cookie !== undefined ? { cookies: { [SESSION_COOKIE]: opts.cookie } } : {}), }); function pngForm(field = 'file', body: Buffer = TINY_PNG): FormData { const form = new FormData(); form.append(field, body, { filename: 'photo.png', contentType: 'image/png' }); return form; } describe('GET /health', () => { it('répond 200 sans authentification', async () => { const res = await app.inject({ method: 'GET', url: '/health' }); expect(res.statusCode).toBe(200); expect(res.json<{ status: string }>().status).toBe('ok'); }); }); describe('GET /targets', () => { it('exige une session', async () => { const res = await app.inject({ method: 'GET', url: '/targets' }); expect(res.statusCode).toBe(401); }); it('renvoie les cibles (nom + libellé, sans chemin)', async () => { const res = await app.inject({ method: 'GET', url: '/targets', cookies: { [SESSION_COOKIE]: session }, }); expect(res.statusCode).toBe(200); expect(res.json()).toEqual([ { name: 'a', label: 'Dossier A' }, { name: 'b', label: 'Dossier B' }, ]); }); }); describe('POST /upload', () => { it('refuse sans session (401)', async () => { const res = await postUpload(pngForm()); expect(res.statusCode).toBe(401); }); it('refuse un cookie de session illisible (401)', async () => { const res = await postUpload(pngForm(), { cookie: 'cookie-bidon' }); expect(res.statusCode).toBe(401); }); it('dépose dans la cible par défaut (première) quand ?target est absent', async () => { const res = await postUpload(pngForm(), { cookie: session }); expect(res.statusCode).toBe(201); const body = res.json<{ target: string; size: number; mimeType: string }>(); expect(body.target).toBe('a'); expect(body.mimeType).toBe('image/png'); expect(body.size).toBe(TINY_PNG.length); expect(await readdir(dirA)).toHaveLength(1); expect(await readdir(dirB)).toHaveLength(0); }); it('dépose dans la cible demandée via ?target=', async () => { const res = await postUpload(pngForm(), { cookie: session, target: 'b' }); expect(res.statusCode).toBe(201); expect(res.json<{ target: string }>().target).toBe('b'); expect(await readdir(dirA)).toHaveLength(0); expect(await readdir(dirB)).toHaveLength(1); }); it('refuse une cible inconnue (400)', async () => { const res = await postUpload(pngForm(), { cookie: session, target: 'inconnu' }); expect(res.statusCode).toBe(400); expect(await readdir(dirA)).toHaveLength(0); }); it('refuse un mauvais nom de champ (400)', async () => { const res = await postUpload(pngForm('autre'), { cookie: session }); expect(res.statusCode).toBe(400); }); it('refuse un type de média non autorisé (400)', async () => { const form = new FormData(); form.append('file', Buffer.from('PK\x03\x04'), { filename: 'archive.zip', contentType: 'application/zip', }); const res = await postUpload(form, { cookie: session }); expect(res.statusCode).toBe(400); }); it('refuse un fichier trop volumineux (413)', async () => { const res = await postUpload(pngForm('file', Buffer.alloc(5000, 1)), { cookie: session }); expect(res.statusCode).toBe(413); }); it('refuse une requête sans fichier (400)', async () => { const form = new FormData(); form.append('title', 'juste du texte'); const res = await postUpload(form, { cookie: session }); expect(res.statusCode).toBe(400); }); });