514de78ba7
- Fix ExemptionManager apiVersion bug (apps/batch resources used wrong API path) - Replace resource: any with proper KubeResource interface (strict TypeScript) - Replace all var(--mui-palette-*) CSS variables with useTheme() + theme.palette.* - Replace custom drawer with MUI Drawer component (proper a11y and theming) - Replace alert() calls with StatusLabel-based inline feedback - Add PolarisErrorBoundary wrapping all registered plugin components - Export getPolarisApiPath/isFullUrl from polaris.ts, deduplicate in PolarisSettings - Fix PolarisDataContext test mock missing triggerRefresh - Fix DashboardView test SimpleTable mock using any - Remove dead NamespaceDetailView (replaced by drawer), unused MockPolarisProvider, unused getSeverityColor export - Add tests for InlineAuditSection, AppBarScoreBadge, topIssues, checkMapping (32 new) - Update CLAUDE.md, CHANGELOG.md, README.md for v0.6.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
217 lines
5.5 KiB
TypeScript
217 lines
5.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { makeAuditData, makeResult } from '../test-utils';
|
|
import { getTopIssues } from './topIssues';
|
|
|
|
describe('getTopIssues', () => {
|
|
it('returns empty array when no results', () => {
|
|
const data = makeAuditData([]);
|
|
expect(getTopIssues(data)).toEqual([]);
|
|
});
|
|
|
|
it('returns empty array when all checks pass', () => {
|
|
const data = makeAuditData([
|
|
makeResult({
|
|
Results: {
|
|
c1: {
|
|
ID: 'c1',
|
|
Message: '',
|
|
Details: [],
|
|
Success: true,
|
|
Severity: 'warning',
|
|
Category: 'X',
|
|
},
|
|
},
|
|
}),
|
|
]);
|
|
expect(getTopIssues(data)).toEqual([]);
|
|
});
|
|
|
|
it('aggregates failing checks from controller-level results', () => {
|
|
const data = makeAuditData([
|
|
makeResult({
|
|
Results: {
|
|
cpuRequestsMissing: {
|
|
ID: 'cpuRequestsMissing',
|
|
Message: 'missing',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'warning',
|
|
Category: 'Efficiency',
|
|
},
|
|
},
|
|
}),
|
|
]);
|
|
const issues = getTopIssues(data);
|
|
expect(issues).toHaveLength(1);
|
|
expect(issues[0].checkId).toBe('cpuRequestsMissing');
|
|
expect(issues[0].checkName).toBe('CPU Requests');
|
|
expect(issues[0].severity).toBe('warning');
|
|
expect(issues[0].count).toBe(1);
|
|
});
|
|
|
|
it('aggregates failing checks from pod and container results', () => {
|
|
const data = makeAuditData([
|
|
makeResult({
|
|
Results: {},
|
|
PodResult: {
|
|
Name: 'pod-1',
|
|
Results: {
|
|
hostIPCSet: {
|
|
ID: 'hostIPCSet',
|
|
Message: '',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'danger',
|
|
Category: 'Security',
|
|
},
|
|
},
|
|
ContainerResults: [
|
|
{
|
|
Name: 'container-1',
|
|
Results: {
|
|
cpuLimitsMissing: {
|
|
ID: 'cpuLimitsMissing',
|
|
Message: '',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'warning',
|
|
Category: 'Efficiency',
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
]);
|
|
const issues = getTopIssues(data);
|
|
expect(issues).toHaveLength(2);
|
|
// Danger first
|
|
expect(issues[0].checkId).toBe('hostIPCSet');
|
|
expect(issues[0].severity).toBe('danger');
|
|
expect(issues[1].checkId).toBe('cpuLimitsMissing');
|
|
});
|
|
|
|
it('counts same check across multiple workloads', () => {
|
|
const data = makeAuditData([
|
|
makeResult({
|
|
Name: 'deploy-1',
|
|
Results: {
|
|
cpuRequestsMissing: {
|
|
ID: 'cpuRequestsMissing',
|
|
Message: '',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'warning',
|
|
Category: 'Efficiency',
|
|
},
|
|
},
|
|
}),
|
|
makeResult({
|
|
Name: 'deploy-2',
|
|
Results: {
|
|
cpuRequestsMissing: {
|
|
ID: 'cpuRequestsMissing',
|
|
Message: '',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'warning',
|
|
Category: 'Efficiency',
|
|
},
|
|
},
|
|
}),
|
|
]);
|
|
const issues = getTopIssues(data);
|
|
expect(issues).toHaveLength(1);
|
|
expect(issues[0].count).toBe(2);
|
|
});
|
|
|
|
it('ignores checks with severity "ignore"', () => {
|
|
const data = makeAuditData([
|
|
makeResult({
|
|
Results: {
|
|
c1: {
|
|
ID: 'c1',
|
|
Message: '',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'ignore',
|
|
Category: 'X',
|
|
},
|
|
},
|
|
}),
|
|
]);
|
|
expect(getTopIssues(data)).toEqual([]);
|
|
});
|
|
|
|
it('sorts danger before warning, then by count descending', () => {
|
|
const data = makeAuditData([
|
|
makeResult({
|
|
Name: 'deploy-1',
|
|
Results: {
|
|
cpuRequestsMissing: {
|
|
ID: 'cpuRequestsMissing',
|
|
Message: '',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'warning',
|
|
Category: 'Efficiency',
|
|
},
|
|
},
|
|
}),
|
|
makeResult({
|
|
Name: 'deploy-2',
|
|
Results: {
|
|
cpuRequestsMissing: {
|
|
ID: 'cpuRequestsMissing',
|
|
Message: '',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'warning',
|
|
Category: 'Efficiency',
|
|
},
|
|
hostIPCSet: {
|
|
ID: 'hostIPCSet',
|
|
Message: '',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'danger',
|
|
Category: 'Security',
|
|
},
|
|
},
|
|
}),
|
|
]);
|
|
|
|
const issues = getTopIssues(data);
|
|
// Danger first regardless of count
|
|
expect(issues[0].severity).toBe('danger');
|
|
expect(issues[1].severity).toBe('warning');
|
|
expect(issues[1].count).toBe(2);
|
|
});
|
|
|
|
it('returns at most 10 issues', () => {
|
|
const results: Record<
|
|
string,
|
|
{
|
|
ID: string;
|
|
Message: string;
|
|
Details: string[];
|
|
Success: boolean;
|
|
Severity: 'warning';
|
|
Category: string;
|
|
}
|
|
> = {};
|
|
for (let i = 0; i < 15; i++) {
|
|
results[`check${i}`] = {
|
|
ID: `check${i}`,
|
|
Message: '',
|
|
Details: [],
|
|
Success: false,
|
|
Severity: 'warning',
|
|
Category: 'X',
|
|
};
|
|
}
|
|
const data = makeAuditData([makeResult({ Results: results })]);
|
|
expect(getTopIssues(data)).toHaveLength(10);
|
|
});
|
|
});
|