import { describe, expect, it } from 'vitest'; import { parseCsvDocument } from '../../src/importers/csv-importer'; import { applyRecognitionOverrides, recognizeImportDocument } from '../../src/recognition/recognition-engine'; import { sourceMetadata } from '../importers/import-source-fixtures'; describe('recognition and mapping engine', () => { it('maps semantic field roles with confidence and reasons', () => { const text = [ 'ID;Title;Severity;Result;Description;ResultDetail;HelpUrl', 'MT.1001;Require multifactor authentication;High;Failed;A long description;Policy evidence;https://maester.dev/tests/MT.1001' ].join('\n'); const document = parseCsvDocument(sourceMetadata('TestResults.csv', text.length, 'text/csv'), text); const recognition = recognizeImportDocument(document); const roles = Object.fromEntries(recognition.fields.map(field => [field.sourceHeader, field])); expect(roles.ID.appliedRole).toBe('rule-id'); expect(roles.Title.appliedRole).toBe('title'); expect(roles.Severity.appliedRole).toBe('severity'); expect(roles.Result.appliedRole).toBe('status'); expect(roles.Description.appliedRole).toBe('description'); expect(roles.ResultDetail.appliedRole).toBe('evidence'); expect(roles.HelpUrl.appliedRole).toBe('framework-reference'); expect(roles.Severity.confidence.level).toBe('high'); expect(roles.Severity.confidence.reasons.length).toBeGreaterThan(0); expect(recognition.tables[0].selected).toBe(true); }); it('keeps ambiguous fields as additional data until they are confirmed', () => { const text = 'Object;Mystery\nserver01;unchanged'; const document = parseCsvDocument(sourceMetadata('unknown-table.csv', text.length), text); const recognition = recognizeImportDocument(document); const objectField = recognition.fields.find(field => field.sourceHeader === 'Object'); expect(recognition.source.selected.toolId).toBe('unknown'); expect(objectField).toMatchObject({ proposedRole: 'asset', appliedRole: 'additional', requiresConfirmation: true, decisionSource: 'automatic' }); expect(document.tables[0].rows[1].cells.map(cell => cell.decodedValue)).toEqual(['server01', 'unchanged']); }); it('applies explicit source, table and field corrections without changing imported rows', () => { const text = 'Object;Mystery\nserver01;unchanged'; const document = parseCsvDocument(sourceMetadata('unknown-table.csv', text.length), text); const recognition = recognizeImportDocument(document); const corrected = applyRecognitionOverrides(recognition, { sourceTool: { toolId: 'internal-scanner', toolName: 'Internal Scanner' }, tableSelections: { [document.tables[0].id]: true }, fieldRoles: [{ tableId: document.tables[0].id, columnIndex: 0, role: 'title' }] }); expect(corrected.source).toMatchObject({ decisionSource: 'user-confirmed', requiresConfirmation: false, selected: { toolId: 'internal-scanner', toolName: 'Internal Scanner' } }); expect(corrected.tables[0]).toMatchObject({ selected: true, decisionSource: 'user-confirmed' }); expect(corrected.fields[0]).toMatchObject({ proposedRole: 'title', appliedRole: 'title', decisionSource: 'user-confirmed', requiresConfirmation: false }); expect(document.tables[0].rows).toHaveLength(2); }); });