From 41b4902b3468a7a232092ba3124e39323e1aae3c Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Tue, 29 Mar 2022 12:25:08 +0900 Subject: [PATCH] Add more unit tests --- src/App-basic-features.spec.ts | 16 +++ src/receivers/AwsLambdaReceiver.spec.ts | 166 +++++++++++++++++++++++- src/receivers/HTTPResponseAck.spec.ts | 22 ++++ 3 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 src/receivers/HTTPResponseAck.spec.ts diff --git a/src/App-basic-features.spec.ts b/src/App-basic-features.spec.ts index c2d3bf2c4..05df8b17a 100644 --- a/src/App-basic-features.spec.ts +++ b/src/App-basic-features.spec.ts @@ -459,6 +459,22 @@ describe('App basic features', () => { } }); + describe('with developerMode', () => { + it('should accept developerMode: true', async () => { + // Arrange + const overrides = mergeOverrides( + withNoopAppMetadata(), + withSuccessfulBotUserFetchingWebClient('B_FAKE_BOT_ID', 'U_FAKE_BOT_USER_ID'), + ); + const MockApp = await importApp(overrides); + // Act + const app = new MockApp({ token: '', appToken: '', developerMode: true }); + // Assert + assert.equal((app as any).logLevel, LogLevel.DEBUG); + assert.equal((app as any).socketMode, true); + }); + }); + // TODO: tests for ignoreSelf option // TODO: tests for logger and logLevel option // TODO: tests for providing botId and botUserId options diff --git a/src/receivers/AwsLambdaReceiver.spec.ts b/src/receivers/AwsLambdaReceiver.spec.ts index d6754bd64..135afc0ce 100644 --- a/src/receivers/AwsLambdaReceiver.spec.ts +++ b/src/receivers/AwsLambdaReceiver.spec.ts @@ -40,6 +40,7 @@ describe('AwsLambdaReceiver', function () { it('should instantiate with default logger', async (): Promise => { const awsReceiver = new AwsLambdaReceiver({ signingSecret: 'my-secret', + logger: noopLogger, }); assert.isNotNull(awsReceiver); }); @@ -47,9 +48,19 @@ describe('AwsLambdaReceiver', function () { it('should have start method', async (): Promise => { const awsReceiver = new AwsLambdaReceiver({ signingSecret: 'my-secret', + logger: noopLogger, }); - const handler: AwsHandler = await awsReceiver.start(); - assert.isNotNull(handler); + const startedHandler: AwsHandler = await awsReceiver.start(); + assert.isNotNull(startedHandler); + }); + + it('should have stop method', async (): Promise => { + const awsReceiver = new AwsLambdaReceiver({ + signingSecret: 'my-secret', + logger: noopLogger, + }); + await awsReceiver.start(); + await awsReceiver.stop(); }); it('should accept events', async (): Promise => { @@ -380,6 +391,157 @@ describe('AwsLambdaReceiver', function () { ); assert.equal(response1.statusCode, 404); }); + + it('should accept ssl_check requests', async (): Promise => { + const awsReceiver = new AwsLambdaReceiver({ + signingSecret: 'my-secret', + logger: noopLogger, + }); + const handler = awsReceiver.toHandler(); + const body = 'ssl_check=1&token=legacy-fixed-token'; + const awsEvent = { + resource: '/slack/events', + path: '/slack/events', + httpMethod: 'POST', + headers: { + Accept: 'application/json,*/*', + 'Content-Type': 'application/x-www-form-urlencoded', + Host: 'xxx.execute-api.ap-northeast-1.amazonaws.com', + 'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)', + }, + multiValueHeaders: {}, + queryStringParameters: null, + multiValueQueryStringParameters: null, + pathParameters: null, + stageVariables: null, + requestContext: {}, + body, + isBase64Encoded: false, + }; + const response = await handler( + awsEvent, + {}, + (_error, _result) => {}, + ); + assert.equal(response.statusCode, 200); + }); + + const urlVerificationBody = JSON.stringify({ + token: 'Jhj5dZrVaK7ZwHHjRyZWjbDl', + challenge: '3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P', + type: 'url_verification', + }); + + it('should accept url_verification requests', async (): Promise => { + const timestamp = Math.floor(Date.now() / 1000); + const awsReceiver = new AwsLambdaReceiver({ + signingSecret: 'my-secret', + logger: noopLogger, + }); + const handler = awsReceiver.toHandler(); + const signature = crypto.createHmac('sha256', 'my-secret').update(`v0:${timestamp}:${urlVerificationBody}`).digest('hex'); + const awsEvent = { + resource: '/slack/events', + path: '/slack/events', + httpMethod: 'POST', + headers: { + Accept: 'application/json,*/*', + 'Content-Type': 'application/json', + Host: 'xxx.execute-api.ap-northeast-1.amazonaws.com', + 'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)', + 'X-Slack-Request-Timestamp': `${timestamp}`, + 'X-Slack-Signature': `v0=${signature}`, + }, + multiValueHeaders: {}, + queryStringParameters: null, + multiValueQueryStringParameters: null, + pathParameters: null, + stageVariables: null, + requestContext: {}, + body: urlVerificationBody, + isBase64Encoded: false, + }; + const response = await handler( + awsEvent, + {}, + (_error, _result) => {}, + ); + assert.equal(response.statusCode, 200); + }); + + it('should detect invalid signature', async (): Promise => { + const awsReceiver = new AwsLambdaReceiver({ + signingSecret: 'my-secret', + logger: noopLogger, + }); + const handler = awsReceiver.toHandler(); + const timestamp = Math.floor(Date.now() / 1000); + const signature = crypto.createHmac('sha256', 'my-secret').update(`v0:${timestamp}:${urlVerificationBody}`).digest('hex'); + const awsEvent = { + resource: '/slack/events', + path: '/slack/events', + httpMethod: 'POST', + headers: { + Accept: 'application/json,*/*', + 'Content-Type': 'application/json', + Host: 'xxx.execute-api.ap-northeast-1.amazonaws.com', + 'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)', + 'X-Slack-Request-Timestamp': `${timestamp}`, + 'X-Slack-Signature': `v0=${signature}XXXXXXXX`, // invalid signature + }, + multiValueHeaders: {}, + queryStringParameters: null, + multiValueQueryStringParameters: null, + pathParameters: null, + stageVariables: null, + requestContext: {}, + body: urlVerificationBody, + isBase64Encoded: false, + }; + const response = await handler( + awsEvent, + {}, + (_error, _result) => {}, + ); + assert.equal(response.statusCode, 401); + }); + + it('should detect too old request timestamp', async (): Promise => { + const awsReceiver = new AwsLambdaReceiver({ + signingSecret: 'my-secret', + logger: noopLogger, + }); + const handler = awsReceiver.toHandler(); + const timestamp = Math.floor(Date.now() / 1000) - 600; // 10 minutes ago + const signature = crypto.createHmac('sha256', 'my-secret').update(`v0:${timestamp}:${urlVerificationBody}`).digest('hex'); + const awsEvent = { + resource: '/slack/events', + path: '/slack/events', + httpMethod: 'POST', + headers: { + Accept: 'application/json,*/*', + 'Content-Type': 'application/json', + Host: 'xxx.execute-api.ap-northeast-1.amazonaws.com', + 'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)', + 'X-Slack-Request-Timestamp': `${timestamp}`, + 'X-Slack-Signature': `v0=${signature}`, + }, + multiValueHeaders: {}, + queryStringParameters: null, + multiValueQueryStringParameters: null, + pathParameters: null, + stageVariables: null, + requestContext: {}, + body: urlVerificationBody, + isBase64Encoded: false, + }; + const response = await handler( + awsEvent, + {}, + (_error, _result) => {}, + ); + assert.equal(response.statusCode, 401); + }); }); // Composable overrides diff --git a/src/receivers/HTTPResponseAck.spec.ts b/src/receivers/HTTPResponseAck.spec.ts new file mode 100644 index 000000000..3856f29a5 --- /dev/null +++ b/src/receivers/HTTPResponseAck.spec.ts @@ -0,0 +1,22 @@ +import 'mocha'; +import sinon from 'sinon'; +import { assert } from 'chai'; +import { IncomingMessage, ServerResponse } from 'http'; +import { HTTPResponseAck } from './HTTPResponseAck'; +import { createFakeLogger } from '../test-helpers'; + +describe('HTTPResponseAck', async () => { + it('should work', async () => { + const httpRequest = sinon.createStubInstance(IncomingMessage) as IncomingMessage; + const httpResponse: ServerResponse = sinon.createStubInstance(ServerResponse) as unknown as ServerResponse; + const ack = new HTTPResponseAck({ + logger: createFakeLogger(), + processBeforeResponse: false, + httpRequest, + httpResponse, + }); + assert.isDefined(ack); + assert.isDefined(ack.bind()); + ack.ack(); // no exception + }); +});