1import { dateTime } from '@grafana/data'; 2import { 3 alertRulesReducer, 4 initialChannelState, 5 initialState, 6 loadAlertRules, 7 loadedAlertRules, 8 notificationChannelReducer, 9 setSearchQuery, 10 notificationChannelLoaded, 11} from './reducers'; 12import { AlertRuleDTO, AlertRulesState, NotificationChannelState, NotifierDTO } from 'app/types'; 13import { reducerTester } from '../../../../test/core/redux/reducerTester'; 14 15describe('Alert rules', () => { 16 const realDateNow = Date.now.bind(global.Date); 17 const anchorUnix = dateTime('2019-09-04T10:01:01+02:00').valueOf(); 18 const dateNowStub = jest.fn(() => anchorUnix); 19 global.Date.now = dateNowStub; 20 21 const newStateDate = dateTime().subtract(1, 'y'); 22 const newStateDateFormatted = newStateDate.format('YYYY-MM-DD'); 23 const newStateDateAge = newStateDate.fromNow(true); 24 const payload: AlertRuleDTO[] = [ 25 { 26 id: 2, 27 dashboardId: 7, 28 dashboardUid: 'ggHbN42mk', 29 dashboardSlug: 'alerting-with-testdata', 30 panelId: 4, 31 name: 'TestData - Always Alerting', 32 state: 'alerting', 33 newStateDate: `${newStateDateFormatted}T10:00:30+02:00`, 34 evalDate: '0001-01-01T00:00:00Z', 35 evalData: { evalMatches: [{ metric: 'A-series', tags: null, value: 215 }] }, 36 executionError: '', 37 url: '/d/ggHbN42mk/alerting-with-testdata', 38 }, 39 { 40 id: 1, 41 dashboardId: 7, 42 dashboardUid: 'ggHbN42mk', 43 dashboardSlug: 'alerting-with-testdata', 44 panelId: 3, 45 name: 'TestData - Always OK', 46 state: 'ok', 47 newStateDate: `${newStateDateFormatted}T10:01:01+02:00`, 48 evalDate: '0001-01-01T00:00:00Z', 49 evalData: {}, 50 executionError: '', 51 url: '/d/ggHbN42mk/alerting-with-testdata', 52 }, 53 { 54 id: 3, 55 dashboardId: 7, 56 dashboardUid: 'ggHbN42mk', 57 dashboardSlug: 'alerting-with-testdata', 58 panelId: 3, 59 name: 'TestData - ok', 60 state: 'ok', 61 newStateDate: `${newStateDateFormatted}T10:01:01+02:00`, 62 evalDate: '0001-01-01T00:00:00Z', 63 evalData: {}, 64 executionError: 'error', 65 url: '/d/ggHbN42mk/alerting-with-testdata', 66 }, 67 { 68 id: 4, 69 dashboardId: 7, 70 dashboardUid: 'ggHbN42mk', 71 dashboardSlug: 'alerting-with-testdata', 72 panelId: 3, 73 name: 'TestData - Paused', 74 state: 'paused', 75 newStateDate: `${newStateDateFormatted}T10:01:01+02:00`, 76 evalDate: '0001-01-01T00:00:00Z', 77 evalData: {}, 78 executionError: 'error', 79 url: '/d/ggHbN42mk/alerting-with-testdata', 80 }, 81 { 82 id: 5, 83 dashboardId: 7, 84 dashboardUid: 'ggHbN42mk', 85 dashboardSlug: 'alerting-with-testdata', 86 panelId: 3, 87 name: 'TestData - Ok', 88 state: 'ok', 89 newStateDate: `${newStateDateFormatted}T10:01:01+02:00`, 90 evalDate: '0001-01-01T00:00:00Z', 91 evalData: { 92 noData: true, 93 }, 94 executionError: 'error', 95 url: '/d/ggHbN42mk/alerting-with-testdata', 96 }, 97 ]; 98 99 afterAll(() => { 100 global.Date.now = realDateNow; 101 }); 102 103 describe('when loadAlertRules is dispatched', () => { 104 it('then state should be correct', () => { 105 reducerTester<AlertRulesState>() 106 .givenReducer(alertRulesReducer, { ...initialState }) 107 .whenActionIsDispatched(loadAlertRules()) 108 .thenStateShouldEqual({ ...initialState, isLoading: true }); 109 }); 110 }); 111 112 describe('when setSearchQuery is dispatched', () => { 113 it('then state should be correct', () => { 114 reducerTester<AlertRulesState>() 115 .givenReducer(alertRulesReducer, { ...initialState }) 116 .whenActionIsDispatched(setSearchQuery('query')) 117 .thenStateShouldEqual({ ...initialState, searchQuery: 'query' }); 118 }); 119 }); 120 121 describe('when loadedAlertRules is dispatched', () => { 122 it('then state should be correct', () => { 123 reducerTester<AlertRulesState>() 124 .givenReducer(alertRulesReducer, { ...initialState, isLoading: true }) 125 .whenActionIsDispatched(loadedAlertRules(payload)) 126 .thenStateShouldEqual({ 127 ...initialState, 128 isLoading: false, 129 items: [ 130 { 131 dashboardId: 7, 132 dashboardSlug: 'alerting-with-testdata', 133 dashboardUid: 'ggHbN42mk', 134 evalData: { 135 evalMatches: [ 136 { 137 metric: 'A-series', 138 tags: null, 139 value: 215, 140 }, 141 ], 142 }, 143 evalDate: '0001-01-01T00:00:00Z', 144 executionError: '', 145 id: 2, 146 name: 'TestData - Always Alerting', 147 newStateDate: `${newStateDateFormatted}T10:00:30+02:00`, 148 panelId: 4, 149 state: 'alerting', 150 stateAge: newStateDateAge, 151 stateClass: 'alert-state-critical', 152 stateIcon: 'heart-break', 153 stateText: 'ALERTING', 154 url: '/d/ggHbN42mk/alerting-with-testdata', 155 }, 156 { 157 dashboardId: 7, 158 dashboardSlug: 'alerting-with-testdata', 159 dashboardUid: 'ggHbN42mk', 160 evalData: {}, 161 evalDate: '0001-01-01T00:00:00Z', 162 executionError: '', 163 id: 1, 164 name: 'TestData - Always OK', 165 newStateDate: `${newStateDateFormatted}T10:01:01+02:00`, 166 panelId: 3, 167 state: 'ok', 168 stateAge: newStateDateAge, 169 stateClass: 'alert-state-ok', 170 stateIcon: 'heart', 171 stateText: 'OK', 172 url: '/d/ggHbN42mk/alerting-with-testdata', 173 }, 174 { 175 dashboardId: 7, 176 dashboardSlug: 'alerting-with-testdata', 177 dashboardUid: 'ggHbN42mk', 178 evalData: {}, 179 evalDate: '0001-01-01T00:00:00Z', 180 executionError: 'error', 181 id: 3, 182 info: 'Execution Error: error', 183 name: 'TestData - ok', 184 newStateDate: `${newStateDateFormatted}T10:01:01+02:00`, 185 panelId: 3, 186 state: 'ok', 187 stateAge: newStateDateAge, 188 stateClass: 'alert-state-ok', 189 stateIcon: 'heart', 190 stateText: 'OK', 191 url: '/d/ggHbN42mk/alerting-with-testdata', 192 }, 193 { 194 dashboardId: 7, 195 dashboardSlug: 'alerting-with-testdata', 196 dashboardUid: 'ggHbN42mk', 197 evalData: {}, 198 evalDate: '0001-01-01T00:00:00Z', 199 executionError: 'error', 200 id: 4, 201 name: 'TestData - Paused', 202 newStateDate: `${newStateDateFormatted}T10:01:01+02:00`, 203 panelId: 3, 204 state: 'paused', 205 stateAge: newStateDateAge, 206 stateClass: 'alert-state-paused', 207 stateIcon: 'pause', 208 stateText: 'PAUSED', 209 url: '/d/ggHbN42mk/alerting-with-testdata', 210 }, 211 { 212 dashboardId: 7, 213 dashboardSlug: 'alerting-with-testdata', 214 dashboardUid: 'ggHbN42mk', 215 evalData: { 216 noData: true, 217 }, 218 evalDate: '0001-01-01T00:00:00Z', 219 executionError: 'error', 220 id: 5, 221 info: 'Query returned no data', 222 name: 'TestData - Ok', 223 newStateDate: `${newStateDateFormatted}T10:01:01+02:00`, 224 panelId: 3, 225 state: 'ok', 226 stateAge: newStateDateAge, 227 stateClass: 'alert-state-ok', 228 stateIcon: 'heart', 229 stateText: 'OK', 230 url: '/d/ggHbN42mk/alerting-with-testdata', 231 }, 232 ], 233 }); 234 }); 235 }); 236}); 237 238describe('Notification channel', () => { 239 const notifiers: NotifierDTO[] = [ 240 { 241 type: 'webhook', 242 name: 'webhook', 243 heading: 'Webhook settings', 244 description: 'Sends HTTP POST request to a URL', 245 info: '', 246 options: [ 247 { 248 element: 'input', 249 inputType: 'text', 250 label: 'Url', 251 description: '', 252 placeholder: '', 253 propertyName: 'url', 254 showWhen: { field: '', is: '' }, 255 required: true, 256 validationRule: '', 257 secure: false, 258 }, 259 { 260 element: 'select', 261 inputType: '', 262 label: 'Http Method', 263 description: '', 264 placeholder: '', 265 propertyName: 'httpMethod', 266 selectOptions: [ 267 { value: 'POST', label: 'POST' }, 268 { value: 'PUT', label: 'PUT' }, 269 ], 270 showWhen: { field: '', is: '' }, 271 required: false, 272 validationRule: '', 273 secure: false, 274 }, 275 { 276 element: 'input', 277 inputType: 'text', 278 label: 'Username', 279 description: '', 280 placeholder: '', 281 propertyName: 'username', 282 showWhen: { field: '', is: '' }, 283 required: false, 284 validationRule: '', 285 secure: false, 286 }, 287 { 288 element: 'input', 289 inputType: 'password', 290 label: 'Password', 291 description: '', 292 placeholder: '', 293 propertyName: 'password', 294 showWhen: { field: '', is: '' }, 295 required: false, 296 validationRule: '', 297 secure: true, 298 }, 299 ], 300 }, 301 ]; 302 303 describe('Load notification channel', () => { 304 it('should migrate non secure settings to secure fields', () => { 305 const payload = { 306 id: 2, 307 uid: '9L3FrrHGk', 308 name: 'Webhook test', 309 type: 'webhook', 310 isDefault: false, 311 sendReminder: false, 312 disableResolveMessage: false, 313 frequency: '', 314 created: '2020-08-28T08:49:24Z', 315 updated: '2020-08-28T08:49:24Z', 316 settings: { 317 autoResolve: true, 318 httpMethod: 'POST', 319 password: 'asdf', 320 severity: 'critical', 321 uploadImage: true, 322 url: 'http://localhost.webhook', 323 username: 'asdf', 324 }, 325 }; 326 327 const expected = { 328 id: 2, 329 uid: '9L3FrrHGk', 330 name: 'Webhook test', 331 type: 'webhook', 332 isDefault: false, 333 sendReminder: false, 334 disableResolveMessage: false, 335 frequency: '', 336 created: '2020-08-28T08:49:24Z', 337 updated: '2020-08-28T08:49:24Z', 338 secureSettings: { 339 password: 'asdf', 340 }, 341 settings: { 342 autoResolve: true, 343 httpMethod: 'POST', 344 password: '', 345 severity: 'critical', 346 uploadImage: true, 347 url: 'http://localhost.webhook', 348 username: 'asdf', 349 }, 350 }; 351 352 reducerTester<NotificationChannelState>() 353 .givenReducer(notificationChannelReducer, { ...initialChannelState, notifiers: notifiers }) 354 .whenActionIsDispatched(notificationChannelLoaded(payload)) 355 .thenStateShouldEqual({ 356 ...initialChannelState, 357 notifiers: notifiers, 358 notificationChannel: expected, 359 }); 360 }); 361 362 it('should handle already secure field', () => { 363 const payload = { 364 id: 2, 365 uid: '9L3FrrHGk', 366 name: 'Webhook test', 367 type: 'webhook', 368 isDefault: false, 369 sendReminder: false, 370 disableResolveMessage: false, 371 frequency: '', 372 created: '2020-08-28T08:49:24Z', 373 updated: '2020-08-28T08:49:24Z', 374 secureFields: { 375 password: true, 376 }, 377 settings: { 378 autoResolve: true, 379 httpMethod: 'POST', 380 password: '', 381 severity: 'critical', 382 uploadImage: true, 383 url: 'http://localhost.webhook', 384 username: 'asdf', 385 }, 386 }; 387 388 const expected = { 389 id: 2, 390 uid: '9L3FrrHGk', 391 name: 'Webhook test', 392 type: 'webhook', 393 isDefault: false, 394 sendReminder: false, 395 disableResolveMessage: false, 396 frequency: '', 397 created: '2020-08-28T08:49:24Z', 398 updated: '2020-08-28T08:49:24Z', 399 secureFields: { 400 password: true, 401 }, 402 settings: { 403 autoResolve: true, 404 httpMethod: 'POST', 405 password: '', 406 severity: 'critical', 407 uploadImage: true, 408 url: 'http://localhost.webhook', 409 username: 'asdf', 410 }, 411 }; 412 413 reducerTester<NotificationChannelState>() 414 .givenReducer(notificationChannelReducer, { ...initialChannelState, notifiers: notifiers }) 415 .whenActionIsDispatched(notificationChannelLoaded(payload)) 416 .thenStateShouldEqual({ 417 ...initialChannelState, 418 notifiers: notifiers, 419 notificationChannel: expected, 420 }); 421 }); 422 }); 423}); 424