/** * extract-doc / access-roles — loadModuleSensitivity (read-only FS loader). * * The « Données sensibles » line of Section 2: the module's entité.md levels * (lib/ba-sensitivity ladder), the HIGHEST level as moduleLevel (restreint > * confidentiel; public never raises it), every absence → source 'none' and * NEVER a throw. */ import { describe, it, expect, beforeAll, afterAll } from 'vitest' import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { loadModuleSensitivity } from '../access-roles.js' const ENTITE_MD = [ '', '### ENT-001 — FichePaie (agrégat racine)', '- **Sensibilité** : confidentiel — financier, rh', '', '| Attribut | Type | Contraintes | Calculé |', '|---|---|---|---|', '| Id | Guid | PK | — |', '', '### ENT-002 — Tarif (agrégat racine)', '- **Sensibilité** : restreint — commercial', '', '| Attribut | Type | Contraintes | Calculé |', '|---|---|---|---|', '| Id | Guid | PK | — |', '', '### ENT-003 — Catalogue (agrégat racine)', '- **Sensibilité** : public', '', '| Attribut | Type | Contraintes | Calculé |', '|---|---|---|---|', '| Id | Guid | PK | — |', '', '### ENT-004 — Periode (lookup)', '', '| Attribut | Type | Contraintes | Calculé |', '|---|---|---|---|', '| Id | Guid | PK | — |', '', ].join('\n') describe('access-roles / loadModuleSensitivity', () => { let root: string beforeAll(() => { root = mkdtempSync(join(tmpdir(), 'doc-sensitivity-')) const dir = join(root, '.smartstack', 'ba', 'RH', 'PAIE') mkdirSync(dir, { recursive: true }) writeFileSync(join(dir, 'entité.md'), ENTITE_MD, 'utf8') mkdirSync(join(root, '.smartstack', 'ba', 'RH', 'ABSENCES'), { recursive: true }) writeFileSync(join(root, '.smartstack', 'ba', 'RH', 'ABSENCES', 'entité.md'), '### ENT-001 — Absence (agrégat racine)\n\n| Attribut | Type | Contraintes | Calculé |\n|---|---|---|---|\n| Id | Guid | PK | — |\n', 'utf8') }) afterAll(() => rmSync(root, { recursive: true, force: true })) it('reads the module (lowercase nav codes resolve case-insensitively), lists every non-interne entity, moduleLevel = the highest sensitive level', async () => { const warnings: string[] = [] const r = await loadModuleSensitivity(root, ['rh'], 'paie', warnings) expect(r.source).toBe('ba') expect(r.moduleLevel).toBe('restreint') expect(r.entities).toEqual([ { entity: 'FichePaie', level: 'confidentiel', categories: ['financier', 'rh'] }, { entity: 'Tarif', level: 'restreint', categories: ['commercial'] }, { entity: 'Catalogue', level: 'public', categories: [] }, ]) expect(warnings).toEqual([]) }) it('a module with only interne entities → source ba, moduleLevel null, nothing to render', async () => { const r = await loadModuleSensitivity(root, ['RH'], 'ABSENCES', []) expect(r).toEqual({ source: 'ba', moduleLevel: null, entities: [] }) }) it('no BA module, no module code, no BA root → source none with a warning where it helps, never a throw', async () => { const warnings: string[] = [] expect(await loadModuleSensitivity(root, ['rh'], 'inconnu', warnings)).toEqual({ source: 'none', moduleLevel: null, entities: [] }) expect(warnings[0]).toContain('inconnu') expect(await loadModuleSensitivity(root, ['rh'], null, [])).toEqual({ source: 'none', moduleLevel: null, entities: [] }) expect(await loadModuleSensitivity(join(root, 'nowhere'), ['rh'], 'paie', [])).toEqual({ source: 'none', moduleLevel: null, entities: [] }) }) })