90 lines
4.0 KiB
TypeScript
90 lines
4.0 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import { describe, expect, it } from 'vitest';
|
|
import type { FindingRecord } from '../../src/domain/finding';
|
|
import { dashboardDomainAssignment, dashboardDomainRecords } from '../../src/features/dashboard/DashboardPanel';
|
|
|
|
const component = readFileSync(new URL('../../src/features/dashboard/DashboardPanel.tsx', import.meta.url), 'utf8');
|
|
|
|
function record(id: string, title: string, fields: Array<[string, string]> = []): FindingRecord {
|
|
return {
|
|
id,
|
|
title,
|
|
fields: fields.map(([label, value]) => ({ label, value })),
|
|
sourceFile: 'synthetic.csv'
|
|
} as unknown as FindingRecord;
|
|
}
|
|
|
|
describe('client info derivation cost', () => {
|
|
/**
|
|
* Ein Mandantenblatt traegt mehrere tausend Datensaetze. Die Ansicht darf sie
|
|
* nicht bei jedem Rendern erneut durchlaufen, sonst haengt sie bei jeder
|
|
* Zustandsaenderung der Anwendung.
|
|
*/
|
|
it('derives every record-level view once inside a memoized hook', () => {
|
|
expect(component).toContain('function useDashboardAnalysis(');
|
|
expect(component).toContain('const analysis = useDashboardAnalysis(activeSheet, dataset, activeProfile, reviews, sourceFilter);');
|
|
// Facetten und Auswertung ueberdauern den Ansichtswechsel, sonst rechnet
|
|
// jeder Besuch der Client Info dieselben Datensaetze erneut durch.
|
|
expect(component).toContain('const analysisCache = new WeakMap<');
|
|
expect(component).toContain('const recordStateCache = new WeakMap<');
|
|
expect(component).toContain('useMemo(() => dashboardEnvironmentDetail(environment.snapshot), [environment.snapshot])');
|
|
// Die teure Ersatzzaehlung aus Findingtexten laeuft nur, solange die
|
|
// Verarbeitungsliste der Sicherungen noch nicht geantwortet hat.
|
|
expect(component).toContain('const derivedGpoCount = useMemo(');
|
|
expect(component).toContain('gpoInventory.gpoCount ?? derivedGpoCount');
|
|
});
|
|
|
|
it('classifies all security domains in a single pass over the records', () => {
|
|
const records = [
|
|
record('r1', 'Inactive user account detected'),
|
|
record('r2', 'Group Policy baseline deviation'),
|
|
record('r3', 'Nothing classifiable here')
|
|
];
|
|
let textReads = 0;
|
|
const counted = records.map(item => ({
|
|
...item,
|
|
get fields() {
|
|
textReads += 1;
|
|
return item.fields;
|
|
}
|
|
})) as FindingRecord[];
|
|
|
|
const assignment = dashboardDomainAssignment(counted);
|
|
|
|
expect(textReads).toBe(counted.length);
|
|
expect(assignment.get('identity')?.map(item => item.id)).toEqual(['r1']);
|
|
expect(assignment.get('gpo')?.map(item => item.id)).toEqual(['r2']);
|
|
expect(assignment.get('unclassified')?.map(item => item.id)).toEqual(['r3']);
|
|
});
|
|
|
|
it('assigns every record to exactly one primary domain', () => {
|
|
const records = [
|
|
record('gpo', 'Account password policy deviation', [['Category', 'GPOs']]),
|
|
record('identity', 'Inactive user account'),
|
|
record('other', 'No matching classification')
|
|
];
|
|
const assignment = dashboardDomainAssignment(records);
|
|
const assigned = [...assignment.values()].flat();
|
|
|
|
expect(assignment.get('gpo')?.map(item => item.id)).toEqual(['gpo']);
|
|
expect(assignment.get('identity')?.map(item => item.id)).toEqual(['identity']);
|
|
expect(assignment.get('unclassified')?.map(item => item.id)).toEqual(['other']);
|
|
expect(assigned.map(item => item.id).sort()).toEqual(records.map(item => item.id).sort());
|
|
expect(new Set(assigned.map(item => item.id)).size).toBe(records.length);
|
|
});
|
|
|
|
it('keeps the single-domain lookup in agreement with the full assignment', () => {
|
|
const records = [
|
|
record('r1', 'Stale computer account'),
|
|
record('r2', 'Certificate template allows SAN'),
|
|
record('r3', 'Conditional access gap')
|
|
];
|
|
|
|
const assignment = dashboardDomainAssignment(records);
|
|
['identity', 'infrastructure', 'authentication', 'gpo'].forEach(domain => {
|
|
expect(dashboardDomainRecords(records, domain)).toEqual(assignment.get(domain) ?? []);
|
|
});
|
|
expect(dashboardDomainRecords(records, 'missing')).toEqual([]);
|
|
});
|
|
});
|