mirror of
https://github.com/grafana/grafana.git
synced 2025-09-20 15:42:53 +08:00

Co-authored-by: Jean-Philippe Quéméner <JohnnyQQQQ@users.noreply.github.com> Co-authored-by: gotjosh <josue.abreu@gmail.com> Co-authored-by: brendamuir <100768211+brendamuir@users.noreply.github.com>
417 lines
12 KiB
TypeScript
417 lines
12 KiB
TypeScript
import { renderHook } from '@testing-library/react-hooks';
|
|
import React from 'react';
|
|
import * as reactRedux from 'react-redux';
|
|
|
|
import { DataSourceJsonData, DataSourceSettings } from '@grafana/data';
|
|
import { config } from '@grafana/runtime';
|
|
import { AlertmanagerChoice, AlertManagerDataSourceJsonData } from 'app/plugins/datasource/alertmanager/types';
|
|
|
|
import { mockDataSource, mockDataSourcesStore, mockStore } from '../mocks';
|
|
|
|
import { useExternalAmSelector, useExternalDataSourceAlertmanagers } from './useExternalAmSelector';
|
|
|
|
const useSelectorMock = jest.spyOn(reactRedux, 'useSelector');
|
|
|
|
describe('useExternalAmSelector', () => {
|
|
beforeEach(() => {
|
|
useSelectorMock.mockClear();
|
|
});
|
|
it('should have one in pending', () => {
|
|
useSelectorMock.mockImplementation((callback) => {
|
|
return callback(createMockStoreState([], [], ['some/url/to/am']));
|
|
});
|
|
const alertmanagers = useExternalAmSelector();
|
|
|
|
expect(alertmanagers).toEqual([
|
|
{
|
|
url: 'some/url/to/am',
|
|
status: 'pending',
|
|
actualUrl: '',
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('should have one active, one pending', () => {
|
|
useSelectorMock.mockImplementation((callback) => {
|
|
return callback(
|
|
createMockStoreState([{ url: 'some/url/to/am/api/v2/alerts' }], [], ['some/url/to/am', 'some/url/to/am1'])
|
|
);
|
|
});
|
|
|
|
const alertmanagers = useExternalAmSelector();
|
|
|
|
expect(alertmanagers).toEqual([
|
|
{
|
|
url: 'some/url/to/am',
|
|
actualUrl: 'some/url/to/am/api/v2/alerts',
|
|
status: 'active',
|
|
},
|
|
{
|
|
url: 'some/url/to/am1',
|
|
actualUrl: '',
|
|
status: 'pending',
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('should have two active', () => {
|
|
useSelectorMock.mockImplementation((callback) => {
|
|
return callback(
|
|
createMockStoreState(
|
|
[{ url: 'some/url/to/am/api/v2/alerts' }, { url: 'some/url/to/am1/api/v2/alerts' }],
|
|
[],
|
|
['some/url/to/am', 'some/url/to/am1']
|
|
)
|
|
);
|
|
});
|
|
|
|
const alertmanagers = useExternalAmSelector();
|
|
|
|
expect(alertmanagers).toEqual([
|
|
{
|
|
url: 'some/url/to/am',
|
|
actualUrl: 'some/url/to/am/api/v2/alerts',
|
|
status: 'active',
|
|
},
|
|
{
|
|
url: 'some/url/to/am1',
|
|
actualUrl: 'some/url/to/am1/api/v2/alerts',
|
|
status: 'active',
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('should have one active, one dropped, one pending', () => {
|
|
useSelectorMock.mockImplementation((callback) => {
|
|
return callback(
|
|
createMockStoreState(
|
|
[{ url: 'some/url/to/am/api/v2/alerts' }],
|
|
[{ url: 'some/dropped/url/api/v2/alerts' }],
|
|
['some/url/to/am', 'some/url/to/am1']
|
|
)
|
|
);
|
|
});
|
|
|
|
const alertmanagers = useExternalAmSelector();
|
|
|
|
expect(alertmanagers).toEqual([
|
|
{
|
|
url: 'some/url/to/am',
|
|
actualUrl: 'some/url/to/am/api/v2/alerts',
|
|
status: 'active',
|
|
},
|
|
{
|
|
url: 'some/url/to/am1',
|
|
actualUrl: '',
|
|
status: 'pending',
|
|
},
|
|
{
|
|
url: 'some/dropped/url',
|
|
actualUrl: 'some/dropped/url/api/v2/alerts',
|
|
status: 'dropped',
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('The number of alert managers should match config entries when there are multiple entries of the same url', () => {
|
|
useSelectorMock.mockImplementation((callback) => {
|
|
return callback(
|
|
createMockStoreState(
|
|
[
|
|
{ url: 'same/url/to/am/api/v2/alerts' },
|
|
{ url: 'same/url/to/am/api/v2/alerts' },
|
|
{ url: 'same/url/to/am/api/v2/alerts' },
|
|
],
|
|
[],
|
|
['same/url/to/am', 'same/url/to/am', 'same/url/to/am']
|
|
)
|
|
);
|
|
});
|
|
|
|
const alertmanagers = useExternalAmSelector();
|
|
|
|
expect(alertmanagers.length).toBe(3);
|
|
expect(alertmanagers).toEqual([
|
|
{
|
|
url: 'same/url/to/am',
|
|
actualUrl: 'same/url/to/am/api/v2/alerts',
|
|
status: 'active',
|
|
},
|
|
{
|
|
url: 'same/url/to/am',
|
|
actualUrl: 'same/url/to/am/api/v2/alerts',
|
|
status: 'active',
|
|
},
|
|
{
|
|
url: 'same/url/to/am',
|
|
actualUrl: 'same/url/to/am/api/v2/alerts',
|
|
status: 'active',
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('useExternalDataSourceAlertmanagers', () => {
|
|
beforeEach(() => {
|
|
useSelectorMock.mockRestore();
|
|
});
|
|
|
|
it('Should merge data sources information from config and api responses', () => {
|
|
// Arrange
|
|
const { dsSettings, dsInstanceSettings } = setupAlertmanagerDataSource({ url: 'http://grafana.com' });
|
|
|
|
config.datasources = {
|
|
'External Alertmanager': dsInstanceSettings,
|
|
};
|
|
|
|
const store = mockDataSourcesStore({
|
|
dataSources: [dsSettings],
|
|
});
|
|
|
|
const wrapper: React.FC = ({ children }) => <reactRedux.Provider store={store}>{children}</reactRedux.Provider>;
|
|
|
|
// Act
|
|
const {
|
|
result: { current },
|
|
} = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper });
|
|
|
|
// Assert
|
|
expect(current).toHaveLength(1);
|
|
expect(current[0].dataSource.uid).toBe('1');
|
|
expect(current[0].url).toBe('http://grafana.com');
|
|
});
|
|
|
|
it('Should have active state if available in the activeAlertManagers', () => {
|
|
// Arrange
|
|
const { dsSettings, dsInstanceSettings } = setupAlertmanagerDataSource({ url: 'http://grafana.com' });
|
|
|
|
config.datasources = {
|
|
'External Alertmanager': dsInstanceSettings,
|
|
};
|
|
|
|
const store = mockStore((state) => {
|
|
state.dataSources.dataSources = [dsSettings];
|
|
state.unifiedAlerting.externalAlertmanagers.discoveredAlertmanagers.result = {
|
|
data: {
|
|
activeAlertManagers: [{ url: 'http://grafana.com/api/v2/alerts' }],
|
|
droppedAlertManagers: [],
|
|
},
|
|
};
|
|
});
|
|
|
|
const wrapper: React.FC = ({ children }) => <reactRedux.Provider store={store}>{children}</reactRedux.Provider>;
|
|
|
|
// Act
|
|
const {
|
|
result: { current },
|
|
} = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper });
|
|
|
|
// Assert
|
|
expect(current).toHaveLength(1);
|
|
expect(current[0].status).toBe('active');
|
|
expect(current[0].statusInconclusive).toBe(false);
|
|
});
|
|
|
|
it('Should have dropped state if available in the droppedAlertManagers', () => {
|
|
// Arrange
|
|
const { dsSettings, dsInstanceSettings } = setupAlertmanagerDataSource({ url: 'http://grafana.com' });
|
|
|
|
config.datasources = {
|
|
'External Alertmanager': dsInstanceSettings,
|
|
};
|
|
|
|
const store = mockStore((state) => {
|
|
state.dataSources.dataSources = [dsSettings];
|
|
state.unifiedAlerting.externalAlertmanagers.discoveredAlertmanagers.result = {
|
|
data: {
|
|
activeAlertManagers: [],
|
|
droppedAlertManagers: [{ url: 'http://grafana.com/api/v2/alerts' }],
|
|
},
|
|
};
|
|
});
|
|
|
|
const wrapper: React.FC = ({ children }) => <reactRedux.Provider store={store}>{children}</reactRedux.Provider>;
|
|
|
|
// Act
|
|
const {
|
|
result: { current },
|
|
} = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper });
|
|
|
|
// Assert
|
|
expect(current).toHaveLength(1);
|
|
expect(current[0].status).toBe('dropped');
|
|
expect(current[0].statusInconclusive).toBe(false);
|
|
});
|
|
|
|
it('Should have pending state if not available neither in dropped nor in active alertManagers', () => {
|
|
// Arrange
|
|
const { dsSettings, dsInstanceSettings } = setupAlertmanagerDataSource();
|
|
|
|
config.datasources = {
|
|
'External Alertmanager': dsInstanceSettings,
|
|
};
|
|
|
|
const store = mockStore((state) => {
|
|
state.dataSources.dataSources = [dsSettings];
|
|
state.unifiedAlerting.externalAlertmanagers.discoveredAlertmanagers.result = {
|
|
data: {
|
|
activeAlertManagers: [],
|
|
droppedAlertManagers: [],
|
|
},
|
|
};
|
|
});
|
|
|
|
const wrapper: React.FC = ({ children }) => <reactRedux.Provider store={store}>{children}</reactRedux.Provider>;
|
|
|
|
// Act
|
|
const {
|
|
result: { current },
|
|
} = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper });
|
|
|
|
// Assert
|
|
expect(current).toHaveLength(1);
|
|
expect(current[0].status).toBe('pending');
|
|
expect(current[0].statusInconclusive).toBe(false);
|
|
});
|
|
|
|
it('Should match Alertmanager url when datasource url does not have protocol specified', () => {
|
|
// Arrange
|
|
const { dsSettings, dsInstanceSettings } = setupAlertmanagerDataSource({ url: 'localhost:9093' });
|
|
|
|
config.datasources = {
|
|
'External Alertmanager': dsInstanceSettings,
|
|
};
|
|
|
|
const store = mockStore((state) => {
|
|
state.dataSources.dataSources = [dsSettings];
|
|
state.unifiedAlerting.externalAlertmanagers.discoveredAlertmanagers.result = {
|
|
data: {
|
|
activeAlertManagers: [{ url: 'http://localhost:9093/api/v2/alerts' }],
|
|
droppedAlertManagers: [],
|
|
},
|
|
};
|
|
});
|
|
|
|
const wrapper: React.FC = ({ children }) => <reactRedux.Provider store={store}>{children}</reactRedux.Provider>;
|
|
|
|
// Act
|
|
const {
|
|
result: { current },
|
|
} = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper });
|
|
|
|
// Assert
|
|
expect(current).toHaveLength(1);
|
|
expect(current[0].status).toBe('active');
|
|
expect(current[0].url).toBe('localhost:9093');
|
|
});
|
|
|
|
it('Should have inconclusive state when there are many Alertmanagers of the same URL', () => {
|
|
// Arrange
|
|
const { dsSettings, dsInstanceSettings } = setupAlertmanagerDataSource({ url: 'http://grafana.com' });
|
|
|
|
config.datasources = {
|
|
'External Alertmanager': dsInstanceSettings,
|
|
};
|
|
|
|
const store = mockStore((state) => {
|
|
state.dataSources.dataSources = [dsSettings];
|
|
state.unifiedAlerting.externalAlertmanagers.discoveredAlertmanagers.result = {
|
|
data: {
|
|
activeAlertManagers: [
|
|
{ url: 'http://grafana.com/api/v2/alerts' },
|
|
{ url: 'http://grafana.com/api/v2/alerts' },
|
|
],
|
|
droppedAlertManagers: [],
|
|
},
|
|
};
|
|
});
|
|
|
|
const wrapper: React.FC = ({ children }) => <reactRedux.Provider store={store}>{children}</reactRedux.Provider>;
|
|
|
|
// Act
|
|
const {
|
|
result: { current },
|
|
} = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper });
|
|
|
|
// Assert
|
|
expect(current).toHaveLength(1);
|
|
expect(current[0].status).toBe('active');
|
|
expect(current[0].statusInconclusive).toBe(true);
|
|
});
|
|
});
|
|
|
|
function setupAlertmanagerDataSource(partialDsSettings?: Partial<DataSourceSettings<AlertManagerDataSourceJsonData>>) {
|
|
const dsCommonConfig = {
|
|
uid: '1',
|
|
name: 'External Alertmanager',
|
|
type: 'alertmanager',
|
|
jsonData: { handleGrafanaManagedAlerts: true } as AlertManagerDataSourceJsonData,
|
|
};
|
|
|
|
const dsInstanceSettings = mockDataSource(dsCommonConfig);
|
|
|
|
const dsSettings = mockApiDataSource({
|
|
...dsCommonConfig,
|
|
...partialDsSettings,
|
|
});
|
|
|
|
return { dsSettings, dsInstanceSettings };
|
|
}
|
|
|
|
function mockApiDataSource(partial: Partial<DataSourceSettings<DataSourceJsonData, {}>> = {}) {
|
|
const dsSettings: DataSourceSettings<DataSourceJsonData, {}> = {
|
|
uid: '1',
|
|
id: 1,
|
|
name: '',
|
|
url: '',
|
|
type: '',
|
|
access: '',
|
|
orgId: 1,
|
|
typeLogoUrl: '',
|
|
typeName: '',
|
|
user: '',
|
|
database: '',
|
|
basicAuth: false,
|
|
isDefault: false,
|
|
basicAuthUser: '',
|
|
jsonData: { handleGrafanaManagedAlerts: true } as AlertManagerDataSourceJsonData,
|
|
secureJsonFields: {},
|
|
readOnly: false,
|
|
withCredentials: false,
|
|
...partial,
|
|
};
|
|
|
|
return dsSettings;
|
|
}
|
|
|
|
const createMockStoreState = (
|
|
activeAlertmanagers: Array<{ url: string }>,
|
|
droppedAlertmanagers: Array<{ url: string }>,
|
|
alertmanagerConfig: string[]
|
|
) => {
|
|
return {
|
|
unifiedAlerting: {
|
|
externalAlertmanagers: {
|
|
discoveredAlertmanagers: {
|
|
result: {
|
|
data: {
|
|
activeAlertManagers: activeAlertmanagers,
|
|
droppedAlertManagers: droppedAlertmanagers,
|
|
},
|
|
},
|
|
dispatched: false,
|
|
loading: false,
|
|
},
|
|
alertmanagerConfig: {
|
|
result: {
|
|
alertmanagers: alertmanagerConfig,
|
|
alertmanagersChoice: AlertmanagerChoice.All,
|
|
},
|
|
dispatched: false,
|
|
loading: false,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
};
|