/** * Tests des mappers Row -> Domain. On instancie les repos avec un BaserowClient mock * minimal qui rend juste getRow/listRows. */ import type { Logger } from 'pino'; import { describe, expect, it, vi } from 'vitest'; import type { BaserowClient, BaserowPaginatedResponse, BaserowRow, } from '../../src/adapters/baserow-client.js'; import { logger } from '../../src/lib/logger.js'; import { AttributionRepo, FormationRepo, ModuleRepo, PersonneRepo, ProjetRepo, } from '../../src/repos/baserow-repo.js'; function fakeClient(rowsByTable: Record): BaserowClient { return { listRows: vi.fn( (tableId: number): Promise => Promise.resolve({ count: rowsByTable[tableId]?.length ?? 0, next: null, previous: null, results: rowsByTable[tableId] ?? [], }), ), getRow: vi.fn((tableId: number, rowId: number): Promise => { const row = (rowsByTable[tableId] ?? []).find((r) => r.id === rowId); if (!row) return Promise.reject(Object.assign(new Error('not found'), { code: 'NOT_FOUND' })); return Promise.resolve(row); }), createRow: vi.fn(), updateRow: vi.fn(), deleteRow: vi.fn(), resolveTableIds: vi.fn(), healthCheck: vi.fn(), } as unknown as BaserowClient; } const log = logger as Logger; describe('PersonneRepo', () => { it('mappe une row Baserow vers Personne', async () => { const row: BaserowRow = { id: 42, order: '1', personne_nom: 'Dupont', personne_prenom: 'Pierre', personne_email: 'p@a.fr', personne_capacite_annuelle: '1000', personne_split_formation_pct: 60, personne_split_agence_pct: 40, personne_roles: [{ id: 1, value: 'formateur', color: 'blue' }], personne_statut: { id: 2, value: 'actif', color: 'green' }, personne_heures_attribuees_formation: '0', personne_heures_attribuees_agence: '0', }; const repo = new PersonneRepo({ client: fakeClient({ 1: [row] }), tableId: 1, entityName: 'Personne', logger: log, }); const personne = await repo.get(42); expect(personne.id).toBe(42); expect(personne.nom).toBe('Dupont'); expect(personne.hasRole('formateur')).toBe(true); expect(personne.statut).toBe('actif'); expect(personne.capaciteAnnuelle.toNumber()).toBe(1000); }); it('list pagine et map', async () => { const repo = new PersonneRepo({ client: fakeClient({ 1: [ { id: 1, order: '1', personne_nom: 'A', personne_prenom: 'B', personne_email: 'a@b.c', personne_capacite_annuelle: '1', personne_split_formation_pct: 50, personne_split_agence_pct: 50, personne_roles: [], personne_statut: 'actif', }, ], }), tableId: 1, entityName: 'Personne', logger: log, }); const res = await repo.list({ size: 50 }); expect(res.items).toHaveLength(1); expect(res.meta.total).toBe(1); }); it('throw NOT_FOUND si row inexistante', async () => { const repo = new PersonneRepo({ client: fakeClient({ 1: [] }), tableId: 1, entityName: 'Personne', logger: log, }); await expect(repo.get(999)).rejects.toMatchObject({ code: 'NOT_FOUND' }); }); }); describe('FormationRepo', () => { it('mappe filiere/statut select', async () => { const row: BaserowRow = { id: 10, order: '1', formation_nom: 'Dev', formation_filiere: { id: 1, value: 'dev', color: 'blue' }, formation_heures_totales: 500, formation_statut: { id: 2, value: 'actif', color: 'green' }, }; const repo = new FormationRepo({ client: fakeClient({ 2: [row] }), tableId: 2, entityName: 'Formation', logger: log, }); const f = await repo.get(10); expect(f.filiere).toBe('dev'); expect(f.statut).toBe('actif'); }); }); describe('ModuleRepo', () => { it('mappe blocId via link field', async () => { const row: BaserowRow = { id: 200, order: '1', module_nom: 'JS', module_heures_prevues: 30, module_statut: 'a_attribuer', module_bloc: [{ id: 100, value: 'Bloc JS' }], }; const repo = new ModuleRepo({ client: fakeClient({ 4: [row] }), tableId: 4, entityName: 'Module', logger: log, }); const m = await repo.get(200); expect(m.blocId).toBe(100); expect(m.statut).toBe('a_attribuer'); }); }); describe('AttributionRepo', () => { it('mappe + create persiste les bons fields', async () => { const row: BaserowRow = { id: 500, order: '1', attribution_heures_attribuees: 10, attribution_heures_realisees: 0, attribution_module: [{ id: 200, value: 'JS' }], attribution_personne: [{ id: 1, value: 'Pierre' }], attribution_statut: 'planifie', }; const client = fakeClient({ 5: [row] }); const repo = new AttributionRepo({ client, tableId: 5, entityName: 'Attribution', logger: log, }); const a = await repo.get(500); expect(a.moduleId).toBe(200); expect(a.personneId).toBe(1); await repo.create({ moduleId: 200, personneId: 1, heuresAttribuees: a.heuresAttribuees, dateDebut: new Date('2026-09-01'), dateFin: null, statut: 'planifie', }); expect(client.createRow).toHaveBeenCalledWith( 5, expect.objectContaining({ attribution_module: [200], attribution_personne: [1], attribution_statut: 'planifie', }), ); }); }); describe('ProjetRepo', () => { it('mappe statut + clientId', async () => { const row: BaserowRow = { id: 300, order: '1', projet_nom: 'Acme', projet_charge_heures: 80, projet_client: [{ id: 50, value: 'Acme Inc' }], projet_statut: { id: 1, value: 'en_cours', color: 'blue' }, projet_type: { id: 2, value: 'site_web', color: 'blue' }, }; const repo = new ProjetRepo({ client: fakeClient({ 7: [row] }), tableId: 7, entityName: 'Projet', logger: log, }); const p = await repo.get(300); expect(p.clientId).toBe(50); expect(p.statut).toBe('en_cours'); expect(p.type).toBe('site_web'); }); });