// @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' })); }); });