46 lines
2.4 KiB
TypeScript
46 lines
2.4 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
import { previewEntryFromDataset } from '../../src/features/import-workflow/import-workflow';
|
|
import { createCanonicalImportBatch } from '../../src/importers/canonical-import';
|
|
import { importFile } from '../../src/importers/importer';
|
|
import { genericProfile } from '../../src/profiles/generic/generic-profile';
|
|
|
|
async function canonicalBatch(batchId: string, text: string) {
|
|
const dataset = await importFile(new File([text], 'synthetic.csv', { type: 'text/csv' }), { profiles: [genericProfile] });
|
|
const entry = previewEntryFromDataset(dataset, {
|
|
sha256: batchId.padEnd(64, '0').slice(0, 64),
|
|
fileName: dataset.fileName,
|
|
sizeBytes: dataset.fileSize,
|
|
lastModified: dataset.lastModified
|
|
});
|
|
return createCanonicalImportBatch(batchId, [entry], '2026-07-20T12:00:00.000Z').model;
|
|
}
|
|
|
|
describe('canonical finding identity', () => {
|
|
it('creates the same durable identity from a stable native rule ID across imports', async () => {
|
|
const first = await canonicalBatch('native-a', 'Rule ID;Title;Severity\nSAFE-1;First title;High');
|
|
const second = await canonicalBatch('native-b', 'Rule ID;Title;Severity\nSAFE-1;Renamed title;High');
|
|
|
|
expect(first.findingDefinitions[0]).toMatchObject({
|
|
canonicalKey: 'generic:safe-1',
|
|
identity: { version: 1, strategy: 'tool-rule-id', durable: true }
|
|
});
|
|
expect(second.findingDefinitions[0].canonicalKey).toBe(first.findingDefinitions[0].canonicalKey);
|
|
expect(second.findingDefinitions[0].id).toBe(first.findingDefinitions[0].id);
|
|
});
|
|
|
|
it('keeps title-only findings import-local and marks their identity unresolved', async () => {
|
|
const first = await canonicalBatch('unresolved-a', 'Title;Severity\nRepeated title;High');
|
|
const second = await canonicalBatch('unresolved-b', 'Title;Severity\nRepeated title;High');
|
|
|
|
expect(first.findingDefinitions[0]).toMatchObject({
|
|
identity: { version: 1, strategy: 'unresolved', durable: false }
|
|
});
|
|
expect(first.findingDefinitions[0].canonicalKey).toMatch(/^unresolved:/);
|
|
expect(first.findingDefinitions[0].canonicalKey).not.toContain('repeated title');
|
|
expect(second.findingDefinitions[0].canonicalKey).not.toBe(first.findingDefinitions[0].canonicalKey);
|
|
expect(first.diagnostics).toContainEqual(expect.objectContaining({ code: 'finding-identity-unresolved', severity: 'warning' }));
|
|
});
|
|
});
|