2022-04-22 14:33:13 +01:00
import { SerializedError } from '@reduxjs/toolkit' ;
2023-02-20 08:47:50 +01:00
import { prettyDOM , render , screen , waitFor } from '@testing-library/react' ;
2022-04-22 14:33:13 +01:00
import userEvent from '@testing-library/user-event' ;
import React from 'react' ;
2023-02-02 09:53:06 +01:00
import { TestProvider } from 'test/helpers/TestProvider' ;
2022-11-03 12:50:32 +01:00
import { byRole , byTestId , byText } from 'testing-library-selector' ;
2022-04-22 14:33:13 +01:00
2023-02-14 16:46:42 +01:00
import { DataSourceSrv , locationService , logInfo , setBackendSrv , setDataSourceSrv } from '@grafana/runtime' ;
2023-01-11 12:52:20 +01:00
import { backendSrv } from 'app/core/services/backend_srv' ;
2022-05-23 09:58:20 -04:00
import { contextSrv } from 'app/core/services/context_srv' ;
2022-10-31 14:42:09 +01:00
import * as ruleActionButtons from 'app/features/alerting/unified/components/rules/RuleActionsButtons' ;
2022-11-03 12:50:32 +01:00
import * as actions from 'app/features/alerting/unified/state/actions' ;
2022-04-26 15:57:00 +02:00
import { AccessControlAction } from 'app/types' ;
2022-04-22 14:33:13 +01:00
import { PromAlertingRuleState , PromApplication } from 'app/types/unified-alerting-dto' ;
2022-10-03 11:00:19 -03:00
import { LogMessages } from './Analytics' ;
2022-04-22 14:33:13 +01:00
import RuleList from './RuleList' ;
2022-05-18 10:45:26 +02:00
import { discoverFeatures } from './api/buildInfo' ;
2022-04-22 14:33:13 +01:00
import { fetchRules } from './api/prometheus' ;
2022-04-26 15:57:00 +02:00
import { deleteNamespace , deleteRulerRulesGroup , fetchRulerRules , setRulerRuleGroup } from './api/ruler' ;
2021-04-07 08:42:43 +03:00
import {
2022-05-16 03:45:41 -07:00
disableRBAC ,
2022-04-26 15:57:00 +02:00
enableRBAC ,
grantUserPermissions ,
2021-04-07 08:42:43 +03:00
mockDataSource ,
2022-04-26 15:57:00 +02:00
MockDataSourceSrv ,
2021-04-07 08:42:43 +03:00
mockPromAlert ,
mockPromAlertingRule ,
mockPromRecordingRule ,
mockPromRuleGroup ,
mockPromRuleNamespace ,
2021-08-26 16:40:27 +03:00
somePromRules ,
someRulerRules ,
2021-04-07 08:42:43 +03:00
} from './mocks' ;
2022-08-01 15:01:14 +02:00
import * as config from './utils/config' ;
2021-04-07 08:42:43 +03:00
import { DataSourceType , GRAFANA_RULES_SOURCE_NAME } from './utils/datasource' ;
2022-04-04 19:30:17 +02:00
jest . mock ( './api/buildInfo' ) ;
2021-04-07 08:42:43 +03:00
jest . mock ( './api/prometheus' ) ;
2021-08-26 16:40:27 +03:00
jest . mock ( './api/ruler' ) ;
2022-10-31 14:42:09 +01:00
jest . mock ( '../../../core/hooks/useMediaQueryChange' ) ;
jest . spyOn ( ruleActionButtons , 'matchesWidth' ) . mockReturnValue ( false ) ;
2021-08-26 16:40:27 +03:00
jest . mock ( 'app/core/core' , ( ) = > ( {
2023-05-22 14:26:20 -03:00
. . . jest . requireActual ( 'app/core/core' ) ,
2021-08-26 16:40:27 +03:00
appEvents : {
subscribe : ( ) = > {
return { unsubscribe : ( ) = > { } } ;
} ,
emit : ( ) = > { } ,
} ,
} ) ) ;
2022-10-03 11:00:19 -03:00
jest . mock ( '@grafana/runtime' , ( ) = > {
const original = jest . requireActual ( '@grafana/runtime' ) ;
return {
. . . original ,
logInfo : jest.fn ( ) ,
} ;
} ) ;
2021-04-07 08:42:43 +03:00
2022-08-01 15:01:14 +02:00
jest . spyOn ( config , 'getAllDataSources' ) ;
2022-11-03 12:50:32 +01:00
jest . spyOn ( actions , 'rulesInSameGroupHaveInvalidFor' ) . mockReturnValue ( [ ] ) ;
2022-08-01 15:01:14 +02:00
2021-04-07 08:42:43 +03:00
const mocks = {
2022-08-01 15:01:14 +02:00
getAllDataSourcesMock : jest.mocked ( config . getAllDataSources ) ,
2022-11-03 12:50:32 +01:00
rulesInSameGroupHaveInvalidForMock : jest.mocked ( actions . rulesInSameGroupHaveInvalidFor ) ,
2021-04-07 08:42:43 +03:00
api : {
2022-05-18 10:45:26 +02:00
discoverFeatures : jest.mocked ( discoverFeatures ) ,
2022-02-10 09:50:59 +11:00
fetchRules : jest.mocked ( fetchRules ) ,
fetchRulerRules : jest.mocked ( fetchRulerRules ) ,
deleteGroup : jest.mocked ( deleteRulerRulesGroup ) ,
deleteNamespace : jest.mocked ( deleteNamespace ) ,
setRulerRuleGroup : jest.mocked ( setRulerRuleGroup ) ,
2021-04-07 08:42:43 +03:00
} ,
} ;
const renderRuleList = ( ) = > {
2021-08-26 16:40:27 +03:00
locationService . push ( '/' ) ;
2021-04-07 08:42:43 +03:00
return render (
2023-02-02 09:53:06 +01:00
< TestProvider >
< RuleList / >
< / TestProvider >
2021-04-07 08:42:43 +03:00
) ;
} ;
const dataSources = {
prom : mockDataSource ( {
name : 'Prometheus' ,
type : DataSourceType . Prometheus ,
} ) ,
2021-07-13 01:06:09 +03:00
promdisabled : mockDataSource ( {
name : 'Prometheus-disabled' ,
type : DataSourceType . Prometheus ,
jsonData : {
manageAlerts : false ,
} ,
} ) ,
2021-04-07 08:42:43 +03:00
loki : mockDataSource ( {
name : 'Loki' ,
type : DataSourceType . Loki ,
} ) ,
promBroken : mockDataSource ( {
name : 'Prometheus-broken' ,
type : DataSourceType . Prometheus ,
} ) ,
} ;
const ui = {
ruleGroup : byTestId ( 'rule-group' ) ,
cloudRulesSourceErrors : byTestId ( 'cloud-rulessource-errors' ) ,
groupCollapseToggle : byTestId ( 'group-collapse-toggle' ) ,
2021-06-23 09:27:47 +03:00
ruleCollapseToggle : byTestId ( 'collapse-toggle' ) ,
2021-04-07 08:42:43 +03:00
rulesTable : byTestId ( 'rules-table' ) ,
2021-06-23 09:27:47 +03:00
ruleRow : byTestId ( 'row' ) ,
expandedContent : byTestId ( 'expanded-content' ) ,
2021-07-26 12:05:49 -07:00
rulesFilterInput : byTestId ( 'search-query-input' ) ,
2021-08-12 10:57:52 +03:00
moreErrorsButton : byRole ( 'button' , { name : /more errors/ } ) ,
2021-08-26 16:40:27 +03:00
editCloudGroupIcon : byTestId ( 'edit-group' ) ,
2023-01-30 16:26:21 +01:00
newRuleButton : byRole ( 'link' , { name : 'Create alert rule' } ) ,
2023-03-03 14:34:06 +01:00
exportButton : byRole ( 'link' , { name : /export/i } ) ,
2021-08-26 16:40:27 +03:00
editGroupModal : {
2023-02-20 08:47:50 +01:00
dialog : byRole ( 'dialog' ) ,
namespaceInput : byRole ( 'textbox' , { name : /^Namespace/ } ) ,
ruleGroupInput : byRole ( 'textbox' , { name : /Evaluation group/ } ) ,
2022-11-03 12:50:32 +01:00
intervalInput : byRole ( 'textbox' , {
name : /Rule group evaluation interval Evaluation interval should be smaller or equal to 'For' values for existing rules in this group./i ,
} ) ,
2023-05-04 17:05:26 +02:00
saveButton : byRole ( 'button' , { name : /Save evaluation interval/ } ) ,
2021-08-26 16:40:27 +03:00
} ,
2021-04-07 08:42:43 +03:00
} ;
2023-01-11 12:52:20 +01:00
beforeAll ( ( ) = > {
setBackendSrv ( backendSrv ) ;
} ) ;
2021-04-07 08:42:43 +03:00
describe ( 'RuleList' , ( ) = > {
2022-05-23 09:58:20 -04:00
beforeEach ( ( ) = > {
contextSrv . isEditor = true ;
2022-11-03 12:50:32 +01:00
mocks . rulesInSameGroupHaveInvalidForMock . mockReturnValue ( [ ] ) ;
2022-05-23 09:58:20 -04:00
} ) ;
2021-04-15 04:53:40 -07:00
afterEach ( ( ) = > {
jest . resetAllMocks ( ) ;
2023-02-14 16:46:42 +01:00
setDataSourceSrv ( undefined as unknown as DataSourceSrv ) ;
2021-04-15 04:53:40 -07:00
} ) ;
2021-04-07 08:42:43 +03:00
it ( 'load & show rule groups from multiple cloud data sources' , async ( ) = > {
2022-05-16 03:45:41 -07:00
disableRBAC ( ) ;
2021-04-07 08:42:43 +03:00
mocks . getAllDataSourcesMock . mockReturnValue ( Object . values ( dataSources ) ) ;
2021-04-15 04:53:40 -07:00
setDataSourceSrv ( new MockDataSourceSrv ( dataSources ) ) ;
2022-05-18 10:45:26 +02:00
mocks . api . discoverFeatures . mockResolvedValue ( {
2022-04-04 19:30:17 +02:00
application : PromApplication.Prometheus ,
features : {
rulerApiEnabled : true ,
} ,
} ) ;
2021-04-07 08:42:43 +03:00
mocks . api . fetchRules . mockImplementation ( ( dataSourceName : string ) = > {
if ( dataSourceName === dataSources . prom . name ) {
return Promise . resolve ( [
mockPromRuleNamespace ( {
name : 'default' ,
dataSourceName : dataSources.prom.name ,
groups : [
mockPromRuleGroup ( {
name : 'group-2' ,
} ) ,
mockPromRuleGroup ( {
name : 'group-1' ,
} ) ,
] ,
} ) ,
] ) ;
} else if ( dataSourceName === dataSources . loki . name ) {
return Promise . resolve ( [
mockPromRuleNamespace ( {
name : 'default' ,
dataSourceName : dataSources.loki.name ,
groups : [
mockPromRuleGroup ( {
name : 'group-1' ,
} ) ,
] ,
} ) ,
mockPromRuleNamespace ( {
name : 'lokins' ,
dataSourceName : dataSources.loki.name ,
groups : [
mockPromRuleGroup ( {
name : 'group-1' ,
} ) ,
] ,
} ) ,
] ) ;
} else if ( dataSourceName === dataSources . promBroken . name ) {
return Promise . reject ( { message : 'this datasource is broken' } as SerializedError ) ;
} else if ( dataSourceName === GRAFANA_RULES_SOURCE_NAME ) {
return Promise . resolve ( [
mockPromRuleNamespace ( {
2021-04-14 15:57:36 +03:00
name : 'foofolder' ,
2021-04-07 08:42:43 +03:00
dataSourceName : GRAFANA_RULES_SOURCE_NAME ,
groups : [
mockPromRuleGroup ( {
name : 'grafana-group' ,
2021-04-19 12:53:02 +03:00
rules : [
mockPromAlertingRule ( {
query : '[]' ,
} ) ,
] ,
2021-04-07 08:42:43 +03:00
} ) ,
] ,
} ) ,
] ) ;
}
return Promise . reject ( new Error ( ` unexpected datasourceName: ${ dataSourceName } ` ) ) ;
} ) ;
2022-10-31 15:55:47 +01:00
mocks . api . fetchRulerRules . mockRejectedValue ( { status : 500 , data : { message : 'Server error' } } ) ;
2021-04-07 08:42:43 +03:00
await renderRuleList ( ) ;
await waitFor ( ( ) = > expect ( mocks . api . fetchRules ) . toHaveBeenCalledTimes ( 4 ) ) ;
const groups = await ui . ruleGroup . findAll ( ) ;
expect ( groups ) . toHaveLength ( 5 ) ;
2021-04-14 15:57:36 +03:00
expect ( groups [ 0 ] ) . toHaveTextContent ( 'foofolder' ) ;
2022-04-20 11:41:33 +02:00
expect ( groups [ 1 ] ) . toHaveTextContent ( 'default group-1' ) ;
expect ( groups [ 2 ] ) . toHaveTextContent ( 'default group-1' ) ;
expect ( groups [ 3 ] ) . toHaveTextContent ( 'default group-2' ) ;
expect ( groups [ 4 ] ) . toHaveTextContent ( 'lokins group-1' ) ;
2021-04-07 08:42:43 +03:00
const errors = await ui . cloudRulesSourceErrors . find ( ) ;
2021-08-12 10:57:52 +03:00
expect ( errors ) . not . toHaveTextContent (
'Failed to load rules state from Prometheus-broken: this datasource is broken'
) ;
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . moreErrorsButton . get ( ) ) ;
2021-04-07 08:42:43 +03:00
expect ( errors ) . toHaveTextContent ( 'Failed to load rules state from Prometheus-broken: this datasource is broken' ) ;
} ) ;
it ( 'expand rule group, rule and alert details' , async ( ) = > {
mocks . getAllDataSourcesMock . mockReturnValue ( [ dataSources . prom ] ) ;
2022-04-04 19:30:17 +02:00
2021-04-15 04:53:40 -07:00
setDataSourceSrv ( new MockDataSourceSrv ( { prom : dataSources.prom } ) ) ;
2022-05-18 10:45:26 +02:00
mocks . api . discoverFeatures . mockResolvedValue ( {
2022-10-24 14:53:11 -05:00
application : PromApplication.Cortex ,
2022-04-04 19:30:17 +02:00
features : {
rulerApiEnabled : true ,
} ,
} ) ;
2021-04-07 08:42:43 +03:00
mocks . api . fetchRules . mockImplementation ( ( dataSourceName : string ) = > {
if ( dataSourceName === GRAFANA_RULES_SOURCE_NAME ) {
return Promise . resolve ( [ ] ) ;
} else {
return Promise . resolve ( [
mockPromRuleNamespace ( {
groups : [
mockPromRuleGroup ( {
name : 'group-1' ,
} ) ,
mockPromRuleGroup ( {
name : 'group-2' ,
rules : [
mockPromRecordingRule ( {
name : 'recordingrule' ,
} ) ,
mockPromAlertingRule ( {
name : 'alertingrule' ,
labels : {
severity : 'warning' ,
foo : 'bar' ,
} ,
query : 'topk(5, foo)[5m]' ,
annotations : {
message : 'great alert' ,
} ,
alerts : [
mockPromAlert ( {
labels : {
foo : 'bar' ,
severity : 'warning' ,
} ,
value : '2e+10' ,
annotations : {
message : 'first alert message' ,
} ,
} ) ,
mockPromAlert ( {
labels : {
foo : 'baz' ,
severity : 'error' ,
} ,
value : '3e+11' ,
annotations : {
message : 'first alert message' ,
} ,
} ) ,
] ,
} ) ,
mockPromAlertingRule ( {
name : 'p-rule' ,
alerts : [ ] ,
state : PromAlertingRuleState.Pending ,
} ) ,
mockPromAlertingRule ( {
name : 'i-rule' ,
alerts : [ ] ,
state : PromAlertingRuleState.Inactive ,
} ) ,
] ,
} ) ,
] ,
} ) ,
] ) ;
}
} ) ;
await renderRuleList ( ) ;
const groups = await ui . ruleGroup . findAll ( ) ;
expect ( groups ) . toHaveLength ( 2 ) ;
2023-03-09 16:24:32 +01:00
await waitFor ( ( ) = > expect ( groups [ 0 ] ) . toHaveTextContent ( /firing|pending|normal/ ) ) ;
2023-03-14 09:45:27 +01:00
await waitFor ( ( ) = > expect ( groups [ 1 ] ) . toHaveTextContent ( /firing|pending|normal/ ) ) ;
2023-03-09 16:24:32 +01:00
2022-12-12 13:53:18 +01:00
expect ( groups [ 0 ] ) . toHaveTextContent ( '1 firing' ) ;
expect ( groups [ 1 ] ) . toHaveTextContent ( '1 firing' ) ;
expect ( groups [ 1 ] ) . toHaveTextContent ( '1 pending' ) ;
expect ( groups [ 1 ] ) . toHaveTextContent ( '1 recording' ) ;
expect ( groups [ 1 ] ) . toHaveTextContent ( '1 normal' ) ;
2021-04-07 08:42:43 +03:00
// expand second group to see rules table
expect ( ui . rulesTable . query ( ) ) . not . toBeInTheDocument ( ) ;
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . groupCollapseToggle . get ( groups [ 1 ] ) ) ;
2021-04-07 08:42:43 +03:00
const table = await ui . rulesTable . find ( groups [ 1 ] ) ;
// check that rule rows are rendered properly
2021-06-23 09:27:47 +03:00
let ruleRows = ui . ruleRow . getAll ( table ) ;
2021-04-07 08:42:43 +03:00
expect ( ruleRows ) . toHaveLength ( 4 ) ;
2021-05-14 13:41:13 +03:00
expect ( ruleRows [ 0 ] ) . toHaveTextContent ( 'Recording rule' ) ;
2021-04-07 08:42:43 +03:00
expect ( ruleRows [ 0 ] ) . toHaveTextContent ( 'recordingrule' ) ;
2021-05-14 13:41:13 +03:00
expect ( ruleRows [ 1 ] ) . toHaveTextContent ( 'Firing' ) ;
2021-04-07 08:42:43 +03:00
expect ( ruleRows [ 1 ] ) . toHaveTextContent ( 'alertingrule' ) ;
2021-05-14 13:41:13 +03:00
expect ( ruleRows [ 2 ] ) . toHaveTextContent ( 'Pending' ) ;
2021-04-07 08:42:43 +03:00
expect ( ruleRows [ 2 ] ) . toHaveTextContent ( 'p-rule' ) ;
2021-05-14 13:41:13 +03:00
expect ( ruleRows [ 3 ] ) . toHaveTextContent ( 'Normal' ) ;
2021-04-07 08:42:43 +03:00
expect ( ruleRows [ 3 ] ) . toHaveTextContent ( 'i-rule' ) ;
expect ( byText ( 'Labels' ) . query ( ) ) . not . toBeInTheDocument ( ) ;
// expand alert details
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . ruleCollapseToggle . get ( ruleRows [ 1 ] ) ) ;
2021-04-07 08:42:43 +03:00
2021-06-23 09:27:47 +03:00
const ruleDetails = ui . expandedContent . get ( ruleRows [ 1 ] ) ;
2021-04-07 08:42:43 +03:00
expect ( ruleDetails ) . toHaveTextContent ( 'Labelsseverity=warningfoo=bar' ) ;
expect ( ruleDetails ) . toHaveTextContent ( 'Expressiontopk ( 5 , foo ) [ 5m ]' ) ;
expect ( ruleDetails ) . toHaveTextContent ( 'messagegreat alert' ) ;
expect ( ruleDetails ) . toHaveTextContent ( 'Matching instances' ) ;
// finally, check instances table
2021-06-23 09:27:47 +03:00
const instancesTable = byTestId ( 'dynamic-table' ) . get ( ruleDetails ) ;
2021-04-07 08:42:43 +03:00
expect ( instancesTable ) . toBeInTheDocument ( ) ;
2021-06-23 09:27:47 +03:00
const instanceRows = byTestId ( 'row' ) . getAll ( instancesTable ) ;
2021-04-07 08:42:43 +03:00
expect ( instanceRows ) . toHaveLength ( 2 ) ;
2023-02-01 13:15:03 +01:00
expect ( instanceRows ! [ 0 ] ) . toHaveTextContent ( 'Firing foo=barseverity=warning2021-03-18 08:47:05' ) ;
expect ( instanceRows ! [ 1 ] ) . toHaveTextContent ( 'Firing foo=bazseverity=error2021-03-18 08:47:05' ) ;
2021-04-07 08:42:43 +03:00
// expand details of an instance
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . ruleCollapseToggle . get ( instanceRows ! [ 0 ] ) ) ;
2021-04-07 08:42:43 +03:00
2021-06-23 09:27:47 +03:00
const alertDetails = byTestId ( 'expanded-content' ) . get ( instanceRows [ 0 ] ) ;
2021-04-07 08:42:43 +03:00
expect ( alertDetails ) . toHaveTextContent ( 'Value2e+10' ) ;
expect ( alertDetails ) . toHaveTextContent ( 'messagefirst alert message' ) ;
// collapse everything again
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . ruleCollapseToggle . get ( instanceRows ! [ 0 ] ) ) ;
2021-06-23 09:27:47 +03:00
expect ( byTestId ( 'expanded-content' ) . query ( instanceRows [ 0 ] ) ) . not . toBeInTheDocument ( ) ;
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . ruleCollapseToggle . getAll ( ruleRows [ 1 ] ) [ 0 ] ) ;
await userEvent . click ( ui . groupCollapseToggle . get ( groups [ 1 ] ) ) ;
2021-04-07 08:42:43 +03:00
expect ( ui . rulesTable . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
2021-07-26 12:05:49 -07:00
it ( 'filters rules and alerts by labels' , async ( ) = > {
mocks . getAllDataSourcesMock . mockReturnValue ( [ dataSources . prom ] ) ;
setDataSourceSrv ( new MockDataSourceSrv ( { prom : dataSources.prom } ) ) ;
2022-04-04 19:30:17 +02:00
2022-05-18 10:45:26 +02:00
mocks . api . discoverFeatures . mockResolvedValue ( {
2022-10-24 14:53:11 -05:00
application : PromApplication.Cortex ,
2022-04-04 19:30:17 +02:00
features : {
rulerApiEnabled : true ,
} ,
} ) ;
2021-08-26 16:40:27 +03:00
mocks . api . fetchRulerRules . mockResolvedValue ( { } ) ;
2021-07-26 12:05:49 -07:00
mocks . api . fetchRules . mockImplementation ( ( dataSourceName : string ) = > {
if ( dataSourceName === GRAFANA_RULES_SOURCE_NAME ) {
return Promise . resolve ( [ ] ) ;
} else {
return Promise . resolve ( [
mockPromRuleNamespace ( {
groups : [
mockPromRuleGroup ( {
name : 'group-1' ,
rules : [
mockPromAlertingRule ( {
name : 'alertingrule' ,
labels : {
severity : 'warning' ,
foo : 'bar' ,
} ,
query : 'topk(5, foo)[5m]' ,
annotations : {
message : 'great alert' ,
} ,
alerts : [
mockPromAlert ( {
labels : {
foo : 'bar' ,
severity : 'warning' ,
} ,
value : '2e+10' ,
annotations : {
message : 'first alert message' ,
} ,
} ) ,
mockPromAlert ( {
labels : {
foo : 'baz' ,
severity : 'error' ,
} ,
value : '3e+11' ,
annotations : {
message : 'first alert message' ,
} ,
} ) ,
] ,
} ) ,
] ,
} ) ,
mockPromRuleGroup ( {
name : 'group-2' ,
rules : [
mockPromAlertingRule ( {
name : 'alertingrule2' ,
labels : {
severity : 'error' ,
foo : 'buzz' ,
} ,
query : 'topk(5, foo)[5m]' ,
annotations : {
message : 'great alert' ,
} ,
alerts : [
mockPromAlert ( {
labels : {
foo : 'buzz' ,
severity : 'error' ,
region : 'EU' ,
} ,
value : '2e+10' ,
annotations : {
message : 'alert message' ,
} ,
} ) ,
mockPromAlert ( {
labels : {
foo : 'buzz' ,
severity : 'error' ,
region : 'US' ,
} ,
value : '3e+11' ,
annotations : {
message : 'alert message' ,
} ,
} ) ,
] ,
} ) ,
] ,
} ) ,
] ,
} ) ,
] ) ;
}
} ) ;
await renderRuleList ( ) ;
2023-03-09 16:24:32 +01:00
2021-07-26 12:05:49 -07:00
const groups = await ui . ruleGroup . findAll ( ) ;
expect ( groups ) . toHaveLength ( 2 ) ;
const filterInput = ui . rulesFilterInput . get ( ) ;
2023-03-09 16:24:32 +01:00
await userEvent . type ( filterInput , 'label:foo=bar{Enter}' ) ;
2021-07-26 12:05:49 -07:00
// Input is debounced so wait for it to be visible
2023-01-26 13:44:14 +01:00
await waitFor ( ( ) = > expect ( filterInput ) . toHaveValue ( 'label:foo=bar' ) ) ;
2021-07-26 12:05:49 -07:00
// Group doesn't contain matching labels
2021-08-26 16:40:27 +03:00
await waitFor ( ( ) = > expect ( ui . ruleGroup . queryAll ( ) ) . toHaveLength ( 1 ) ) ;
2021-07-26 12:05:49 -07:00
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . groupCollapseToggle . get ( groups [ 0 ] ) ) ;
2021-07-26 12:05:49 -07:00
const ruleRows = ui . ruleRow . getAll ( groups [ 0 ] ) ;
expect ( ruleRows ) . toHaveLength ( 1 ) ;
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . ruleCollapseToggle . get ( ruleRows [ 0 ] ) ) ;
2021-07-26 12:05:49 -07:00
const ruleDetails = ui . expandedContent . get ( ruleRows [ 0 ] ) ;
expect ( ruleDetails ) . toHaveTextContent ( 'Labelsseverity=warningfoo=bar' ) ;
// Check for different label matchers
2022-04-21 13:15:21 +01:00
await userEvent . clear ( filterInput ) ;
2023-03-09 16:24:32 +01:00
await userEvent . type ( filterInput , 'label:foo!=bar label:foo!=baz{Enter}' ) ;
2021-07-26 12:05:49 -07:00
// Group doesn't contain matching labels
2021-08-26 16:40:27 +03:00
await waitFor ( ( ) = > expect ( ui . ruleGroup . queryAll ( ) ) . toHaveLength ( 1 ) ) ;
await waitFor ( ( ) = > expect ( ui . ruleGroup . get ( ) ) . toHaveTextContent ( 'group-2' ) ) ;
2022-04-21 13:15:21 +01:00
await userEvent . clear ( filterInput ) ;
2023-03-09 16:24:32 +01:00
await userEvent . type ( filterInput , 'label:"foo=~b.+"{Enter}' ) ;
2021-08-26 16:40:27 +03:00
await waitFor ( ( ) = > expect ( ui . ruleGroup . queryAll ( ) ) . toHaveLength ( 2 ) ) ;
2022-04-21 13:15:21 +01:00
await userEvent . clear ( filterInput ) ;
2023-03-09 16:24:32 +01:00
await userEvent . type ( filterInput , 'label:region=US{Enter}' ) ;
2021-08-26 16:40:27 +03:00
await waitFor ( ( ) = > expect ( ui . ruleGroup . queryAll ( ) ) . toHaveLength ( 1 ) ) ;
await waitFor ( ( ) = > expect ( ui . ruleGroup . get ( ) ) . toHaveTextContent ( 'group-2' ) ) ;
} ) ;
describe ( 'edit lotex groups, namespaces' , ( ) = > {
const testDatasources = {
prom : dataSources.prom ,
} ;
function testCase ( name : string , fn : ( ) = > Promise < void > ) {
it ( name , async ( ) = > {
mocks . getAllDataSourcesMock . mockReturnValue ( Object . values ( testDatasources ) ) ;
setDataSourceSrv ( new MockDataSourceSrv ( testDatasources ) ) ;
2022-04-04 19:30:17 +02:00
2022-05-18 10:45:26 +02:00
mocks . api . discoverFeatures . mockResolvedValue ( {
2022-10-24 14:53:11 -05:00
application : PromApplication.Cortex ,
2022-04-04 19:30:17 +02:00
features : {
rulerApiEnabled : true ,
} ,
} ) ;
2021-08-26 16:40:27 +03:00
mocks . api . fetchRules . mockImplementation ( ( sourceName ) = >
Promise . resolve ( sourceName === testDatasources . prom . name ? somePromRules ( ) : [ ] )
) ;
2022-04-04 19:30:17 +02:00
mocks . api . fetchRulerRules . mockImplementation ( ( { dataSourceName } ) = >
Promise . resolve ( dataSourceName === testDatasources . prom . name ? someRulerRules : { } )
2021-08-26 16:40:27 +03:00
) ;
mocks . api . setRulerRuleGroup . mockResolvedValue ( ) ;
mocks . api . deleteNamespace . mockResolvedValue ( ) ;
await renderRuleList ( ) ;
expect ( await ui . rulesFilterInput . find ( ) ) . toHaveValue ( '' ) ;
2023-02-20 08:47:50 +01:00
await waitFor ( ( ) = > expect ( ui . ruleGroup . queryAll ( ) ) . toHaveLength ( 3 ) ) ;
2021-08-26 16:40:27 +03:00
const groups = await ui . ruleGroup . findAll ( ) ;
expect ( groups ) . toHaveLength ( 3 ) ;
// open edit dialog
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . editCloudGroupIcon . get ( groups [ 0 ] ) ) ;
2023-02-20 08:47:50 +01:00
await waitFor ( ( ) = > expect ( ui . editGroupModal . dialog . get ( ) ) . toBeInTheDocument ( ) ) ;
prettyDOM ( ui . editGroupModal . dialog . get ( ) ) ;
expect ( ui . editGroupModal . namespaceInput . get ( ) ) . toHaveDisplayValue ( 'namespace1' ) ;
expect ( ui . editGroupModal . ruleGroupInput . get ( ) ) . toHaveDisplayValue ( 'group1' ) ;
2021-08-26 16:40:27 +03:00
await fn ( ) ;
} ) ;
}
testCase ( 'rename both lotex namespace and group' , async ( ) = > {
// make changes to form
2022-04-21 13:15:21 +01:00
await userEvent . clear ( ui . editGroupModal . namespaceInput . get ( ) ) ;
await userEvent . type ( ui . editGroupModal . namespaceInput . get ( ) , 'super namespace' ) ;
2021-08-26 16:40:27 +03:00
2022-04-21 13:15:21 +01:00
await userEvent . clear ( ui . editGroupModal . ruleGroupInput . get ( ) ) ;
await userEvent . type ( ui . editGroupModal . ruleGroupInput . get ( ) , 'super group' ) ;
2021-08-26 16:40:27 +03:00
2022-04-21 13:15:21 +01:00
await userEvent . type ( ui . editGroupModal . intervalInput . get ( ) , '5m' ) ;
2021-08-26 16:40:27 +03:00
// submit, check that appropriate calls were made
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . editGroupModal . saveButton . get ( ) ) ;
2021-08-26 16:40:27 +03:00
await waitFor ( ( ) = > expect ( ui . editGroupModal . namespaceInput . query ( ) ) . not . toBeInTheDocument ( ) ) ;
expect ( mocks . api . setRulerRuleGroup ) . toHaveBeenCalledTimes ( 2 ) ;
expect ( mocks . api . deleteNamespace ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( mocks . api . deleteGroup ) . not . toHaveBeenCalled ( ) ;
expect ( mocks . api . fetchRulerRules ) . toHaveBeenCalledTimes ( 4 ) ;
2022-04-04 19:30:17 +02:00
expect ( mocks . api . setRulerRuleGroup ) . toHaveBeenNthCalledWith (
1 ,
{ dataSourceName : testDatasources.prom.name , apiVersion : 'legacy' } ,
'super namespace' ,
{
. . . someRulerRules [ 'namespace1' ] [ 0 ] ,
name : 'super group' ,
interval : '5m' ,
}
) ;
2021-08-26 16:40:27 +03:00
expect ( mocks . api . setRulerRuleGroup ) . toHaveBeenNthCalledWith (
2 ,
2022-04-04 19:30:17 +02:00
{ dataSourceName : testDatasources.prom.name , apiVersion : 'legacy' } ,
2021-08-26 16:40:27 +03:00
'super namespace' ,
someRulerRules [ 'namespace1' ] [ 1 ]
) ;
2022-04-04 19:30:17 +02:00
expect ( mocks . api . deleteNamespace ) . toHaveBeenLastCalledWith (
{ dataSourceName : testDatasources.prom.name , apiVersion : 'legacy' } ,
'namespace1'
) ;
2021-08-26 16:40:27 +03:00
} ) ;
testCase ( 'rename just the lotex group' , async ( ) = > {
// make changes to form
2023-02-20 08:47:50 +01:00
await userEvent . clear ( ui . editGroupModal . ruleGroupInput . get ( ) ) ;
await userEvent . type ( ui . editGroupModal . ruleGroupInput . get ( ) , 'super group' ) ;
2022-11-03 12:50:32 +01:00
await userEvent . type (
screen . getByRole ( 'textbox' , {
name : /rule group evaluation interval evaluation interval should be smaller or equal to 'for' values for existing rules in this group\./i ,
} ) ,
'5m'
) ;
2021-08-26 16:40:27 +03:00
// submit, check that appropriate calls were made
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . editGroupModal . saveButton . get ( ) ) ;
2021-08-26 16:40:27 +03:00
await waitFor ( ( ) = > expect ( ui . editGroupModal . namespaceInput . query ( ) ) . not . toBeInTheDocument ( ) ) ;
expect ( mocks . api . setRulerRuleGroup ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( mocks . api . deleteGroup ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( mocks . api . deleteNamespace ) . not . toHaveBeenCalled ( ) ;
expect ( mocks . api . fetchRulerRules ) . toHaveBeenCalledTimes ( 4 ) ;
2022-04-04 19:30:17 +02:00
expect ( mocks . api . setRulerRuleGroup ) . toHaveBeenNthCalledWith (
1 ,
{ dataSourceName : testDatasources.prom.name , apiVersion : 'legacy' } ,
'namespace1' ,
{
. . . someRulerRules [ 'namespace1' ] [ 0 ] ,
name : 'super group' ,
interval : '5m' ,
}
) ;
expect ( mocks . api . deleteGroup ) . toHaveBeenLastCalledWith (
{ dataSourceName : testDatasources.prom.name , apiVersion : 'legacy' } ,
'namespace1' ,
'group1'
) ;
2021-08-26 16:40:27 +03:00
} ) ;
testCase ( 'edit lotex group eval interval, no renaming' , async ( ) = > {
// make changes to form
2022-04-21 13:15:21 +01:00
await userEvent . type ( ui . editGroupModal . intervalInput . get ( ) , '5m' ) ;
2021-08-26 16:40:27 +03:00
// submit, check that appropriate calls were made
2022-04-21 13:15:21 +01:00
await userEvent . click ( ui . editGroupModal . saveButton . get ( ) ) ;
2021-08-26 16:40:27 +03:00
await waitFor ( ( ) = > expect ( ui . editGroupModal . namespaceInput . query ( ) ) . not . toBeInTheDocument ( ) ) ;
expect ( mocks . api . setRulerRuleGroup ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( mocks . api . deleteGroup ) . not . toHaveBeenCalled ( ) ;
expect ( mocks . api . deleteNamespace ) . not . toHaveBeenCalled ( ) ;
expect ( mocks . api . fetchRulerRules ) . toHaveBeenCalledTimes ( 4 ) ;
2022-04-04 19:30:17 +02:00
expect ( mocks . api . setRulerRuleGroup ) . toHaveBeenNthCalledWith (
1 ,
{ dataSourceName : testDatasources.prom.name , apiVersion : 'legacy' } ,
'namespace1' ,
{
. . . someRulerRules [ 'namespace1' ] [ 0 ] ,
interval : '5m' ,
}
) ;
2021-08-26 16:40:27 +03:00
} ) ;
2021-07-26 12:05:49 -07:00
} ) ;
2022-04-26 15:57:00 +02:00
describe ( 'RBAC Enabled' , ( ) = > {
2023-01-30 11:13:08 +01:00
describe ( 'Export button' , ( ) = > {
it ( 'Export button should be visible when the user has alert provisioning read permissions' , async ( ) = > {
enableRBAC ( ) ;
grantUserPermissions ( [ AccessControlAction . AlertingProvisioningRead ] ) ;
mocks . getAllDataSourcesMock . mockReturnValue ( [ ] ) ;
setDataSourceSrv ( new MockDataSourceSrv ( { } ) ) ;
mocks . api . fetchRules . mockResolvedValue ( [ ] ) ;
mocks . api . fetchRulerRules . mockResolvedValue ( { } ) ;
renderRuleList ( ) ;
expect ( ui . exportButton . get ( ) ) . toBeInTheDocument ( ) ;
} ) ;
it ( 'Export button should not be visible when the user has no alert provisioning read permissions' , async ( ) = > {
enableRBAC ( ) ;
mocks . getAllDataSourcesMock . mockReturnValue ( [ ] ) ;
setDataSourceSrv ( new MockDataSourceSrv ( { } ) ) ;
mocks . api . fetchRules . mockResolvedValue ( [ ] ) ;
mocks . api . fetchRulerRules . mockResolvedValue ( { } ) ;
renderRuleList ( ) ;
expect ( ui . exportButton . query ( ) ) . not . toBeInTheDocument ( ) ;
} ) ;
} ) ;
2022-04-26 15:57:00 +02:00
describe ( 'Grafana Managed Alerts' , ( ) = > {
it ( 'New alert button should be visible when the user has alert rule create and folder read permissions and no rules exists' , async ( ) = > {
enableRBAC ( ) ;
grantUserPermissions ( [
AccessControlAction . FoldersRead ,
AccessControlAction . AlertingRuleCreate ,
AccessControlAction . AlertingRuleRead ,
] ) ;
mocks . getAllDataSourcesMock . mockReturnValue ( [ ] ) ;
setDataSourceSrv ( new MockDataSourceSrv ( { } ) ) ;
mocks . api . fetchRules . mockResolvedValue ( [ ] ) ;
mocks . api . fetchRulerRules . mockResolvedValue ( { } ) ;
renderRuleList ( ) ;
await waitFor ( ( ) = > expect ( mocks . api . fetchRules ) . toHaveBeenCalledTimes ( 1 ) ) ;
expect ( ui . newRuleButton . get ( ) ) . toBeInTheDocument ( ) ;
} ) ;
it ( 'New alert button should be visible when the user has alert rule create and folder read permissions and rules already exists' , async ( ) = > {
enableRBAC ( ) ;
grantUserPermissions ( [
AccessControlAction . FoldersRead ,
AccessControlAction . AlertingRuleCreate ,
AccessControlAction . AlertingRuleRead ,
] ) ;
mocks . getAllDataSourcesMock . mockReturnValue ( [ ] ) ;
setDataSourceSrv ( new MockDataSourceSrv ( { } ) ) ;
mocks . api . fetchRules . mockResolvedValue ( somePromRules ( 'grafana' ) ) ;
mocks . api . fetchRulerRules . mockResolvedValue ( someRulerRules ) ;
renderRuleList ( ) ;
await waitFor ( ( ) = > expect ( mocks . api . fetchRules ) . toHaveBeenCalledTimes ( 1 ) ) ;
expect ( ui . newRuleButton . get ( ) ) . toBeInTheDocument ( ) ;
} ) ;
} ) ;
describe ( 'Cloud Alerts' , ( ) = > {
it ( 'New alert button should be visible when the user has the alert rule external write and datasource read permissions and no rules exists' , async ( ) = > {
enableRBAC ( ) ;
grantUserPermissions ( [
// AccessControlAction.AlertingRuleRead,
AccessControlAction . DataSourcesRead ,
AccessControlAction . AlertingRuleExternalRead ,
AccessControlAction . AlertingRuleExternalWrite ,
] ) ;
mocks . getAllDataSourcesMock . mockReturnValue ( [ dataSources . prom ] ) ;
setDataSourceSrv ( new MockDataSourceSrv ( { prom : dataSources.prom } ) ) ;
2022-05-18 10:45:26 +02:00
mocks . api . discoverFeatures . mockResolvedValue ( {
2022-10-24 14:53:11 -05:00
application : PromApplication.Cortex ,
2022-04-26 15:57:00 +02:00
features : {
rulerApiEnabled : true ,
} ,
} ) ;
mocks . api . fetchRules . mockResolvedValue ( [ ] ) ;
mocks . api . fetchRulerRules . mockResolvedValue ( { } ) ;
renderRuleList ( ) ;
await waitFor ( ( ) = > expect ( mocks . api . fetchRules ) . toHaveBeenCalledTimes ( 1 ) ) ;
expect ( ui . newRuleButton . get ( ) ) . toBeInTheDocument ( ) ;
} ) ;
it ( 'New alert button should be visible when the user has the alert rule external write and data source read permissions and rules already exists' , async ( ) = > {
enableRBAC ( ) ;
grantUserPermissions ( [
AccessControlAction . DataSourcesRead ,
AccessControlAction . AlertingRuleExternalRead ,
AccessControlAction . AlertingRuleExternalWrite ,
] ) ;
mocks . getAllDataSourcesMock . mockReturnValue ( [ dataSources . prom ] ) ;
setDataSourceSrv ( new MockDataSourceSrv ( { prom : dataSources.prom } ) ) ;
2022-05-18 10:45:26 +02:00
mocks . api . discoverFeatures . mockResolvedValue ( {
2022-10-24 14:53:11 -05:00
application : PromApplication.Cortex ,
2022-04-26 15:57:00 +02:00
features : {
rulerApiEnabled : true ,
} ,
} ) ;
mocks . api . fetchRules . mockResolvedValue ( somePromRules ( 'Cortex' ) ) ;
mocks . api . fetchRulerRules . mockResolvedValue ( someRulerRules ) ;
renderRuleList ( ) ;
await waitFor ( ( ) = > expect ( mocks . api . fetchRules ) . toHaveBeenCalledTimes ( 1 ) ) ;
expect ( ui . newRuleButton . get ( ) ) . toBeInTheDocument ( ) ;
} ) ;
} ) ;
} ) ;
2022-10-03 11:00:19 -03:00
describe ( 'Analytics' , ( ) = > {
it ( 'Sends log info when creating an alert rule from a scratch' , async ( ) = > {
enableRBAC ( ) ;
grantUserPermissions ( [
AccessControlAction . FoldersRead ,
AccessControlAction . AlertingRuleCreate ,
AccessControlAction . AlertingRuleRead ,
] ) ;
mocks . getAllDataSourcesMock . mockReturnValue ( [ ] ) ;
setDataSourceSrv ( new MockDataSourceSrv ( { } ) ) ;
mocks . api . fetchRules . mockResolvedValue ( [ ] ) ;
mocks . api . fetchRulerRules . mockResolvedValue ( { } ) ;
renderRuleList ( ) ;
await waitFor ( ( ) = > expect ( mocks . api . fetchRules ) . toHaveBeenCalledTimes ( 1 ) ) ;
2023-01-30 16:26:21 +01:00
const button = screen . getByText ( 'Create alert rule' ) ;
2022-10-03 11:00:19 -03:00
button . addEventListener ( 'click' , ( event ) = > event . preventDefault ( ) , false ) ;
expect ( button ) . toBeEnabled ( ) ;
await userEvent . click ( button ) ;
expect ( logInfo ) . toHaveBeenCalledWith ( LogMessages . alertRuleFromScratch ) ;
} ) ;
} ) ;
2021-04-07 08:42:43 +03:00
} ) ;