Alerting: Restructure mock server handlers slightly and remove unnecessary handlers (#87759)

This commit is contained in:
Tom Ratcliffe 2024-05-15 16:53:16 +01:00 committed by GitHub
parent 1af4256a7c
commit f4d55d2612
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 138 additions and 136 deletions

View File

@ -14,12 +14,7 @@ import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRul
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
import { mockApi, mockFeatureDiscoveryApi, setupMswServer } from './mockApi';
import { grantUserPermissions, labelsPluginMetaMock, mockDataSource } from './mocks';
import {
defaultGrafanaAlertingConfigurationStatusResponse,
emptyExternalAlertmanagersResponse,
mockAlertmanagerChoiceResponse,
mockAlertmanagersResponse,
} from './mocks/alertmanagerApi';
import { emptyExternalAlertmanagersResponse, mockAlertmanagersResponse } from './mocks/alertmanagerApi';
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
import { setupDataSources } from './testSetup/datasources';
import { buildInfoResponse } from './testSetup/featureDiscovery';
@ -55,9 +50,7 @@ setupDataSources(dataSources.default);
const server = setupMswServer();
mockFeatureDiscoveryApi(server).discoverDsFeatures(dataSources.default, buildInfoResponse.mimir);
mockAlertmanagerChoiceResponse(server, defaultGrafanaAlertingConfigurationStatusResponse);
mockAlertmanagersResponse(server, emptyExternalAlertmanagersResponse);
mockApi(server).eval({ results: {} });
mockApi(server).plugins.getPluginSettings({ ...labelsPluginMetaMock, enabled: false });
// these tests are rather slow because we have to wait for various API calls and mocks to be called

View File

@ -4,15 +4,10 @@ import React from 'react';
import { renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor';
import { clickSelectOption } from 'test/helpers/selectOptionInTest';
import { byRole } from 'testing-library-selector';
import 'whatwg-fetch';
import { setDataSourceSrv } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import { mockApi, setupMswServer } from 'app/features/alerting/unified/mockApi';
import {
defaultGrafanaAlertingConfigurationStatusResponse,
mockAlertmanagerChoiceResponse,
} from 'app/features/alerting/unified/mocks/alertmanagerApi';
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
import { DashboardSearchHit, DashboardSearchItemType } from 'app/features/search/types';
import { AccessControlAction } from 'app/types';
import { GrafanaAlertStateDecision, PromApplication } from 'app/types/unified-alerting-dto';
@ -67,12 +62,10 @@ const mocks = {
},
};
const server = setupMswServer();
setupMswServer();
describe('RuleEditor grafana managed rules', () => {
beforeEach(() => {
mockApi(server).eval({ results: {} });
mockAlertmanagerChoiceResponse(server, defaultGrafanaAlertingConfigurationStatusResponse);
jest.clearAllMocks();
contextSrv.isEditor = true;
contextSrv.hasEditPermissionInFolders = true;

View File

@ -4,11 +4,10 @@ import React from 'react';
import { renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor';
import { clickSelectOption } from 'test/helpers/selectOptionInTest';
import { byText } from 'testing-library-selector';
import 'whatwg-fetch';
import { setDataSourceSrv } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import { mockApi, setupMswServer } from 'app/features/alerting/unified/mockApi';
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
import { AccessControlAction } from 'app/types';
import { PromApplication } from 'app/types/unified-alerting-dto';
@ -17,7 +16,7 @@ import { searchFolders } from '../../manage-dashboards/state/actions';
import { discoverFeatures } from './api/buildInfo';
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
import { RecordingRuleEditorProps } from './components/rule-editor/RecordingRuleEditor';
import { MockDataSourceSrv, grantUserPermissions, labelsPluginMetaMock, mockDataSource } from './mocks';
import { MockDataSourceSrv, grantUserPermissions, mockDataSource } from './mocks';
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
import * as config from './utils/config';
@ -93,13 +92,10 @@ const mocks = {
},
};
const server = setupMswServer();
mockApi(server).plugins.getPluginSettings({ ...labelsPluginMetaMock, enabled: false });
mockApi(server).eval({ results: { A: { frames: [] } } });
setupMswServer();
describe('RuleEditor recording rules', () => {
beforeEach(() => {
mockApi(server).eval({ results: {} });
jest.clearAllMocks();
contextSrv.isEditor = true;
contextSrv.hasEditPermissionInFolders = true;

View File

@ -6,12 +6,12 @@ import { dateTime } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { config, locationService, setDataSourceSrv } from '@grafana/runtime';
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
import { waitForServerRequest } from 'app/features/alerting/unified/mocks/server/events';
import {
MOCK_DATASOURCE_UID_BROKEN_ALERTMANAGER,
MOCK_DATASOURCE_NAME_BROKEN_ALERTMANAGER,
} from 'app/features/alerting/unified/mocks/datasources';
import { waitForServerRequest } from 'app/features/alerting/unified/mocks/server/events';
import { silenceCreateHandler } from 'app/features/alerting/unified/mocks/silences';
} from 'app/features/alerting/unified/mocks/server/handlers/datasources';
import { silenceCreateHandler } from 'app/features/alerting/unified/mocks/server/handlers/silences';
import { MatcherOperator } from 'app/plugins/datasource/alertmanager/types';
import { AccessControlAction } from 'app/types';

View File

@ -1,18 +1,12 @@
import { render, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Route } from 'react-router-dom';
import { Props } from 'react-virtualized-auto-sizer';
import { render, waitFor, waitForElementToBeRemoved, userEvent } from 'test/test-utils';
import { byRole, byTestId, byText } from 'testing-library-selector';
import { locationService } from '@grafana/runtime';
import { TestProvider } from '../../../../../../test/helpers/TestProvider';
import { AlertmanagerChoice } from '../../../../../plugins/datasource/alertmanager/types';
import { DashboardSearchItemType } from '../../../../search/types';
import { mockAlertRuleApi, mockApi, mockExportApi, mockSearchApi, setupMswServer } from '../../mockApi';
import { mockAlertRuleApi, mockExportApi, mockSearchApi, setupMswServer } from '../../mockApi';
import { getGrafanaRule, mockDashboardSearchItem, mockDataSource } from '../../mocks';
import { mockAlertmanagerChoiceResponse } from '../../mocks/alertmanagerApi';
import { setupDataSources } from '../../testSetup/datasources';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
@ -62,17 +56,13 @@ const dataSources = {
};
function renderModifyExport(ruleId: string) {
locationService.push(`/alerting/${ruleId}/modify-export`);
render(<Route path="/alerting/:id/modify-export" component={GrafanaModifyExport} />, { wrapper: TestProvider });
render(<Route path="/alerting/:id/modify-export" component={GrafanaModifyExport} />, {
historyOptions: { initialEntries: [`/alerting/${ruleId}/modify-export`] },
});
}
const server = setupMswServer();
mockAlertmanagerChoiceResponse(server, {
alertmanagersChoice: AlertmanagerChoice.Internal,
numExternalAlertmanagers: 0,
});
describe('GrafanaModifyExport', () => {
setupDataSources(dataSources.default);
@ -97,7 +87,6 @@ describe('GrafanaModifyExport', () => {
});
it('Should render edit form for the specified rule', async () => {
mockApi(server).eval({ results: { A: { frames: [] } } });
mockSearchApi(server).search([
mockDashboardSearchItem({
title: grafanaRule.namespace.name,

View File

@ -36,7 +36,6 @@ const ui = {
describe('GrafanaReceiverForm', () => {
beforeEach(() => {
server.resetHandlers();
clearPluginSettingsCache();
});

View File

@ -15,7 +15,6 @@ const server = setupMswServer();
describe('useOnCallIntegration', () => {
afterEach(() => {
server.resetHandlers();
clearPluginSettingsCache();
});

View File

@ -19,7 +19,7 @@ import {
} from 'app/features/alerting/unified/api/ruler';
import * as useContactPoints from 'app/features/alerting/unified/components/contact-points/useContactPoints';
import * as dsByPermission from 'app/features/alerting/unified/hooks/useAlertManagerSources';
import { mockApi, setupMswServer } from 'app/features/alerting/unified/mockApi';
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
import { MockDataSourceSrv, grantUserPermissions, mockDataSource } from 'app/features/alerting/unified/mocks';
import { AlertmanagerProvider } from 'app/features/alerting/unified/state/AlertmanagerContext';
import { fetchRulerRulesIfNotFetchedYet } from 'app/features/alerting/unified/state/actions';
@ -103,11 +103,10 @@ const mocks = {
},
};
const server = setupMswServer();
setupMswServer();
describe('Can create a new grafana managed alert unsing simplified routing', () => {
beforeEach(() => {
mockApi(server).eval({ results: {} });
jest.clearAllMocks();
contextSrv.isEditor = true;
contextSrv.hasEditPermissionInFolders = true;

View File

@ -2,11 +2,10 @@ import { http, HttpResponse, RequestHandler } from 'msw';
import { setupServer } from 'msw/node';
import { GrafanaAlertingConfigurationStatusResponse } from 'app/features/alerting/unified/api/alertmanagerApi';
import { grafanaAlertingConfigurationStatusHandler } from 'app/features/alerting/unified/mocks/server/handlers/alertmanagers';
import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types';
import { AccessControlAction } from 'app/types';
import { grafanaAlertingConfigurationStatusHandler } from '../../../mocks/alertmanagerApi';
const grafanaAlertingConfigurationMockedResponse: GrafanaAlertingConfigurationStatusResponse = {
alertmanagersChoice: AlertmanagerChoice.Internal,
numExternalAlertmanagers: 0,

View File

@ -5,7 +5,7 @@ import { setupServer, SetupServer } from 'msw/node';
import { DataSourceInstanceSettings, PluginMeta } from '@grafana/data';
import { setBackendSrv } from '@grafana/runtime';
import { AlertRuleUpdated } from 'app/features/alerting/unified/api/alertRuleApi';
import allHandlers from 'app/features/alerting/unified/mocks/server/handlers';
import allHandlers from 'app/features/alerting/unified/mocks/server/all-handlers';
import { DashboardDTO, FolderDTO, NotifierDTO, OrgUser } from 'app/types';
import {
PromBuildInfoResponse,
@ -28,7 +28,6 @@ import {
import { DashboardSearchItem } from '../../search/types';
import { CreateIntegrationDTO, NewOnCallIntegrationDTO, OnCallIntegrationDTO } from './api/onCallApi';
import { AlertingQueryResponse } from './state/AlertingQueryRunner';
type Configurator<T> = (builder: T) => T;
@ -203,13 +202,6 @@ export function mockApi(server: SetupServer) {
);
},
eval: (response: AlertingQueryResponse) => {
server.use(
http.post('/api/v1/eval', () => {
return HttpResponse.json(response);
})
);
},
grafanaNotifiers: (response: NotifierDTO[]) => {
server.use(http.get(`api/alert-notifiers`, () => HttpResponse.json(response)));
},

View File

@ -1,13 +1,11 @@
import { http, HttpResponse } from 'msw';
import { SetupServer } from 'msw/node';
import { MOCK_SILENCE_ID_EXISTING, mockAlertmanagerAlert } from 'app/features/alerting/unified/mocks';
import { MOCK_DATASOURCE_UID_BROKEN_ALERTMANAGER } from 'app/features/alerting/unified/mocks/datasources';
import { grafanaAlertingConfigurationStatusHandler } from 'app/features/alerting/unified/mocks/server/handlers/alertmanagers';
import {
AlertmanagerChoice,
AlertManagerCortexConfig,
AlertState,
ExternalAlertmanagersStatusResponse,
} from '../../../../plugins/datasource/alertmanager/types';
import { GrafanaAlertingConfigurationStatusResponse } from '../api/alertmanagerApi';
@ -18,10 +16,6 @@ export const defaultGrafanaAlertingConfigurationStatusResponse: GrafanaAlertingC
numExternalAlertmanagers: 0,
};
export const grafanaAlertingConfigurationStatusHandler = (
response = defaultGrafanaAlertingConfigurationStatusResponse
) => http.get('/api/v1/ngalert', () => HttpResponse.json(response));
export function mockAlertmanagerChoiceResponse(
server: SetupServer,
response: GrafanaAlertingConfigurationStatusResponse
@ -50,20 +44,3 @@ export function mockAlertmanagerConfigResponse(
)
);
}
export const alertmanagerAlertsListHandler = () =>
http.get<{ datasourceUid: string }>('/api/alertmanager/:datasourceUid/api/v2/alerts', ({ params }) => {
if (params.datasourceUid === MOCK_DATASOURCE_UID_BROKEN_ALERTMANAGER) {
return HttpResponse.json({ traceId: '' }, { status: 502 });
}
return HttpResponse.json([
mockAlertmanagerAlert({
labels: { foo: 'bar', buzz: 'bazz' },
status: { state: AlertState.Suppressed, silencedBy: [MOCK_SILENCE_ID_EXISTING], inhibitedBy: [] },
}),
mockAlertmanagerAlert({
labels: { foo: 'bar', buzz: 'bazz' },
status: { state: AlertState.Suppressed, silencedBy: [MOCK_SILENCE_ID_EXISTING], inhibitedBy: [] },
}),
]);
});

View File

@ -0,0 +1,24 @@
/**
* Contains all handlers that are required for test rendering of components within Alerting
*/
import alertmanagerHandlers from 'app/features/alerting/unified/mocks/server/handlers/alertmanagers';
import datasourcesHandlers from 'app/features/alerting/unified/mocks/server/handlers/datasources';
import evalHandlers from 'app/features/alerting/unified/mocks/server/handlers/eval';
import folderHandlers from 'app/features/alerting/unified/mocks/server/handlers/folders';
import pluginsHandlers from 'app/features/alerting/unified/mocks/server/handlers/plugins';
import silenceHandlers from 'app/features/alerting/unified/mocks/server/handlers/silences';
/**
* Array of all mock handlers that are required across Alerting tests
*/
const allHandlers = [
...alertmanagerHandlers,
...datasourcesHandlers,
...evalHandlers,
...folderHandlers,
...pluginsHandlers,
...silenceHandlers,
];
export default allHandlers;

View File

@ -1,5 +1,5 @@
import server from 'app/features/alerting/unified/mockApi';
import { grafanaAlertingConfigurationStatusHandler } from 'app/features/alerting/unified/mocks/alertmanagerApi';
import { grafanaAlertingConfigurationStatusHandler } from 'app/features/alerting/unified/mocks/server/handlers/alertmanagers';
import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types';
/**

View File

@ -1,36 +0,0 @@
/**
* Contains all handlers that are required for test rendering of components within Alerting
*/
import {
alertmanagerAlertsListHandler,
grafanaAlertingConfigurationStatusHandler,
} from 'app/features/alerting/unified/mocks/alertmanagerApi';
import { datasourceBuildInfoHandler } from 'app/features/alerting/unified/mocks/datasources';
import { folderHandler } from 'app/features/alerting/unified/mocks/folders';
import { pluginsHandler } from 'app/features/alerting/unified/mocks/plugins';
import {
silenceCreateHandler,
silenceGetHandler,
silencesListHandler,
} from 'app/features/alerting/unified/mocks/silences';
/**
* All mock handlers that are required across Alerting tests
*/
const allHandlers = [
grafanaAlertingConfigurationStatusHandler(),
alertmanagerAlertsListHandler(),
folderHandler(),
pluginsHandler(),
silencesListHandler(),
silenceGetHandler(),
silenceCreateHandler(),
datasourceBuildInfoHandler(),
];
export default allHandlers;

View File

@ -0,0 +1,30 @@
import { http, HttpResponse } from 'msw';
import { MOCK_SILENCE_ID_EXISTING, mockAlertmanagerAlert } from 'app/features/alerting/unified/mocks';
import { defaultGrafanaAlertingConfigurationStatusResponse } from 'app/features/alerting/unified/mocks/alertmanagerApi';
import { MOCK_DATASOURCE_UID_BROKEN_ALERTMANAGER } from 'app/features/alerting/unified/mocks/server/handlers/datasources';
import { AlertState } from 'app/plugins/datasource/alertmanager/types';
export const grafanaAlertingConfigurationStatusHandler = (
response = defaultGrafanaAlertingConfigurationStatusResponse
) => http.get('/api/v1/ngalert', () => HttpResponse.json(response));
export const alertmanagerAlertsListHandler = () =>
http.get<{ datasourceUid: string }>('/api/alertmanager/:datasourceUid/api/v2/alerts', ({ params }) => {
if (params.datasourceUid === MOCK_DATASOURCE_UID_BROKEN_ALERTMANAGER) {
return HttpResponse.json({ traceId: '' }, { status: 502 });
}
return HttpResponse.json([
mockAlertmanagerAlert({
labels: { foo: 'bar', buzz: 'bazz' },
status: { state: AlertState.Suppressed, silencedBy: [MOCK_SILENCE_ID_EXISTING], inhibitedBy: [] },
}),
mockAlertmanagerAlert({
labels: { foo: 'bar', buzz: 'bazz' },
status: { state: AlertState.Suppressed, silencedBy: [MOCK_SILENCE_ID_EXISTING], inhibitedBy: [] },
}),
]);
});
const handlers = [alertmanagerAlertsListHandler(), grafanaAlertingConfigurationStatusHandler()];
export default handlers;

View File

@ -1,10 +1,15 @@
import { HttpResponse, http } from 'msw';
// TODO: Add more accurate endpoint responses as tests require
export const datasourceBuildInfoHandler = () =>
http.get('/api/datasources/proxy/uid/:datasourceUid/api/v1/status/buildinfo', () => HttpResponse.json({}));
import { http, HttpResponse } from 'msw';
/** UID of the alertmanager that is expected to be broken in tests */
export const MOCK_DATASOURCE_UID_BROKEN_ALERTMANAGER = 'FwkfQfEmYlAthB';
/** Display name of the alertmanager that is expected to be broken in tests */
export const MOCK_DATASOURCE_NAME_BROKEN_ALERTMANAGER = 'broken alertmanager';
export const MOCK_DATASOURCE_EXTERNAL_VANILLA_ALERTMANAGER_UID = 'vanilla-alertmanager';
export const MOCK_DATASOURCE_PROVISIONED_MIMIR_ALERTMANAGER_UID = 'provisioned-alertmanager';
// TODO: Add more accurate endpoint responses as tests require
export const datasourceBuildInfoHandler = () =>
http.get('/api/datasources/proxy/uid/:datasourceUid/api/v1/status/buildinfo', () => HttpResponse.json({}));
const datasourcesHandlers = [datasourceBuildInfoHandler()];
export default datasourcesHandlers;

View File

@ -0,0 +1,14 @@
import { HttpResponse, http } from 'msw';
import { AlertingQueryResponse } from 'app/features/alerting/unified/state/AlertingQueryRunner';
const defaultPostEvalResponse = {
results: {},
};
const postEvalHandler = (response: AlertingQueryResponse = defaultPostEvalResponse) =>
http.post('/api/v1/eval', () => {
return HttpResponse.json(response);
});
const handlers = [postEvalHandler()];
export default handlers;

View File

@ -2,5 +2,9 @@ import { HttpResponse, http } from 'msw';
import { mockFolder } from 'app/features/alerting/unified/mocks';
export const folderHandler = (response = mockFolder()) =>
export const getFolderHandler = (response = mockFolder()) =>
http.get(`/api/folders/:folderUid`, () => HttpResponse.json(response));
const handlers = [getFolderHandler()];
export default handlers;

View File

@ -3,10 +3,13 @@ import { http, HttpResponse } from 'msw';
import { PluginMeta } from '@grafana/data';
import { plugins } from 'app/features/alerting/unified/testSetup/plugins';
export const pluginsHandler = (pluginsArray: PluginMeta[] = plugins) =>
export const getPluginsHandler = (pluginsArray: PluginMeta[] = plugins) =>
http.get<{ pluginId: string }>(`/api/plugins/:pluginId/settings`, ({ params: { pluginId } }) => {
const matchingPlugin = pluginsArray.find((plugin) => plugin.id === pluginId);
return matchingPlugin
? HttpResponse.json<PluginMeta>(matchingPlugin)
: HttpResponse.json({ message: 'Plugin not found, no installed plugin with that id' }, { status: 404 });
});
const handlers = [getPluginsHandler()];
export default handlers;

View File

@ -1,13 +1,9 @@
import { HttpResponse, http } from 'msw';
import { mockSilences } from 'app/features/alerting/unified/mocks';
import { MOCK_DATASOURCE_UID_BROKEN_ALERTMANAGER } from 'app/features/alerting/unified/mocks/datasources';
import { MOCK_DATASOURCE_UID_BROKEN_ALERTMANAGER } from 'app/features/alerting/unified/mocks/server/handlers/datasources';
//////////////
// Silences //
//////////////
export const silencesListHandler = (silences = mockSilences) =>
const silencesListHandler = (silences = mockSilences) =>
http.get<{ datasourceUid: string }>('/api/alertmanager/:datasourceUid/api/v2/silences', ({ params }) => {
if (params.datasourceUid === MOCK_DATASOURCE_UID_BROKEN_ALERTMANAGER) {
return HttpResponse.json({ traceId: '' }, { status: 502 });
@ -15,7 +11,7 @@ export const silencesListHandler = (silences = mockSilences) =>
return HttpResponse.json(silences);
});
export const silenceGetHandler = () =>
const silenceGetHandler = () =>
http.get<{ uuid: string }>('/api/alertmanager/:datasourceUid/api/v2/silence/:uuid', ({ params }) => {
const { uuid } = params;
const matchingMockSilence = mockSilences.find((silence) => silence.id === uuid);
@ -30,3 +26,6 @@ export const silenceCreateHandler = () =>
http.post('/api/alertmanager/:datasourceUid/api/v2/silences', () =>
HttpResponse.json({ silenceId: '4bda5b38-7939-4887-9ec2-16323b8e3b4e' })
);
const handlers = [silencesListHandler(), silenceGetHandler(), silenceCreateHandler()];
export default handlers;

View File

@ -4,7 +4,7 @@ import { PluginMeta, PluginType } from '@grafana/data';
import { config, setPluginExtensionsHook } from '@grafana/runtime';
import { mockPluginLinkExtension } from '../mocks';
import { pluginsHandler } from '../mocks/plugins';
import { getPluginsHandler } from '../mocks/server/handlers/plugins';
export function setupPlugins(plugins: PluginMeta[]): { apiHandlers: RequestHandler[] } {
plugins.forEach((plugin) => {
@ -18,7 +18,7 @@ export function setupPlugins(plugins: PluginMeta[]): { apiHandlers: RequestHandl
});
return {
apiHandlers: [pluginsHandler(plugins)],
apiHandlers: [getPluginsHandler(plugins)],
};
}
@ -105,4 +105,27 @@ export const plugins: PluginMeta[] = [
module: 'public/plugins/grafana-asserts-app/module.js',
baseUrl: 'public/plugins/grafana-asserts-app',
},
{
id: 'grafana-oncall-app',
name: 'OnCall',
type: PluginType.app,
enabled: true,
info: {
author: {
name: 'Grafana Labs',
url: '',
},
description: 'OnCall',
links: [],
logos: {
small: '',
large: '',
},
screenshots: [],
version: 'local-dev',
updated: '2024-04-09',
},
module: 'public/plugins/grafana-oncall-app/module.js',
baseUrl: 'public/plugins/grafana-oncall-app',
},
];