72 lines
3.3 KiB
TypeScript
72 lines
3.3 KiB
TypeScript
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);
|
|
});
|
|
});
|