Assessor_2.0/tests/features/defender-dashboard.test.ts

68 lines
4.4 KiB
TypeScript

import { readFileSync } from 'node:fs';
import { describe, expect, it } from 'vitest';
import type { ImportedDataset } from '../../src/domain/dataset';
import { rowsToCsv } from '../../src/exports/csv-export';
import { defenderModelForDataset } from '../../src/features/defender-for-iot/defender-dataset-model';
import { makeSheet } from '../../src/importers/sheet-factory';
function dataset(): ImportedDataset {
const summary = [
['Name', 'IP', 'Site', 'Sensor', 'TotalCVEs', 'Critical', 'High', 'Medium', 'Low'],
['device-a', '', 'Site A', 'sensor-a', '3', '1', '2', '0', '0']
];
const observations = [
{ name: 'device-a', ip: '', site: 'Site A', sensor: 'sensor-a', cve: 'CVE-2026-1001', severity: 'Critical', description: 'Synthetic description', additionalSourceFields: {}, source: { tableIndex: 1, rowIndex: 1 } },
{ name: 'device-a', ip: '', site: 'Site A', sensor: 'sensor-b', cve: 'CVE-2026-1001', severity: 'Critical', description: 'Synthetic description', additionalSourceFields: {}, source: { tableIndex: 1, rowIndex: 2 } }
];
const detail = [
['CVE', 'Device Identity', 'Name', 'IP', 'Site', 'Reporting Sensors', 'Severity', 'Beschreibung', 'Observation Count', 'Source Observations'],
['CVE-2026-1001', 'name:device-a|site:site a', 'device-a', '', 'Site A', 'sensor-a, sensor-b', 'Critical', 'Synthetic description', '2', JSON.stringify(observations)]
];
return {
fileName: 'Synthetic project', fileSize: 1, lastModified: 0, format: 'Project · Microsoft Defender for IoT', rawText: '',
profileId: 'defender-for-iot-vulnerabilities', profileVersion: '1.0.0', scanTool: 'Microsoft Defender for IoT',
sheets: [
makeSheet('Vulnerabilities', detail, rowsToCsv(detail), 'synthetic.xlsx', { sourceTableIndex: 1, sourceSheet: 'Vulnerabilities', toolId: 'defender-for-iot-vulnerabilities' }),
makeSheet('Assets', summary, rowsToCsv(summary), 'synthetic.xlsx', { sourceTableIndex: 0, sourceSheet: 'Assets', toolId: 'defender-for-iot-vulnerabilities' })
]
};
}
describe('Defender vulnerability dashboard projection', () => {
it('reconstructs raw observations without double-counting the actionable occurrence', () => {
const model = defenderModelForDataset(dataset());
expect(model.occurrences).toHaveLength(1);
expect(model.observations).toHaveLength(2);
expect(model.devices[0].severity.critical).toMatchObject({ detail: 1, summary: 1, state: 'complete' });
expect(model.devices[0].severity.high).toMatchObject({ detail: 0, summary: 2, aggregateOnly: 2, state: 'aggregate-only' });
});
it('keeps the required drill-downs and avoids clipping or ellipsis rules', () => {
const component = readFileSync(new URL('../../src/features/defender-for-iot/DefenderVulnerabilityPanel.tsx', import.meta.url), 'utf8');
const dashboard = readFileSync(new URL('../../src/features/dashboard/DashboardPanel.tsx', import.meta.url), 'utf8');
const styles = readFileSync(new URL('../../src/styles/defender-for-iot.css', import.meta.url), 'utf8');
expect(component).toContain('CVE view');
expect(component).toContain('Device view');
expect(component).toContain('Site dashboard');
expect(component).not.toContain('<th>Source row</th>');
expect(component).toContain('aggregate-only');
expect(component).toContain('className="dashboard-page client-command-center defender-page"');
expect(component).toContain('className="client-overview-header defender-header"');
expect(component).toContain('<span>Source:</span><strong>Microsoft Defender for IoT</strong>');
expect(dashboard).toContain("[['overview', 'Overview'] as const]");
expect(dashboard).toContain("[['defender', 'Microsoft Defender for IoT'] as const]");
expect(dashboard).toContain('<DefenderVulnerabilityPanel dataset={dataset} navigation={clientInfoTabs} />');
expect(styles).toContain('padding: var(--panel-inset)');
expect(styles).toContain('grid-template-columns: repeat(6, minmax(0, 1fr))');
expect(styles).toContain('background: var(--tile-surface)');
expect(styles).toMatch(/\.defender-drilldown \{[^}]*background: var\(--tile-surface\)/);
expect(styles).not.toMatch(/\.defender-drilldown \{[^}]*background: var\(--client-info-surface\)/);
expect(styles).toContain('outline: var(--bw-emphasis) solid var(--marking-color)');
expect(styles).toContain('outline: var(--bw-emphasis) solid var(--focus-ring)');
expect(styles).not.toContain('text-overflow: ellipsis');
expect(styles).not.toContain('line-clamp');
});
});