diff --git a/frontend/src/container/InfraMonitoringHosts/__tests__/HostsEmptyOrIncorrectMetrics.test.tsx b/frontend/src/container/InfraMonitoringHosts/__tests__/HostsEmptyOrIncorrectMetrics.test.tsx new file mode 100644 index 000000000000..0b2aa28531d0 --- /dev/null +++ b/frontend/src/container/InfraMonitoringHosts/__tests__/HostsEmptyOrIncorrectMetrics.test.tsx @@ -0,0 +1,43 @@ +import { render, screen } from '@testing-library/react'; + +import HostsEmptyOrIncorrectMetrics from '../HostsEmptyOrIncorrectMetrics'; + +describe('HostsEmptyOrIncorrectMetrics', () => { + it('shows no data message when noData is true', () => { + render(); + expect( + screen.getByText('No host metrics data received yet.'), + ).toBeInTheDocument(); + expect( + screen.getByText(/Infrastructure monitoring requires the/), + ).toBeInTheDocument(); + }); + + it('shows incorrect data message when incorrectData is true', () => { + render(); + expect( + screen.getByText( + 'To see host metrics, upgrade to the latest version of SigNoz k8s-infra chart. Please contact support if you need help.', + ), + ).toBeInTheDocument(); + }); + + it('does not show no data message when noData is false', () => { + render(); + expect( + screen.queryByText('No host metrics data received yet.'), + ).not.toBeInTheDocument(); + expect( + screen.queryByText(/Infrastructure monitoring requires the/), + ).not.toBeInTheDocument(); + }); + + it('does not show incorrect data message when incorrectData is false', () => { + render(); + expect( + screen.queryByText( + 'To see host metrics, upgrade to the latest version of SigNoz k8s-infra chart. Please contact support if you need help.', + ), + ).not.toBeInTheDocument(); + }); +}); diff --git a/frontend/src/container/InfraMonitoringHosts/__tests__/HostsList.test.tsx b/frontend/src/container/InfraMonitoringHosts/__tests__/HostsList.test.tsx new file mode 100644 index 000000000000..270b2a8cd660 --- /dev/null +++ b/frontend/src/container/InfraMonitoringHosts/__tests__/HostsList.test.tsx @@ -0,0 +1,166 @@ +/* eslint-disable react/button-has-type */ +import { render } from '@testing-library/react'; +import ROUTES from 'constants/routes'; +import * as useGetHostListHooks from 'hooks/infraMonitoring/useGetHostList'; +import * as appContextHooks from 'providers/App/App'; +import * as timezoneHooks from 'providers/Timezone'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; +import store from 'store'; +import { LicenseEvent } from 'types/api/licensesV3/getActive'; + +import HostsList from '../HostsList'; + +jest.mock('lib/getMinMax', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => ({ + minTime: 1713734400000, + maxTime: 1713738000000, + isValidTimeFormat: jest.fn().mockReturnValue(true), + })), +})); +jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({ + __esModule: true, + default: ({ onSelect, selectedTime, selectedValue }: any): JSX.Element => ( +
+ +
+ ), +})); + +const queryClient = new QueryClient(); + +jest.mock('uplot', () => { + const paths = { + spline: jest.fn(), + bars: jest.fn(), + }; + const uplotMock = jest.fn(() => ({ + paths, + })); + return { + paths, + default: uplotMock, + }; +}); +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useSelector: (): any => ({ + globalTime: { + selectedTime: { + startTime: 1713734400000, + endTime: 1713738000000, + }, + maxTime: 1713738000000, + minTime: 1713734400000, + }, + }), +})); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn().mockReturnValue({ + pathname: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS, + }), +})); +jest.mock('react-router-dom-v5-compat', () => { + const actual = jest.requireActual('react-router-dom-v5-compat'); + return { + ...actual, + useSearchParams: jest + .fn() + .mockReturnValue([ + { get: jest.fn(), entries: jest.fn().mockReturnValue([]) }, + jest.fn(), + ]), + useNavigationType: (): any => 'PUSH', + }; +}); +jest.mock('hooks/useSafeNavigate', () => ({ + useSafeNavigate: (): any => ({ + safeNavigate: jest.fn(), + }), +})); + +jest.spyOn(timezoneHooks, 'useTimezone').mockReturnValue({ + timezone: { + offset: 0, + }, + browserTimezone: { + offset: 0, + }, +} as any); +jest.spyOn(useGetHostListHooks, 'useGetHostList').mockReturnValue({ + data: { + payload: { + data: { + records: [ + { + hostName: 'test-host', + active: true, + cpu: 0.75, + memory: 0.65, + wait: 0.03, + }, + ], + isSendingK8SAgentMetrics: false, + sentAnyHostMetricsData: true, + }, + }, + }, + isLoading: false, + isError: false, +} as any); +jest.spyOn(appContextHooks, 'useAppContext').mockReturnValue({ + user: { + role: 'admin', + }, + activeLicenseV3: { + event_queue: { + created_at: '0', + event: LicenseEvent.NO_EVENT, + scheduled_at: '0', + status: '', + updated_at: '0', + }, + license: { + license_key: 'test-license-key', + license_type: 'trial', + org_id: 'test-org-id', + plan_id: 'test-plan-id', + plan_name: 'test-plan-name', + plan_type: 'trial', + plan_version: 'test-plan-version', + }, + }, +} as any); + +describe('HostsList', () => { + it('renders hosts list table', () => { + const { container } = render( + + + + + + + , + ); + expect(container.querySelector('.hosts-list-table')).toBeInTheDocument(); + }); + + it('renders filters', () => { + const { container } = render( + + + + + + + , + ); + expect(container.querySelector('.filters')).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/container/InfraMonitoringHosts/__tests__/HostsListControl.test.tsx b/frontend/src/container/InfraMonitoringHosts/__tests__/HostsListControl.test.tsx new file mode 100644 index 000000000000..d5bad4c81f8f --- /dev/null +++ b/frontend/src/container/InfraMonitoringHosts/__tests__/HostsListControl.test.tsx @@ -0,0 +1,37 @@ +import { render, screen } from '@testing-library/react'; + +import HostsListControls from '../HostsListControls'; + +jest.mock('container/QueryBuilder/filters/QueryBuilderSearch', () => ({ + __esModule: true, + default: (): JSX.Element => ( +
Search
+ ), +})); + +jest.mock('container/TopNav/DateTimeSelectionV2', () => ({ + __esModule: true, + default: (): JSX.Element => ( +
Date Time
+ ), +})); + +describe('HostsListControls', () => { + const mockHandleFiltersChange = jest.fn(); + const mockFilters = { + items: [], + op: 'AND', + }; + + it('renders search and date time filters', () => { + render( + , + ); + + expect(screen.getByTestId('query-builder-search')).toBeInTheDocument(); + expect(screen.getByTestId('date-time-selection')).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/container/InfraMonitoringHosts/__tests__/HostsListTable.test.tsx b/frontend/src/container/InfraMonitoringHosts/__tests__/HostsListTable.test.tsx new file mode 100644 index 000000000000..1f71b947c51a --- /dev/null +++ b/frontend/src/container/InfraMonitoringHosts/__tests__/HostsListTable.test.tsx @@ -0,0 +1,139 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import { render, screen } from '@testing-library/react'; + +import HostsListTable from '../HostsListTable'; + +jest.mock('uplot', () => { + const paths = { + spline: jest.fn(), + bars: jest.fn(), + }; + const uplotMock = jest.fn(() => ({ + paths, + })); + return { + paths, + default: uplotMock, + }; +}); + +const EMPTY_STATE_CONTAINER_CLASS = '.hosts-empty-state-container'; + +describe('HostsListTable', () => { + const mockHost = { + hostName: 'test-host-1', + active: true, + cpu: 0.75, + memory: 0.65, + wait: 0.03, + load15: 1.5, + os: 'linux', + }; + + const mockTableData = { + payload: { + data: { + hosts: [mockHost], + }, + }, + }; + const mockOnHostClick = jest.fn(); + const mockSetCurrentPage = jest.fn(); + const mockSetOrderBy = jest.fn(); + const mockSetPageSize = jest.fn(); + const mockProps = { + isLoading: false, + isError: false, + isFetching: false, + tableData: mockTableData, + hostMetricsData: [mockHost], + filters: { + items: [], + op: 'AND', + }, + onHostClick: mockOnHostClick, + currentPage: 1, + setCurrentPage: mockSetCurrentPage, + pageSize: 10, + setOrderBy: mockSetOrderBy, + setPageSize: mockSetPageSize, + } as any; + + it('renders loading state if isLoading is true', () => { + const { container } = render(); + expect(container.querySelector('.hosts-list-loading-state')).toBeTruthy(); + }); + + it('renders loading state if isFetching is true', () => { + const { container } = render(); + expect(container.querySelector('.hosts-list-loading-state')).toBeTruthy(); + }); + + it('renders error state if isError is true', () => { + render(); + expect(screen.getByText('Something went wrong')).toBeTruthy(); + }); + + it('renders empty state if no hosts are found', () => { + const { container } = render(); + expect(container.querySelector(EMPTY_STATE_CONTAINER_CLASS)).toBeTruthy(); + }); + + it('renders empty state if sentAnyHostMetricsData is false', () => { + const { container } = render( + , + ); + expect(container.querySelector(EMPTY_STATE_CONTAINER_CLASS)).toBeTruthy(); + }); + + it('renders empty state if isSendingIncorrectK8SAgentMetrics is true', () => { + const { container } = render( + , + ); + expect(container.querySelector(EMPTY_STATE_CONTAINER_CLASS)).toBeTruthy(); + }); + + it('renders table data', () => { + const { container } = render( + , + ); + expect(container.querySelector('.hosts-list-table')).toBeTruthy(); + }); +}); diff --git a/frontend/src/container/InfraMonitoringHosts/__tests__/utilts.test.tsx b/frontend/src/container/InfraMonitoringHosts/__tests__/utilts.test.tsx new file mode 100644 index 000000000000..a38681f37256 --- /dev/null +++ b/frontend/src/container/InfraMonitoringHosts/__tests__/utilts.test.tsx @@ -0,0 +1,104 @@ +import { render } from '@testing-library/react'; + +import { formatDataForTable, GetHostsQuickFiltersConfig } from '../utils'; + +const PROGRESS_BAR_CLASS = '.progress-bar'; + +jest.mock('uplot', () => { + const paths = { + spline: jest.fn(), + bars: jest.fn(), + }; + const uplotMock = jest.fn(() => ({ + paths, + })); + return { + paths, + default: uplotMock, + }; +}); + +describe('InfraMonitoringHosts utils', () => { + describe('formatDataForTable', () => { + it('should format host data correctly', () => { + const mockData = [ + { + hostName: 'test-host', + active: true, + cpu: 0.95, + memory: 0.85, + wait: 0.05, + load15: 2.5, + os: 'linux', + }, + ] as any; + + const result = formatDataForTable(mockData); + + expect(result[0].hostName).toBe('test-host'); + expect(result[0].wait).toBe('5%'); + expect(result[0].load15).toBe(2.5); + + // Test active tag rendering + const activeTag = render(result[0].active as JSX.Element); + expect(activeTag.container.textContent).toBe('ACTIVE'); + expect(activeTag.container.querySelector('.active')).toBeTruthy(); + + // Test CPU progress bar + const cpuProgress = render(result[0].cpu as JSX.Element); + const cpuProgressBar = cpuProgress.container.querySelector( + PROGRESS_BAR_CLASS, + ); + expect(cpuProgressBar).toBeTruthy(); + + // Test memory progress bar + const memoryProgress = render(result[0].memory as JSX.Element); + const memoryProgressBar = memoryProgress.container.querySelector( + PROGRESS_BAR_CLASS, + ); + expect(memoryProgressBar).toBeTruthy(); + }); + + it('should handle inactive hosts', () => { + const mockData = [ + { + hostName: 'test-host', + active: false, + cpu: 0.3, + memory: 0.4, + wait: 0.02, + load15: 1.2, + os: 'linux', + cpuTimeSeries: [], + memoryTimeSeries: [], + waitTimeSeries: [], + load15TimeSeries: [], + }, + ] as any; + + const result = formatDataForTable(mockData); + + const inactiveTag = render(result[0].active as JSX.Element); + expect(inactiveTag.container.textContent).toBe('INACTIVE'); + expect(inactiveTag.container.querySelector('.inactive')).toBeTruthy(); + }); + }); + + describe('GetHostsQuickFiltersConfig', () => { + it('should return correct config when dotMetricsEnabled is true', () => { + const result = GetHostsQuickFiltersConfig(true); + + expect(result[0].attributeKey.key).toBe('host.name'); + expect(result[1].attributeKey.key).toBe('os.type'); + expect(result[0].aggregateAttribute).toBe('system.cpu.load_average.15m'); + }); + + it('should return correct config when dotMetricsEnabled is false', () => { + const result = GetHostsQuickFiltersConfig(false); + + expect(result[0].attributeKey.key).toBe('host_name'); + expect(result[1].attributeKey.key).toBe('os_type'); + expect(result[0].aggregateAttribute).toBe('system_cpu_load_average_15m'); + }); + }); +});