Files

114 lines
4.6 KiB
TypeScript

import { describe, test, expect } from 'bun:test';
import { shouldBlockUpdate, combineScanSummaries } from '../src/lib/server/scheduler/tasks/update-utils';
const clean = { critical: 0, high: 0, medium: 0, low: 0, negligible: 0, unknown: 0 };
describe('shouldBlockUpdate', () => {
test('never blocks with "never" criteria', () => {
const vulny = { ...clean, critical: 5, high: 10 };
expect(shouldBlockUpdate('never', vulny).blocked).toBe(false);
});
test('"any" blocks when any vulnerabilities exist', () => {
expect(shouldBlockUpdate('any', { ...clean, low: 1 }).blocked).toBe(true);
expect(shouldBlockUpdate('any', clean).blocked).toBe(false);
});
test('"critical_high" blocks on critical or high only', () => {
expect(shouldBlockUpdate('critical_high', { ...clean, critical: 1 }).blocked).toBe(true);
expect(shouldBlockUpdate('critical_high', { ...clean, high: 1 }).blocked).toBe(true);
expect(shouldBlockUpdate('critical_high', { ...clean, medium: 5 }).blocked).toBe(false);
expect(shouldBlockUpdate('critical_high', { ...clean, low: 10 }).blocked).toBe(false);
});
test('"critical" blocks on critical only', () => {
expect(shouldBlockUpdate('critical', { ...clean, critical: 1 }).blocked).toBe(true);
expect(shouldBlockUpdate('critical', { ...clean, high: 10 }).blocked).toBe(false);
});
test('"more_than_current" blocks when new > current', () => {
const current = { ...clean, low: 5 };
const newMore = { ...clean, low: 6 };
const newLess = { ...clean, low: 4 };
const newEqual = { ...clean, low: 5 };
expect(shouldBlockUpdate('more_than_current', newMore, current).blocked).toBe(true);
expect(shouldBlockUpdate('more_than_current', newLess, current).blocked).toBe(false);
expect(shouldBlockUpdate('more_than_current', newEqual, current).blocked).toBe(false);
});
test('"more_than_current" does not block without current scan', () => {
expect(shouldBlockUpdate('more_than_current', { ...clean, critical: 100 }).blocked).toBe(false);
});
test('returns reason string when blocked', () => {
const result = shouldBlockUpdate('critical', { ...clean, critical: 3 });
expect(result.blocked).toBe(true);
expect(result.reason).toContain('3 critical');
});
// --- M7 regression ---
// The 'more_than_current' comparison is gated on a current scan summary; the
// env-wide update path used to pass undefined, so it silently never blocked.
// These pin the contract that env-update-check.ts now relies on.
test('M7: passing undefined current scan never blocks, even for a very vulnerable image', () => {
const veryVulnerable = { ...clean, critical: 50, high: 50, medium: 50, low: 50 };
expect(shouldBlockUpdate('more_than_current', veryVulnerable, undefined).blocked).toBe(false);
});
test('M7: blocks when current scan is supplied and new has more vulns', () => {
const current = { ...clean, critical: 1, high: 1 }; // 2 total
const newMore = { ...clean, critical: 2, high: 1 }; // 3 total
const { blocked, reason } = shouldBlockUpdate('more_than_current', newMore, current);
expect(blocked).toBe(true);
expect(reason).toContain('3');
expect(reason).toContain('2');
});
test('returns empty reason when not blocked', () => {
expect(shouldBlockUpdate('never', { ...clean, critical: 5 }).reason).toBe('');
});
test('"any" reason includes all severity counts', () => {
const scan = { ...clean, critical: 1, high: 2, medium: 3, low: 4 };
const result = shouldBlockUpdate('any', scan);
expect(result.reason).toContain('1 critical');
expect(result.reason).toContain('2 high');
expect(result.reason).toContain('3 medium');
expect(result.reason).toContain('4 low');
});
});
describe('combineScanSummaries', () => {
test('returns zeros for empty input', () => {
expect(combineScanSummaries([])).toEqual(clean);
});
test('returns single result as-is', () => {
const summary = { ...clean, critical: 3, high: 5 };
expect(combineScanSummaries([{ summary }])).toEqual(summary);
});
test('takes maximum of each severity across scanners', () => {
const grype = { summary: { critical: 2, high: 5, medium: 0, low: 1, negligible: 0, unknown: 0 } };
const trivy = { summary: { critical: 3, high: 1, medium: 4, low: 0, negligible: 2, unknown: 1 } };
expect(combineScanSummaries([grype, trivy])).toEqual({
critical: 3,
high: 5,
medium: 4,
low: 1,
negligible: 2,
unknown: 1
});
});
test('handles three scanners', () => {
const a = { summary: { ...clean, critical: 1 } };
const b = { summary: { ...clean, critical: 5 } };
const c = { summary: { ...clean, critical: 3 } };
expect(combineScanSummaries([a, b, c]).critical).toBe(5);
});
});