Skip to content

Commit

Permalink
[AO] Add API integration test scenarios to the Threshold rule (#162501)
Browse files Browse the repository at this point in the history
## Summary

It fixes  #162491
  • Loading branch information
fkanout committed Aug 4, 2023
1 parent 1f05a38 commit 6902008
Show file tree
Hide file tree
Showing 8 changed files with 986 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const deleteDataView = async ({
id: string;
}) => {
const { body } = await supertest
.delete(`/api/content_management/rpc/create`)
.post(`/api/content_management/rpc/delete`)
.set('kbn-xsrf', 'foo')
.send({
contentTypeId: 'index-pattern',
Expand Down
10 changes: 7 additions & 3 deletions x-pack/test/alerting_api_integration/observability/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
// eslint-disable-next-line import/no-default-export
export default function ({ loadTestFile }: any) {
describe('Observability Rules', () => {
describe('MetricsUI Endpoints', () => {
describe('Rules Endpoints', () => {
loadTestFile(require.resolve('./metric_threshold_rule'));
loadTestFile(require.resolve('./threshold_rule'));
loadTestFile(require.resolve('./threshold_rule/avg_pct_fired'));
loadTestFile(require.resolve('./threshold_rule/avg_pct_no_data'));
loadTestFile(require.resolve('./threshold_rule/avg_us_fired'));
loadTestFile(require.resolve('./threshold_rule/custom_eq_avg_bytes_fired'));
loadTestFile(require.resolve('./threshold_rule/documents_count_fired'));
loadTestFile(require.resolve('./threshold_rule/group_by_fired'));
loadTestFile(require.resolve('./threshold_rule_data_view'));
});

describe('Synthetics', () => {
loadTestFile(require.resolve('./synthetics_rule'));
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { cleanup, generate } from '@kbn/infra-forge';
import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types';
import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor';
import expect from '@kbn/expect';
import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants';
import { createIndexConnector, createRule } from '../helpers/alerting_api_helper';
import { createDataView, deleteDataView } from '../helpers/data_view';
import { waitForAlertInIndex, waitForRuleStatus } from '../helpers/alerting_wait_for_helpers';
import { FtrProviderContext } from '../../common/ftr_provider_context';

// eslint-disable-next-line import/no-default-export
export default function ({ getService }: FtrProviderContext) {
const esClient = getService('es');
const supertest = getService('supertest');
const esDeleteAllIndices = getService('esDeleteAllIndices');
const logger = getService('log');

describe('Threshold rule - AVG - PCT - FIRED', () => {
const THRESHOLD_RULE_ALERT_INDEX = '.alerts-observability.threshold.alerts-default';
const ALERT_ACTION_INDEX = 'alert-action-threshold';
const DATA_VIEW_ID = 'data-view-id';
let infraDataIndex: string;
let actionId: string;
let ruleId: string;

before(async () => {
infraDataIndex = await generate({ esClient, lookback: 'now-15m', logger });
await createDataView({
supertest,
name: 'metrics-fake_hosts',
id: DATA_VIEW_ID,
title: 'metrics-fake_hosts',
});
});

after(async () => {
await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo');
await supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo');
await esClient.deleteByQuery({
index: THRESHOLD_RULE_ALERT_INDEX,
query: { term: { 'kibana.alert.rule.uuid': ruleId } },
});
await esClient.deleteByQuery({
index: '.kibana-event-log-*',
query: { term: { 'kibana.alert.rule.consumer': 'alerts' } },
});
await deleteDataView({
supertest,
id: DATA_VIEW_ID,
});
await esDeleteAllIndices([ALERT_ACTION_INDEX, infraDataIndex]);
await cleanup({ esClient, logger });
});

describe('Rule creation', () => {
it('creates rule successfully', async () => {
actionId = await createIndexConnector({
supertest,
name: 'Index Connector: Threshold API test',
indexName: ALERT_ACTION_INDEX,
});

const createdRule = await createRule({
supertest,
tags: ['observability'],
consumer: 'alerts',
name: 'Threshold rule',
ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
params: {
criteria: [
{
aggType: Aggregators.CUSTOM,
comparator: Comparator.GT,
threshold: [0.5],
timeSize: 5,
timeUnit: 'm',
customMetrics: [
{ name: 'A', field: 'system.cpu.user.pct', aggType: Aggregators.AVERAGE },
],
},
],
alertOnNoData: true,
alertOnGroupDisappear: true,
searchConfiguration: {
query: {
query: '',
language: 'kuery',
},
index: DATA_VIEW_ID,
},
},
actions: [
{
group: FIRED_ACTIONS_ID,
id: actionId,
params: {
documents: [
{
ruleType: '{{rule.type}}',
},
],
},
frequency: {
notify_when: 'onActionGroupChange',
throttle: null,
summary: false,
},
},
],
});
ruleId = createdRule.id;
expect(ruleId).not.to.be(undefined);
});

it('should be active', async () => {
const executionStatus = await waitForRuleStatus({
id: ruleId,
expectedStatus: 'active',
supertest,
});
expect(executionStatus.status).to.be('active');
});

it('should set correct information in the alert document', async () => {
const resp = await waitForAlertInIndex({
esClient,
indexName: THRESHOLD_RULE_ALERT_INDEX,
ruleId,
});

expect(resp.hits.hits[0]._source).property(
'kibana.alert.rule.category',
'Threshold (Technical Preview)'
);
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'alerts');
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule');
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability');
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0);
expect(resp.hits.hits[0]._source).property(
'kibana.alert.rule.rule_type_id',
'observability.rules.threshold'
);
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.uuid', ruleId);
expect(resp.hits.hits[0]._source).property('kibana.space_ids').contain('default');
expect(resp.hits.hits[0]._source)
.property('kibana.alert.rule.tags')
.contain('observability');
expect(resp.hits.hits[0]._source).property('kibana.alert.action_group', 'threshold.fired');
expect(resp.hits.hits[0]._source).property('tags').contain('observability');
expect(resp.hits.hits[0]._source).property('kibana.alert.instance.id', '*');
expect(resp.hits.hits[0]._source).property('kibana.alert.workflow_status', 'open');
expect(resp.hits.hits[0]._source).property('event.kind', 'signal');
expect(resp.hits.hits[0]._source).property('event.action', 'open');

expect(resp.hits.hits[0]._source)
.property('kibana.alert.rule.parameters')
.eql({
criteria: [
{
aggType: 'custom',
comparator: '>',
threshold: [0.5],
timeSize: 5,
timeUnit: 'm',
customMetrics: [{ name: 'A', field: 'system.cpu.user.pct', aggType: 'avg' }],
},
],
alertOnNoData: true,
alertOnGroupDisappear: true,
searchConfiguration: { index: 'data-view-id', query: { query: '', language: 'kuery' } },
});
});
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types';
import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor';
import expect from '@kbn/expect';
import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants';

import { createIndexConnector, createRule } from '../helpers/alerting_api_helper';
import { createDataView, deleteDataView } from '../helpers/data_view';
import { waitForAlertInIndex, waitForRuleStatus } from '../helpers/alerting_wait_for_helpers';
import { FtrProviderContext } from '../../common/ftr_provider_context';

// eslint-disable-next-line import/no-default-export
export default function ({ getService }: FtrProviderContext) {
const esClient = getService('es');
const supertest = getService('supertest');

describe('Threshold rule - AVG - PCT - NoData', () => {
const THRESHOLD_RULE_ALERT_INDEX = '.alerts-observability.threshold.alerts-default';
const ALERT_ACTION_INDEX = 'alert-action-threshold';
const DATA_VIEW_ID = 'data-view-id-no-data';
let actionId: string;
let ruleId: string;

before(async () => {
await createDataView({
supertest,
name: 'no-data-pattern',
id: DATA_VIEW_ID,
title: 'no-data-pattern',
});
});

after(async () => {
await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo');
await supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo');
await esClient.deleteByQuery({
index: THRESHOLD_RULE_ALERT_INDEX,
query: { term: { 'kibana.alert.rule.uuid': ruleId } },
});
await esClient.deleteByQuery({
index: '.kibana-event-log-*',
query: { term: { 'kibana.alert.rule.consumer': 'alerts' } },
});
await deleteDataView({
supertest,
id: DATA_VIEW_ID,
});
});

describe('Rule creation', () => {
it('creates rule successfully', async () => {
actionId = await createIndexConnector({
supertest,
name: 'Index Connector: Threshold API test',
indexName: ALERT_ACTION_INDEX,
});

const createdRule = await createRule({
supertest,
tags: ['observability'],
consumer: 'alerts',
name: 'Threshold rule',
ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
params: {
criteria: [
{
aggType: Aggregators.CUSTOM,
comparator: Comparator.GT,
threshold: [0.5],
timeSize: 5,
timeUnit: 'm',
customMetrics: [
{ name: 'A', field: 'system.cpu.user.pct', aggType: Aggregators.AVERAGE },
],
},
],
alertOnNoData: true,
alertOnGroupDisappear: true,
searchConfiguration: {
query: {
query: '',
language: 'kuery',
},
index: DATA_VIEW_ID,
},
},
actions: [
{
group: FIRED_ACTIONS_ID,
id: actionId,
params: {
documents: [
{
ruleType: '{{rule.type}}',
},
],
},
frequency: {
notify_when: 'onActionGroupChange',
throttle: null,
summary: false,
},
},
],
});
ruleId = createdRule.id;
expect(ruleId).not.to.be(undefined);
});

it('should be active', async () => {
const executionStatus = await waitForRuleStatus({
id: ruleId,
expectedStatus: 'active',
supertest,
});
expect(executionStatus.status).to.be('active');
});

it('should set correct information in the alert document', async () => {
const resp = await waitForAlertInIndex({
esClient,
indexName: THRESHOLD_RULE_ALERT_INDEX,
ruleId,
});

expect(resp.hits.hits[0]._source).property(
'kibana.alert.rule.category',
'Threshold (Technical Preview)'
);
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'alerts');
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule');
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability');
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0);
expect(resp.hits.hits[0]._source).property(
'kibana.alert.rule.rule_type_id',
'observability.rules.threshold'
);
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.uuid', ruleId);
expect(resp.hits.hits[0]._source).property('kibana.space_ids').contain('default');
expect(resp.hits.hits[0]._source)
.property('kibana.alert.rule.tags')
.contain('observability');
expect(resp.hits.hits[0]._source).property('kibana.alert.action_group', 'threshold.nodata');
expect(resp.hits.hits[0]._source).property('tags').contain('observability');
expect(resp.hits.hits[0]._source).property('kibana.alert.instance.id', '*');
expect(resp.hits.hits[0]._source).property('kibana.alert.workflow_status', 'open');
expect(resp.hits.hits[0]._source).property('event.kind', 'signal');
expect(resp.hits.hits[0]._source).property('event.action', 'open');

expect(resp.hits.hits[0]._source)
.property('kibana.alert.rule.parameters')
.eql({
criteria: [
{
aggType: 'custom',
comparator: '>',
threshold: [0.5],
timeSize: 5,
timeUnit: 'm',
customMetrics: [{ name: 'A', field: 'system.cpu.user.pct', aggType: 'avg' }],
},
],
alertOnNoData: true,
alertOnGroupDisappear: true,
searchConfiguration: {
index: 'data-view-id-no-data',
query: { query: '', language: 'kuery' },
},
});
});
});
});
}
Loading

0 comments on commit 6902008

Please sign in to comment.