kibana/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts
Mike Côté da8ce374cf
Make xpack.actions.rejectUnauthorized setting work (#88690)
* Remove ActionsConfigType due to being a duplicate

* Fix rejectUnauthorized not being configured

* Move proxySettings to configurationUtilities

* Fix isAxiosError check to code

* Add functional test

* Remove comment

* Close webhook server

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
2021-01-28 13:44:25 -05:00

226 lines
7.2 KiB
TypeScript

/*
* 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 { Logger } from '../../../../../src/core/server';
import { Services, ActionTypeExecutorResult } from '../types';
import { validateParams, validateSecrets } from '../lib';
import { getActionType, SlackActionType, SlackActionTypeExecutorOptions } from './slack';
import { actionsConfigMock } from '../actions_config.mock';
import { actionsMock } from '../mocks';
import { createActionTypeRegistry } from './index.test';
jest.mock('@slack/webhook', () => {
return {
IncomingWebhook: jest.fn().mockImplementation(() => {
return { send: (message: string) => {} };
}),
};
});
const ACTION_TYPE_ID = '.slack';
const services: Services = actionsMock.createServices();
let actionType: SlackActionType;
let mockedLogger: jest.Mocked<Logger>;
beforeAll(() => {
const { logger } = createActionTypeRegistry();
actionType = getActionType({
async executor(options) {
return { status: 'ok', actionId: options.actionId };
},
configurationUtilities: actionsConfigMock.create(),
logger,
});
mockedLogger = logger;
expect(actionType).toBeTruthy();
});
describe('action registeration', () => {
test('returns action type', () => {
expect(actionType.id).toEqual(ACTION_TYPE_ID);
expect(actionType.name).toEqual('Slack');
});
});
describe('validateParams()', () => {
test('should validate and pass when params is valid', () => {
expect(validateParams(actionType, { message: 'a message' })).toEqual({
message: 'a message',
});
});
test('should validate and throw error when params is invalid', () => {
expect(() => {
validateParams(actionType, {});
}).toThrowErrorMatchingInlineSnapshot(
`"error validating action params: [message]: expected value of type [string] but got [undefined]"`
);
expect(() => {
validateParams(actionType, { message: 1 });
}).toThrowErrorMatchingInlineSnapshot(
`"error validating action params: [message]: expected value of type [string] but got [number]"`
);
});
});
describe('validateActionTypeSecrets()', () => {
test('should validate and pass when config is valid', () => {
validateSecrets(actionType, {
webhookUrl: 'https://example.com',
});
});
test('should validate and throw error when config is invalid', () => {
expect(() => {
validateSecrets(actionType, {});
}).toThrowErrorMatchingInlineSnapshot(
`"error validating action type secrets: [webhookUrl]: expected value of type [string] but got [undefined]"`
);
expect(() => {
validateSecrets(actionType, { webhookUrl: 1 });
}).toThrowErrorMatchingInlineSnapshot(
`"error validating action type secrets: [webhookUrl]: expected value of type [string] but got [number]"`
);
expect(() => {
validateSecrets(actionType, { webhookUrl: 'fee-fi-fo-fum' });
}).toThrowErrorMatchingInlineSnapshot(
`"error validating action type secrets: error configuring slack action: unable to parse host name from webhookUrl"`
);
});
test('should validate and pass when the slack webhookUrl is added to allowedHosts', () => {
actionType = getActionType({
logger: mockedLogger,
configurationUtilities: {
...actionsConfigMock.create(),
ensureUriAllowed: (url) => {
expect(url).toEqual('https://api.slack.com/');
},
},
});
expect(validateSecrets(actionType, { webhookUrl: 'https://api.slack.com/' })).toEqual({
webhookUrl: 'https://api.slack.com/',
});
});
test('config validation returns an error if the specified URL isnt added to allowedHosts', () => {
actionType = getActionType({
logger: mockedLogger,
configurationUtilities: {
...actionsConfigMock.create(),
ensureUriAllowed: () => {
throw new Error(`target hostname is not added to allowedHosts`);
},
},
});
expect(() => {
validateSecrets(actionType, { webhookUrl: 'https://api.slack.com/' });
}).toThrowErrorMatchingInlineSnapshot(
`"error validating action type secrets: error configuring slack action: target hostname is not added to allowedHosts"`
);
});
});
describe('execute()', () => {
beforeAll(() => {
async function mockSlackExecutor(options: SlackActionTypeExecutorOptions) {
const { params } = options;
const { message } = params;
if (message == null) throw new Error('message property required in parameter');
const failureMatch = message.match(/^failure: (.*)$/);
if (failureMatch != null) {
const failMessage = failureMatch[1];
throw new Error(`slack mockExecutor failure: ${failMessage}`);
}
return {
text: `slack mockExecutor success: ${message}`,
actionId: '',
status: 'ok',
} as ActionTypeExecutorResult<void>;
}
actionType = getActionType({
executor: mockSlackExecutor,
logger: mockedLogger,
configurationUtilities: actionsConfigMock.create(),
});
});
test('calls the mock executor with success', async () => {
const response = await actionType.executor({
actionId: 'some-id',
services,
config: {},
secrets: { webhookUrl: 'http://example.com' },
params: { message: 'this invocation should succeed' },
});
expect(response).toMatchInlineSnapshot(`
Object {
"actionId": "",
"status": "ok",
"text": "slack mockExecutor success: this invocation should succeed",
}
`);
});
test('calls the mock executor with failure', async () => {
await expect(
actionType.executor({
actionId: 'some-id',
services,
config: {},
secrets: { webhookUrl: 'http://example.com' },
params: { message: 'failure: this invocation should fail' },
})
).rejects.toThrowErrorMatchingInlineSnapshot(
`"slack mockExecutor failure: this invocation should fail"`
);
});
test('calls the mock executor with success proxy', async () => {
const configurationUtilities = actionsConfigMock.create();
configurationUtilities.getProxySettings.mockReturnValue({
proxyUrl: 'https://someproxyhost',
proxyRejectUnauthorizedCertificates: false,
});
const actionTypeProxy = getActionType({
logger: mockedLogger,
configurationUtilities,
});
await actionTypeProxy.executor({
actionId: 'some-id',
services,
config: {},
secrets: { webhookUrl: 'http://example.com' },
params: { message: 'this invocation should succeed' },
});
expect(mockedLogger.debug).toHaveBeenCalledWith(
'IncomingWebhook was called with proxyUrl https://someproxyhost'
);
});
test('renders parameter templates as expected', async () => {
expect(actionType.renderParameterTemplates).toBeTruthy();
const paramsWithTemplates = {
message: '{{rogue}}',
};
const variables = {
rogue: '*bold*',
};
const params = actionType.renderParameterTemplates!(paramsWithTemplates, variables);
expect(params.message).toBe('`*bold*`');
});
});