Assessor_2.0/tests/features/mixed-work-reference-reader...

68 lines
4.3 KiB
TypeScript

// @vitest-environment jsdom
import { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, expect, it, vi } from 'vitest';
import type { ImportedDataset } from '../../src/domain/dataset';
import type { UnifiedResultProjection } from '../../src/domain/unified-result';
import { createReferenceRecognizer } from '../../src/domain/reference-recognition';
import { bundledReferenceCatalogs } from '../../server/reference-data/reference-catalogs';
import { appendPolicyObservationsToDataset } from '../../src/features/project-import/policy-observations-to-dataset';
import { FindingReader } from '../../src/features/finding-reader/FindingReader';
import { makeSheet } from '../../src/importers/sheet-factory';
afterEach(() => { vi.unstubAllGlobals(); document.body.replaceChildren(); });
it.each([false, true])('retains recognized references when opening the scanner Reader (GPOs: %s)', async withGpos => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
const sheet = makeSheet('All Records', [
['Title', 'Status', 'Category'],
['Synthetic scanner finding', 'Fail', 'Synthetic'],
['Synthetic indexed finding without references', 'Pass', 'Synthetic'],
['Synthetic unindexed finding', 'Fail', 'Synthetic']
], '', 'Synthetic', { isProjectSheet: true });
const references = createReferenceRecognizer(bundledReferenceCatalogs)([
{ key: 'attack', label: 'MITRE ATT&CK', value: 'T1059.001' },
{ key: 'description', label: 'Description', value: 'T9999 D3-CH' }
]);
const recognition = { runId: 'synthetic-reference-run', recognizerVersion: '1.0.0',
createdAt: '2026-09-26T00:00:00Z', references };
sheet.analysis.records.forEach((record, index) => { record.findingOccurrenceId = `synthetic-occurrence-${index}`; });
sheet.analysis.records[0].referenceRecognition = recognition;
sheet.analysis.records[1].referenceRecognition = { ...recognition, references: [] };
const dataset = { sheets: [sheet], rawText: '', profileId: 'generic', scanTool: 'Synthetic' } as ImportedDataset;
const observation = { sourceKind: 'policy-observation', sourceResultId: 'synthetic-policy', projectId: 'synthetic',
assessmentId: 'slot', policyBackupInstanceId: 'synthetic-backup', title: 'Synthetic GPO setting',
importedAt: '2026-09-26T00:00:00Z', policyScope: 'computer', policyDeliveryMechanism: 'registry-policy',
policyIdentityStatus: 'resolved', policyIdentityKey: 'synthetic-setting', policyState: 'configured',
policyValues: [{ kind: 'registry-policy', operation: 'set-value', key: 'Software\\Synthetic', valueName: 'Mode',
registryType: 4, data: 0 }] } as unknown as UnifiedResultProjection;
const projected = appendPolicyObservationsToDataset(dataset, 'Synthetic', withGpos ? [observation] : [])!;
const records = projected.sheets[0].analysis.records;
expect(records).toHaveLength(withGpos ? 4 : 3);
expect(records[0].referenceRecognition).toEqual(recognition);
expect(records[1].referenceRecognition?.references).toEqual([]);
expect(records[2].referenceRecognition).toBeUndefined();
if (withGpos) expect(records[3].referenceRecognition).toBeUndefined();
const host = document.createElement('div'); document.body.append(host);
const root = createRoot(host);
try {
const record = records[0];
await act(async () => root.render(<FindingReader record={record} title={record.title} linkedSheets={[]}
resultReferences={[]} search="" position={0} recordCount={records.length}
onPrevious={() => {}} onNext={() => {}} onReview={() => {}}/>));
const section = host.querySelector('[aria-label="Catalog references"]')!;
expect(section).not.toBeNull();
expect(section.textContent).toContain('ATT&CK T1059.001');
expect(section.textContent).toContain('D3FEND D3-CH');
expect(section.textContent).toContain('Mentioned · Not found in this catalog');
expect(section.textContent).not.toContain('Not indexed yet');
const details = section.querySelector('details')!;
await act(async () => details.querySelector('summary')!.click());
expect(details.open).toBe(true);
expect(details.textContent).toContain('MITRE ATT&CK · attack');
expect(details.textContent).toContain('synthetic-reference-run');
expect(details.textContent).toContain(references[0].catalogVersion);
expect(details.textContent).toContain(references[0].catalogSha256);
} finally { await act(async () => root.unmount()); }
});