91 lines
3.6 KiB
TypeScript
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']);
|
|
});
|
|
});
|