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('