import { describe, expect, it } from 'vitest'; import { bsiConsistency, bsiConsistencyFingerprintSource } from '../../src/compliance/bsi-consistency'; import type { BsiIsoCrosswalk } from '../../src/compliance/bsi-grundschutz'; // Synthetic BSI mapping: two ISO controls reach three requirements; SYN.9.1 is reachable from no ISO control. const crosswalk: BsiIsoCrosswalk = { schemaVersion: 1, framework: 'bsi-iso27001-annex-a-to-grundschutz-plus-plus', title: 'Synthetic', version: '0.1-synthetic', sourceUrl: 'https://example.invalid/synthetic', sourceSha256: '0'.repeat(64), license: 'Synthetic', targetCatalog: 'synthetic', entries: [ { uuid: 'a', isoControlId: '8.9', relationship: 'subset-of', targets: ['KONF.1.1', 'KONF.2.1'] }, { uuid: 'b', isoControlId: '5.15', relationship: 'equivalent-to', targets: ['BER.3.5'] } ] }; describe('ISO <-> BSI consistency', () => { it('has nothing to compare while no BSI requirement is accepted directly', () => { expect(bsiConsistency(['8.9'], [], crosswalk)).toBeUndefined(); }); it('is consistent when one direct requirement lies in the set derived from the accepted ISO controls', () => { // subset-of reaches two requirements; one hit is enough, and an extra direct requirement does not break it. expect(bsiConsistency(['8.9'], ['KONF.2.1', 'SYN.9.1'], crosswalk)).toMatchObject({ state: 'consistent', overlapRequirementIds: ['KONF.2.1'], unreachableRequirementIds: ['SYN.9.1'], derivedRequirementIds: ['KONF.1.1', 'KONF.2.1'], reverseCandidates: [] }); }); it('is inconsistent when a reachable direct requirement is not derived, and names the ISO control the BSI mapping points to', () => { expect(bsiConsistency(['8.9'], ['BER.3.5'], crosswalk)).toMatchObject({ state: 'inconsistent', overlapRequirementIds: [], reverseCandidates: [{ isoControlId: '5.15', requirementIds: ['BER.3.5'] }] }); // Without any accepted ISO control, a reachable direct requirement is a disagreement as well. expect(bsiConsistency([], ['KONF.1.1'], crosswalk)?.state).toBe('inconsistent'); }); it('reports no ISO reference when no direct requirement is reachable from any ISO control', () => { expect(bsiConsistency(['8.9'], ['SYN.9.1'], crosswalk)).toMatchObject({ state: 'no-reference', unreachableRequirementIds: ['SYN.9.1'], reverseCandidates: [] }); }); it('fingerprints the compared sets independent of order, and changes with a decision or the mapping version', () => { const base = bsiConsistencyFingerprintSource(['8.9', '5.15'], ['BER.3.5', 'KONF.1.1'], '0.1'); expect(bsiConsistencyFingerprintSource(['5.15', '8.9', '8.9'], ['KONF.1.1', 'BER.3.5'], '0.1')).toBe(base); expect(bsiConsistencyFingerprintSource(['8.9'], ['BER.3.5', 'KONF.1.1'], '0.1')).not.toBe(base); expect(bsiConsistencyFingerprintSource(['8.9', '5.15'], ['BER.3.5', 'KONF.1.1'], '0.2')).not.toBe(base); }); });