import { describe, expect, it } from 'vitest'; import { groundRegulatoryMappingResult, regulatoryMappingResultValidationErrors } from '../../src/domain/regulatory-mapping'; const grounded = new Map([['5.1', new Set(['semantic-5.1', 'reference-5.1'])]]); describe('regulatory mapping output validation', () => { it('accepts a grounded proposal and rejects unknown controls, duplicate controls, and foreign evidence', () => { const valid = { schemaVersion: 1, outcome: 'mapped', mappings: [{ controlId: '5.1', relationship: 'primary', confidence: 'high', rationale: 'Synthetic rationale.', evidenceChunkIds: ['semantic-5.1', 'reference-5.1'] }] }; expect(regulatoryMappingResultValidationErrors(valid, grounded)).toEqual([]); expect(regulatoryMappingResultValidationErrors({ ...valid, mappings: [ ...valid.mappings, { ...valid.mappings[0], evidenceChunkIds: ['other'] } ] }, grounded)).toEqual(expect.arrayContaining([ expect.stringContaining('duplicates control 5.1'), expect.stringContaining('not grounded') ])); expect(regulatoryMappingResultValidationErrors({ ...valid, mappings: [{ ...valid.mappings[0], controlId: '9.9' }] }, grounded)) .toEqual(expect.arrayContaining([expect.stringContaining('unknown candidate control')])); }); it('trims ungrounded evidence ids, drops fully ungrounded known-control mappings, and leaves unknown controls for validation', () => { const result = groundRegulatoryMappingResult({ schemaVersion: 1, outcome: 'mapped', mappings: [ { controlId: '5.1', relationship: 'primary', confidence: 'high', rationale: 'One good id kept.', evidenceChunkIds: ['semantic-5.1', 'foreign'] }, { controlId: '5.1', relationship: 'supporting', confidence: 'low', rationale: 'No real evidence.', evidenceChunkIds: ['foreign', 'other'] } ] }, grounded) as { mappings: Array<{ evidenceChunkIds: string[] }> }; expect(result.mappings).toHaveLength(1); expect(result.mappings[0].evidenceChunkIds).toEqual(['semantic-5.1']); const unknown = groundRegulatoryMappingResult({ schemaVersion: 1, outcome: 'mapped', mappings: [{ controlId: '9.9', relationship: 'primary', confidence: 'high', rationale: 'x', evidenceChunkIds: ['semantic-5.1'] }] }, grounded) as { mappings: unknown[] }; expect(unknown.mappings).toHaveLength(1); expect(regulatoryMappingResultValidationErrors(unknown, grounded)).toEqual(expect.arrayContaining([expect.stringContaining('unknown candidate control')])); }); it('requires refusal outcomes to contain no mappings and a bounded reason', () => { expect(regulatoryMappingResultValidationErrors({ schemaVersion: 1, outcome: 'no_relevant_control', reason: 'No direct relationship.', mappings: [] }, grounded)).toEqual([]); expect(regulatoryMappingResultValidationErrors({ schemaVersion: 1, outcome: 'insufficient_context', reason: 'Unknown.', mappings: [{ controlId: '5.1', relationship: 'supporting', confidence: 'low', rationale: 'Synthetic.', evidenceChunkIds: ['semantic-5.1'] }] }, grounded)).toContain('A refusal outcome must not include mappings.'); }); });