133 lines
6.0 KiB
TypeScript
133 lines
6.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
buildPolicyAnalysisOverview,
|
|
type BuildPolicyAnalysisOverviewInput
|
|
} from '../../src/domain/policy-analysis-overview';
|
|
|
|
const timestamp = '2026-08-04T16:00:00.000Z';
|
|
|
|
function input(options: { aggregation?: boolean; baseline?: 'complete' | 'partial' | 'missing'; evaluation?: boolean } = {}): BuildPolicyAnalysisOverviewInput {
|
|
const aggregation = {
|
|
version: 1,
|
|
run: {
|
|
id: 'aggregation-1', environmentProfileId: 'profile-1', sourceAnalysisRunIds: ['identity-run-1'],
|
|
status: 'complete', diagnosticCodes: [], snapshotId: 'snapshot-1'
|
|
},
|
|
snapshot: { instanceIds: ['backup-1'] }, states: [], reviewHints: []
|
|
};
|
|
const evaluation = {
|
|
version: 1,
|
|
run: {
|
|
id: 'evaluation-1', aggregationRunId: 'aggregation-1', environmentProfileId: 'profile-1',
|
|
baselineAnalysisIds: ['baseline-analysis-1'], status: 'partial', diagnosticCodes: [],
|
|
completedAt: timestamp, matchCount: 0, deviationCount: 0, notApplicableCount: 0, reviewCount: 1
|
|
},
|
|
results: [{
|
|
id: 'evaluation-result-1', identity: { id: 'identity-1' }, findingEligibility: 'ineligible',
|
|
classification: 'review_required', baselineState: 'catalog-gap', diagnosticCodes: ['policy-evaluation-catalog-gap'],
|
|
observationIds: [], backupInstanceIds: []
|
|
}], evidence: []
|
|
};
|
|
return {
|
|
projectId: 'project-1', generatedAt: timestamp,
|
|
identityAnalyses: [{
|
|
run: {
|
|
id: 'identity-run-1', artifactId: 'artifact-1', sourceProcessingRunId: 'processing-1',
|
|
status: 'complete', observationCount: 0, resolvedCount: 0, diagnosticCodes: [], completedAt: timestamp
|
|
},
|
|
resolutions: []
|
|
}],
|
|
profiles: [{
|
|
id: 'profile-1', name: 'Windows Server 2025', revision: 1,
|
|
contexts: [{
|
|
id: 'context-1', productFamily: 'Windows Server', productVersion: '2025',
|
|
role: 'Member Server', scope: 'computer'
|
|
}]
|
|
}],
|
|
aggregations: options.aggregation ? [aggregation] : [],
|
|
evaluations: options.evaluation ? [evaluation] : [],
|
|
findingRuns: [],
|
|
processingRuns: [{ id: 'processing-1', artifactId: 'artifact-1', completedAt: timestamp }],
|
|
baselineRecords: options.baseline === 'missing' || !options.baseline ? [] : [{
|
|
id: 'baseline-1', status: 'eligible',
|
|
metadata: {
|
|
productContexts: [{ productFamily: 'Windows Server', productVersion: '2025', roles: ['Any'], scopes: ['mixed'] }]
|
|
}
|
|
}],
|
|
baselineAnalyses: options.baseline === 'missing' || !options.baseline ? [] : [{
|
|
analysis: {
|
|
id: 'baseline-analysis-1', baselineId: 'baseline-1', analyzedAt: timestamp, status: options.baseline
|
|
}
|
|
}]
|
|
} as unknown as BuildPolicyAnalysisOverviewInput;
|
|
}
|
|
|
|
describe('policy analysis action readiness', () => {
|
|
it('offers deterministic reprocessing and aggregation for active source and profile revisions', () => {
|
|
const overview = buildPolicyAnalysisOverview(input());
|
|
expect(overview.actions.reprocess).toEqual([expect.objectContaining({
|
|
status: 'ready', artifactId: 'artifact-1', expectedLatestProcessingRunId: 'processing-1'
|
|
})]);
|
|
expect(overview.actions.aggregate).toEqual([expect.objectContaining({
|
|
status: 'ready', environmentProfileId: 'profile-1', sourceAnalysisRunIds: ['identity-run-1']
|
|
})]);
|
|
});
|
|
|
|
it('selects one matching complete baseline analysis and offers evaluation', () => {
|
|
const overview = buildPolicyAnalysisOverview(input({ aggregation: true, baseline: 'complete' }));
|
|
expect(overview.actions.aggregate[0]).toMatchObject({ status: 'completed', existingRunId: 'aggregation-1' });
|
|
expect(overview.actions.evaluate).toEqual([expect.objectContaining({
|
|
status: 'ready', aggregationRunId: 'aggregation-1', baselineAnalysisIds: ['baseline-analysis-1']
|
|
})]);
|
|
});
|
|
it('excludes superseded profile and identity-analysis revisions from new actions', () => {
|
|
const data = input();
|
|
const oldProfile = data.profiles[0];
|
|
data.profiles.push({
|
|
...oldProfile,
|
|
id: 'profile-2',
|
|
name: 'Windows Server 2025 revised',
|
|
revision: 2,
|
|
supersedesProfileId: oldProfile.id
|
|
});
|
|
const oldAnalysis = data.identityAnalyses[0];
|
|
data.identityAnalyses.push({
|
|
...oldAnalysis,
|
|
run: {
|
|
...oldAnalysis.run,
|
|
id: 'identity-run-2',
|
|
sourceProcessingRunId: 'processing-2',
|
|
supersedesAnalysisRunId: oldAnalysis.run.id
|
|
}
|
|
});
|
|
data.processingRuns!.push({ id: 'processing-2', artifactId: 'artifact-1', completedAt: '2026-08-04T16:01:00.000Z' } as never);
|
|
|
|
const overview = buildPolicyAnalysisOverview(data);
|
|
expect(overview.actions.aggregate).toHaveLength(1);
|
|
expect(overview.actions.aggregate[0]).toMatchObject({
|
|
environmentProfileId: 'profile-2', sourceAnalysisRunIds: ['identity-run-2']
|
|
});
|
|
expect(overview.actions.reprocess[0].expectedLatestProcessingRunId).toBe('processing-2');
|
|
});
|
|
|
|
|
|
it('marks partial baseline evidence review-only and blocks missing catalog evidence', () => {
|
|
const partial = buildPolicyAnalysisOverview(input({ aggregation: true, baseline: 'partial' }));
|
|
expect(partial.actions.evaluate[0]).toMatchObject({
|
|
status: 'review-only', reasonCodes: ['policy-action-baseline-analysis-partial']
|
|
});
|
|
const missing = buildPolicyAnalysisOverview(input({ aggregation: true, baseline: 'missing' }));
|
|
expect(missing.actions.evaluate[0]).toMatchObject({ status: 'blocked' });
|
|
expect(missing.actions.evaluate[0].reasonCodes).toContain('policy-action-baseline-no-match');
|
|
});
|
|
|
|
it('keeps a partial evaluation review-only at the Finding Factory boundary', () => {
|
|
const overview = buildPolicyAnalysisOverview(input({ aggregation: true, baseline: 'complete', evaluation: true }));
|
|
expect(overview.actions.evaluate[0]).toMatchObject({ status: 'completed', existingRunId: 'evaluation-1' });
|
|
expect(overview.actions.createFindings).toEqual([expect.objectContaining({
|
|
status: 'review-only', evaluationRunId: 'evaluation-1', eligibleResultCount: 0,
|
|
reasonCodes: ['policy-action-evaluation-partial']
|
|
})]);
|
|
});
|
|
});
|