import { describe, it, expect } from 'vitest' import { groupByFamily, provisionSummary, summarizeEvidence, type FamilyGroup, } from '../advisor/evidence-grouping' import type { EvidenceUnit } from '../advisor/contract' function u(p: Partial & { document: string }): EvidenceUnit { return { evidence_id: Math.random().toString(36).slice(2), ...p } } // The Datenschutzerklärung scenario the user reviewed: 6 Kernnormen (5 DSGVO Artikel + § 25 TDDDG) // + 2 Leitlinien (DSK, EDPB) across 8 evidence units. const DSE: EvidenceUnit[] = [ u({ document: 'DSGVO', section: 'Art. 6', bindingness: 'binding' }), u({ document: 'DSGVO', section: 'Art. 7', bindingness: 'binding' }), u({ document: 'DSGVO', section: 'Art. 12', bindingness: 'binding' }), u({ document: 'DSGVO', section: 'Art. 13', bindingness: 'binding' }), u({ document: 'DSGVO', section: 'Art. 14', bindingness: 'binding' }), u({ document: 'TDDDG', section: '§ 25', bindingness: 'binding' }), u({ document: 'DSK', bindingness: 'guidance' }), u({ document: 'EDPB WP 259', bindingness: 'guidance' }), ] describe('groupByFamily', () => { it('groups a family and collects distinct provisions in order', () => { const groups = groupByFamily(DSE) const dsgvo = groups.find((g) => g.key === 'dsgvo')! expect(dsgvo.units).toBe(5) expect(dsgvo.sections).toEqual(['Art. 6', 'Art. 7', 'Art. 12', 'Art. 13', 'Art. 14']) expect(dsgvo.bindingness).toBe('binding') }) it('does not duplicate a repeated section', () => { const groups = groupByFamily([ u({ document: 'DSGVO', section: 'Art. 13', bindingness: 'binding' }), u({ document: 'DSGVO', section: 'Art. 13', bindingness: 'binding' }), ]) expect(groups[0].sections).toEqual(['Art. 13']) expect(groups[0].units).toBe(2) }) }) describe('summarizeEvidence', () => { it('splits binding norms from guidance with correct counts', () => { const m = summarizeEvidence(DSE) expect(m.hasBindingness).toBe(true) expect(m.normProvisions).toBe(6) // 5 DSGVO Artikel + § 25 TDDDG expect(m.guidanceCount).toBe(2) // DSK + EDPB expect(m.unitCount).toBe(8) expect(m.norms.map((g) => g.key).sort()).toEqual(['dsgvo', 'tddg']) }) it('degrades to a neutral breakdown when bindingness is absent', () => { const m = summarizeEvidence([ u({ document: 'DSGVO', section: 'Art. 30' }), u({ document: 'CRA', section: 'Art. 14' }), ]) expect(m.hasBindingness).toBe(false) expect(m.groups).toHaveLength(2) expect(m.normProvisions).toBe(0) expect(m.guidanceCount).toBe(0) }) }) describe('provisionSummary', () => { const g = (sections: string[], units = sections.length): FamilyGroup => ({ key: 'k', label: 'L', sections, units, bindingness: 'binding', }) it('names Artikel, §§, single provisions and bare units', () => { expect(provisionSummary(g(['Art. 6', 'Art. 7', 'Art. 13']))).toBe('3 Artikel') expect(provisionSummary(g(['§ 25']))).toBe('§ 25') expect(provisionSummary(g(['§ 25', '§ 26']))).toBe('2 §§') expect(provisionSummary(g(['Art. 13', '§ 25', 'Anhang I']))).toBe('3 Fundstellen') expect(provisionSummary(g([], 3))).toBe('3 Fundstellen') expect(provisionSummary(g([], 1))).toBe('1 Fundstelle') }) })