diff --git a/frontend/src/routes/Governance/overview/Overview.test.tsx b/frontend/src/routes/Governance/overview/Overview.test.tsx index 81bbec69b46..ae41da7a308 100644 --- a/frontend/src/routes/Governance/overview/Overview.test.tsx +++ b/frontend/src/routes/Governance/overview/Overview.test.tsx @@ -14,9 +14,11 @@ import { mockPolicy, mockPolicyNoStatus, } from '../governance.sharedMocks' -import GovernanceOverview from './Overview' +import GovernanceOverview, { SecurityGroupViolations } from './Overview' +import { SecurityGroupPolicySummarySidebar } from './SecurityGroupPolicySummarySidebar' import userEvent from '@testing-library/user-event' import { defaultContext, PluginDataContext } from '../../../lib/PluginDataContext' +import { Policy, PolicyApiVersion, PolicyKind } from '../../../resources' describe('Overview Page', () => { beforeEach(async () => nockIgnoreApiPaths()) @@ -152,4 +154,132 @@ describe('Overview Page', () => { userEvent.click(screen.getByText(/show 85 more/i)) expect(queryByText(/show 85 more/i)).not.toBeInTheDocument() }) + + test('Should aggregate Standards card by trimmed annotation value, not raw comma-split token', async () => { + // Regression test: a standard listed anywhere but first in a comma-separated + // policy.open-cluster-management.io/standards annotation must not produce a + // separate row on the Standards card just because of the leading space left + // behind by String.split(','). + const policyWithStandardFirstInList: Policy = { + apiVersion: PolicyApiVersion, + kind: PolicyKind, + metadata: { + name: 'policy-standards-first', + namespace: 'test', + uid: 'standards-test-uid-1', + annotations: { + 'policy.open-cluster-management.io/standards': 'NIST SP 800-53, PCI-DSS 4.0', + }, + }, + spec: { + disabled: false, + 'policy-templates': [], + remediationAction: 'inform', + }, + status: { + compliant: 'Compliant', + }, + } + const policyWithStandardLastInList: Policy = { + apiVersion: PolicyApiVersion, + kind: PolicyKind, + metadata: { + name: 'policy-standards-last', + namespace: 'test', + uid: 'standards-test-uid-2', + annotations: { + 'policy.open-cluster-management.io/standards': 'CIS OpenShift Benchmark, PCI-DSS 4.0, NIST SP 800-53', + }, + }, + spec: { + disabled: false, + 'policy-templates': [], + remediationAction: 'inform', + }, + status: { + compliant: 'Compliant', + }, + } + + const pluginData = { + ...defaultContext, + loadStarted: true, + loadCompleted: true, + } + render( + + { + snapshot.set(policiesState, [policyWithStandardFirstInList, policyWithStandardLastInList]) + snapshot.set(managedClustersState, mockManagedClusters) + }} + > + + + + + + ) + + // Before the fix this rendered two separate rows/spans for the same logical + // standard (one from the untrimmed " NIST SP 800-53" split token). + expect(screen.getAllByText('NIST SP 800-53').length).toBe(1) + expect(screen.getAllByText('PCI-DSS 4.0').length).toBe(1) + }) + + test('SecurityGroupPolicySummarySidebar should match policies by trimmed annotation value', async () => { + // Regression test for the sidebar drill-down: a policy whose standard is not + // the first item in its comma-separated annotation must still match the + // clicked-on violation, which is keyed by the trimmed value. + const policyWithStandardLastInList: Policy = { + apiVersion: PolicyApiVersion, + kind: PolicyKind, + metadata: { + name: 'policy-standards-last-sidebar', + namespace: 'test', + uid: 'standards-test-uid-3', + annotations: { + 'policy.open-cluster-management.io/standards': 'CIS OpenShift Benchmark, PCI-DSS 4.0, NIST SP 800-53', + }, + }, + spec: { + disabled: false, + 'policy-templates': [], + remediationAction: 'inform', + }, + status: { + compliant: 'Compliant', + }, + } + const violation: SecurityGroupViolations = { + name: 'NIST SP 800-53', + compliant: 1, + noncompliant: 0, + pending: 0, + } + + const pluginData = { + ...defaultContext, + loadStarted: true, + loadCompleted: true, + } + render( + + { + snapshot.set(policiesState, [policyWithStandardLastInList]) + }} + > + + + + + + ) + + // Before the fix, the sidebar's filter compared the untrimmed " NIST SP 800-53" + // split token against violation.name ("NIST SP 800-53") and never matched, + // so the policy would not appear in this list. + expect(await screen.findByText('policy-standards-last-sidebar')).toBeInTheDocument() + }) }) diff --git a/frontend/src/routes/Governance/overview/Overview.tsx b/frontend/src/routes/Governance/overview/Overview.tsx index 900870c74b0..fbc0336b1a4 100644 --- a/frontend/src/routes/Governance/overview/Overview.tsx +++ b/frontend/src/routes/Governance/overview/Overview.tsx @@ -102,7 +102,7 @@ function useSecurityGroupViolations(group: string, policies: Policy[]) { if (policy.spec.disabled) continue const annotation = policy.metadata.annotations?.[`policy.open-cluster-management.io/${group}`] if (!annotation) continue - const names = annotation.split(',') + const names = annotation.split(',').map((name) => name.trim()) for (const name of names) { let v = clusterViolations[name] if (!v) { diff --git a/frontend/src/routes/Governance/overview/SecurityGroupPolicySummarySidebar.tsx b/frontend/src/routes/Governance/overview/SecurityGroupPolicySummarySidebar.tsx index 525d83b966c..64a1411a057 100644 --- a/frontend/src/routes/Governance/overview/SecurityGroupPolicySummarySidebar.tsx +++ b/frontend/src/routes/Governance/overview/SecurityGroupPolicySummarySidebar.tsx @@ -36,7 +36,7 @@ export function SecurityGroupPolicySummarySidebar(props: { if (!annotation) { return false } - const names = annotation.split(',') + const names = annotation.split(',').map((name) => name.trim()) for (const name of names) { if (name === violation.name && policy.status?.compliant) { return true