Assessor_2.0/tests/features/operating-system-inventory....

105 lines
6.8 KiB
TypeScript

import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it } from 'vitest';
import type { EnvironmentObservation, EnvironmentSnapshot } from '../../src/domain/environment-context';
import { operatingSystemGroup, operatingSystemInventory } from '../../src/features/dashboard/operating-system-inventory';
import { OperatingSystemInventory } from '../../src/features/dashboard/OperatingSystemInventory';
function os(name: string, count: unknown, row: number, run = 'run'): EnvironmentObservation[] {
return [['operating_system.name', name], ['operating_system.count', count]].map(([property, raw], column) => ({
version: 1, id: `${run}:${row}:${column}`, scanRunId: run, importFileId: `${run}:file`, tool: { id: 'pingcastle', name: 'PingCastle' },
subject: { type: 'computer-information.operating-systems', identity: name }, property: String(property),
label: column ? 'Nb OS' : 'Operating system', originalValue: raw, normalizedValue: raw, valueType: 'string', state: 'observed',
sectionKey: 'sectionComputerInformation', source: { rowIndex: row, columnIndex: column },
attributes: { tableName: 'Operating systems', sourceTableIndex: 0 }, mappingVersion: '1'
})) as EnvironmentObservation[];
}
function osStates(row: number, values: Record<'Active' | 'Inactive' | 'Enabled' | 'Disabled', unknown>, run = 'run'): EnvironmentObservation[] {
return Object.entries(values).map(([label, raw], column) => ({
version: 1, id: `${run}:${row}:state:${column}`, scanRunId: run, importFileId: `${run}:file`, tool: { id: 'pingcastle', name: 'PingCastle' },
subject: { type: 'computer-information.operating-systems', identity: `row-${row}` },
property: `pingcastle.sectioncomputerinformation.operating.system.list.nb.${label.toLowerCase()}`,
label: `Nb ${label}`, originalValue: raw, normalizedValue: raw, valueType: 'string', state: 'observed',
sectionKey: 'sectionComputerInformation', source: { rowIndex: row, columnIndex: column + 2 },
attributes: { tableName: 'Operating systems', sourceTableIndex: 0 }, mappingVersion: '1'
})) as EnvironmentObservation[];
}
function snapshot(observations: EnvironmentObservation[]): EnvironmentSnapshot {
return { version: 1, projectId: 'synthetic', scanRunIds: ['run'], generatedAt: '', sections: [], observations,
coverage: { observed: observations.length, explicitlyAbsent: 0, empty: 0, restricted: 0, incomplete: 0 } };
}
describe('operating system inventory', () => {
it('splits server/client products, retains other systems and pairs exact native counts including zero', () => {
const source = snapshot([...os('Windows Server 2022', 12, 1), ...os('Windows 11 24H2', '1,200', 2),
...os('Windows 10 22H2', 0, 3), ...os('Debian', 4, 4), ...os('OperatingSystem not set', 3, 5)]);
const before = JSON.stringify(source);
const groups = operatingSystemInventory(source);
expect(groups.map(group => [group.label, group.total])).toEqual([['Servers', 12], ['Clients', 1200], ['Other / unclassified', 7]]);
expect(groups[1].entries.map(entry => entry.count)).toEqual([1200, 0]);
expect(renderToStaticMarkup(createElement(OperatingSystemInventory, { groups }))).toContain('1,200');
expect(JSON.stringify(source)).toBe(before);
});
it('does not infer Linux roles, supports explicit roles and preserves conflicting classifications', () => {
expect(operatingSystemGroup('Debian')).toBe('Other / unclassified');
expect(operatingSystemGroup('Debian', ['Server'])).toBe('Servers');
expect(operatingSystemGroup('Windows 11', ['Server', 'Workstation'])).toBe('Other / unclassified');
expect(operatingSystemGroup('Windows 2019')).toBe('Servers');
expect(operatingSystemGroup('Windows 2000')).toBe('Other / unclassified');
});
it('keeps missing, null, negative, boolean and malformed counts unknown rather than zero', () => {
for (const raw of [undefined, null, '', -1, true, '1.5', '1,2', 'unknown']) {
const group = operatingSystemInventory(snapshot(os('Windows 11', raw, 1)))[1];
expect(group.entries[0].count).toBeUndefined();
expect(group.total).toBeUndefined();
}
expect(operatingSystemInventory().every(group => group.total === undefined)).toBe(true);
});
it('never borrows counts from another report, table or source row', () => {
const fields = os('Windows 11', 2, 1);
fields[1].source.rowIndex = 2;
const foreign = os('Windows Server 2022', 99, 1, 'foreign');
const groups = operatingSystemInventory(snapshot([...fields, ...foreign]));
expect(groups[1].entries[0].count).toBeUndefined();
expect(groups[0].total).toBe(99);
fields[1].source.rowIndex = 1;
fields[1].attributes.sourceTableIndex = 2;
expect(operatingSystemInventory(snapshot(fields))[1].total).toBeUndefined();
});
it('does not sum duplicate OS rows or overlapping inventories and invalidates on changed inputs', () => {
const rows = [...os('Windows 11', 4, 1), ...os('Windows 11', 4, 2)];
expect(operatingSystemInventory(snapshot(rows))[1].total).toBeUndefined();
const groups = operatingSystemInventory(snapshot([...os('Windows 11', 4, 1), ...os('Windows 11', 7, 1, 'other')]));
expect(groups[1].total).toBeUndefined();
expect(groups[1].entries.map(entry => entry.count)).toEqual([7, 4]);
expect(renderToStaticMarkup(createElement(OperatingSystemInventory, { groups }))).toContain('overlapping counts are not added');
expect(operatingSystemInventory(snapshot(os('Windows 11', 8, 1)))[1].total).toBe(8);
});
it('shows PingCastle active, inactive, enabled and disabled counts from the exact OS source row', () => {
const groups = operatingSystemInventory(snapshot([
...os('Windows Server 2022', 12, 1),
...osStates(1, { Active: 10, Inactive: 2, Enabled: 9, Disabled: 3 }),
...os('Windows 11', 8, 2)
]));
expect(groups[0].entries[0]).toMatchObject({ count: 12, active: 10, inactive: 2, enabled: 9, disabled: 3 });
expect(groups[1].entries[0]).toMatchObject({ count: 8, active: undefined, inactive: undefined, enabled: undefined, disabled: undefined });
const markup = renderToStaticMarkup(createElement(OperatingSystemInventory, { groups }));
['Active', 'Inactive', 'Enabled', 'Disabled'].forEach(label => expect(markup).toContain(`>${label}<`));
expect(markup).toContain('>10<');
expect(markup).toContain('>3<');
expect(markup).toContain('>—<');
});
it('does not borrow or guess PingCastle state counts across source rows', () => {
const groups = operatingSystemInventory(snapshot([
...os('Windows Server 2022', 12, 1),
...osStates(2, { Active: 10, Inactive: 2, Enabled: 9, Disabled: 3 })
]));
expect(groups[0].entries[0]).toMatchObject({
count: 12, active: undefined, inactive: undefined, enabled: undefined, disabled: undefined
});
});
});