Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions x-pack/plugins/cloud_security_posture/common/types_old.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export interface BaseCspSetupStatus {
vuln_mgmt: BaseCspSetupBothPolicy;
isPluginInitialized: boolean;
installedPackageVersion?: string | undefined;
hasMisconfigurationsFindings?: boolean;
}

export type CspSetupStatus = BaseCspSetupStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import { useKibana } from './use_kibana';

const SUBSCRIPTION_QUERY_KEY = 'csp_subscription_query_key';

export const useSubscriptionStatus = () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if we rename the only hook, should we also rename the file? not like we strictly follow one hool per file policy, so just a nit

export const useIsSubscriptionStatusValid = () => {
const { licensing } = useKibana().services;
const { isCloudEnabled } = useContext(SetupContext);

return useQuery([SUBSCRIPTION_QUERY_KEY], async () => {
const license = await licensing.refresh();
return isSubscriptionAllowed(isCloudEnabled, license);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
* 2.0.
*/

import { useSubscriptionStatus } from '../common/hooks/use_subscription_status';
import Chance from 'chance';
import {
DEFAULT_NO_DATA_TEST_SUBJECT,
ERROR_STATE_TEST_SUBJECT,
isCommonError,
LOADING_STATE_TEST_SUBJECT,
PACKAGE_NOT_INSTALLED_TEST_SUBJECT,
SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT,
} from './cloud_posture_page';
import { createReactQueryResponse } from '../test/fixtures/react_query';
import { TestProvider } from '../test/test_provider';
Expand All @@ -23,27 +21,17 @@ import React, { ComponentProps } from 'react';
import { UseQueryResult } from '@tanstack/react-query';
import { CloudPosturePage } from './cloud_posture_page';
import { NoDataPage } from '@kbn/kibana-react-plugin/public';
import { useLicenseManagementLocatorApi } from '../common/api/use_license_management_locator_api';

const chance = new Chance();

jest.mock('../common/api/use_setup_status_api');
jest.mock('../common/api/use_license_management_locator_api');
jest.mock('../common/hooks/use_subscription_status');
jest.mock('../common/hooks/use_is_subscription_status_valid');
jest.mock('../common/navigation/use_csp_integration_link');

describe('<CloudPosturePage />', () => {
beforeEach(() => {
jest.resetAllMocks();

(useSubscriptionStatus as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: true,
})
);

(useLicenseManagementLocatorApi as jest.Mock).mockImplementation(undefined);
});

const renderCloudPosturePage = (
Expand Down Expand Up @@ -72,101 +60,16 @@ describe('<CloudPosturePage />', () => {
);
};

it('renders with license url locator', () => {
(useSubscriptionStatus as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: false,
})
);

(useLicenseManagementLocatorApi as jest.Mock).mockImplementation(() => 'http://license-url');

renderCloudPosturePage();

expect(screen.getByTestId('has_locator')).toBeInTheDocument();
});

it('renders no license url locator', () => {
(useSubscriptionStatus as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: false,
})
);

(useLicenseManagementLocatorApi as jest.Mock).mockImplementation(undefined);

renderCloudPosturePage();
expect(screen.getByTestId('no_locator')).toBeInTheDocument();
});

it('renders children if setup status is indexed', () => {
const children = chance.sentence();
renderCloudPosturePage({ children });

expect(screen.getByText(children)).toBeInTheDocument();
expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
});

it('renders default loading state when the subscription query is loading', () => {
(useSubscriptionStatus as jest.Mock).mockImplementation(
() =>
createReactQueryResponse({
status: 'loading',
}) as unknown as UseQueryResult
);

const children = chance.sentence();
renderCloudPosturePage({ children });

expect(screen.getByTestId(LOADING_STATE_TEST_SUBJECT)).toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
});

it('renders default error state when the subscription query has an error', () => {
(useSubscriptionStatus as jest.Mock).mockImplementation(
() =>
createReactQueryResponse({
status: 'error',
error: new Error('error'),
}) as unknown as UseQueryResult
);

const children = chance.sentence();
renderCloudPosturePage({ children });

expect(screen.getByTestId(ERROR_STATE_TEST_SUBJECT)).toBeInTheDocument();
expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
});

it('renders subscription not allowed prompt if subscription is not installed', () => {
(useSubscriptionStatus as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: false,
})
);

const children = chance.sentence();
renderCloudPosturePage({ children });

expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.getByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).toBeInTheDocument();
expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
});

it('renders default loading text when query isLoading', () => {
const query = createReactQueryResponse({
status: 'loading',
Expand All @@ -176,7 +79,6 @@ describe('<CloudPosturePage />', () => {
renderCloudPosturePage({ children, query });

expect(screen.getByTestId(LOADING_STATE_TEST_SUBJECT)).toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
Expand All @@ -191,7 +93,6 @@ describe('<CloudPosturePage />', () => {
renderCloudPosturePage({ children, query });

expect(screen.getByTestId(LOADING_STATE_TEST_SUBJECT)).toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
Expand Down Expand Up @@ -220,7 +121,6 @@ describe('<CloudPosturePage />', () => {
expect(screen.getByText(text, { exact: false })).toBeInTheDocument()
);
expect(screen.getByTestId(ERROR_STATE_TEST_SUBJECT)).toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
Expand Down Expand Up @@ -253,7 +153,6 @@ describe('<CloudPosturePage />', () => {
[error, statusCode].forEach((text) => expect(screen.queryByText(text)).not.toBeInTheDocument());
expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
});
Expand All @@ -275,7 +174,6 @@ describe('<CloudPosturePage />', () => {
expect(screen.getByText(loading)).toBeInTheDocument();
expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
});
Expand All @@ -291,7 +189,6 @@ describe('<CloudPosturePage />', () => {

expect(screen.getByTestId(DEFAULT_NO_DATA_TEST_SUBJECT)).toBeInTheDocument();
expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
Expand Down Expand Up @@ -320,7 +217,6 @@ describe('<CloudPosturePage />', () => {
expect(screen.getByText(pageTitle)).toBeInTheDocument();
expect(screen.getAllByText(solution, { exact: false })[0]).toBeInTheDocument();
expect(screen.queryByTestId(LOADING_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByText(children)).not.toBeInTheDocument();
expect(screen.queryByTestId(ERROR_STATE_TEST_SUBJECT)).not.toBeInTheDocument();
expect(screen.queryByTestId(PACKAGE_NOT_INSTALLED_TEST_SUBJECT)).not.toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { EuiEmptyPrompt } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { NoDataPage, NoDataPageProps } from '@kbn/kibana-react-plugin/public';
import { css } from '@emotion/react';
import { SubscriptionNotAllowed } from './subscription_not_allowed';
import { useSubscriptionStatus } from '../common/hooks/use_subscription_status';
import { FullSizeCenteredPage } from './full_size_centered_page';
import { CspLoadingState } from './csp_loading_state';

Expand All @@ -22,7 +20,6 @@ export const PACKAGE_NOT_INSTALLED_TEST_SUBJECT = 'cloud_posture_page_package_no
export const CSPM_INTEGRATION_NOT_INSTALLED_TEST_SUBJECT = 'cloud_posture_page_cspm_not_installed';
export const KSPM_INTEGRATION_NOT_INSTALLED_TEST_SUBJECT = 'cloud_posture_page_kspm_not_installed';
export const DEFAULT_NO_DATA_TEST_SUBJECT = 'cloud_posture_page_no_data';
export const SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT = 'cloud_posture_page_subscription_not_allowed';

interface CommonError {
body: {
Expand Down Expand Up @@ -150,12 +147,6 @@ export const defaultNoDataRenderer = () => (
</FullSizeCenteredPage>
);

const subscriptionNotAllowedRenderer = () => (
<FullSizeCenteredPage data-test-subj={SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT}>
<SubscriptionNotAllowed />
</FullSizeCenteredPage>
);

interface CloudPosturePageProps<TData, TError> {
children: React.ReactNode;
query?: UseQueryResult<TData, TError>;
Expand All @@ -171,21 +162,7 @@ export const CloudPosturePage = <TData, TError>({
errorRender = defaultErrorRenderer,
noDataRenderer = defaultNoDataRenderer,
}: CloudPosturePageProps<TData, TError>) => {
const subscriptionStatus = useSubscriptionStatus();

const render = () => {
if (subscriptionStatus.isError) {
return defaultErrorRenderer(subscriptionStatus.error);
}

if (subscriptionStatus.isLoading) {
return defaultLoadingRenderer();
}

if (!subscriptionStatus.data) {
return subscriptionNotAllowedRenderer();
}

if (!query) {
return children;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/
import React from 'react';
import { render, waitFor, within } from '@testing-library/react';
import { render, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {
CspPolicyTemplateForm,
Expand Down Expand Up @@ -53,9 +53,12 @@ import {
GCP_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ,
SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ,
SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ,
SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT,
} from '../test_subjects';
import { ExperimentalFeaturesService } from '@kbn/fleet-plugin/public/services';
import { createFleetTestRendererMock } from '@kbn/fleet-plugin/public/mock';
import { useIsSubscriptionStatusValid } from '../../common/hooks/use_is_subscription_status_valid';
import { useLicenseManagementLocatorApi } from '../../common/api/use_license_management_locator_api';

// mock useParams
jest.mock('react-router-dom', () => ({
Expand All @@ -66,6 +69,8 @@ jest.mock('react-router-dom', () => ({
}));
jest.mock('../../common/api/use_setup_status_api');
jest.mock('../../common/api/use_package_policy_list');
jest.mock('../../common/hooks/use_is_subscription_status_valid');
jest.mock('../../common/api/use_license_management_locator_api');
jest.mock('@kbn/fleet-plugin/public/services/experimental_features');

const onChange = jest.fn();
Expand All @@ -85,9 +90,11 @@ describe('<CspPolicyTemplateForm />', () => {
(useParams as jest.Mock).mockReturnValue({
integration: undefined,
});

mockedExperimentalFeaturesService.get.mockReturnValue({
secretsStorage: true,
} as any);

(usePackagePolicyList as jest.Mock).mockImplementation((packageName) =>
createReactQueryResponseWithRefetch({
status: 'success',
Expand All @@ -96,13 +103,22 @@ describe('<CspPolicyTemplateForm />', () => {
},
})
);

onChange.mockClear();

(useCspSetupStatusApi as jest.Mock).mockImplementation(() =>
createReactQueryResponseWithRefetch({
status: 'success',
data: { status: 'indexed', installedPackageVersion: '1.2.13' },
})
);

(useIsSubscriptionStatusValid as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: true,
})
);
});

const WrappedComponent = ({
Expand Down Expand Up @@ -145,6 +161,53 @@ describe('<CspPolicyTemplateForm />', () => {
);
};

it('shows license block if subscription is not allowed', () => {
(useIsSubscriptionStatusValid as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: false,
})
);

const policy = getMockPolicyK8s();
const { rerender } = render(<WrappedComponent newPolicy={policy} />);

rerender(<WrappedComponent newPolicy={{ ...policy, namespace: 'some-namespace' }} />);
expect(screen.getByTestId(SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT)).toBeInTheDocument();
});

it('license block renders with license url locator', () => {
(useIsSubscriptionStatusValid as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: false,
})
);
(useLicenseManagementLocatorApi as jest.Mock).mockImplementation(() => 'http://license-url');

const policy = getMockPolicyK8s();
const { rerender } = render(<WrappedComponent newPolicy={policy} />);

rerender(<WrappedComponent newPolicy={{ ...policy, namespace: 'some-namespace' }} />);
expect(screen.getByTestId('has_locator')).toBeInTheDocument();
});

it('license block renders without license url locator', () => {
(useIsSubscriptionStatusValid as jest.Mock).mockImplementation(() =>
createReactQueryResponse({
status: 'success',
data: false,
})
);
(useLicenseManagementLocatorApi as jest.Mock).mockImplementation(undefined);

const policy = getMockPolicyK8s();
const { rerender } = render(<WrappedComponent newPolicy={policy} />);

rerender(<WrappedComponent newPolicy={{ ...policy, namespace: 'some-namespace' }} />);
expect(screen.getByTestId('no_locator')).toBeInTheDocument();
});

it('updates package policy namespace to default when it changes', () => {
const policy = getMockPolicyK8s();
const { rerender } = render(<WrappedComponent newPolicy={policy} />);
Expand Down
Loading