Assessor_2.0/tests/analysis/analysis.test.ts

91 lines
3.6 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { detectHeaderRow } from '../../src/analysis/header-detector';
import { buildRecord } from '../../src/analysis/record-builder';
import { analyzeRows } from '../../src/analysis/schema-detector';
import { detectColumnType } from '../../src/analysis/type-detector';
import { normalizeNumericValue } from '../../src/shared/text';
describe('analysis primitives', () => {
it('detects the actual header below a title row', () => {
expect(detectHeaderRow([
['Synthetic Security Assessment'],
['Name', 'Severity', 'Weight'],
['A', 'High', '10'],
['B', 'Low', '2']
])).toBe(1);
});
it('detects numeric and date columns', () => {
expect(detectColumnType(['1', '2', '3', '4'])).toBe('Zahl');
expect(detectColumnType(['01.01.2026', '02.01.2026', '03.01.2026'])).toBe('Datum');
});
it('normalizes German, English and percent numbers without evaluating expressions', () => {
expect(normalizeNumericValue('1.234,50')).toBe(1234.5);
expect(normalizeNumericValue('1,234.50')).toBe(1234.5);
expect(normalizeNumericValue('72,5%')).toBe(72.5);
expect(normalizeNumericValue('=1+1')).toBeNull();
});
it('selects Benchmark Item as title instead of file provenance headers', () => {
const record = buildRecord({
headers: ['Source: File', 'Source: Sheet', 'Source: Scan', 'Rule ID', 'Benchmark Item', 'Result'],
row: [
'WIN-9FPMF1CCPQ0-CIS_Microsoft_Windows_Server_2025_Benchmark-20260417T013626Z.html',
'Assessment Results',
'CIS-CAT Pro · 2026-07-30',
'2.2.2',
"2.2.2 Ensure 'Access this computer from the network' is set to 'Administrators'",
'fail'
],
index: 0,
sheetName: 'Test'
});
expect(record.title).toBe("2.2.2 Ensure 'Access this computer from the network' is set to 'Administrators'");
});
it('selects Name as title instead of ShortName for PurpleKnight records', () => {
const record = buildRecord({
headers: ['Source: File', 'Source: Sheet', 'Source: Scan', 'ShortName', 'Name', 'Status', 'Weight'],
row: [
'Security_Assessment_Report.xlsx',
'Indicators results',
'PurpleKnight · 2026-07-25',
'SI000055',
'Permission changes on AdminSDHolder object',
'IOEs found',
'10'
],
index: 0,
sheetName: 'Test'
});
expect(record.title).toBe('Permission changes on AdminSDHolder object');
});
/**
* Regression: Eine einzelne Zeile hat nichts, wovon sie eine Kopfzeile sein
* koennte - die Kopfzeilenerkennung waehlte sie trotzdem immer als Kopf, und
* darunter blieb keine Datenzeile mehr uebrig. CIS-CAT gibt so z. B. den
* Hinweis "No matching system items were found." als einzeilige
* Belegtabelle aus; ohne diese Ausnahme zeigte das Ergebnisfenster ein
* leeres Tabellengeruest ohne jeden sichtbaren Inhalt.
*/
it('behandelt eine einzelne Tabellenzeile als Datenzeile, nicht als verschluckte Kopfzeile', () => {
const analysis = analyzeRows([['No matching system items were found.']], 'Test');
expect(analysis.headers).toEqual(['Hinweis']);
expect(analysis.records).toHaveLength(1);
expect(analysis.records[0].fields).toEqual([
{ label: 'Hinweis', value: 'No matching system items were found.', index: 0 }
]);
});
it('benennt mehrspaltige Einzelzeilen generisch, statt eine Kopfzeile zu verschlucken', () => {
const analysis = analyzeRows([['a', 'b']], 'Test');
expect(analysis.headers).toEqual(['Feld 1', 'Feld 2']);
expect(analysis.records).toHaveLength(1);
expect(analysis.records[0].fields.map(field => field.value)).toEqual(['a', 'b']);
});
});