grafana/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx
Sonia Aguilar 718401d250
Alerting: Add smart type selection when creating a new alert rule (#71071)
* Add smart type selection when creating a new alert rule

* Auto switch when switch button has not been clicked yet

* remove unnecessay code after the last refacgtor

* Refactor

* Remove unneeded prop

* Move SmartAlertTypeDetector to its own file

* Fix tests

* Refactor: new useSetExpressionAndDataSource hook

* Fix expressions not been propagated when switching from one type to another

* Change texts

* Update tests

* Update text in switch button

* Update texts and tests

* Refactor: move code to getCanSwitch new method

* Move smart alert after queries and remove auto-switch

* Remove expressions and restore them when switching between grafana and cloud type

* Rename previous expression state

* Fix tests

* Add data source name for data source-managed alert selection

* Update reducer when changing cloud data source

* PR review suggestions

* PR review suggestions 2nd part

* PR review suggestions 3th part

* Fix canSwitch

* Update texts on smart alert

---------

Co-authored-by: Virginia Cepeda <virginia.cepeda@grafana.com>
2023-07-13 17:02:47 +02:00

211 lines
7.0 KiB
TypeScript

import { screen, waitForElementToBeRemoved } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor';
import { byRole, byText } from 'testing-library-selector';
import { setDataSourceSrv } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import { PromApiFeatures, PromApplication } from 'app/types/unified-alerting-dto';
import { searchFolders } from '../../manage-dashboards/state/actions';
import { discoverFeatures } from './api/buildInfo';
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
import { disableRBAC, mockDataSource, MockDataSourceSrv } from './mocks';
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
import * as config from './utils/config';
import { DataSourceType } from './utils/datasource';
jest.mock('./components/rule-editor/ExpressionEditor', () => ({
// eslint-disable-next-line react/display-name
ExpressionEditor: ({ value, onChange }: ExpressionEditorProps) => (
<input value={value} data-testid="expr" onChange={(e) => onChange(e.target.value)} />
),
}));
jest.mock('./api/buildInfo');
jest.mock('./api/ruler');
jest.mock('../../../../app/features/manage-dashboards/state/actions');
// there's no angular scope in test and things go terribly wrong when trying to render the query editor row.
// lets just skip it
jest.mock('app/features/query/components/QueryEditorRow', () => ({
// eslint-disable-next-line react/display-name
QueryEditorRow: () => <p>hi</p>,
}));
jest.mock('./components/rule-editor/util', () => {
const originalModule = jest.requireActual('./components/rule-editor/util');
return {
...originalModule,
getThresholdsForQueries: jest.fn(() => ({})),
};
});
const dataSources = {
// can edit rules
loki: mockDataSource(
{
type: DataSourceType.Loki,
name: 'loki with ruler',
},
{ alerting: true }
),
loki_disabled: mockDataSource(
{
type: DataSourceType.Loki,
name: 'loki disabled for alerting',
jsonData: {
manageAlerts: false,
},
},
{ alerting: true }
),
// can edit rules
prom: mockDataSource(
{
type: DataSourceType.Prometheus,
name: 'cortex with ruler',
},
{ alerting: true }
),
// cannot edit rules
loki_local_rule_store: mockDataSource(
{
type: DataSourceType.Loki,
name: 'loki with local rule store',
},
{ alerting: true }
),
// cannot edit rules
prom_no_ruler_api: mockDataSource(
{
type: DataSourceType.Loki,
name: 'cortex without ruler api',
},
{ alerting: true }
),
// not a supported datasource type
splunk: mockDataSource(
{
type: 'splunk',
name: 'splunk',
},
{ alerting: true }
),
};
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: jest.fn(() => ({
getInstanceSettings: () => dataSources.prom,
get: () => dataSources.prom,
})),
}));
jest.spyOn(config, 'getAllDataSources');
const mocks = {
getAllDataSources: jest.mocked(config.getAllDataSources),
searchFolders: jest.mocked(searchFolders),
api: {
discoverFeatures: jest.mocked(discoverFeatures),
fetchRulerRulesGroup: jest.mocked(fetchRulerRulesGroup),
setRulerRuleGroup: jest.mocked(setRulerRuleGroup),
fetchRulerRulesNamespace: jest.mocked(fetchRulerRulesNamespace),
fetchRulerRules: jest.mocked(fetchRulerRules),
fetchRulerRulesIfNotFetchedYet: jest.mocked(fetchRulerRulesIfNotFetchedYet),
},
};
function getDiscoverFeaturesMock(application: PromApplication, features?: Partial<PromApiFeatures['features']>) {
return {
application: application,
features: {
rulerApiEnabled: false,
alertManagerConfigApi: false,
federatedRules: false,
querySharding: false,
...features,
},
};
}
describe('RuleEditor cloud: checking editable data sources', () => {
beforeEach(() => {
jest.clearAllMocks();
contextSrv.isEditor = true;
contextSrv.hasEditPermissionInFolders = true;
});
disableRBAC();
it('for cloud alerts, should only allow to select editable rules sources', async () => {
mocks.api.discoverFeatures.mockImplementation(async (dataSourceName) => {
if (dataSourceName === 'loki with ruler' || dataSourceName === 'cortex with ruler') {
return getDiscoverFeaturesMock(PromApplication.Cortex, { rulerApiEnabled: true });
}
if (dataSourceName === 'loki with local rule store') {
return getDiscoverFeaturesMock(PromApplication.Cortex);
}
if (dataSourceName === 'cortex without ruler api') {
return getDiscoverFeaturesMock(PromApplication.Cortex);
}
throw new Error(`${dataSourceName} not handled`);
});
mocks.api.fetchRulerRulesGroup.mockImplementation(async ({ dataSourceName }) => {
if (dataSourceName === 'loki with ruler' || dataSourceName === 'cortex with ruler') {
return null;
}
if (dataSourceName === 'loki with local rule store') {
throw {
status: 400,
data: {
message: 'GetRuleGroup unsupported in rule local store',
},
};
}
if (dataSourceName === 'cortex without ruler api') {
throw new Error('404 from rules config endpoint. Perhaps ruler API is not enabled?');
}
return null;
});
setDataSourceSrv(new MockDataSourceSrv(dataSources));
mocks.getAllDataSources.mockReturnValue(Object.values(dataSources));
mocks.searchFolders.mockResolvedValue([]);
// render rule editor, select mimir/loki managed alerts
renderRuleEditor();
await waitForElementToBeRemoved(screen.getAllByTestId('Spinner'));
await ui.inputs.name.find();
const removeExpressionsButtons = screen.getAllByLabelText('Remove expression');
expect(removeExpressionsButtons).toHaveLength(2);
const switchToCloudButton = screen.getByText('Switch to data source-managed alert rule');
expect(switchToCloudButton).toBeInTheDocument();
await userEvent.click(switchToCloudButton);
//expressions are removed after switching to data-source managed
expect(screen.queryAllByLabelText('Remove expression')).toHaveLength(0);
// check that only rules sources that have ruler available are there
const dataSourceSelect = ui.inputs.dataSource.get();
await userEvent.click(byRole('combobox').get(dataSourceSelect));
expect(await byText('loki with ruler').query()).toBeInTheDocument();
expect(byText('cortex with ruler').query()).toBeInTheDocument();
expect(byText('loki with local rule store').query()).not.toBeInTheDocument();
expect(byText('prom without ruler api').query()).not.toBeInTheDocument();
expect(byText('splunk').query()).not.toBeInTheDocument();
expect(byText('loki disabled for alerting').query()).not.toBeInTheDocument();
});
});