diff --git a/x-pack/plugins/alerting/server/task_runner/running_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/running_handler.test.ts new file mode 100644 index 00000000000000..ce3a3c6680e7b0 --- /dev/null +++ b/x-pack/plugins/alerting/server/task_runner/running_handler.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { ISavedObjectsRepository, Logger } from '@kbn/core/server'; + +import { partiallyUpdateAlert } from '../saved_objects/partially_update_alert'; +import { RunningHandler } from './running_handler'; + +jest.mock('../saved_objects/partially_update_alert', () => ({ + partiallyUpdateAlert: jest.fn(), +})); + +describe('isRunning handler', () => { + const soClient = jest.fn() as unknown as ISavedObjectsRepository; + const logger = { + error: jest.fn(), + } as unknown as Logger; + const ruleTypeId = 'myType'; + beforeEach(() => { + (partiallyUpdateAlert as jest.Mock).mockClear(); + (logger.error as jest.Mock).mockClear(); + jest.useFakeTimers(); + }); + afterEach(() => { + jest.useRealTimers(); + }); + + test('Should resolve if nothing got started', async () => { + (partiallyUpdateAlert as jest.Mock).mockImplementation(() => Promise.resolve('resolve')); + const runHandler = new RunningHandler(soClient, logger, ruleTypeId); + const resp = await runHandler.waitFor(); + expect(partiallyUpdateAlert).toHaveBeenCalledTimes(0); + expect(logger.error).toHaveBeenCalledTimes(0); + expect(resp).toBe(undefined); + }); + + test('Should return the promise from partiallyUpdateAlert when the update isRunning has been a success', async () => { + (partiallyUpdateAlert as jest.Mock).mockImplementation(() => Promise.resolve('resolve')); + const runHandler = new RunningHandler(soClient, logger, ruleTypeId); + runHandler.start('9876543210'); + jest.runAllTimers(); + const resp = await runHandler.waitFor(); + + expect(partiallyUpdateAlert).toHaveBeenCalledTimes(1); + expect((partiallyUpdateAlert as jest.Mock).mock.calls[0]).toMatchInlineSnapshot(` + Array [ + [MockFunction], + "9876543210", + Object { + "running": true, + }, + Object { + "ignore404": true, + "namespace": undefined, + "refresh": false, + }, + ] + `); + expect(logger.error).toHaveBeenCalledTimes(0); + expect(resp).toBe('resolve'); + }); + + test('Should reject when the update isRunning has been a failure', async () => { + (partiallyUpdateAlert as jest.Mock).mockImplementation(() => + Promise.reject(new Error('error')) + ); + const runHandler = new RunningHandler(soClient, logger, ruleTypeId); + runHandler.start('9876543210'); + jest.runAllTimers(); + + await expect(runHandler.waitFor()).rejects.toThrow(); + expect(partiallyUpdateAlert).toHaveBeenCalledTimes(1); + expect(logger.error).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/alerting/server/task_runner/running_handler.ts b/x-pack/plugins/alerting/server/task_runner/running_handler.ts index 42a1240a5b4c3b..118f13ab6eed96 100644 --- a/x-pack/plugins/alerting/server/task_runner/running_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/running_handler.ts @@ -39,27 +39,33 @@ export class RunningHandler { public async waitFor(): Promise { this.stop(); - if (!this.isUpdating) return Promise.resolve(); - else if (this.isUpdating && this.runningPromise) return this.runningPromise; + if (this.isUpdating && this.runningPromise) return this.runningPromise; else return Promise.resolve(); } private setRunning(ruleId: string, namespace?: string) { this.isUpdating = true; - this.runningPromise = partiallyUpdateAlert(this.client, ruleId, { running: true }, { - ignore404: true, - namespace: namespace, - refresh: false, - }); + this.runningPromise = partiallyUpdateAlert( + this.client, + ruleId, + { running: true }, + { + ignore404: true, + namespace, + refresh: false, + } + ); this.runningPromise - .then((mail) => { - this.runningPromise = undefined; - this.isUpdating = false - }) - .catch((err) => { - this.runningPromise = undefined; - this.isUpdating = false - this.logger.error(`error updating running attribute rule for ${this.ruleTypeId}:${ruleId} ${err.message}`); - }) + .then(() => { + this.runningPromise = undefined; + this.isUpdating = false; + }) + .catch((err) => { + this.runningPromise = undefined; + this.isUpdating = false; + this.logger.error( + `error updating running attribute rule for ${this.ruleTypeId}:${ruleId} ${err.message}` + ); + }); } } diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 74a9102a1fc791..4606f173b6cc4d 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -150,7 +150,11 @@ export class TaskRunner< this.alertingEventLogger = new AlertingEventLogger(this.context.eventLogger); this.stackTraceLog = null; this.ruleMonitoring = new RuleMonitoringService(); - this.ruleRunning = new RunningHandler(this.context.internalSavedObjectsRepository, this.logger, loggerId ) + this.ruleRunning = new RunningHandler( + this.context.internalSavedObjectsRepository, + this.logger, + loggerId + ); this.ruleResult = new RuleResultService(); } @@ -167,13 +171,19 @@ export class TaskRunner< const client = this.context.internalSavedObjectsRepository; try { await this.ruleRunning.waitFor(); + // eslint-disable-next-line no-empty } catch {} try { - await partiallyUpdateAlert(client, ruleId, {...attributes, running: false}, { - ignore404: true, - namespace, - refresh: false, - }); + await partiallyUpdateAlert( + client, + ruleId, + { ...attributes, running: false }, + { + ignore404: true, + namespace, + refresh: false, + } + ); } catch (err) { this.logger.error(`error updating rule for ${this.ruleType.id}:${ruleId} ${err.message}`); }