grafana/public/app/features/alerting/state/reducers.test.ts
Peter Holmberg 400aafa3b3
Migration: Edit notification channel (#25980)
* implement edit page

* connectWithCleanup

* remove angular related code

* use loadingindicator

* use the correct loading component

* handle secureFields

* fixed implementation of secure fields

* Keep secureFields after rerendering the form

* CollapsableSection and Page refactor

* use checkbox instead of switch

* fix comment

* add cursor to section

* Fixed issues after PR review

* Fix issue with some settings being undefined

* new reducer and start with test

* algorithm to migrate secure fields

* UX: Minor UI Tweaks

* Added field around checkboxes, and missing required field

* fixed test

* tests for util

* minor tweaks and changes

* define as records

* fix typ error

* forward invalid to textarea and inputcontrol

* merge formdata and redux data in test

* fix issue with creating channel

* do not figure out securefields in migration

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
2020-09-09 12:46:19 +02:00

424 lines
13 KiB
TypeScript

import { dateTime } from '@grafana/data';
import {
alertRulesReducer,
initialChannelState,
initialState,
loadAlertRules,
loadedAlertRules,
notificationChannelReducer,
setSearchQuery,
notificationChannelLoaded,
} from './reducers';
import { AlertRuleDTO, AlertRulesState, NotificationChannelState, NotifierDTO } from 'app/types';
import { reducerTester } from '../../../../test/core/redux/reducerTester';
describe('Alert rules', () => {
const realDateNow = Date.now.bind(global.Date);
const anchorUnix = dateTime('2019-09-04T10:01:01+02:00').valueOf();
const dateNowStub = jest.fn(() => anchorUnix);
global.Date.now = dateNowStub;
const newStateDate = dateTime().subtract(1, 'y');
const newStateDateFormatted = newStateDate.format('YYYY-MM-DD');
const newStateDateAge = newStateDate.fromNow(true);
const payload: AlertRuleDTO[] = [
{
id: 2,
dashboardId: 7,
dashboardUid: 'ggHbN42mk',
dashboardSlug: 'alerting-with-testdata',
panelId: 4,
name: 'TestData - Always Alerting',
state: 'alerting',
newStateDate: `${newStateDateFormatted}T10:00:30+02:00`,
evalDate: '0001-01-01T00:00:00Z',
evalData: { evalMatches: [{ metric: 'A-series', tags: null, value: 215 }] },
executionError: '',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
{
id: 1,
dashboardId: 7,
dashboardUid: 'ggHbN42mk',
dashboardSlug: 'alerting-with-testdata',
panelId: 3,
name: 'TestData - Always OK',
state: 'ok',
newStateDate: `${newStateDateFormatted}T10:01:01+02:00`,
evalDate: '0001-01-01T00:00:00Z',
evalData: {},
executionError: '',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
{
id: 3,
dashboardId: 7,
dashboardUid: 'ggHbN42mk',
dashboardSlug: 'alerting-with-testdata',
panelId: 3,
name: 'TestData - ok',
state: 'ok',
newStateDate: `${newStateDateFormatted}T10:01:01+02:00`,
evalDate: '0001-01-01T00:00:00Z',
evalData: {},
executionError: 'error',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
{
id: 4,
dashboardId: 7,
dashboardUid: 'ggHbN42mk',
dashboardSlug: 'alerting-with-testdata',
panelId: 3,
name: 'TestData - Paused',
state: 'paused',
newStateDate: `${newStateDateFormatted}T10:01:01+02:00`,
evalDate: '0001-01-01T00:00:00Z',
evalData: {},
executionError: 'error',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
{
id: 5,
dashboardId: 7,
dashboardUid: 'ggHbN42mk',
dashboardSlug: 'alerting-with-testdata',
panelId: 3,
name: 'TestData - Ok',
state: 'ok',
newStateDate: `${newStateDateFormatted}T10:01:01+02:00`,
evalDate: '0001-01-01T00:00:00Z',
evalData: {
noData: true,
},
executionError: 'error',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
];
afterAll(() => {
global.Date.now = realDateNow;
});
describe('when loadAlertRules is dispatched', () => {
it('then state should be correct', () => {
reducerTester<AlertRulesState>()
.givenReducer(alertRulesReducer, { ...initialState })
.whenActionIsDispatched(loadAlertRules())
.thenStateShouldEqual({ ...initialState, isLoading: true });
});
});
describe('when setSearchQuery is dispatched', () => {
it('then state should be correct', () => {
reducerTester<AlertRulesState>()
.givenReducer(alertRulesReducer, { ...initialState })
.whenActionIsDispatched(setSearchQuery('query'))
.thenStateShouldEqual({ ...initialState, searchQuery: 'query' });
});
});
describe('when loadedAlertRules is dispatched', () => {
it('then state should be correct', () => {
reducerTester<AlertRulesState>()
.givenReducer(alertRulesReducer, { ...initialState, isLoading: true })
.whenActionIsDispatched(loadedAlertRules(payload))
.thenStateShouldEqual({
...initialState,
isLoading: false,
items: [
{
dashboardId: 7,
dashboardSlug: 'alerting-with-testdata',
dashboardUid: 'ggHbN42mk',
evalData: {
evalMatches: [
{
metric: 'A-series',
tags: null,
value: 215,
},
],
},
evalDate: '0001-01-01T00:00:00Z',
executionError: '',
id: 2,
name: 'TestData - Always Alerting',
newStateDate: `${newStateDateFormatted}T10:00:30+02:00`,
panelId: 4,
state: 'alerting',
stateAge: newStateDateAge,
stateClass: 'alert-state-critical',
stateIcon: 'heart-break',
stateText: 'ALERTING',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
{
dashboardId: 7,
dashboardSlug: 'alerting-with-testdata',
dashboardUid: 'ggHbN42mk',
evalData: {},
evalDate: '0001-01-01T00:00:00Z',
executionError: '',
id: 1,
name: 'TestData - Always OK',
newStateDate: `${newStateDateFormatted}T10:01:01+02:00`,
panelId: 3,
state: 'ok',
stateAge: newStateDateAge,
stateClass: 'alert-state-ok',
stateIcon: 'heart',
stateText: 'OK',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
{
dashboardId: 7,
dashboardSlug: 'alerting-with-testdata',
dashboardUid: 'ggHbN42mk',
evalData: {},
evalDate: '0001-01-01T00:00:00Z',
executionError: 'error',
id: 3,
info: 'Execution Error: error',
name: 'TestData - ok',
newStateDate: `${newStateDateFormatted}T10:01:01+02:00`,
panelId: 3,
state: 'ok',
stateAge: newStateDateAge,
stateClass: 'alert-state-ok',
stateIcon: 'heart',
stateText: 'OK',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
{
dashboardId: 7,
dashboardSlug: 'alerting-with-testdata',
dashboardUid: 'ggHbN42mk',
evalData: {},
evalDate: '0001-01-01T00:00:00Z',
executionError: 'error',
id: 4,
name: 'TestData - Paused',
newStateDate: `${newStateDateFormatted}T10:01:01+02:00`,
panelId: 3,
state: 'paused',
stateAge: newStateDateAge,
stateClass: 'alert-state-paused',
stateIcon: 'pause',
stateText: 'PAUSED',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
{
dashboardId: 7,
dashboardSlug: 'alerting-with-testdata',
dashboardUid: 'ggHbN42mk',
evalData: {
noData: true,
},
evalDate: '0001-01-01T00:00:00Z',
executionError: 'error',
id: 5,
info: 'Query returned no data',
name: 'TestData - Ok',
newStateDate: `${newStateDateFormatted}T10:01:01+02:00`,
panelId: 3,
state: 'ok',
stateAge: newStateDateAge,
stateClass: 'alert-state-ok',
stateIcon: 'heart',
stateText: 'OK',
url: '/d/ggHbN42mk/alerting-with-testdata',
},
],
});
});
});
});
describe('Notification channel', () => {
const notifiers: NotifierDTO[] = [
{
type: 'webhook',
name: 'webhook',
heading: 'Webhook settings',
description: 'Sends HTTP POST request to a URL',
info: '',
options: [
{
element: 'input',
inputType: 'text',
label: 'Url',
description: '',
placeholder: '',
propertyName: 'url',
showWhen: { field: '', is: '' },
required: true,
validationRule: '',
secure: false,
},
{
element: 'select',
inputType: '',
label: 'Http Method',
description: '',
placeholder: '',
propertyName: 'httpMethod',
selectOptions: [
{ value: 'POST', label: 'POST' },
{ value: 'PUT', label: 'PUT' },
],
showWhen: { field: '', is: '' },
required: false,
validationRule: '',
secure: false,
},
{
element: 'input',
inputType: 'text',
label: 'Username',
description: '',
placeholder: '',
propertyName: 'username',
showWhen: { field: '', is: '' },
required: false,
validationRule: '',
secure: false,
},
{
element: 'input',
inputType: 'password',
label: 'Password',
description: '',
placeholder: '',
propertyName: 'password',
showWhen: { field: '', is: '' },
required: false,
validationRule: '',
secure: true,
},
],
},
];
describe('Load notification channel', () => {
it('should migrate non secure settings to secure fields', () => {
const payload = {
id: 2,
uid: '9L3FrrHGk',
name: 'Webhook test',
type: 'webhook',
isDefault: false,
sendReminder: false,
disableResolveMessage: false,
frequency: '',
created: '2020-08-28T08:49:24Z',
updated: '2020-08-28T08:49:24Z',
settings: {
autoResolve: true,
httpMethod: 'POST',
password: 'asdf',
severity: 'critical',
uploadImage: true,
url: 'http://localhost.webhook',
username: 'asdf',
},
};
const expected = {
id: 2,
uid: '9L3FrrHGk',
name: 'Webhook test',
type: 'webhook',
isDefault: false,
sendReminder: false,
disableResolveMessage: false,
frequency: '',
created: '2020-08-28T08:49:24Z',
updated: '2020-08-28T08:49:24Z',
secureSettings: {
password: 'asdf',
},
settings: {
autoResolve: true,
httpMethod: 'POST',
password: '',
severity: 'critical',
uploadImage: true,
url: 'http://localhost.webhook',
username: 'asdf',
},
};
reducerTester<NotificationChannelState>()
.givenReducer(notificationChannelReducer, { ...initialChannelState, notifiers: notifiers })
.whenActionIsDispatched(notificationChannelLoaded(payload))
.thenStateShouldEqual({
...initialChannelState,
notifiers: notifiers,
notificationChannel: expected,
});
});
it('should handle already secure field', () => {
const payload = {
id: 2,
uid: '9L3FrrHGk',
name: 'Webhook test',
type: 'webhook',
isDefault: false,
sendReminder: false,
disableResolveMessage: false,
frequency: '',
created: '2020-08-28T08:49:24Z',
updated: '2020-08-28T08:49:24Z',
secureFields: {
password: true,
},
settings: {
autoResolve: true,
httpMethod: 'POST',
password: '',
severity: 'critical',
uploadImage: true,
url: 'http://localhost.webhook',
username: 'asdf',
},
};
const expected = {
id: 2,
uid: '9L3FrrHGk',
name: 'Webhook test',
type: 'webhook',
isDefault: false,
sendReminder: false,
disableResolveMessage: false,
frequency: '',
created: '2020-08-28T08:49:24Z',
updated: '2020-08-28T08:49:24Z',
secureFields: {
password: true,
},
settings: {
autoResolve: true,
httpMethod: 'POST',
password: '',
severity: 'critical',
uploadImage: true,
url: 'http://localhost.webhook',
username: 'asdf',
},
};
reducerTester<NotificationChannelState>()
.givenReducer(notificationChannelReducer, { ...initialChannelState, notifiers: notifiers })
.whenActionIsDispatched(notificationChannelLoaded(payload))
.thenStateShouldEqual({
...initialChannelState,
notifiers: notifiers,
notificationChannel: expected,
});
});
});
});