Skip to content

Commit

Permalink
feat(pci.kubernetes): add more text and better test
Browse files Browse the repository at this point in the history
ref: TAPC-33
Signed-off-by: Pierre-Philippe <[email protected]>
  • Loading branch information
Pierre-Philippe committed Oct 18, 2024
1 parent a2136b1 commit 4b652cb
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@
"kube_service_cluster_admission_plugins_desactivated": "Désactivé",
"kube_service_cluster_admission_plugins_to_activate": "Activer",
"kube_service_cluster_admission_plugins_info_restrictions": "Pour des raisons de sécurité, il n'est pas possible de désactiver le plugin Node Restriction.",
"kube_service_cluster_admission_plugins_info_restrictions_redeploy_after_change_admission": "Attention: veuillez noter que toute action sur  les Admisions Plugins implique un redéploiement de l'API Server de votre cluster.",
"kube_service_cluster_admission_plugins_to_desactivate": "Désactiver",
"kube_service_cluster_admission_plugins_always_pull_image_explanation": "Force chaque nouveau pod à télécharger les images requises à chaque fois.",
"kube_service_cluster_admission_plugins_node_restriction_explanation": "L'utilisation du plug-in du contrôleur d'admission NodeRestriction limite les objets Node et Pod qu'un kubelet peut modifier. Lorsqu'elles sont limitées par ce contrôleur d'admission, les kubelets ne sont autorisés à modifier que leur propre objet API Node et uniquement les objets API Pod qui sont liés à leur nœud.",
"kube_service_cluster_admission_plugins_error": "Une erreur est survenue lors de la réinitialisation de votre cluster : {{ message }}",
"kube_service_cluster_version": "Version mineure de Kubernetes",
"kube_service_cluster_update_available": "Une nouvelle mise à jour touchant à la sécurité (<i>patch version</i>) est disponible",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { describe, it, expect, vi } from 'vitest';
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
import AdmissionPlugins from './AdmissionPlugins.component';

const navigate = vi.fn();
vi.mock('react-router-dom', () => ({
...vi.importActual('react-router-dom'),
useNavigate: () => navigate,
}));

describe('AdmissionPlugins', () => {
it('renders plugins correctly', () => {
const enabled = ['NodeRestriction'];
const disabled = ['AlwaysPullImages'];

render(<AdmissionPlugins enabled={enabled} disabled={disabled} />);
render(
<AdmissionPlugins
enabled={enabled}
disabled={disabled}
isProcessing={false}
/>,
);

// Check plugin labels
expect(screen.getByText('Plugin Node Restriction')).toBeInTheDocument();
Expand Down Expand Up @@ -37,20 +49,37 @@ describe('AdmissionPlugins', () => {
});

it('renders the mutation link', () => {
render(<AdmissionPlugins enabled={[]} disabled={[]} />);
render(
<AdmissionPlugins enabled={[]} disabled={[]} isProcessing={false} />,
);

const mutationLink = screen.getByText(
'kube_service_cluster_admission_plugins_mutation',
);
expect(mutationLink).toBeInTheDocument();
});

it('renders the mutation link', () => {
render(<AdmissionPlugins enabled={[]} disabled={[]} />);
it('disables the mutation link when isProcessing is true', () => {
render(<AdmissionPlugins enabled={[]} disabled={[]} isProcessing />);

const mutationLink = screen.getByText(
'kube_service_cluster_admission_plugins_mutation',
);
expect(mutationLink).toBeInTheDocument();
expect(mutationLink).toBeDisabled();
mutationLink.click();
expect(navigate).not.toHaveBeenCalledWith('./admission-plugin');
});

it('navigates to the admission-plugin page when the mutation link is clicked', () => {
render(
<AdmissionPlugins enabled={[]} disabled={[]} isProcessing={false} />,
);

const mutationLink = screen.getByText(
'kube_service_cluster_admission_plugins_mutation',
);
mutationLink.click();

expect(navigate).toHaveBeenCalledWith('./admission-plugin');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useNavigate } from 'react-router-dom';
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
import { OsdsChip, OsdsText, OsdsLink } from '@ovhcloud/ods-components/react';
import { useTranslation } from 'react-i18next';
import { TKube } from '@/types';
import { TAdmissionPlugin } from '@/types';
import usePluginState from '@/hooks/usePluginState';

export const plugins = [
Expand All @@ -16,23 +16,29 @@ export const plugins = [
label: 'Plugin Node Restriction',
value: 'node',
disabled: true,
tip: 'kube_service_cluster_admission_plugins_node_restriction_explanation',
},
{
name: 'AlwaysPullImages',
label: 'Plugin Always Pull Images',
value: 'pull',
tip: 'kube_service_cluster_admission_plugins_always_pull_image_explanation',
},
];

export type AdmissionPluginsProps = TAdmissionPlugin & {
isProcessing: boolean;
};

const AdmissionPlugins = ({
isProcessing,
disabled,
enabled,
}: TKube['customization']['apiServer']['admissionPlugins']) => {
}: AdmissionPluginsProps) => {
const { t } = useTranslation(['service']);

const pluginsState = usePluginState(enabled, disabled);
const navigate = useNavigate();

const pluginsState = usePluginState(enabled, disabled);
return (
<div className="mb-4 flex flex-wrap justify-between gap-4">
{plugins.map((plugin) => (
Expand Down Expand Up @@ -64,7 +70,13 @@ const AdmissionPlugins = ({
</div>
))}
<OsdsLink
onClick={() => navigate('./admission-plugin')}
// FIXME ODSDS 18
disabled={isProcessing || undefined}
onClick={() => {
if (!isProcessing) {
navigate('./admission-plugin');
}
}}
color={ODS_THEME_COLOR_INTENT.primary}
className="flex font-bold"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { TKube } from '@/types';
import ClusterStatus from './ClusterStatus.component';
import TileLine from './TileLine.component';
import AdmissionPlugins from './AdmissionPlugins.component';
import { isProcessing } from './ClusterManagement.component';

export type ClusterInformationProps = {
kubeDetail: TKube;
Expand Down Expand Up @@ -70,6 +71,7 @@ export default function ClusterInformation({
title={t('kube_service_cluster_admission_plugins')}
value={
<AdmissionPlugins
isProcessing={isProcessing(kubeDetail.status)}
{...kubeDetail.customization.apiServer.admissionPlugins}
/>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ export type ClusterManagementProps = {
kubeDetail: TKube;
};

export const isProcessing = (status: string) =>
PROCESSING_STATUS.includes(status);

export default function ClusterManagement({
kubeDetail,
}: Readonly<ClusterManagementProps>) {
const { t } = useTranslation('service');
const { t: tDetail } = useTranslation('listing');

const isProcessing = (status: string) => PROCESSING_STATUS.includes(status);

const hrefRenameCluster = useHref('./name');
const hrefResetClusterConfig = useHref('./reset-kubeconfig');
const hrefResetCluster = useHref('./reset');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import AdmissionPluginsModal from './AdmissionPlugins.page';
import * as useKubernetesModule from '@/api/hooks/useKubernetes';
import { wrapper } from '@/wrapperRenders';

const navigate = vi.fn();
const plugState = vi.fn();
const updateAdmissionPlugin = vi.fn();

vi.mock('@/api/hooks/useKubernetes', () => ({
useKubernetesCluster: vi.fn(),
}));

vi.mock('react-router-dom', () => ({
...vi.importActual('react-router-dom'),
useNavigate: () => navigate,
useParams: () => ({}),
}));

vi.mock('../hooks/usePluginState', () => plugState);
vi.mock('@/api/hooks/useAdmissionPlugin/useAdmissionPlugin', () => ({
useUpdateAdmissionPlugin: () => ({
updateAdmissionPlugins: updateAdmissionPlugin,
}),
}));

describe('AdmissionPluginsModal', () => {
beforeEach(() => {
vi.clearAllMocks();
plugState.mockReturnValue(() => 'enabled');
updateAdmissionPlugin.mockReturnValue({
updateAdmissionPlugins: vi.fn(),
isPending: false,
});
});

it('renders the modal with a spinner when loading', async () => {
(useKubernetesModule.useKubernetesCluster as Mock).mockReturnValue({
data: null,
isPending: true,
});

render(<AdmissionPluginsModal />, { wrapper });
expect(screen.getByTestId('wrapper')).toBeInTheDocument();
});

it('renders the modal with plugins when data is available', async () => {
(useKubernetesModule.useKubernetesCluster as Mock).mockReturnValue({
data: {
customization: {
apiServer: {
admissionPlugins: { enabled: ['NodeRestrictions'], disabled: [] },
},
},
},
isPending: false,
});

const { container } = render(<AdmissionPluginsModal />, { wrapper });
const modal = container.querySelector('osds-modal');
expect(modal).toBeInTheDocument();
expect(modal).toHaveProperty(
'headline',
'kube_service_cluster_admission_plugins_mutation',
);
});

it('handles plugin switch change', () => {
(useKubernetesModule.useKubernetesCluster as Mock).mockReturnValue({
data: {
customization: {
apiServer: {
admissionPlugins: { enabled: ['AlwaysPullImages'], disabled: [] },
},
},
},
isPending: false,
});

render(<AdmissionPluginsModal />, { wrapper });
const switchElement = screen.getAllByText(
'kube_service_cluster_admission_plugins_to_activate',
);
fireEvent.change(switchElement[0], { detail: { current: 'enabled' } });
});

it('handles cancel button click', () => {
render(<AdmissionPluginsModal />, { wrapper });
const cancelButton = screen.getByText(
'common:common_stepper_cancel_button_label',
);
fireEvent.click(cancelButton);
expect(navigate).toHaveBeenCalledWith('..');
});

it('handles save button click', () => {
render(<AdmissionPluginsModal />, { wrapper });
const saveButton = screen.getByText('common:common_save_button_label');
fireEvent.click(saveButton);
expect(updateAdmissionPlugin).toHaveBeenCalled();
});
});
Loading

0 comments on commit 4b652cb

Please sign in to comment.