Repository: ovh/manager
Pull request #15853: feat(veeam): display consumed VMs
Author: login: sefiani-ovh
Labels: name: feature
description: New feature
name: universe-bare-metal-cloud
Created: 2025-03-07T08:24:27Z | Merged: 2025-03-27T15:18:28Z

## Description

ref: #MANAGER-17126




Diff:
diff --git a/packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.spec.tsx b/packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.spec.tsx
index cfde4c555638..4153a93bb891 100644
--- a/packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.spec.tsx
+++ b/packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.spec.tsx
@@ -1,20 +1,21 @@
 import React from 'react';
 import { vi } from 'vitest';
-import { useServiceDetails } from '@ovh-ux/manager-react-components';
-import {
-  QueryClient,
-  QueryClientProvider,
-  UseQueryResult,
-} from '@tanstack/react-query';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { AxiosResponse } from 'axios';
+
 import { renderHook, waitFor } from '@testing-library/react';
-import { ApiError, ApiResponse } from '@ovh-ux/manager-core-api';
-import { ServiceDetails } from '@ovh-ux/manager-module-common-api';
+import { ApiResponse } from '@ovh-ux/manager-core-api';
+import {
+  ServiceDetails,
+  useServiceDetailsQueryOption,
+} from '@ovh-ux/manager-module-common-api';
 import useVeeamBackupConsumption from './useVeeamBackupConsumption';
 import { getServiceConsumption } from '@/data/api/service-consumption';
 import { TServiceConsumption } from '@/type/service-consumption.type';
 
 vi.mock('@ovh-ux/manager-react-components');
 vi.mock('@/data/api/service-consumption');
+vi.mock('@ovh-ux/manager-module-common-api');
 
 describe('useVeeamBackupConsumption tests suite', () => {
   const resourceName = 'id-resource-veeam';
@@ -22,17 +23,24 @@ describe('useVeeamBackupConsumption tests suite', () => {
   const consumptions = [
     { planCode: 'planCode1', quantity: 100, uniqueId: 'id' },
   ];
+  let queryClient: QueryClient;
+  beforeEach(() => {
+    queryClient = new QueryClient();
+  });
 
   afterEach(() => vi.clearAllMocks());
 
   it('Should compute service consumptions', async () => {
-    vi.mocked(useServiceDetails).mockReturnValue({
-      data: {
-        data: {
-          serviceId,
-        },
-      },
-    } as UseQueryResult<ApiResponse<ServiceDetails>, ApiError>);
+    vi.mocked(useServiceDetailsQueryOption).mockReturnValue({
+      queryKey: ['services', 'details', resourceName] as any,
+      queryFn: vi.fn(),
+      retry: false,
+      enabled: !!resourceName,
+    });
+
+    vi.spyOn(QueryClient.prototype, 'fetchQuery').mockResolvedValueOnce({
+      data: { serviceId },
+    } as AxiosResponse<ServiceDetails>);
 
     vi.mocked(getServiceConsumption).mockResolvedValueOnce({
       data: consumptions,
@@ -42,18 +50,29 @@ describe('useVeeamBackupConsumption tests suite', () => {
       () => useVeeamBackupConsumption(resourceName),
       {
         wrapper: ({ children }) => (
-          <QueryClientProvider client={new QueryClient()}>
+          <QueryClientProvider client={queryClient}>
             {children}
           </QueryClientProvider>
         ),
       },
     );
 
-    expect(vi.mocked(useServiceDetails)).toHaveBeenCalledWith({ resourceName });
-    expect(vi.mocked(getServiceConsumption)).toHaveBeenCalledWith(
-      serviceId,
-      expect.any(AbortSignal),
+    expect(vi.mocked(useServiceDetailsQueryOption)).toHaveBeenCalledWith({
+      resourceName,
+    });
+
+    expect(QueryClient.prototype.fetchQuery).toHaveBeenCalledWith(
+      expect.objectContaining({
+        queryKey: ['services', 'details', resourceName],
+      }),
     );
+
+    await waitFor(() => {
+      expect(vi.mocked(getServiceConsumption)).toHaveBeenCalledWith(
+        serviceId,
+        expect.any(AbortSignal),
+      );
+    });
     await waitFor(() => {
       expect(result.current.data).toEqual(consumptions);
     });
diff --git a/packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.ts b/packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.ts
index 407c24f6546a..acc072b85a68 100644
--- a/packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.ts
+++ b/packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.ts
@@ -1,33 +1,35 @@
-import { useServiceDetails } from '@ovh-ux/manager-react-components';
-import { useQuery } from '@tanstack/react-query';
+import { useServiceDetailsQueryOption } from '@ovh-ux/manager-module-common-api';
+import { useQuery, useQueryClient, queryOptions } from '@tanstack/react-query';
 import { getServiceConsumption } from '@/data/api/service-consumption';
 
-export default function useVeeamBackupConsumption(resourceName: string) {
-  const {
-    data: service,
-    isLoading: isLoadingService,
-    isPending: isPendingService,
-    isError: isErrorService,
-  } = useServiceDetails({
+export function useVeeamBackupConsumptionQueryOptions(resourceName: string) {
+  const queryClient = useQueryClient();
+  const serviceDetailsQueryOption = useServiceDetailsQueryOption({
     resourceName,
   });
-
-  const { data: consumptions, isError, isLoading, isPending } = useQuery({
-    queryKey: [
-      'services',
-      'consumption',
-      { serviceId: service?.data?.serviceId },
-    ],
-    queryFn: ({ signal }) =>
-      getServiceConsumption(service?.data?.serviceId, signal),
+  return queryOptions({
+    queryKey: ['services', 'consumption', resourceName],
+    queryFn: async ({ signal }) => {
+      const service = await queryClient.fetchQuery(serviceDetailsQueryOption);
+      return getServiceConsumption(service?.data?.serviceId, signal);
+    },
     retry: false,
-    enabled: !!service && !!resourceName,
+    enabled: !!resourceName,
   });
+}
+
+export default function useVeeamBackupConsumption(resourceName: string) {
+  const options = useVeeamBackupConsumptionQueryOptions(resourceName);
+
+  const { data: consumptions, isError, isLoading, isPending, error } = useQuery(
+    options,
+  );
 
   return {
+    error,
     data: consumptions?.data,
-    isError: isError || isErrorService,
-    isPending: isPending || isPendingService,
-    isLoading: isLoading || isLoadingService,
+    isError,
+    isPending,
+    isLoading,
   };
 }
diff --git a/packages/manager/apps/veeam-backup/src/pages/dashboard/ConsumedVms.component.tsx b/packages/manager/apps/veeam-backup/src/pages/dashboard/ConsumedVms.component.tsx
new file mode 100644
index 000000000000..1577351801e3
--- /dev/null
+++ b/packages/manager/apps/veeam-backup/src/pages/dashboard/ConsumedVms.component.tsx
@@ -0,0 +1,66 @@
+import { useTranslation } from 'react-i18next';
+import { AxiosError } from 'axios';
+
+import { VeeamBackup } from '@ovh-ux/manager-module-vcd-api';
+import { OdsBadge, OdsText, OdsSkeleton } from '@ovhcloud/ods-components/react';
+
+import { useQuery } from '@tanstack/react-query';
+
+import { useVeeamBackupConsumptionQueryOptions } from '@/data/hooks/useVeeamBackupConsumption';
+import { filterConsumedVms } from './helpers';
+
+export const ConsumedVms = ({
+  backup,
+  id,
+}: {
+  backup: VeeamBackup;
+  id: string;
+}): JSX.Element => {
+  const { t } = useTranslation('dashboard');
+
+  const optionsOfVeeamBackupComsumption = useVeeamBackupConsumptionQueryOptions(
+    id,
+  );
+
+  const { data: consumptions, isLoading, isError, error } = useQuery({
+    ...optionsOfVeeamBackupComsumption,
+    select: (response) => filterConsumedVms(response?.data ?? []),
+  });
+
+  if (backup?.currentState?.vms) {
+    return (
+      <div className="flex flex-col">
+        <OdsBadge
+          label={`VMs ${backup.currentState.vms}`}
+          color="information"
+        />
+        <OdsText>{t('consumed_vms_label')}</OdsText>
+      </div>
+    );
+  }
+  if (isLoading) {
+    return (
+      <>
+        <OdsSkeleton />
+        <OdsSkeleton />
+      </>
+    );
+  }
+  if (isError && (error as AxiosError)?.response?.status !== 404) {
+    return (
+      <OdsBadge
+        label="-"
+        data-testid="consumed-vms"
+        color="information"
+      ></OdsBadge>
+    );
+  }
+
+  return (
+    <OdsBadge
+      label={`${consumptions?.length || 0} VM(s)`}
+      data-testid="consumed-vms"
+      color="information"
+    ></OdsBadge>
+  );
+};
diff --git a/packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.constants.ts b/packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.constants.ts
index 49b186cf592c..20caa408e4ed 100644
--- a/packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.constants.ts
+++ b/packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.constants.ts
@@ -5,3 +5,5 @@ export const VEEAM_BACKUP_CONSUMPTION_PLAN_CODE = {
   SILVER: `${VEEAM_BACKUP_FAMILY_PLAN_CODE}-silver`,
   GOLD: `${VEEAM_BACKUP_FAMILY_PLAN_CODE}-gold`,
 };
+
+export const VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE = 'backup-veeam-vcd-vm';
diff --git a/packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.page.tsx b/packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.page.tsx
index fbb4b762ec9f..e0fafe390e02 100644
--- a/packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.page.tsx
+++ b/packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.page.tsx
@@ -26,6 +26,7 @@ import { urls } from '@/routes/routes.constant';
 import { SuccessMessages } from '@/components/Messages/SuccessMessage.component';
 import { OrganizationCell } from '../listing/DatagridCell.component';
 import { DisplayNameWithEditButton } from './DisplayName.component';
+import { ConsumedVms } from './ConsumedVms.component';
 import { OfferProgress } from './OfferProgress.component';
 import { SubscriptionTile } from './SubscriptionTile.component';
 import { ComingSoonBadge } from '@/components/ComingSoonBadge/ComingSoonBadge';
@@ -33,6 +34,7 @@ import { BillingLink } from '@/components/Links/BillingLink.component';
 import { Loading } from '@/components/Loading/Loading';
 import { BackupStatusBadge } from '@/components/BackupStatus/BackupStatusBadge.component';
 import useVeeamBackupConsumption from '@/data/hooks/useVeeamBackupConsumption';
+
 import { VEEAM_BACKUP_CONSUMPTION_PLAN_CODE } from '@/pages/dashboard/Dashboard.constants';
 import { CHANGELOG_LINKS } from '@/constants';
 
@@ -142,17 +144,7 @@ export default function DashboardPage() {
                 {
                   id: 'consumedVms',
                   label: t('consumed_vms'),
-                  value: data?.data?.currentState?.vms ? (
-                    <div className="flex flex-col">
-                      <OdsBadge
-                        className="mt-1"
-                        label={`${data.data.currentState.vms} VMs`}
-                      />
-                      <OdsText>{t('consumed_vms_label')}</OdsText>
-                    </div>
-                  ) : (
-                    <ComingSoonBadge />
-                  ),
+                  value: <ConsumedVms id={id} backup={data?.data} />,
                 },
                 ...(data?.data?.currentState?.offers?.map((offer) => ({
                   id: offer.name,
diff --git a/packages/manager/apps/veeam-backup/src/pages/dashboard/consumedVms.spec.tsx b/packages/manager/apps/veeam-backup/src/pages/dashboard/consumedVms.spec.tsx
new file mode 100644
index 000000000000..0ecb76f0296c
--- /dev/null
+++ b/packages/manager/apps/veeam-backup/src/pages/dashboard/consumedVms.spec.tsx
@@ -0,0 +1,174 @@
+import { vitest } from 'vitest';
+import { waitFor, screen, render } from '@testing-library/react';
+import { useQuery } from '@tanstack/react-query';
+
+import { ConsumedVms } from './ConsumedVms.component';
+import { VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE } from '@/pages/dashboard/Dashboard.constants';
+
+vitest.mock('@/data/hooks/useVeeamBackupConsumption', () => ({
+  useVeeamBackupConsumptionQueryOptions: vitest.fn(() => ({})),
+}));
+
+vitest.mock('@tanstack/react-query', () => ({
+  useQuery: vitest.fn(() => ({
+    data: [],
+    isLoading: false,
+    isError: false,
+    isPending: false,
+  })),
+}));
+describe('Consumed VMs display', () => {
+  beforeAll(() => {
+    vitest.resetAllMocks();
+  });
+
+  const testCases = [
+    {
+      contextLabel: 'displays 0 VMs when consumption data is empty',
+      mock: {
+        data: { data: [] },
+        isLoading: false,
+        isError: false,
+      },
+      expected: '0 VM(s)',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel:
+        'displays 0 VM(s) when consumption data does not have the correct plan code',
+      mock: {
+        data: {
+          data: [
+            { planCode: 'randomCode', uniqueId: 'id1', quantity: 0 },
+            { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+          ],
+        },
+        isLoading: false,
+        isError: false,
+      },
+      expected: '0 VM(s)',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel:
+        'displays 2 VM(s) when consumption data contains two items with the correct plan code',
+      mock: {
+        data: {
+          data: [
+            { planCode: 'randomCode1', uniqueId: 'id1', quantity: 0 },
+            { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+            {
+              planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+              uniqueId: 'id3',
+              quantity: 0,
+            },
+            {
+              planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+              uniqueId: 'id4',
+              quantity: 0,
+            },
+          ],
+        },
+        isLoading: false,
+        isError: false,
+      },
+      expected: '2 VM(s)',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel:
+        'displays 1 VM(s) when consumption data contains one items with the correct plan code',
+      mock: {
+        data: {
+          data: [
+            { planCode: 'randomCode1', uniqueId: 'id1', quantity: 0 },
+            { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+            {
+              planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+              uniqueId: 'id3',
+              quantity: 0,
+            },
+          ],
+        },
+        isLoading: false,
+        isError: false,
+      },
+      expected: '1 VM(s)',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel: 'displays "-" when the consumption query is loading',
+      mock: {
+        data: null as any,
+        isLoading: true,
+        isError: false,
+      },
+      expected: 2,
+    },
+    {
+      contextLabel: 'displays "-" when the consumption query has an error',
+      mock: {
+        data: null,
+        isLoading: false,
+        isError: true,
+      },
+      expected: '-',
+      expectedTestId: 'consumed-vms',
+    },
+
+    {
+      contextLabel:
+        'displays 0 Vm(s) when the return query is  error with code 404',
+      mock: {
+        data: null as unknown,
+        isLoading: false,
+        isError: true,
+        error: { response: { status: 404 } },
+      },
+      expected: '0 VM(s)',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel:
+        'displays "-" when the return query is error with code different than 404',
+      mock: {
+        data: null as unknown,
+        isLoading: false,
+        isError: true,
+        error: { response: { status: 503 } },
+      },
+      expected: '-',
+      expectedTestId: 'consumed-vms',
+    },
+  ];
+
+  testCases.forEach(({ contextLabel, mock, expected, expectedTestId }) => {
+    it(contextLabel, async () => {
+      vitest.mocked(useQuery).mockImplementation((options) => {
+        return {
+          ...mock,
+          data: mock.data ? options.select(mock.data) : null,
+        } as ReturnType<typeof useQuery>;
+      });
+
+      const { container } = render(
+        <ConsumedVms id="toto" backup={undefined} />,
+      );
+
+      if (expectedTestId) {
+        await waitFor(() => {
+          expect(screen.queryByTestId(expectedTestId)).toHaveAttribute(
+            'label',
+            expected,
+          );
+        });
+      } else {
+        await waitFor(() => {
+          expect(container.getElementsByTagName('ods-skeleton')).toHaveLength(
+            expected as number,
+          );
+        });
+      }
+    });
+  });
+});
diff --git a/packages/manager/apps/veeam-backup/src/pages/dashboard/helpers.spec.tsx b/packages/manager/apps/veeam-backup/src/pages/dashboard/helpers.spec.tsx
new file mode 100644
index 000000000000..dba5ed3c2663
--- /dev/null
+++ b/packages/manager/apps/veeam-backup/src/pages/dashboard/helpers.spec.tsx
@@ -0,0 +1,54 @@
+import { describe, it, expect } from 'vitest';
+import { filterConsumedVms } from './helpers';
+import { VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE } from '@/pages/dashboard/Dashboard.constants';
+import { TServiceConsumption } from '@/type/service-consumption.type';
+
+describe('filterConsumedVms', () => {
+  it('returns only VMs with the correct planCode', () => {
+    const consumptions: TServiceConsumption[] = [
+      { planCode: 'randomCode1', uniqueId: 'id1', quantity: 0 },
+      { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+      {
+        planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+        uniqueId: 'id3',
+        quantity: 0,
+      },
+      {
+        planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+        uniqueId: 'id4',
+        quantity: 0,
+      },
+    ];
+
+    const result = filterConsumedVms(consumptions);
+    expect(result).toHaveLength(2);
+    expect(result).toEqual([
+      {
+        planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+        uniqueId: 'id3',
+        quantity: 0,
+      },
+      {
+        planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+        uniqueId: 'id4',
+        quantity: 0,
+      },
+    ]);
+  });
+
+  it('returns an empty array when no VMs match the planCode', () => {
+    const consumptions: TServiceConsumption[] = [
+      { planCode: 'randomCode1', uniqueId: 'id1', quantity: 0 },
+      { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+    ];
+
+    const result = filterConsumedVms(consumptions);
+    expect(result).toHaveLength(0);
+  });
+
+  it('returns an empty array when input is empty', () => {
+    const consumptions: TServiceConsumption[] = [];
+    const result = filterConsumedVms(consumptions);
+    expect(result).toHaveLength(0);
+  });
+});
diff --git a/packages/manager/apps/veeam-backup/src/pages/dashboard/helpers.ts b/packages/manager/apps/veeam-backup/src/pages/dashboard/helpers.ts
new file mode 100644
index 000000000000..dc37c2b519af
--- /dev/null
+++ b/packages/manager/apps/veeam-backup/src/pages/dashboard/helpers.ts
@@ -0,0 +1,9 @@
+import { TServiceConsumption } from '@/type/service-consumption.type';
+import { VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE } from '@/pages/dashboard/Dashboard.constants';
+
+export const filterConsumedVms = (consumptions: TServiceConsumption[]) => {
+  return consumptions.filter(
+    (consumption) =>
+      consumption.planCode === VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+  );
+};


Reviews:
user: login: darsene
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T09:07:43Z
user: login: pauldkn
body: You have SonarCloud warning about unused import of `useMemo` and `useCallback`
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T10:18:20Z
user: login: tibs245
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T09:22:55Z
user: login: tibs245
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T09:26:07Z
user: login: darsene
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T09:29:49Z
user: login: pauldkn
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T09:42:42Z
user: login: tibs245
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T10:28:10Z
user: login: tibs245
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T10:32:22Z
user: login: tibs245
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:28:28Z
user: login: tibs245
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:31:10Z
user: login: darsene
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:42:25Z
user: login: darsene
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T15:40:12Z
user: login: darsene
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T08:58:00Z
user: login: tibs245
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T09:08:07Z
user: login: tibs245
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T16:18:22Z
user: login: darsene
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T16:20:22Z
user: login: pauldkn
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:08:12Z

Review comments:
path: packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.page.tsx
user: login: darsene
body: Can you add a unit/integration test for that case ?
created_at: 2025-03-07T09:07:37Z
author_association: CONTRIBUTOR
path: packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.page.tsx
user: login: pauldkn
body: You have SonarCloud warning about unused import of `useMemo` and `useCallback`
created_at: 2025-03-07T10:17:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,4 +79,62 @@ describe('dashboard', () => {
 
     expect(screen.getByText(labels.dashboard.terminated_service)).toBeVisible();
   });
+
+  it('displays 0 VMs when no consumption data is empty', async () => {
+    vi.mocked(useVeeamBackupConsumption).mockImplementation(() => ({
+      data: [],
+      isLoading: false,
+      isError: false,
+      isPending: false,
+    }));
+
+    await renderTest();
+    await goToDashboard(backupList[0].iam.displayName);
+    await waitFor(() => {
+      expect(screen.queryByTestId('consumed-vms')).toHaveTextContent('0 VMs');
+    });
+  });
+
+  it('displays 0 VMs when  consumption data not having  backup-veeam-vcd-vm plan Code', async () => {
+    vi.mocked(useVeeamBackupConsumption).mockImplementation(() => ({
+      data: [{ planCode: 'randomCode', uniqueId: 'id', quantity: 0 }],
+      isLoading: false,
+      isError: false,
+      isPending: false,
+    }));
+
+    await renderTest();
+    await goToDashboard(backupList[0].iam.displayName);
+    await waitFor(() => {
+      expect(screen.queryByTestId('consumed-vms')).toHaveTextContent('0 VMs');
+    });
+  });
+
+  it('displays 2 VMs when  consumption data not having  backup-veeam-vcd-vm plan Code', async () => {
+    vi.mocked(useVeeamBackupConsumption).mockImplementation(() => ({
+      data: [
+        { planCode: 'randomCode1', uniqueId: 'id1', quantity: 0 },
+        { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+        {
+          planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+          uniqueId: 'id3',
+          quantity: 0,
+        },
+        {
+          planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+          uniqueId: 'id4',
+          quantity: 0,
+        },
+      ],
+      isLoading: false,
+      isError: false,
+      isPending: false,
+    }));
+
+    await renderTest();
+    await goToDashboard(backupList[0].iam.displayName);
+    await waitFor(() => {
+      expect(screen.queryByTestId('consumed-vms')).toHaveTextContent('2 VMs');
+    });
+  });
path: packages/manager/apps/veeam-backup/src/pages/dashboard/dashboard.spec.tsx
user: login: darsene
body: **niptick** : can be grouped in a `it.each([{data:[], case:'your test case'}, expectedContent:'xx VMs'])('$case')`
created_at: 2025-03-11T09:16:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +57,14 @@ export default function DashboardPage() {
   const { t } = useTranslation('dashboard');
   const displayName = getVeeamBackupDisplayName(data?.data);
 
+  const consumedVms = useMemo(() => {
+    if (!Array.isArray(consumptions)) return null;
path: packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.page.tsx
user: login: tibs245
body: ```suggestion
    if (!Array.isArray(consumptions)) return '-';
```

May be better to show `- VMs` inst end `VMs` during loading of consumed data 
created_at: 2025-03-11T09:22:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,4 +79,62 @@ describe('dashboard', () => {
 
     expect(screen.getByText(labels.dashboard.terminated_service)).toBeVisible();
   });
+
+  it('displays 0 VMs when no consumption data is empty', async () => {
+    vi.mocked(useVeeamBackupConsumption).mockImplementation(() => ({
+      data: [],
+      isLoading: false,
+      isError: false,
+      isPending: false,
+    }));
+
+    await renderTest();
+    await goToDashboard(backupList[0].iam.displayName);
+    await waitFor(() => {
+      expect(screen.queryByTestId('consumed-vms')).toHaveTextContent('0 VMs');
+    });
+  });
+
+  it('displays 0 VMs when  consumption data not having  backup-veeam-vcd-vm plan Code', async () => {
+    vi.mocked(useVeeamBackupConsumption).mockImplementation(() => ({
+      data: [{ planCode: 'randomCode', uniqueId: 'id', quantity: 0 }],
+      isLoading: false,
+      isError: false,
+      isPending: false,
+    }));
+
+    await renderTest();
+    await goToDashboard(backupList[0].iam.displayName);
+    await waitFor(() => {
+      expect(screen.queryByTestId('consumed-vms')).toHaveTextContent('0 VMs');
+    });
+  });
+
+  it('displays 2 VMs when  consumption data not having  backup-veeam-vcd-vm plan Code', async () => {
+    vi.mocked(useVeeamBackupConsumption).mockImplementation(() => ({
+      data: [
+        { planCode: 'randomCode1', uniqueId: 'id1', quantity: 0 },
+        { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+        {
+          planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+          uniqueId: 'id3',
+          quantity: 0,
+        },
+        {
+          planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+          uniqueId: 'id4',
+          quantity: 0,
+        },
+      ],
+      isLoading: false,
+      isError: false,
+      isPending: false,
+    }));
+
+    await renderTest();
+    await goToDashboard(backupList[0].iam.displayName);
+    await waitFor(() => {
+      expect(screen.queryByTestId('consumed-vms')).toHaveTextContent('2 VMs');
+    });
+  });
path: packages/manager/apps/veeam-backup/src/pages/dashboard/dashboard.spec.tsx
user: login: tibs245
body: ```suggestion
const testCases = [
  {
    description: 'displays 0 VMs when no consumption data is empty',
    mockData: [],
    expectedText: '0 VMs',
  },
  {
    description: 'displays 0 VMs when consumption data not having backup-veeam-vcd-vm plan Code',
    mockData: [{ planCode: 'randomCode', uniqueId: 'id', quantity: 0 }],
    expectedText: '0 VMs',
  },
  {
    description: 'displays 2 VMs when consumption data having backup-veeam-vcd-vm plan Code',
    mockData: [
      { planCode: 'randomCode1', uniqueId: 'id1', quantity: 0 },
      { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
      {
        planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
        uniqueId: 'id3',
        quantity: 0,
      },
      {
        planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
        uniqueId: 'id4',
        quantity: 0,
      },
    ],
    expectedText: '2 VMs',
  },
];

describe('dashboard', () => {
  it('displays the dashboard page when clicking on the link', async () => {
    await renderTest();
    await goToDashboard(backupList[0].iam.displayName);
    expect(screen.getByText(labels.dashboard.terminated_service)).toBeVisible();
  });

  it.each(testCases)('$description', async ({ mockData, expectedText }) => {
    vi.mocked(useVeeamBackupConsumption).mockImplementation(() => ({
      data: mockData,
      isLoading: false,
      isError: false,
      isPending: false,
    }));

    await renderTest();
    await goToDashboard(backupList[0].iam.displayName);
    await waitFor(() => {
      expect(screen.queryByTestId('consumed-vms')).toHaveTextContent(expectedText);
    });
  });
});
```
created_at: 2025-03-11T09:26:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -164,7 +175,13 @@ export default function DashboardPage() {
                       <Description>{t('consumed_vms_label')}</Description>
                     </div>
                   ) : (
-                    <ComingSoonBadge />
+                    <OsdsChip
+                      data-testid="consumed-vms"
+                      color={ODS_THEME_COLOR_INTENT.primary}
+                      inline
+                    >
+                      {consumedVms} VMs
path: packages/manager/apps/veeam-backup/src/pages/dashboard/Dashboard.page.tsx
user: login: pauldkn
body: The singular form should be used when count is lower than 2 (`0 VM`, `1 VM`, `2 VMs` etc.)
created_at: 2025-03-11T09:42:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,33 +1,36 @@
-import { useServiceDetails } from '@ovh-ux/manager-react-components';
-import { useQuery } from '@tanstack/react-query';
+import { useServiceDetailsQueryOption } from '@ovh-ux/manager-module-common-api';
+import { useQuery, useQueryClient, queryOptions } from '@tanstack/react-query';
 import { getServiceConsumption } from '@/data/api/service-consumption';
 
-export default function useVeeamBackupConsumption(resourceName: string) {
-  const {
-    data: service,
-    isLoading: isLoadingService,
-    isPending: isPendingService,
-    isError: isErrorService,
-  } = useServiceDetails({
+export function useVeeamBackupConsumptionQueryOptions(resourceName: string) {
+  const queryClient = useQueryClient();
+  const serviceDetailsQueryOption = useServiceDetailsQueryOption({
     resourceName,
   });
+  return queryOptions({
+    queryKey: ['services', 'consumption', resourceName],
+    queryFn: async ({ signal }) => {
+      const service = await queryClient.fetchQuery(serviceDetailsQueryOption);
 
-  const { data: consumptions, isError, isLoading, isPending } = useQuery({
-    queryKey: [
-      'services',
-      'consumption',
-      { serviceId: service?.data?.serviceId },
-    ],
-    queryFn: ({ signal }) =>
-      getServiceConsumption(service?.data?.serviceId, signal),
+      return getServiceConsumption(service?.data?.serviceId, signal);
+    },
     retry: false,
-    enabled: !!service && !!resourceName,
+    enabled: !!resourceName,
   });
+}
+
+export default function useVeeamBackupConsumption(resourceName: string) {
+  const options = useVeeamBackupConsumptionQueryOptions(resourceName);
+
+  const { data: consumptions, isError, isLoading, isPending, error } = useQuery(
+    options,
+  );
 
   return {
+    error,
     data: consumptions?.data,
-    isError: isError || isErrorService,
-    isPending: isPending || isPendingService,
-    isLoading: isLoading || isLoadingService,
+    isError,
+    isPending,
+    isLoading,
   };
path: packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.ts
user: login: tibs245
body: I think you need update unit test about this hook
created_at: 2025-03-12T10:28:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+import React, { Fragment } from 'react';
+import { useTranslation } from 'react-i18next';
+import { AxiosError } from 'axios';
+
+import { VeeamBackup } from '@ovh-ux/manager-module-vcd-api';
+import { OsdsChip } from '@ovhcloud/ods-components/react';
+
+import { Description } from '@ovh-ux/manager-react-components';
+import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
+import { useQuery } from '@tanstack/react-query';
+
+import { useVeeamBackupConsumptionQueryOptions } from '@/data/hooks/useVeeamBackupConsumption';
+import { filterConsumedVms } from './helpers';
+
+export const ConsumedVms = ({
+  backup,
+  id,
+}: {
+  backup: VeeamBackup;
+  id: string;
+}): JSX.Element => {
+  const { t } = useTranslation('dashboard');
+
+  const optionsOfVeeamBackupComsumption = useVeeamBackupConsumptionQueryOptions(
+    id,
+  );
+
+  const { data: consumptions, isLoading, isError, error } = useQuery({
+    ...optionsOfVeeamBackupComsumption,
+    select: (response) => filterConsumedVms(response?.data ?? []),
+  });
+
+  return (
+    <Fragment>
+      {backup?.currentState?.vms ? (
+        <div className="flex flex-col">
+          <OsdsChip color={ODS_THEME_COLOR_INTENT.primary} inline>
+            {backup.currentState.vms} VMs
+          </OsdsChip>
+          <Description>{t('consumed_vms_label')}</Description>
+        </div>
+      ) : (
+        <OsdsChip
+          data-testid="consumed-vms"
+          color={ODS_THEME_COLOR_INTENT.primary}
+          inline
+        >
+          {isLoading ||
+          (isError && (error as AxiosError)?.response?.status !== 404)
+            ? '-'
+            : `${consumptions?.length ?? 0} ${
+                consumptions?.length === 1 ? 'VM' : 'VMs'
+              }`}
+        </OsdsChip>
+      )}
+    </Fragment>
+  );
path: packages/manager/apps/veeam-backup/src/pages/dashboard/ConsumedVms.component.tsx
user: login: tibs245
body: ```suggestion
  if (backup?.currentState?.vms) {
  return (
        <div className="flex flex-col">
          <OsdsChip color={ODS_THEME_COLOR_INTENT.primary} inline>
            {backup.currentState.vms} VMs
          </OsdsChip>
          <Description>{t('consumed_vms_label')}</Description>
        </div>
      )
  }
  
  return (
        <OsdsChip
          data-testid="consumed-vms"
          color={ODS_THEME_COLOR_INTENT.primary}
          inline
        >
          {isLoading ||
          (isError && (error as AxiosError)?.response?.status !== 404)
            ? '-'
            : `${consumptions?.length ?? 0} ${
                consumptions?.length <= 1 ? 'VM' : 'VMs'
              }`}
        </OsdsChip>
      );
```
created_at: 2025-03-12T10:32:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,18 +50,29 @@ describe('useVeeamBackupConsumption tests suite', () => {
       () => useVeeamBackupConsumption(resourceName),
       {
         wrapper: ({ children }) => (
-          <QueryClientProvider client={new QueryClient()}>
+          <QueryClientProvider client={queryClient}>
             {children}
           </QueryClientProvider>
         ),
       },
     );
 
-    expect(vi.mocked(useServiceDetails)).toHaveBeenCalledWith({ resourceName });
-    expect(vi.mocked(getServiceConsumption)).toHaveBeenCalledWith(
-      serviceId,
-      expect.any(AbortSignal),
+    expect(vi.mocked(useServiceDetailsQueryOption)).toHaveBeenCalledWith({
+      resourceName,
+    });
+
+    expect(QueryClient.prototype.fetchQuery).toHaveBeenCalledWith(
+      expect.objectContaining({
+        queryKey: ['services', 'details', resourceName],
+      }),
     );
+    // We need to wait
path: packages/manager/apps/veeam-backup/src/data/hooks/useVeeamBackupConsumption.spec.tsx
user: login: tibs245
body: Can delete this comment line ?
created_at: 2025-03-12T13:28:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+import React, { Fragment } from 'react';
+import { useTranslation } from 'react-i18next';
+import { AxiosError } from 'axios';
+
+import { VeeamBackup } from '@ovh-ux/manager-module-vcd-api';
+import { OsdsChip } from '@ovhcloud/ods-components/react';
+
+import { Description } from '@ovh-ux/manager-react-components';
+import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
+import { useQuery } from '@tanstack/react-query';
+
+import { useVeeamBackupConsumptionQueryOptions } from '@/data/hooks/useVeeamBackupConsumption';
+import { filterConsumedVms } from './helpers';
+
+export const ConsumedVms = ({
+  backup,
+  id,
+}: {
+  backup: VeeamBackup;
+  id: string;
+}): JSX.Element => {
+  const { t } = useTranslation('dashboard');
+
+  const optionsOfVeeamBackupComsumption = useVeeamBackupConsumptionQueryOptions(
+    id,
+  );
+
+  const { data: consumptions, isLoading, isError, error } = useQuery({
+    ...optionsOfVeeamBackupComsumption,
+    select: (response) => filterConsumedVms(response?.data ?? []),
+  });
+
+  return (
+    <Fragment>
+      {backup?.currentState?.vms ? (
+        <div className="flex flex-col">
+          <OsdsChip color={ODS_THEME_COLOR_INTENT.primary} inline>
+            {backup.currentState.vms} VMs
+          </OsdsChip>
+          <Description>{t('consumed_vms_label')}</Description>
+        </div>
+      ) : (
+        <OsdsChip
+          data-testid="consumed-vms"
+          color={ODS_THEME_COLOR_INTENT.primary}
+          inline
+        >
+          {isLoading ||
+          (isError && (error as AxiosError)?.response?.status !== 404)
+            ? '-'
+            : `${consumptions?.length ?? 0} ${
+                consumptions?.length === 1 ? 'VM' : 'VMs'
path: packages/manager/apps/veeam-backup/src/pages/dashboard/ConsumedVms.component.tsx
user: login: tibs245
body: ```suggestion
                consumptions?.length <= 1 ? 'VM' : 'VMs'
```
created_at: 2025-03-12T13:31:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+import React, { Fragment } from 'react';
+import { useTranslation } from 'react-i18next';
+import { AxiosError } from 'axios';
+
+import { VeeamBackup } from '@ovh-ux/manager-module-vcd-api';
+import { OsdsChip } from '@ovhcloud/ods-components/react';
+
+import { Description } from '@ovh-ux/manager-react-components';
+import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
+import { useQuery } from '@tanstack/react-query';
+
+import { useVeeamBackupConsumptionQueryOptions } from '@/data/hooks/useVeeamBackupConsumption';
+import { filterConsumedVms } from './helpers';
+
+export const ConsumedVms = ({
+  backup,
+  id,
+}: {
+  backup: VeeamBackup;
+  id: string;
+}): JSX.Element => {
+  const { t } = useTranslation('dashboard');
+
+  const optionsOfVeeamBackupComsumption = useVeeamBackupConsumptionQueryOptions(
+    id,
+  );
+
+  const { data: consumptions, isLoading, isError, error } = useQuery({
+    ...optionsOfVeeamBackupComsumption,
+    select: (response) => filterConsumedVms(response?.data ?? []),
+  });
+
+  return (
+    <Fragment>
+      {backup?.currentState?.vms ? (
+        <div className="flex flex-col">
+          <OsdsChip color={ODS_THEME_COLOR_INTENT.primary} inline>
+            {backup.currentState.vms} VMs
+          </OsdsChip>
+          <Description>{t('consumed_vms_label')}</Description>
+        </div>
+      ) : (
+        <OsdsChip
+          data-testid="consumed-vms"
+          color={ODS_THEME_COLOR_INTENT.primary}
+          inline
+        >
+          {isLoading ||
+          (isError && (error as AxiosError)?.response?.status !== 404)
+            ? '-'
+            : `${consumptions?.length ?? 0} ${
+                consumptions?.length === 1 ? 'VM' : 'VMs'
path: packages/manager/apps/veeam-backup/src/pages/dashboard/ConsumedVms.component.tsx
user: login: darsene
body: Moreover, you have a sonar issue : [Extract this nested ternary operation into an independent statement.](https://sonarcloud.io/project/issues?pullRequest=15853&open=AZWJ4-SZowtWkZsWlnc7&id=ovh_manager)
created_at: 2025-03-12T13:42:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,63 @@
+import React, { Fragment } from 'react';
+import { useTranslation } from 'react-i18next';
+import { AxiosError } from 'axios';
+
+import { VeeamBackup } from '@ovh-ux/manager-module-vcd-api';
+import { OsdsChip } from '@ovhcloud/ods-components/react';
+
+import { Description } from '@ovh-ux/manager-react-components';
+import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
+import { useQuery } from '@tanstack/react-query';
+
+import { useVeeamBackupConsumptionQueryOptions } from '@/data/hooks/useVeeamBackupConsumption';
+import { filterConsumedVms } from './helpers';
+
+export const ConsumedVms = ({
+  backup,
+  id,
+}: {
+  backup: VeeamBackup;
+  id: string;
+}): JSX.Element => {
+  const { t } = useTranslation('dashboard');
+
+  const optionsOfVeeamBackupComsumption = useVeeamBackupConsumptionQueryOptions(
+    id,
+  );
+
+  const { data: consumptions, isLoading, isError, error } = useQuery({
+    ...optionsOfVeeamBackupComsumption,
+    select: (response) => filterConsumedVms(response?.data ?? []),
+  });
+
+  return (
+    <Fragment>
+      {backup?.currentState?.vms ? (
+        <div className="flex flex-col">
+          <OsdsChip color={ODS_THEME_COLOR_INTENT.primary} inline>
+            {backup.currentState.vms} VMs
+          </OsdsChip>
+          <Description>{t('consumed_vms_label')}</Description>
+        </div>
+      ) : (
+        <OsdsChip
+          data-testid="consumed-vms"
+          color={ODS_THEME_COLOR_INTENT.primary}
+          inline
+        >
+          {(() => {
+            if (
+              isLoading ||
+              (isError && (error as AxiosError)?.response?.status !== 404)
+            ) {
+              return '-';
+            }
+
+            const count = consumptions?.length ?? 0;
+            return `${count} ${count === 1 ? 'VM' : 'VMs'}`;
path: packages/manager/apps/veeam-backup/src/pages/dashboard/ConsumedVms.component.tsx
user: login: darsene
body: Are you sure plural rules are the same for all the languages ? 
Should be done in translation file in my opinion
created_at: 2025-03-12T15:34:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,164 @@
+import { vitest } from 'vitest';
+import { waitFor, screen, render } from '@testing-library/react';
+import { useQuery } from '@tanstack/react-query';
+
+import { ConsumedVms } from './ConsumedVms.component';
+import { VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE } from '@/pages/dashboard/Dashboard.constants';
+
+vitest.mock('@/data/hooks/useVeeamBackupConsumption', () => ({
+  useVeeamBackupConsumptionQueryOptions: vitest.fn(() => ({})),
+}));
+
+vitest.mock('@tanstack/react-query', () => ({
+  useQuery: vitest.fn(() => ({
+    data: [],
+    isLoading: false,
+    isError: false,
+    isPending: false,
+  })),
+}));
+describe('Consumed VMs display', () => {
+  beforeAll(() => {
+    vitest.resetAllMocks();
+  });
+
+  const testCases = [
+    {
+      contextLabel: 'displays 0 VMs when consumption data is empty',
+      mock: {
+        data: { data: [] },
+        isLoading: false,
+        isError: false,
+      },
+      expected: '0 VMs',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel:
+        'displays 0 VMs when consumption data does not have the correct plan code',
+      mock: {
+        data: {
+          data: [
+            { planCode: 'randomCode', uniqueId: 'id1', quantity: 0 },
+            { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+          ],
+        },
+        isLoading: false,
+        isError: false,
+      },
+      expected: '0 VMs',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel:
+        'displays 2 VMs when consumption data contains two items with the correct plan code',
+      mock: {
+        data: {
+          data: [
+            { planCode: 'randomCode1', uniqueId: 'id1', quantity: 0 },
+            { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+            {
+              planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+              uniqueId: 'id3',
+              quantity: 0,
+            },
+            {
+              planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+              uniqueId: 'id4',
+              quantity: 0,
+            },
+          ],
+        },
+        isLoading: false,
+        isError: false,
+      },
+      expected: '2 VMs',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel:
+        'displays 1 VM when consumption data contains one items with the correct plan code',
+      mock: {
+        data: {
+          data: [
+            { planCode: 'randomCode1', uniqueId: 'id1', quantity: 0 },
+            { planCode: 'randomCode2', uniqueId: 'id2', quantity: 0 },
+            {
+              planCode: VEEAM_BACKUP_CONSUMPTION_VCD_VM_CODE,
+              uniqueId: 'id3',
+              quantity: 0,
+            },
+          ],
+        },
+        isLoading: false,
+        isError: false,
+      },
+      expected: '1 VM',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel: 'displays "-" when the consumption query is loading',
+      mock: {
+        data: null,
+        isLoading: true,
+        isError: false,
+      },
+      expected: '-',
+      expectedTestId: 'consumed-vms',
+    },
+    {
+      contextLabel: 'displays "-" when the consumption query has an error',
+      mock: {
+        data: null,
+        isLoading: false,
+        isError: true,
+      },
+      expected: '-',
+      expectedTestId: 'consumed-vms',
+    },
+
+    {
+      contextLabel:
+        'displays 0 Vms when the return query is  error with code 404',
+      mock: {
+        data: null as unknown,
+        isLoading: false,
+        isError: true,
+        error: { response: { status: 404 } },
+      },
+      expected: '0 VM',
path: packages/manager/apps/veeam-backup/src/pages/dashboard/consumedVms.spec.tsx
user: login: darsene
body: Your test is not enough accurate as it should fail. Current label is `0 VMs`
created_at: 2025-03-12T15:38:05Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-03-21T16:09:52Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=ovh_manager&pullRequest=15853) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=ovh_manager&pullRequest=15853&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=ovh_manager&pullRequest=15853&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=ovh_manager&pullRequest=15853&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=ovh_manager&pullRequest=15853&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=ovh_manager&pullRequest=15853&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=ovh_manager&pullRequest=15853)


author_association: NONE