import { describe, expect, it } from 'vitest' import { enumValuesOf, splitBaList, splitTopLevel } from '../ba-list-split.js' // Fixtures verbatim from the DemoGestionFlotte signalement (2026-09-02): // the reference grammar of /ba-create-screen puts commas INSIDE parentheses, // so a bare split(',') double-counted every such field (SCR-017/018 false // positives) and truncated the fragments it cited (SCR-019). describe('ba-list-split / splitTopLevel', () => { it('never splits on a comma inside parentheses', () => { expect(splitTopLevel('code (text, requis), name (text, tri)')).toEqual([ 'code (text, requis)', 'name (text, tri)', ]) }) it('handles nested parentheses and semicolons', () => { expect(splitTopLevel('a (x (y, z), w); b')).toEqual(['a (x (y, z), w)', 'b']) }) it('clamps an orphan closing paren to depth 0 (malformed doc)', () => { expect(splitTopLevel('a), b, c')).toEqual(['a)', 'b', 'c']) }) it('protects «…» and backtick spans', () => { expect(splitTopLevel('label «A, B», autre')).toEqual(['label «A, B»', 'autre']) expect(splitTopLevel('`a, b`, c')).toEqual(['`a, b`', 'c']) }) // Malformed-input guard: an unbalanced protection is disabled for the value — // the split degrades to the old NOISY bare split instead of folding the whole // list into one entry and muting the audits (the fail-open the review caught). it('an unclosed paren degrades to the noisy bare split', () => { expect(splitTopLevel('a (b, c')).toEqual(['a (b', 'c']) }) it("an unmatched « never latches — the list still splits", () => { expect(splitTopLevel('label («titre incomplet, code (text), Label (text)')).toEqual([ 'label («titre incomplet', 'code (text)', 'Label (text)', ]) }) it('an odd backtick count never latches — the list still splits', () => { expect(splitTopLevel('`a, b')).toEqual(['`a', 'b']) }) it('keeps empty segments for the caller to filter', () => { expect(splitTopLevel('a,, b')).toEqual(['a', '', 'b']) }) it('honours a custom separator set', () => { expect(splitTopLevel('a, b; c', ',')).toEqual(['a', 'b; c']) }) }) describe('ba-list-split / splitBaList', () => { it('counts the signalement cantons line as 7 fields, not 13', () => { const line = 'Code (text, requis), Label (text, requis), VehicleRegistryName (text, requis), ' + 'MaxPlateDepositMonths (number, requis), MonthlyDepositFee (number, requis), ' + 'IsActive (boolean), SortOrder (number, requis)' expect(splitBaList(line)).toHaveLength(7) }) it('keeps a select facet list intact (the SCR-019 truncated-evidence shape)', () => { expect( splitBaList('RecurrenceRegime (select : Durée / Kilométrage / Sans récurrence, requis), Label (text)'), ).toEqual(['RecurrenceRegime (select : Durée / Kilométrage / Sans récurrence, requis)', 'Label (text)']) }) it('keeps a lookup filter intact — `(lookup, entity X)` is ONE entry', () => { expect(splitBaList('vehicleId (lookup, entity Vehicle), status (select)')).toEqual([ 'vehicleId (lookup, entity Vehicle)', 'status (select)', ]) }) it('trims, strips ONE trailing dot and drops empties and the — placeholder', () => { expect(splitBaList(' a. ; — ;; b ')).toEqual(['a', 'b']) }) }) describe('ba-list-split / enumValuesOf', () => { it('reads only the /-bearing top-level segments — a facet is never a phantom value', () => { expect(enumValuesOf('draft/submitted/approved, requis')).toEqual(['draft', 'submitted', 'approved']) }) it('keeps multi-word values (spaced slashes)', () => { expect(enumValuesOf('Durée / Kilométrage / Sans récurrence')).toEqual([ 'Durée', 'Kilométrage', 'Sans récurrence', ]) }) // Off-grammar (no `/` anywhere): fall back to the top-level segments rather // than [] — an empty result would silently disengage the XD legs guarded on // `enumValues.size > 0` (the fail-open the review caught). it('a comma-authored enum keeps the audit engaged (segments, never [])', () => { expect(enumValuesOf('brouillon, actif, clos, requis')).toEqual([ 'brouillon', 'actif', 'clos', 'requis', ]) }) it('a slash-less facet cell falls back to its segments (noisy, like the old split)', () => { expect(enumValuesOf('requis, défaut brouillon')).toEqual(['requis', 'défaut brouillon']) }) })