64 lines
3.6 KiB
TypeScript
64 lines
3.6 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import type { FindingReview } from '../../src/domain/canonical-model';
|
|
import type { ImportedDataset } from '../../src/domain/dataset';
|
|
import { defaultReview } from '../../src/domain/review';
|
|
import {
|
|
hydrateWorkspaceReviews, reviewFromDatabase, reviewToDatabase
|
|
} from '../../src/features/review-workspace/database-review';
|
|
|
|
const timestamp = '2026-09-06T10:00:00.000Z';
|
|
|
|
describe('database-backed finding reviews', () => {
|
|
it('round-trips every FindingReader workflow field without losing exact labels or unknown markers', () => {
|
|
const ui = {
|
|
...defaultReview(), include: true, status: 'Clarification Needed', priority: 'Critical', owner: 'Security Team',
|
|
due: '2026-10-15', prerequisites: 'Maintenance window', access: 'Domain Admin', nextStep: 'Validate change',
|
|
evidence: 'Control reports compliant', comment: 'Coordinate first', markerQuestion: true, markerBlocked: true,
|
|
markerQuickwin: true, markerExternal: true, markerNotRelevant: true
|
|
};
|
|
const current: FindingReview = {
|
|
id: 'review-1', target: { kind: 'occurrence', occurrenceId: 'occurrence-1' }, workflowStatus: 'open',
|
|
priority: 'medium', includeInActionPlan: false, markers: ['future-marker'], createdAt: timestamp,
|
|
updatedAt: timestamp, revision: 3
|
|
};
|
|
|
|
const stored = reviewToDatabase(ui, current.target, current, '2026-09-06T11:00:00.000Z');
|
|
|
|
expect(stored).toMatchObject({
|
|
workflowStatus: 'deferred', workflowLabel: 'Clarification Needed', priority: 'critical', includeInActionPlan: true,
|
|
owner: 'Security Team', dueAt: '2026-10-15', prerequisites: 'Maintenance window', requiredAccess: 'Domain Admin',
|
|
nextStep: 'Validate change', acceptanceEvidence: 'Control reports compliant', comment: 'Coordinate first', revision: 4
|
|
});
|
|
expect(stored.markers).toEqual([
|
|
'future-marker', 'question-needed', 'permission-blocked', 'quick-win', 'external-team', 'not-relevant'
|
|
]);
|
|
expect(reviewFromDatabase(stored)).toEqual(ui);
|
|
});
|
|
|
|
it('hydrates duplicate dataset projections from one database review and lets database state win over the browser cache', () => {
|
|
const makeRecord = (id: string) => ({
|
|
id, findingOccurrenceId: 'occurrence-1', findingDefinitionId: 'definition-1', index: 0, title: 'Synthetic finding',
|
|
fields: [], filled: [], meta: []
|
|
});
|
|
const dataset = {
|
|
fileName: 'Synthetic', fileSize: 1, lastModified: 0, format: 'CSV', rawText: '', profileId: 'generic', scanTool: 'Synthetic',
|
|
sheets: [
|
|
{ id: 'all', name: 'All', rows: [], raw: '', analysis: { headerIndex: 0, headers: [], records: [makeRecord('aggregate')], columns: [], signals: [], title: 'All' } },
|
|
{ id: 'source', name: 'Source', rows: [], raw: '', analysis: { headerIndex: 0, headers: [], records: [makeRecord('source-record')], columns: [], signals: [], title: 'Source' } }
|
|
]
|
|
} satisfies ImportedDataset;
|
|
const databaseReview: FindingReview = {
|
|
id: 'review-1', target: { kind: 'occurrence', occurrenceId: 'occurrence-1' }, workflowStatus: 'resolved',
|
|
workflowLabel: 'Done', priority: 'high', includeInActionPlan: true, owner: 'Database Team', markers: [],
|
|
createdAt: timestamp, updatedAt: timestamp, revision: 2
|
|
};
|
|
|
|
const hydrated = hydrateWorkspaceReviews(dataset, [databaseReview], {
|
|
aggregate: { ...defaultReview(), owner: 'Old local value' }
|
|
});
|
|
|
|
expect(hydrated.reviews.aggregate).toMatchObject({ status: 'Done', priority: 'High', owner: 'Database Team' });
|
|
expect(hydrated.reviews['source-record']).toEqual(hydrated.reviews.aggregate);
|
|
});
|
|
});
|