Assessor_2.0/tests/importers/canonical-identity.test.ts

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