From f95264ccadc34f2dfd64920a4a9af6db25f7c3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20=C3=81brah=C3=A1m?= Date: Thu, 6 Feb 2025 16:17:47 +0100 Subject: [PATCH] [EDR Workflows] Fix agent count on policy deploy modal (#209593) ## Summary Fixes the agent count issue on the warning model when saving a Defend package policy. Now it uses the same `active` field instead of `all`, as the `AgentSummary` component. Also, re-enables flaky unit test for `PolicySettingsLayout`: closes: #179984 ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- ...etch_endpoint_policy_agent_summary.test.ts | 10 ++- .../public/management/mocks/fleet_mocks.ts | 6 +- .../policy_settings_layout.test.tsx | 63 +++++++++++++++---- .../policy_settings_layout.tsx | 2 +- 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/hooks/policy/use_fetch_endpoint_policy_agent_summary.test.ts b/x-pack/solutions/security/plugins/security_solution/public/management/hooks/policy/use_fetch_endpoint_policy_agent_summary.test.ts index 2a98d2046b775..3f3734b97e630 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/hooks/policy/use_fetch_endpoint_policy_agent_summary.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/management/hooks/policy/use_fetch_endpoint_policy_agent_summary.test.ts @@ -12,6 +12,7 @@ import type { PolicyData } from '../../../../common/endpoint/types'; import { allFleetHttpMocks } from '../../mocks'; import { FleetPackagePolicyGenerator } from '../../../../common/endpoint/data_generators/fleet_package_policy_generator'; import { useFetchAgentByAgentPolicySummary } from './use_fetch_endpoint_policy_agent_summary'; +import type { GetAgentStatusResponse } from '@kbn/fleet-plugin/common'; import { agentRouteService, API_VERSIONS } from '@kbn/fleet-plugin/common'; const useQueryMock = _useQuery as jest.Mock; @@ -59,8 +60,9 @@ describe('When using the `useFetchEndpointPolicyAgentSummary()` hook', () => { query: { policyId: policy.policy_ids[0] }, version: API_VERSIONS.public.v1, }); - expect(data).toEqual({ - total: 50, + const expectedData: GetAgentStatusResponse['results'] = { + active: 50, + all: 0, inactive: 5, online: 40, error: 0, @@ -68,7 +70,9 @@ describe('When using the `useFetchEndpointPolicyAgentSummary()` hook', () => { updating: 0, other: 0, events: 0, - }); + unenrolled: 0, + }; + expect(data).toEqual(expectedData); }); it('should apply default values to api returned data', async () => { diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/mocks/fleet_mocks.ts b/x-pack/solutions/security/plugins/security_solution/public/management/mocks/fleet_mocks.ts index 17b436eae89b5..c50c474497171 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/mocks/fleet_mocks.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/management/mocks/fleet_mocks.ts @@ -415,10 +415,10 @@ export const fleetGetAgentStatusHttpMock = id: 'agentStatus', path: AGENT_API_ROUTES.STATUS_PATTERN, method: 'get', - handler: () => { + handler: (): GetAgentStatusResponse => { return { results: { - total: 50, + active: 50, inactive: 5, online: 40, error: 0, @@ -426,6 +426,8 @@ export const fleetGetAgentStatusHttpMock = updating: 0, other: 0, events: 0, + unenrolled: 0, + all: 0, }, }; }, diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/view/policy_settings_layout/policy_settings_layout.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/view/policy_settings_layout/policy_settings_layout.test.tsx index f26d520406407..52cd78fb5b479 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/view/policy_settings_layout/policy_settings_layout.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/view/policy_settings_layout/policy_settings_layout.test.tsx @@ -25,6 +25,7 @@ import { cloneDeep } from 'lodash'; import { set } from '@kbn/safer-lodash-set'; import { ProtectionModes } from '../../../../../../common/endpoint/types'; import { waitFor, cleanup } from '@testing-library/react'; +import type { GetAgentStatusResponse } from '@kbn/fleet-plugin/common'; import { packagePolicyRouteService, API_VERSIONS } from '@kbn/fleet-plugin/common'; import { getPolicyDataForUpdate } from '../../../../../../common/endpoint/service/policy'; import { getDeferred } from '../../../../mocks/utils'; @@ -34,8 +35,7 @@ jest.mock('../../../../../common/components/user_privileges'); const useUserPrivilegesMock = _useUserPrivileges as jest.Mock; -// Failing: See https://github.com/elastic/kibana/issues/179984 -describe.skip('When rendering PolicySettingsLayout', () => { +describe('When rendering PolicySettingsLayout', () => { jest.setTimeout(15000); const testSubj = getPolicySettingsFormTestSubjects(); @@ -84,6 +84,14 @@ describe.skip('When rendering PolicySettingsLayout', () => { } }; + /** + * Performs a minimal number of updates to make 'Save' button enabled. + */ + const makeMinimalUpdates = async () => { + const { getByTestId } = renderResult; + await userEvent.click(getByTestId(testSubj.malware.enableDisableSwitch)); + }; + /** * Makes updates to the policy form on the UI and return back a new (cloned) `PolicyData` * with the updates reflected in it @@ -114,9 +122,11 @@ describe.skip('When rendering PolicySettingsLayout', () => { await userEvent.type(getByTestId(testSubj.ransomware.notifyCustomMessage), 'foo message'); set(policySettings, 'windows.popup.ransomware.message', 'foo message'); - await userEvent.click(getByTestId(testSubj.advancedSection.showHideButton)); - await userEvent.type(getByTestId('linux.advanced.agent.connection_delay'), '1000'); - set(policySettings, 'linux.advanced.agent.connection_delay', '1000'); + // skipping Advanced Options as changing them takes too long. + // todo: re-enable them with this issue: https://github.com/elastic/security-team/issues/11765 + // await userEvent.click(getByTestId(testSubj.advancedSection.showHideButton)); + // await userEvent.type(getByTestId('linux.advanced.agent.connection_delay'), '1000'); + // set(policySettings, 'linux.advanced.agent.connection_delay', '1000'); return expectedUpdates; }; @@ -131,7 +141,7 @@ describe.skip('When rendering PolicySettingsLayout', () => { it('should render layout with expected content when changes have been made', async () => { const { getByTestId } = render(); - await makeUpdates(); + await makeMinimalUpdates(); expect(getByTestId('endpointPolicyForm')); expect(getByTestId('policyDetailsCancelButton')).not.toBeDisabled(); expect(getByTestId('policyDetailsSaveButton')).not.toBeDisabled(); @@ -153,7 +163,7 @@ describe.skip('When rendering PolicySettingsLayout', () => { const deferred = getDeferred(); apiMocks.responseProvider.updateEndpointPolicy.mockDelay.mockReturnValue(deferred.promise); const { getByTestId } = render(); - await makeUpdates(); + await makeMinimalUpdates(); await clickSave(true, false); await waitFor(() => { @@ -164,13 +174,11 @@ describe.skip('When rendering PolicySettingsLayout', () => { expect( getByTestId('policyDetailsSaveButton').querySelector('.euiLoadingSpinner') ).not.toBeNull(); - - deferred.resolve(); }); it('should show success toast on update success', async () => { render(); - await makeUpdates(); + await makeMinimalUpdates(); await clickSave(); await waitFor(() => { @@ -189,7 +197,7 @@ describe.skip('When rendering PolicySettingsLayout', () => { throw new Error('oh oh!'); }); render(); - await makeUpdates(); + await makeMinimalUpdates(); await clickSave(); await waitFor(() => { @@ -202,6 +210,39 @@ describe.skip('When rendering PolicySettingsLayout', () => { title: 'Failed!', }); }); + + it('should not show warning about endpoints if there are no active endpoints', async () => { + apiMocks.responseProvider.agentStatus.mockReturnValue({ + results: { active: 0 }, + } as GetAgentStatusResponse); + + const { getByTestId, queryByTestId } = render(); + await makeMinimalUpdates(); + + await userEvent.click(getByTestId('policyDetailsSaveButton')); + await waitFor(() => { + expect(getByTestId('confirmModalConfirmButton')).toBeInTheDocument(); + }); + expect(queryByTestId('policyDetailsWarningCallout')).not.toBeInTheDocument(); + }); + + it('should show warning about endpoints with the number of active endpoints', async () => { + apiMocks.responseProvider.agentStatus.mockReturnValue({ + results: { active: 6 }, + } as GetAgentStatusResponse); + + const { getByTestId } = render(); + await makeMinimalUpdates(); + + await userEvent.click(getByTestId('policyDetailsSaveButton')); + await waitFor(() => { + expect(getByTestId('confirmModalConfirmButton')).toBeInTheDocument(); + }); + + const callout = getByTestId('policyDetailsWarningCallout'); + expect(callout).toBeInTheDocument(); + expect(callout.textContent).toContain('This action will update 6 endpoints'); + }); }); describe('and user has View Only permissions', () => { diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/view/policy_settings_layout/policy_settings_layout.tsx b/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/view/policy_settings_layout/policy_settings_layout.tsx index dedb2a7403229..e5fd394e16d1b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/view/policy_settings_layout/policy_settings_layout.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/view/policy_settings_layout/policy_settings_layout.tsx @@ -170,7 +170,7 @@ export const PolicySettingsLayout = memo( <> {showConfirm && (