import { afterEach, describe, expect, it } from 'vitest'; import { buildFakeRepos } from '../helpers/fake-repos.js'; import { makeAttribution, makeModule, makePersonne } from '../helpers/fixtures.js'; import { READ_ALL_TOKEN, WRITE_ALL_TOKEN, buildTestApp, installTestContainer, resetTestContainer, } from '../helpers/test-app.js'; function postAttribuer( app: ReturnType, body: Record, token = WRITE_ALL_TOKEN, ) { return app.request('/api/v1/modules/200/attribuer', { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), }); } describe('POST /api/v1/modules/:id/attribuer', () => { afterEach(resetTestContainer); it('happy path 201 + persistance via repo', async () => { const repos = buildFakeRepos({ personnes: [makePersonne({ id: 1, roles: ['formateur'] })], modules: [makeModule(200, 100)], }); const app = buildTestApp(installTestContainer({ repos })); const res = await postAttribuer(app, { personne_id: 1, heures: 10, date_debut: '2026-09-01T00:00:00Z', }); expect(res.status).toBe(201); const body = (await res.json()) as { data: { attribution_id: number; statut: string } }; expect(body.data.statut).toBe('planifie'); expect(repos.attributions.lastCreated?.heuresAttribuees.toNumber()).toBe(10); }); it('422 RG-01 si heures > capacite module', async () => { const repos = buildFakeRepos({ personnes: [makePersonne({ id: 1, roles: ['formateur'] })], modules: [makeModule(200, 100)], // heuresPrevues = 30 attributions: [makeAttribution({ id: 500, moduleId: 200, personneId: 99 })], // 10h deja }); const app = buildTestApp(installTestContainer({ repos })); const res = await postAttribuer(app, { personne_id: 1, heures: 50 }); expect(res.status).toBe(422); const body = (await res.json()) as { error: { code: string; details: { rule: string } } }; expect(body.error.code).toBe('RG_VIOLATION'); expect(body.error.details.rule).toBe('RG-01'); }); it('422 (validation) si role formateur manquant', async () => { const repos = buildFakeRepos({ personnes: [makePersonne({ id: 1, roles: ['developpeur'] })], modules: [makeModule(200, 100)], }); const app = buildTestApp(installTestContainer({ repos })); const res = await postAttribuer(app, { personne_id: 1, heures: 5 }); expect(res.status).toBe(400); const body = (await res.json()) as { error: { code: string } }; expect(body.error.code).toBe('VALIDATION_ERROR'); }); it('400 si body invalide (heures negatives)', async () => { const repos = buildFakeRepos({}); const app = buildTestApp(installTestContainer({ repos })); const res = await postAttribuer(app, { personne_id: 1, heures: -3 }); expect(res.status).toBe(400); }); it('401 sans token', async () => { const repos = buildFakeRepos({}); const app = buildTestApp(installTestContainer({ repos })); const res = await app.request('/api/v1/modules/200/attribuer', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ personne_id: 1, heures: 5 }), }); expect(res.status).toBe(401); }); it('403 avec mauvais scope', async () => { const repos = buildFakeRepos({}); const app = buildTestApp(installTestContainer({ repos })); const res = await postAttribuer(app, { personne_id: 1, heures: 5 }, READ_ALL_TOKEN); expect(res.status).toBe(403); }); });