diff --git a/x-pack/legacy/plugins/alerting/server/lib/alert_instance.test.ts b/x-pack/legacy/plugins/alerting/server/lib/alert_instance.test.ts index d3f1100621246..6a80f4d2de4cb 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/alert_instance.test.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/alert_instance.test.ts @@ -18,9 +18,17 @@ afterAll(() => clock.restore()); describe('hasScheduledActions()', () => { test('defaults to false', () => { const alertInstance = new AlertInstance(); - expect(alertInstance.hasScheduledActions(null)).toEqual(false); + expect(alertInstance.hasScheduledActions()).toEqual(false); }); + test('returns true when scheduleActions is called', () => { + const alertInstance = new AlertInstance(); + alertInstance.scheduleActions('default'); + expect(alertInstance.hasScheduledActions()).toEqual(true); + }); +}); + +describe('isThrottled', () => { test(`should throttle when group didn't change and throttle period is still active`, () => { const alertInstance = new AlertInstance({ meta: { @@ -32,7 +40,7 @@ describe('hasScheduledActions()', () => { }); clock.tick(30000); alertInstance.scheduleActions('default'); - expect(alertInstance.hasScheduledActions('1m')).toEqual(false); + expect(alertInstance.isThrottled('1m')).toEqual(true); }); test(`shouldn't throttle when group didn't change and throttle period expired`, () => { @@ -46,7 +54,7 @@ describe('hasScheduledActions()', () => { }); clock.tick(30000); alertInstance.scheduleActions('default'); - expect(alertInstance.hasScheduledActions('15s')).toEqual(true); + expect(alertInstance.isThrottled('15s')).toEqual(false); }); test(`shouldn't throttle when group changes`, () => { @@ -60,7 +68,7 @@ describe('hasScheduledActions()', () => { }); clock.tick(5000); alertInstance.scheduleActions('other-group'); - expect(alertInstance.hasScheduledActions('1m')).toEqual(true); + expect(alertInstance.isThrottled('1m')).toEqual(false); }); }); @@ -75,9 +83,9 @@ describe('unscheduleActions()', () => { test('makes hasScheduledActions() return false', () => { const alertInstance = new AlertInstance(); alertInstance.scheduleActions('default'); - expect(alertInstance.hasScheduledActions(null)).toEqual(true); + expect(alertInstance.hasScheduledActions()).toEqual(true); alertInstance.unscheduleActions(); - expect(alertInstance.hasScheduledActions(null)).toEqual(false); + expect(alertInstance.hasScheduledActions()).toEqual(false); }); test('makes getScheduledActionOptions() return undefined', () => { @@ -113,10 +121,10 @@ describe('scheduleActions()', () => { }, }); alertInstance.replaceState({ otherField: true }).scheduleActions('default', { field: true }); - expect(alertInstance.hasScheduledActions(null)).toEqual(true); + expect(alertInstance.hasScheduledActions()).toEqual(true); }); - test('makes hasScheduledActions() return false when throttled', () => { + test('makes isThrottled() return true when throttled', () => { const alertInstance = new AlertInstance({ state: { foo: true }, meta: { @@ -127,10 +135,10 @@ describe('scheduleActions()', () => { }, }); alertInstance.replaceState({ otherField: true }).scheduleActions('default', { field: true }); - expect(alertInstance.hasScheduledActions('1m')).toEqual(false); + expect(alertInstance.isThrottled('1m')).toEqual(true); }); - test('make hasScheduledActions() return true when throttled expired', () => { + test('make isThrottled() return false when throttled expired', () => { const alertInstance = new AlertInstance({ state: { foo: true }, meta: { @@ -142,7 +150,7 @@ describe('scheduleActions()', () => { }); clock.tick(120000); alertInstance.replaceState({ otherField: true }).scheduleActions('default', { field: true }); - expect(alertInstance.hasScheduledActions('1m')).toEqual(true); + expect(alertInstance.isThrottled('1m')).toEqual(false); }); test('makes getScheduledActionOptions() return given options', () => { diff --git a/x-pack/legacy/plugins/alerting/server/lib/alert_instance.ts b/x-pack/legacy/plugins/alerting/server/lib/alert_instance.ts index ec2dc1dfa4691..1e2cc26f364ad 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/alert_instance.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/alert_instance.ts @@ -35,13 +35,14 @@ export class AlertInstance { this.meta = meta; } - hasScheduledActions(throttle: string | null) { - // scheduleActions function wasn't called + hasScheduledActions() { + return this.scheduledExecutionOptions !== undefined; + } + + isThrottled(throttle: string | null) { if (this.scheduledExecutionOptions === undefined) { return false; } - // Shouldn't schedule actions if still within throttling window - // Reset if actionGroup changes const throttleMills = throttle ? parseDuration(throttle) : 0; const actionGroup = this.scheduledExecutionOptions.actionGroup; if ( @@ -49,9 +50,9 @@ export class AlertInstance { this.meta.lastScheduledActions.group === actionGroup && new Date(this.meta.lastScheduledActions.date).getTime() + throttleMills > Date.now() ) { - return false; + return true; } - return true; + return false; } getScheduledActionOptions() { @@ -68,7 +69,7 @@ export class AlertInstance { } scheduleActions(actionGroup: string, context: Context = {}) { - if (this.hasScheduledActions(null)) { + if (this.hasScheduledActions()) { throw new Error('Alert instance execution has already been scheduled, cannot schedule twice'); } this.scheduledExecutionOptions = { actionGroup, context, state: this.state }; diff --git a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts index 0c6bd1b4a777a..66d445f57fe73 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts @@ -148,8 +148,12 @@ export class TaskRunnerFactory { await Promise.all( Object.keys(alertInstances).map(alertInstanceId => { const alertInstance = alertInstances[alertInstanceId]; - if (alertInstance.hasScheduledActions(throttle)) { - if (muteAll || mutedInstanceIds.includes(alertInstanceId)) { + if (alertInstance.hasScheduledActions()) { + if ( + alertInstance.isThrottled(throttle) || + muteAll || + mutedInstanceIds.includes(alertInstanceId) + ) { return; } const { actionGroup, context, state } = alertInstance.getScheduledActionOptions()!; diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index 7b2c2c2b7840f..2ac8fff6ef8ab 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -13,11 +13,7 @@ require('@kbn/test').runTestsCli([ require.resolve('../test/api_integration/config_security_basic.js'), require.resolve('../test/api_integration/config.js'), require.resolve('../test/alerting_api_integration/spaces_only/config.ts'), - // FLAKY: https://github.com/elastic/kibana/issues/50079 - // FLAKY: https://github.com/elastic/kibana/issues/50074 - // FLAKY: https://github.com/elastic/kibana/issues/48709 - // FLAKY: https://github.com/elastic/kibana/issues/50078 - // require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'), + require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'), require.resolve('../test/plugin_api_integration/config.js'), require.resolve('../test/kerberos_api_integration/config'), require.resolve('../test/kerberos_api_integration/anonymous_access.config'), diff --git a/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts b/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts index ed6775a0fb984..44f796e9a0ac7 100644 --- a/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts +++ b/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts @@ -9,7 +9,7 @@ export function getTestAlertData(overwrites = {}) { enabled: true, name: 'abc', alertTypeId: 'test.noop', - interval: '10s', + interval: '1m', throttle: '1m', actions: [], alertTypeParams: {}, diff --git a/x-pack/test/alerting_api_integration/common/lib/index.ts b/x-pack/test/alerting_api_integration/common/lib/index.ts index 444b767456bde..31d257e3b25ac 100644 --- a/x-pack/test/alerting_api_integration/common/lib/index.ts +++ b/x-pack/test/alerting_api_integration/common/lib/index.ts @@ -9,3 +9,4 @@ export { getUrlPrefix } from './space_test_utils'; export { ES_TEST_INDEX_NAME, ESTestIndexTool } from './es_test_index_tool'; export { getTestAlertData } from './get_test_alert_data'; export { AlertUtils } from './alert_utils'; +export { TaskManagerUtils } from './task_manager_utils'; diff --git a/x-pack/test/alerting_api_integration/common/lib/task_manager_utils.ts b/x-pack/test/alerting_api_integration/common/lib/task_manager_utils.ts new file mode 100644 index 0000000000000..b72960b162e76 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/lib/task_manager_utils.ts @@ -0,0 +1,46 @@ +/* + * 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. + */ + +export class TaskManagerUtils { + private readonly es: any; + private readonly retry: any; + + constructor(es: any, retry: any) { + this.es = es; + this.retry = retry; + } + + async waitForIdle(taskRunAtFilter: Date) { + return await this.retry.try(async () => { + const searchResult = await this.es.search({ + index: '.kibana_task_manager', + body: { + query: { + bool: { + must: [ + { + terms: { + 'task.scope': ['actions', 'alerting'], + }, + }, + { + range: { + 'task.scheduledAt': { + gte: taskRunAtFilter, + }, + }, + }, + ], + }, + }, + }, + }); + if (searchResult.hits.total.value) { + throw new Error(`Expected 0 tasks but received ${searchResult.hits.total.value}`); + } + }); + } +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index 1252dd1400807..0a300c4ce65da 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -14,6 +14,7 @@ import { getTestAlertData, ObjectRemover, AlertUtils, + TaskManagerUtils, } from '../../../common/lib'; // eslint-disable-next-line import/no-default-export @@ -23,6 +24,7 @@ export default function alertTests({ getService }: FtrProviderContext) { const retry = getService('retry'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const esTestIndexTool = new ESTestIndexTool(es, retry); + const taskManagerUtils = new TaskManagerUtils(es, retry); describe('alerts', () => { const authorizationIndex = '.kibana-test-authorization'; @@ -73,6 +75,7 @@ export default function alertTests({ getService }: FtrProviderContext) { after(() => objectRemover.add(space.id, indexRecordActionId, 'action')); it('should schedule task, run alert and schedule actions when appropriate', async () => { + const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await alertUtils.createAlwaysFiringAction({ reference }); @@ -90,10 +93,19 @@ export default function alertTests({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': expect(response.statusCode).to.eql(200); - const alertTestRecord = ( - await esTestIndexTool.waitForDocs('alert:test.always-firing', reference) - )[0]; - expect(alertTestRecord._source).to.eql({ + + // Wait for the action to index a document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.index-record', reference); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Ensure only 1 alert executed with proper params + const alertSearchResult = await esTestIndexTool.search( + 'alert:test.always-firing', + reference + ); + expect(alertSearchResult.hits.total.value).to.eql(1); + expect(alertSearchResult.hits.hits[0]._source).to.eql({ source: 'alert:test.always-firing', reference, state: {}, @@ -102,10 +114,14 @@ export default function alertTests({ getService }: FtrProviderContext) { reference, }, }); - const actionTestRecord = ( - await esTestIndexTool.waitForDocs('action:test.index-record', reference) - )[0]; - expect(actionTestRecord._source).to.eql({ + + // Ensure only 1 action executed with proper params + const actionSearchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + expect(actionSearchResult.hits.total.value).to.eql(1); + expect(actionSearchResult.hits.hits[0]._source).to.eql({ config: { unencrypted: `This value shouldn't get encrypted`, }, @@ -127,7 +143,6 @@ export default function alertTests({ getService }: FtrProviderContext) { }); it('should handle custom retry logic when appropriate', async () => { - // We'll use this start time to query tasks created after this point const testStart = new Date(); // We have to provide the test.rate-limit the next runAt, for testing purposes const retryDate = new Date(Date.now() + 60000); @@ -150,7 +165,6 @@ export default function alertTests({ getService }: FtrProviderContext) { .auth(user.username, user.password) .send( getTestAlertData({ - interval: '1m', alertTypeId: 'test.always-firing', alertTypeParams: { index: ES_TEST_INDEX_NAME, @@ -185,6 +199,8 @@ export default function alertTests({ getService }: FtrProviderContext) { case 'space_1_all at space1': expect(response.statusCode).to.eql(200); objectRemover.add(space.id, response.body.id, 'alert'); + + // Wait for the task to be attempted once and idle const scheduledActionTask = await retry.try(async () => { const searchResult = await es.search({ index: '.kibana_task_manager', @@ -222,6 +238,8 @@ export default function alertTests({ getService }: FtrProviderContext) { expect(searchResult.hits.total.value).to.eql(1); return searchResult.hits.hits[0]; }); + + // Ensure the next runAt is set to the retryDate by custom logic expect(scheduledActionTask._source.task.runAt).to.eql(retryDate.toISOString()); break; default: @@ -230,7 +248,8 @@ export default function alertTests({ getService }: FtrProviderContext) { }); it('should have proper callCluster and savedObjectsClient authorization for alert type executor when appropriate', async () => { - let alertTestRecord: any; + let searchResult: any; + const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await supertestWithoutAuth .post(`${getUrlPrefix(space.id)}/api/alert`) @@ -263,20 +282,26 @@ export default function alertTests({ getService }: FtrProviderContext) { case 'space_1_all at space1': expect(response.statusCode).to.eql(200); objectRemover.add(space.id, response.body.id, 'alert'); - alertTestRecord = ( - await esTestIndexTool.waitForDocs('alert:test.authorization', reference) - )[0]; - expect(alertTestRecord._source.state).to.eql({ + + // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('alert:test.authorization', reference); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Ensure only 1 document exists with proper params + searchResult = await esTestIndexTool.search('alert:test.authorization', reference); + expect(searchResult.hits.total.value).to.eql(1); + expect(searchResult.hits.hits[0]._source.state).to.eql({ callClusterSuccess: false, savedObjectsClientSuccess: false, callClusterError: { - ...alertTestRecord._source.state.callClusterError, + ...searchResult.hits.hits[0]._source.state.callClusterError, statusCode: 403, }, savedObjectsClientError: { - ...alertTestRecord._source.state.savedObjectsClientError, + ...searchResult.hits.hits[0]._source.state.savedObjectsClientError, output: { - ...alertTestRecord._source.state.savedObjectsClientError.output, + ...searchResult.hits.hits[0]._source.state.savedObjectsClientError.output, statusCode: 403, }, }, @@ -285,16 +310,22 @@ export default function alertTests({ getService }: FtrProviderContext) { case 'superuser at space1': expect(response.statusCode).to.eql(200); objectRemover.add(space.id, response.body.id, 'alert'); - alertTestRecord = ( - await esTestIndexTool.waitForDocs('alert:test.authorization', reference) - )[0]; - expect(alertTestRecord._source.state).to.eql({ + + // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('alert:test.authorization', reference); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Ensure only 1 document exists with proper params + searchResult = await esTestIndexTool.search('alert:test.authorization', reference); + expect(searchResult.hits.total.value).to.eql(1); + expect(searchResult.hits.hits[0]._source.state).to.eql({ callClusterSuccess: true, savedObjectsClientSuccess: false, savedObjectsClientError: { - ...alertTestRecord._source.state.savedObjectsClientError, + ...searchResult.hits.hits[0]._source.state.savedObjectsClientError, output: { - ...alertTestRecord._source.state.savedObjectsClientError.output, + ...searchResult.hits.hits[0]._source.state.savedObjectsClientError.output, statusCode: 404, }, }, @@ -306,7 +337,8 @@ export default function alertTests({ getService }: FtrProviderContext) { }); it('should have proper callCluster and savedObjectsClient authorization for action type executor when appropriate', async () => { - let actionTestRecord: any; + let searchResult: any; + const testStart = new Date(); const reference = alertUtils.generateReference(); const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/action`) @@ -358,20 +390,26 @@ export default function alertTests({ getService }: FtrProviderContext) { case 'space_1_all at space1': expect(response.statusCode).to.eql(200); objectRemover.add(space.id, response.body.id, 'alert'); - actionTestRecord = ( - await esTestIndexTool.waitForDocs('action:test.authorization', reference) - )[0]; - expect(actionTestRecord._source.state).to.eql({ + + // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.authorization', reference); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Ensure only 1 document with proper params exists + searchResult = await esTestIndexTool.search('action:test.authorization', reference); + expect(searchResult.hits.total.value).to.eql(1); + expect(searchResult.hits.hits[0]._source.state).to.eql({ callClusterSuccess: false, savedObjectsClientSuccess: false, callClusterError: { - ...actionTestRecord._source.state.callClusterError, + ...searchResult.hits.hits[0]._source.state.callClusterError, statusCode: 403, }, savedObjectsClientError: { - ...actionTestRecord._source.state.savedObjectsClientError, + ...searchResult.hits.hits[0]._source.state.savedObjectsClientError, output: { - ...actionTestRecord._source.state.savedObjectsClientError.output, + ...searchResult.hits.hits[0]._source.state.savedObjectsClientError.output, statusCode: 403, }, }, @@ -380,16 +418,22 @@ export default function alertTests({ getService }: FtrProviderContext) { case 'superuser at space1': expect(response.statusCode).to.eql(200); objectRemover.add(space.id, response.body.id, 'alert'); - actionTestRecord = ( - await esTestIndexTool.waitForDocs('action:test.authorization', reference) - )[0]; - expect(actionTestRecord._source.state).to.eql({ + + // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.authorization', reference); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Ensure only 1 document with proper params exists + searchResult = await esTestIndexTool.search('action:test.authorization', reference); + expect(searchResult.hits.total.value).to.eql(1); + expect(searchResult.hits.hits[0]._source.state).to.eql({ callClusterSuccess: true, savedObjectsClientSuccess: false, savedObjectsClientError: { - ...actionTestRecord._source.state.savedObjectsClientError, + ...searchResult.hits.hits[0]._source.state.savedObjectsClientError, output: { - ...actionTestRecord._source.state.savedObjectsClientError.output, + ...searchResult.hits.hits[0]._source.state.savedObjectsClientError.output, statusCode: 404, }, }, @@ -401,6 +445,7 @@ export default function alertTests({ getService }: FtrProviderContext) { }); it('should throttle alerts when appropriate', async () => { + const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await alertUtils.createAlwaysFiringAction({ reference, @@ -422,13 +467,17 @@ export default function alertTests({ getService }: FtrProviderContext) { break; case 'space_1_all at space1': case 'superuser at space1': - // Wait until alerts scheduled actions 3 times to ensure actions had a chance to execute twice + // Wait until alerts scheduled actions 3 times before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 3); - const executedActionsResult = await esTestIndexTool.search( + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Ensure actions only executed once + const searchResult = await esTestIndexTool.search( 'action:test.index-record', reference ); - expect(executedActionsResult.hits.total.value).to.eql(1); + expect(searchResult.hits.total.value).to.eql(1); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -436,6 +485,7 @@ export default function alertTests({ getService }: FtrProviderContext) { }); it('should not throttle when changing groups', async () => { + const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await alertUtils.createAlwaysFiringAction({ reference, @@ -482,14 +532,18 @@ export default function alertTests({ getService }: FtrProviderContext) { break; case 'space_1_all at space1': case 'superuser at space1': - // Wait until alerts scheduled actions 3 times to ensure actions had a chance to execute twice - await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 3); - const executedActionsResult = await esTestIndexTool.search( + // Wait for actions to execute twice before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Ensure only 2 actions with proper params exists + const searchResult = await esTestIndexTool.search( 'action:test.index-record', reference ); - expect(executedActionsResult.hits.total.value).to.eql(2); - const messages: string[] = executedActionsResult.hits.hits.map( + expect(searchResult.hits.total.value).to.eql(2); + const messages: string[] = searchResult.hits.hits.map( (hit: { _source: { params: { message: string } } }) => hit._source.params.message ); expect(messages.sort()).to.eql(['from:default', 'from:other']); @@ -500,6 +554,7 @@ export default function alertTests({ getService }: FtrProviderContext) { }); it('should reset throttle window when not firing', async () => { + const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await alertUtils.createAlwaysFiringAction({ reference, @@ -526,13 +581,17 @@ export default function alertTests({ getService }: FtrProviderContext) { break; case 'space_1_all at space1': case 'superuser at space1': - // Wait until alerts scheduled actions 4 times to ensure actions had a chance to execute twice - await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 4); - const executedActionsResult = await esTestIndexTool.search( + // Actions should execute twice before widning things down + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 2); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Ensure only 2 actions are executed + const searchResult = await esTestIndexTool.search( 'action:test.index-record', reference ); - expect(executedActionsResult.hits.total.value).to.eql(2); + expect(searchResult.hits.total.value).to.eql(2); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -540,6 +599,7 @@ export default function alertTests({ getService }: FtrProviderContext) { }); it(`shouldn't schedule actions when alert is muted`, async () => { + const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await alertUtils.createAlwaysFiringAction({ reference, @@ -564,8 +624,14 @@ export default function alertTests({ getService }: FtrProviderContext) { case 'superuser at space1': await alertUtils.muteAll(response.body.id); await alertUtils.enable(response.body.id); - // Wait until alerts schedule actions twice to ensure actions had a chance to skip execution once + + // Wait until alerts schedule actions twice to ensure actions had a chance to skip + // execution once before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 2); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Should not have executed any action const executedActionsResult = await esTestIndexTool.search( 'action:test.index-record', reference @@ -578,6 +644,7 @@ export default function alertTests({ getService }: FtrProviderContext) { }); it(`shouldn't schedule actions when alert instance is muted`, async () => { + const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await alertUtils.createAlwaysFiringAction({ reference, @@ -602,8 +669,14 @@ export default function alertTests({ getService }: FtrProviderContext) { case 'superuser at space1': await alertUtils.muteInstance(response.body.id, '1'); await alertUtils.enable(response.body.id); - // Wait until alerts scheduled actions twice to ensure actions had a chance to execute once + + // Wait until alerts scheduled actions twice to ensure actions had a chance to execute + // once before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 2); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Should not have executed any action const executedActionsResult = await esTestIndexTool.search( 'action:test.index-record', reference @@ -616,6 +689,7 @@ export default function alertTests({ getService }: FtrProviderContext) { }); it(`should unmute all instances when unmuting an alert`, async () => { + const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await alertUtils.createAlwaysFiringAction({ reference, @@ -642,13 +716,18 @@ export default function alertTests({ getService }: FtrProviderContext) { await alertUtils.muteAll(response.body.id); await alertUtils.unmuteAll(response.body.id); await alertUtils.enable(response.body.id); - // Wait until alerts scheduled actions twice to ensure actions had a chance to execute once - await esTestIndexTool.waitForDocs('alert:test.always-firing', reference, 2); - const executedActionsResult = await esTestIndexTool.search( + + // Ensure actions are executed once before disabling the alert and waiting for tasks to finish + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 1); + await alertUtils.disable(response.body.id); + await taskManagerUtils.waitForIdle(testStart); + + // Should have one document indexed by the action + const searchResult = await esTestIndexTool.search( 'action:test.index-record', reference ); - expect(executedActionsResult.hits.total.value).to.eql(1); + expect(searchResult.hits.total.value).to.eql(1); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts index 21b27cfb6f8f8..f51f188d5f737 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts @@ -60,7 +60,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { alertTypeId: 'test.noop', alertTypeParams: {}, createdBy: user.username, - interval: '10s', + interval: '1m', scheduledTaskId: response.body.scheduledTaskId, throttle: '1m', updatedBy: user.username, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts index 935ae3ed96915..a2912c9b022b3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts @@ -58,7 +58,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { id: createdAlert.id, name: 'abc', alertTypeId: 'test.noop', - interval: '10s', + interval: '1m', enabled: true, actions: [], alertTypeParams: {}, @@ -114,7 +114,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { id: createdAlert.id, name: 'abc', alertTypeId: 'test.noop', - interval: '10s', + interval: '1m', enabled: true, actions: [], alertTypeParams: {}, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts index db798b0d8bc7c..f6c65d286b266 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts @@ -52,7 +52,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { id: createdAlert.id, name: 'abc', alertTypeId: 'test.noop', - interval: '10s', + interval: '1m', enabled: true, actions: [], alertTypeParams: {}, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts index ae5853eead7e7..52c8571ae5929 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts @@ -233,7 +233,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .auth(user.username, user.password) .send({ name: 'bcd', - interval: '10s', + interval: '1m', throttle: '1m', alertTypeParams: {}, actions: [], diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts index af9804473a448..a643bd8f9f64f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts @@ -18,11 +18,7 @@ export default function alertingApiIntegrationTests({ const spacesService: SpacesService = getService('spaces'); const esArchiver = getService('esArchiver'); - // FLAKY: https://github.com/elastic/kibana/issues/50079 - // FLAKY: https://github.com/elastic/kibana/issues/50074 - // FLAKY: https://github.com/elastic/kibana/issues/48709 - // FLAKY: https://github.com/elastic/kibana/issues/50078 - describe.skip('alerting api integration security and spaces enabled', function() { + describe('alerting api integration security and spaces enabled', function() { this.tags('ciGroup1'); before(async () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts index 6ad6a54d3dccb..152f441cf8b02 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts @@ -42,7 +42,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { alertTypeId: 'test.noop', alertTypeParams: {}, createdBy: null, - interval: '10s', + interval: '1m', scheduledTaskId: response.body.scheduledTaskId, updatedBy: null, throttle: '1m', diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts index 97892e3a95d3f..e3ec322fbf1cd 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts @@ -41,7 +41,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { id: createdAlert.id, name: 'abc', alertTypeId: 'test.noop', - interval: '10s', + interval: '1m', enabled: true, actions: [], alertTypeParams: {}, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts index 391d78dda6647..c3d56adc0708a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts @@ -35,7 +35,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { id: createdAlert.id, name: 'abc', alertTypeId: 'test.noop', - interval: '10s', + interval: '1m', enabled: true, actions: [], alertTypeParams: {},