diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/preview_metric_threshold_alert.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/preview_metric_threshold_alert.test.ts new file mode 100644 index 0000000000000..c26b44dfe8ff8 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/preview_metric_threshold_alert.test.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as mocks from './test_mocks'; +import { Comparator, Aggregators, MetricExpressionParams } from './types'; +import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; +import { previewMetricThresholdAlert } from './preview_metric_threshold_alert'; + +describe('Previewing the metric threshold alert type', () => { + describe('querying the entire infrastructure', () => { + test('returns the expected results using a bucket interval equal to the alert interval', async () => { + const [ungroupedResult] = await previewMetricThresholdAlert({ + ...baseParams, + lookback: 'h', + alertInterval: '1m', + }); + const [firedResults, noDataResults, errorResults] = ungroupedResult; + expect(firedResults).toBe(30); + expect(noDataResults).toBe(0); + expect(errorResults).toBe(0); + }); + + test('returns the expected results using a bucket interval shorter than the alert interval', async () => { + const [ungroupedResult] = await previewMetricThresholdAlert({ + ...baseParams, + lookback: 'h', + alertInterval: '3m', + }); + const [firedResults, noDataResults, errorResults] = ungroupedResult; + expect(firedResults).toBe(10); + expect(noDataResults).toBe(0); + expect(errorResults).toBe(0); + }); + test('returns the expected results using a bucket interval longer than the alert interval', async () => { + const [ungroupedResult] = await previewMetricThresholdAlert({ + ...baseParams, + lookback: 'h', + alertInterval: '30s', + }); + const [firedResults, noDataResults, errorResults] = ungroupedResult; + expect(firedResults).toBe(60); + expect(noDataResults).toBe(0); + expect(errorResults).toBe(0); + }); + }); + describe('querying with a groupBy parameter', () => { + test('returns the expected results', async () => { + const [resultA, resultB] = await previewMetricThresholdAlert({ + ...baseParams, + params: { + ...baseParams.params, + groupBy: ['something'], + }, + lookback: 'h', + alertInterval: '1m', + }); + const [firedResultsA, noDataResultsA, errorResultsA] = resultA; + expect(firedResultsA).toBe(30); + expect(noDataResultsA).toBe(0); + expect(errorResultsA).toBe(0); + const [firedResultsB, noDataResultsB, errorResultsB] = resultB; + expect(firedResultsB).toBe(60); + expect(noDataResultsB).toBe(0); + expect(errorResultsB).toBe(0); + }); + }); + describe('querying a data set with a period of No Data', () => { + test('returns the expected results', async () => { + const [ungroupedResult] = await previewMetricThresholdAlert({ + ...baseParams, + params: { + ...baseParams.params, + criteria: [ + { + ...baseCriterion, + metric: 'test.metric.2', + } as MetricExpressionParams, + ], + }, + lookback: 'h', + alertInterval: '1m', + }); + const [firedResults, noDataResults, errorResults] = ungroupedResult; + expect(firedResults).toBe(25); + expect(noDataResults).toBe(10); + expect(errorResults).toBe(0); + }); + }); +}); + +const services: AlertServicesMock = alertsMock.createAlertServices(); +services.callCluster.mockImplementation(async (_: string, { body, index }: any) => { + const metric = body.query.bool.filter[1]?.exists.field; + if (body.aggs.groupings) { + if (body.aggs.groupings.composite.after) { + return mocks.compositeEndResponse; + } + return mocks.basicCompositePreviewResponse; + } + if (metric === 'test.metric.2') { + return mocks.alternateMetricPreviewResponse; + } + return mocks.basicMetricPreviewResponse; +}); + +const baseCriterion = { + aggType: Aggregators.AVERAGE, + metric: 'test.metric.1', + timeSize: 1, + timeUnit: 'm', + comparator: Comparator.GT, + threshold: [0.75], +} as MetricExpressionParams; + +const config = { + metricAlias: 'metricbeat-*', + fields: { + timefield: '@timestamp', + }, +} as any; + +const baseParams = { + callCluster: services.callCluster, + params: { + criteria: [baseCriterion], + groupBy: undefined, + filterQuery: undefined, + }, + config, +}; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts index 164f1ed6d18e5..d1178f6766979 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts @@ -38,6 +38,13 @@ const bucketsC = [ }, ]; +const previewBucketsA = Array.from(Array(60), (_, i) => bucketsA[i % 2]); // Repeat bucketsA to a total length of 60 +const previewBucketsB = Array.from(Array(60), (_, i) => bucketsB[i % 2]); +const previewBucketsWithNulls = [ + ...Array.from(Array(10), (_, i) => ({ aggregatedValue: { value: null } })), + ...previewBucketsA.slice(10), +]; + export const basicMetricResponse = { aggregations: { aggregatedIntervals: { @@ -150,3 +157,50 @@ export const changedSourceIdResponse = { }, }, }; + +export const basicMetricPreviewResponse = { + aggregations: { + aggregatedIntervals: { + buckets: previewBucketsA, + }, + }, +}; + +export const alternateMetricPreviewResponse = { + aggregations: { + aggregatedIntervals: { + buckets: previewBucketsWithNulls, + }, + }, +}; + +export const basicCompositePreviewResponse = { + aggregations: { + groupings: { + after_key: { groupBy0: 'foo' }, + buckets: [ + { + key: { + groupBy0: 'a', + }, + aggregatedIntervals: { + buckets: previewBucketsA, + }, + }, + { + key: { + groupBy0: 'b', + }, + aggregatedIntervals: { + buckets: previewBucketsB, + }, + }, + ], + }, + }, + hits: { + total: { + value: 2, + }, + }, +}; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts index 62ab94b7d8c83..cbe4014b24348 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts @@ -35,7 +35,7 @@ interface NonCountMetricExpressionParams extends BaseMetricExpressionParams { } interface CountMetricExpressionParams extends BaseMetricExpressionParams { - aggType: 'count'; + aggType: Aggregators.COUNT; metric: never; }