diff --git a/__tests__/integration.js b/__tests__/integration.js index 9ebd5e50..2a51d0ee 100644 --- a/__tests__/integration.js +++ b/__tests__/integration.js @@ -2,48 +2,44 @@ const path = require('path') const fs = require('fs') const serverlessExpress = require('../src/index') const app = require('../examples/basic-starter-api-gateway-v1/src/app') -const { log, makeEvent, expectedRootResponse, makeResponse } = require('../jest-helpers') +const { log, makeEvent, expectedRootResponse, makeResponse, EACH_MATRIX } = require('../jest-helpers') const serverlessExpressInstance = serverlessExpress({ app, log }) -describe('integration tests', () => { +describe.each(EACH_MATRIX)('%s:%s: integration tests', (eventSourceName, frameworkName) => { test('handler returns promise', () => { - const response = serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/', httpMethod: 'GET' - })) + }) + const response = serverlessExpressInstance.handler(event) expect(response.then).toBeTruthy() }) - test('GET HTML (initial request)', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ path: '/', httpMethod: 'GET' })) - - delete response.multiValueHeaders.date - expect(response.body.startsWith('')).toBe(true) - const expectedResponse = expectedRootResponse() - delete response.body - delete expectedResponse.body - expect(response).toEqual(expectedResponse) - }) - - test('GET HTML (subsequent request)', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + test('GET HTML', async () => { + const event = makeEvent({ + eventSourceName, path: '/', httpMethod: 'GET' - })) - delete response.multiValueHeaders.date + }) + const response = await serverlessExpressInstance.handler(event) + expect(response.body.startsWith('')).toBe(true) const expectedResponse = expectedRootResponse() delete response.body delete expectedResponse.body + delete response.multiValueHeaders.date expect(response).toEqual(expectedResponse) }) test('GET JSON collection', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/users', httpMethod: 'GET' - })) + }) + const response = await serverlessExpressInstance.handler(event) delete response.multiValueHeaders.date expect(response).toEqual(makeResponse({ @@ -56,10 +52,12 @@ describe('integration tests', () => { }) test('GET missing route', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/nothing-here', httpMethod: 'GET' - })) + }) + const response = await serverlessExpressInstance.handler(event) delete response.multiValueHeaders.date expect(response.body.startsWith('')).toBe(true) @@ -78,10 +76,12 @@ describe('integration tests', () => { }) test('GET JSON single', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/users/1', httpMethod: 'GET' - })) + }) + const response = await serverlessExpressInstance.handler(event) delete response.multiValueHeaders.date expect(response).toEqual(makeResponse({ @@ -107,6 +107,7 @@ describe('integration tests', () => { } const event = makeEvent({ + eventSourceName, path: '/users/1', httpMethod: 'GET' }) @@ -116,6 +117,7 @@ describe('integration tests', () => { test('GET JSON single (resolutionMode = PROMISE)', async () => { const event = makeEvent({ + eventSourceName, path: '/users/1', httpMethod: 'GET' }) @@ -133,10 +135,12 @@ describe('integration tests', () => { }) test('GET JSON single 404', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/users/3', httpMethod: 'GET' - })) + }) + const response = await serverlessExpressInstance.handler(event) delete response.multiValueHeaders.date expect(response).toEqual(makeResponse({ body: '{}', @@ -150,6 +154,7 @@ describe('integration tests', () => { test('success - image response', async () => { const event = makeEvent({ + eventSourceName, path: '/sam', httpMethod: 'GET' }) @@ -179,6 +184,7 @@ describe('integration tests', () => { test('POST JSON', async () => { const event = makeEvent({ + eventSourceName, path: '/users', httpMethod: 'POST', body: `{"name": "${newName}"}` @@ -197,10 +203,12 @@ describe('integration tests', () => { }) test('GET JSON single (again; post-creation) 200', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/users/3', httpMethod: 'GET' - })) + }) + const response = await serverlessExpressInstance.handler(event) delete response.multiValueHeaders.date expect(response).toEqual(makeResponse({ body: `{"id":3,"name":"${newName}"}`, @@ -213,10 +221,12 @@ describe('integration tests', () => { }) test('DELETE JSON', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/users/1', httpMethod: 'DELETE' - })) + }) + const response = await serverlessExpressInstance.handler(event) delete response.multiValueHeaders.date expect(response).toEqual(makeResponse({ @@ -230,11 +240,13 @@ describe('integration tests', () => { }) test('PUT JSON', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/users/2', httpMethod: 'PUT', body: '{"name": "Samuel"}' - })) + }) + const response = await serverlessExpressInstance.handler(event) delete response.multiValueHeaders.date expect(response).toEqual(makeResponse({ body: '{"id":2,"name":"Samuel"}', @@ -247,12 +259,14 @@ describe('integration tests', () => { }) test('base64 encoded request', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/users/2', httpMethod: 'PUT', body: global.btoa('{"name": "Samuel"}'), isBase64Encoded: true - })) + }) + const response = await serverlessExpressInstance.handler(event) delete response.multiValueHeaders.date expect(response).toEqual(makeResponse({ body: '{"id":2,"name":"Samuel"}', @@ -287,10 +301,12 @@ describe('integration tests', () => { }) test('Multiple headers of the same name (set-cookie)', async () => { - const response = await serverlessExpressInstance.handler(makeEvent({ + const event = makeEvent({ + eventSourceName, path: '/cookie', httpMethod: 'GET' - })) + }) + const response = await serverlessExpressInstance.handler(event) delete response.multiValueHeaders.date const expectedSetCookieHeaders = [ diff --git a/jest-helpers.js b/jest-helpers.js deleted file mode 100644 index a184e27f..00000000 --- a/jest-helpers.js +++ /dev/null @@ -1,122 +0,0 @@ -const apiGatewayV1Event = require('./examples/basic-starter-api-gateway-v1/api-gateway-event.json') -const apiGatewayV2Event = require('./examples/basic-starter-api-gateway-v2/api-gateway-event.json') - -const log = { - info: () => null, - debug: () => null, - error: () => null -} - -class MockContext { - constructor (resolve) { - this.resolve = resolve - } - - succeed (successResponse) { - this.resolve(successResponse) - } -} - -function clone (json) { - return JSON.parse(JSON.stringify(json)) -} - -/** - * Simple object check. - * @param item - * @returns {boolean} - */ -function isObject (item) { - return (item && typeof item === 'object' && !Array.isArray(item)) -} - -/** - * Deep merge two objects. - * @param target - * @param ...sources - */ -function mergeDeep (target, ...sources) { - if (!sources.length) return target - const source = sources.shift() - - if (isObject(target) && isObject(source)) { - for (const key in source) { - if (isObject(source[key])) { - if (!target[key]) Object.assign(target, { [key]: {} }) - mergeDeep(target[key], source[key]) - } else { - Object.assign(target, { [key]: source[key] }) - } - } - } - - return mergeDeep(target, ...sources) -} - -function makeEvent (eventOverrides = {}) { - const baseEvent = clone(apiGatewayV1Event) - const multiValueHeaders = { - ...baseEvent.multiValueHeaders, - ...eventOverrides.multiValueHeaders - } - const root = { - ...baseEvent, - ...eventOverrides - } - root.multiValueHeaders = multiValueHeaders - root.pathParameters.proxy = eventOverrides.path && eventOverrides.path.replace(/^\//, '') - return root -} - -function makeApiGatewayV2Event (values) { - const baseEvent = clone(apiGatewayV2Event) - - if (!values && !values.requestContext && !values.requestContext.http && !values.requestContext.http.path) { - values.requestContext.http.path = values.rawPath - } - - return mergeDeep(baseEvent, values) -} - -function expectedRootResponse () { - return makeResponse({ - multiValueHeaders: { - 'content-length': ['4659'], - 'content-type': ['text/html; charset=utf-8'], - etag: ['W/"1233-UcgIN3SD7YNLl2bLEKDbkMGu6io"'] - } - }) -} - -function makeResponse (response) { - const baseResponse = { - body: '', - isBase64Encoded: false, - statusCode: 200 - } - const baseHeaders = { - 'access-control-allow-origin': ['*'], - 'content-type': ['application/json; charset=utf-8'], - 'x-powered-by': ['Express'] - } - const multiValueHeaders = { - ...baseHeaders, - ...response.multiValueHeaders - } - const finalResponse = { - ...baseResponse, - ...response - } - finalResponse.multiValueHeaders = multiValueHeaders - return finalResponse -} - -module.exports = { - log, - MockContext, - clone, - makeEvent, - expectedRootResponse, - makeResponse, - makeApiGatewayV2Event -} diff --git a/jest-helpers/alb-event.js b/jest-helpers/alb-event.js new file mode 100644 index 00000000..63334535 --- /dev/null +++ b/jest-helpers/alb-event.js @@ -0,0 +1,40 @@ +const clone = require('./clone') +const mergeDeep = require('./merge-deep') + +const albEvent = { + 'requestContext': { + 'elb': { + 'targetGroupArn': 'arn:aws:elasticloadbalancing:us-east-1:347971939225:targetgroup/aws-s-Targe-RJF5FKWHX6Y8/29425aed99131fd0' + } + }, + 'httpMethod': 'POST', + 'path': '/users', + 'multiValueQueryStringParameters': {}, + 'multiValueHeaders': { + 'accept': ['*/*'], + 'accept-encoding': ['gzip, deflate'], + 'accept-language': ['en-US,en;q=0.9'], + 'cache-control': ['no-cache'], + 'connection': ['keep-alive'], + 'content-length': ['26'], + 'content-type': ['application/json'], + 'host': ['aws-ser-alb-p9y7dvwm0r42-2135869912.us-east-1.elb.amazonaws.com'], + 'origin': ['http://aws-ser-alb-p9y7dvwm0r42-2135869912.us-east-1.elb.amazonaws.com'], + 'pragma': ['no-cache'], + 'referer': ['http://aws-ser-alb-p9y7dvwm0r42-2135869912.us-east-1.elb.amazonaws.com/'], + 'user-agent': ['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'], + 'x-amzn-trace-id': ['Root=1-5cdf3407-76d73870a87c746001f27090'], + 'x-forwarded-for': ['72.21.198.66'], + 'x-forwarded-port': ['80'], + 'x-forwarded-proto': ['http'] + }, + 'body': '{"name":"postmaaaateeeee"}', + 'isBase64Encoded': false +} + +module.exports = function makeAlbEvent (values = {}) { + const baseEvent = clone(albEvent) + const mergedEvent = mergeDeep(baseEvent, values) + + return mergedEvent +} diff --git a/jest-helpers/api-gateway-v1-event.js b/jest-helpers/api-gateway-v1-event.js new file mode 100644 index 00000000..729db02e --- /dev/null +++ b/jest-helpers/api-gateway-v1-event.js @@ -0,0 +1,150 @@ +const clone = require('./clone') +const mergeDeep = require('./merge-deep') + +const apiGatewayV1Event = { + 'resource': '/{proxy+}', + 'path': '/users', + 'httpMethod': 'POST', + 'headers': { + 'Accept': '*/*', + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'en-US,en;q=0.9', + 'cache-control': 'no-cache', + 'CloudFront-Forwarded-Proto': 'https', + 'CloudFront-Is-Desktop-Viewer': 'true', + 'CloudFront-Is-Mobile-Viewer': 'false', + 'CloudFront-Is-SmartTV-Viewer': 'false', + 'CloudFront-Is-Tablet-Viewer': 'false', + 'CloudFront-Viewer-Country': 'US', + 'content-type': 'application/json', + 'Host': 'xxxxxx.execute-api.us-east-1.amazonaws.com', + 'origin': 'https://xxxxxx.execute-api.us-east-1.amazonaws.com', + 'pragma': 'no-cache', + 'Referer': 'https://xxxxxx.execute-api.us-east-1.amazonaws.com/prod/', + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36', + 'Via': '2.0 00f0a41f749793b9dd653153037c957e.cloudfront.net (CloudFront)', + 'X-Amz-Cf-Id': '2D5N65SYHJdnJfEmAV_hC0Mw3QvkbUXDumJKAL786IGHRdq_MggPtA==', + 'X-Amzn-Trace-Id': 'Root=1-5cdf30d0-31a428004abe13807f9445b0', + 'X-Forwarded-For': '11.111.111.111, 11.111.111.111', + 'X-Forwarded-Port': '443', + 'X-Forwarded-Proto': 'https' + }, + 'multiValueHeaders': { + 'Accept': [ + '*/*' + ], + 'Accept-Encoding': [ + 'gzip, deflate, br' + ], + 'Accept-Language': [ + 'en-US,en;q=0.9' + ], + 'cache-control': [ + 'no-cache' + ], + 'CloudFront-Forwarded-Proto': [ + 'https' + ], + 'CloudFront-Is-Desktop-Viewer': [ + 'true' + ], + 'CloudFront-Is-Mobile-Viewer': [ + 'false' + ], + 'CloudFront-Is-SmartTV-Viewer': [ + 'false' + ], + 'CloudFront-Is-Tablet-Viewer': [ + 'false' + ], + 'CloudFront-Viewer-Country': [ + 'US' + ], + 'content-type': [ + 'application/json' + ], + 'Host': [ + 'xxxxxx.execute-api.us-east-1.amazonaws.com' + ], + 'origin': [ + 'https://xxxxxx.execute-api.us-east-1.amazonaws.com' + ], + 'pragma': [ + 'no-cache' + ], + 'Referer': [ + 'https://xxxxxx.execute-api.us-east-1.amazonaws.com/prod/' + ], + 'User-Agent': [ + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36' + ], + 'Via': [ + '2.0 00f0a41f749793b9dd653153037c957e.cloudfront.net (CloudFront)' + ], + 'X-Amz-Cf-Id': [ + '2D5N65SYHJdnJfEmAV_hC0Mw3QvkbUXDumJKAL786IGHRdq_MggPtA==' + ], + 'X-Amzn-Trace-Id': [ + 'Root=1-5cdf30d0-31a428004abe13807f9445b0' + ], + 'X-Forwarded-For': [ + '11.111.111.111, 11.111.111.111' + ], + 'X-Forwarded-Port': [ + '443' + ], + 'X-Forwarded-Proto': [ + 'https' + ] + }, + 'queryStringParameters': null, + 'multiValueQueryStringParameters': null, + 'pathParameters': { + // Default pathParameters.proxy to path (without the '/' prefix) + // 'proxy': 'users' + }, + 'stageVariables': {}, + 'requestContext': { + 'resourceId': 'xxxxx', + 'resourcePath': '/{proxy+}', + 'httpMethod': 'POST', + 'extendedRequestId': 'Z2SQlEORIAMFjpA=', + 'requestTime': '17/May/2019:22:08:16 +0000', + // Default requestContext.path to `${requestContext.stage}${path}` + // 'path': '/prod/users', + 'accountId': 'xxxxxxxx', + 'protocol': 'HTTP/1.1', + 'stage': 'prod', + 'domainPrefix': 'xxxxxx', + 'requestTimeEpoch': 1558130896565, + 'requestId': '4589cf16-78f0-11e9-9c65-816a9b037cec', + 'identity': { + 'cognitoIdentityPoolId': null, + 'accountId': null, + 'cognitoIdentityId': null, + 'caller': null, + 'sourceIp': '11.111.111.111', + 'principalOrgId': null, + 'accessKey': null, + 'cognitoAuthenticationType': null, + 'cognitoAuthenticationProvider': null, + 'userArn': null, + 'userAgent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36', + 'user': null + }, + 'domainName': 'xxxxxx.execute-api.us-east-1.amazonaws.com', + 'apiId': 'xxxxxx' + }, + 'body': '{"name": "Sandy Samantha Salamander"}', + 'isBase64Encoded': false +} + +module.exports = function makeApiGatewayV1Event (values = {}) { + const baseEvent = clone(apiGatewayV1Event) + const mergedEvent = mergeDeep(baseEvent, values) + + if (!mergedEvent.pathParameters.proxy) mergedEvent.pathParameters.proxy = values.path.replace(/^\//, '') + if (!mergedEvent.requestContext.path) mergedEvent.requestContext.path = `${mergedEvent.requestContext.stage}${mergedEvent.path}` + + return mergedEvent +} diff --git a/jest-helpers/api-gateway-v2-event.js b/jest-helpers/api-gateway-v2-event.js new file mode 100644 index 00000000..25f9a588 --- /dev/null +++ b/jest-helpers/api-gateway-v2-event.js @@ -0,0 +1,58 @@ +const clone = require('./clone') +const mergeDeep = require('./merge-deep') + +const apiGatewayV2Event = { + 'version': '2.0', + 'routeKey': '$default', + 'rawPath': '/users', + 'rawQueryString': '', + 'headers': { + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', + 'accept-encoding': 'gzip, deflate, br', + 'accept-language': 'en-US,en;q=0.9', + 'cache-control': 'max-age=0', + 'content-length': '0', + 'host': '6bwvllq3t2.execute-api.us-east-1.amazonaws.com', + 'sec-fetch-dest': 'document', + 'sec-fetch-mode': 'navigate', + 'sec-fetch-site': 'none', + 'sec-fetch-user': '?1', + 'upgrade-insecure-requests': '1', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36', + 'x-amzn-trace-id': 'Root=1-5ff59707-4914805430277a6209549a59', + 'x-forwarded-for': '203.123.103.37', + 'x-forwarded-port': '443', + 'x-forwarded-proto': 'https' + }, + 'requestContext': { + 'accountId': '347971939225', + 'apiId': '6bwvllq3t2', + 'domainName': '6bwvllq3t2.execute-api.us-east-1.amazonaws.com', + 'domainPrefix': '6bwvllq3t2', + 'http': { + 'method': 'GET', + // Default requestContext.http.path to rawPath + // 'path': '/users', + 'protocol': 'HTTP/1.1', + 'sourceIp': '203.123.103.37', + 'userAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36' + }, + 'requestId': 'YuSJQjZfoAMESbg=', + 'routeKey': '$default', + 'stage': '$default', + 'time': '06/Jan/2021:10:55:03 +0000', + 'timeEpoch': 1609930503973 + }, + 'isBase64Encoded': false +} + +module.exports = + +function makeApiGatewayV2Event (values = {}) { + const baseEvent = clone(apiGatewayV2Event) + const mergedEvent = mergeDeep(baseEvent, values) + + mergedEvent.requestContext.http.path = mergedEvent.requestContext.http.path || values.rawPath + + return mergedEvent +} diff --git a/jest-helpers/clone.js b/jest-helpers/clone.js new file mode 100644 index 00000000..2bc0fde1 --- /dev/null +++ b/jest-helpers/clone.js @@ -0,0 +1,3 @@ +module.exports = function clone (json) { + return JSON.parse(JSON.stringify(json)) +} diff --git a/jest-helpers/index.js b/jest-helpers/index.js new file mode 100644 index 00000000..fdc436c6 --- /dev/null +++ b/jest-helpers/index.js @@ -0,0 +1,96 @@ +const makeApiGatewayV1Event = require('./api-gateway-v1-event') +const makeApiGatewayV2Event = require('./api-gateway-v2-event') +const makeAlbEvent = require('./alb-event') +const makeLambdaEdgeEvent = require('./lambda-edge-event.js') + +const EVENT_SOURCE_NAMES = [ + // 'alb', + 'apiGatewayV1' + // 'apiGatewayV2', + // 'lambdaEdge' +] + +const FRAMEWORK_NAMES = [ + 'express' +] +const EACH_MATRIX = [] + +EVENT_SOURCE_NAMES.forEach(eventSource => { + FRAMEWORK_NAMES.forEach(framework => { + EACH_MATRIX.push([ eventSource, framework ]) + }) +}) + +const log = { + info: () => null, + debug: () => null, + error: () => null +} + +class MockContext { + constructor (resolve) { + this.resolve = resolve + } + + succeed (successResponse) { + this.resolve(successResponse) + } +} + +function makeEvent ({ eventSourceName, ...rest }) { + switch (eventSourceName) { + case 'alb': + return makeAlbEvent(rest) + case 'apiGatewayV1': + return makeApiGatewayV1Event(rest) + case 'apiGatewayV2': + return makeApiGatewayV2Event(rest) + case 'lambdaEdge': + return makeLambdaEdgeEvent(rest) + default: + throw new Error(`Unknown eventSourceName ${eventSourceName}`) + } +} + +function expectedRootResponse () { + return makeResponse({ + multiValueHeaders: { + 'content-length': ['4659'], + 'content-type': ['text/html; charset=utf-8'], + etag: ['W/"1233-UcgIN3SD7YNLl2bLEKDbkMGu6io"'] + } + }) +} + +function makeResponse (response) { + const baseResponse = { + body: '', + isBase64Encoded: false, + statusCode: 200 + } + const baseHeaders = { + 'access-control-allow-origin': ['*'], + 'content-type': ['application/json; charset=utf-8'], + 'x-powered-by': ['Express'] + } + const multiValueHeaders = { + ...baseHeaders, + ...response.multiValueHeaders + } + const finalResponse = { + ...baseResponse, + ...response + } + finalResponse.multiValueHeaders = multiValueHeaders + return finalResponse +} + +module.exports = { + log, + MockContext, + makeEvent, + expectedRootResponse, + makeResponse, + makeApiGatewayV2Event, + EACH_MATRIX +} diff --git a/jest-helpers/lambda-edge-event.js b/jest-helpers/lambda-edge-event.js new file mode 100644 index 00000000..9acf61ad --- /dev/null +++ b/jest-helpers/lambda-edge-event.js @@ -0,0 +1,128 @@ +const clone = require('./clone') +const mergeDeep = require('./merge-deep') + +const lambdaEdgeEvent = { + 'Records': [ + { + 'cf': { + 'config': { + 'distributionDomainName': 'd3qj9vk9486y6c.cloudfront.net', + 'distributionId': 'E2I5C7O4FEQEKZ', + 'eventType': 'viewer-request', + 'requestId': 'BKXC0kFgBfWSEgribSo9EwziZB1FztiXQ96VRvTfFNHYCBv7Ko-RBQ==' + }, + 'request': { + 'body': { + 'action': 'read-only', + 'data': 'eyJuYW1lIjoiU2FtIn0=', + 'encoding': 'base64', + 'inputTruncated': false + }, + 'clientIp': '203.123.103.37', + 'headers': { + 'host': [ + { + 'key': 'Host', + 'value': 'd3qj9vk9486y6c.cloudfront.net' + } + ], + 'user-agent': [ + { + 'key': 'User-Agent', + 'value': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36' + } + ], + 'content-length': [ + { + 'key': 'content-length', + 'value': '14' + } + ], + 'cache-control': [ + { + 'key': 'Cache-Control', + 'value': 'max-age=0' + } + ], + 'accept': [ + { + 'key': 'accept', + 'value': 'application/json,text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' + } + ], + 'if-none-match': [ + { + 'key': 'if-none-match', + 'value': 'W/"2e-Lu6qxFOQSPFulDAGUFiiK6QgREo"' + } + ], + 'accept-language': [ + { + 'key': 'accept-language', + 'value': 'en-US,en;q=0.9' + } + ], + 'upgrade-insecure-requests': [ + { + 'key': 'upgrade-insecure-requests', + 'value': '1' + } + ], + 'content-type': [ + { + 'key': 'content-type', + 'value': 'application/json' + } + ], + 'origin': [ + { + 'key': 'Origin', + 'value': 'https://d3qj9vk9486y6c.cloudfront.net' + } + ], + 'sec-fetch-site': [ + { + 'key': 'Sec-Fetch-Site', + 'value': 'same-origin' + } + ], + 'sec-fetch-mode': [ + { + 'key': 'Sec-Fetch-Mode', + 'value': 'cors' + } + ], + 'sec-fetch-dest': [ + { + 'key': 'Sec-Fetch-Dest', + 'value': 'empty' + } + ], + 'referer': [ + { + 'key': 'Referer', + 'value': 'https://d3qj9vk9486y6c.cloudfront.net/users' + } + ], + 'accept-encoding': [ + { + 'key': 'Accept-Encoding', + 'value': 'gzip, deflate, br' + } + ] + }, + 'method': 'GET', + 'querystring': '', + 'uri': '/' + } + } + } + ] +} + +module.exports = function makeLambdaEdgeEvent (values = {}) { + const baseEvent = clone(lambdaEdgeEvent) + const mergedEvent = mergeDeep(baseEvent, values) + + return mergedEvent +} diff --git a/jest-helpers/merge-deep.js b/jest-helpers/merge-deep.js new file mode 100644 index 00000000..52901e0a --- /dev/null +++ b/jest-helpers/merge-deep.js @@ -0,0 +1,32 @@ +/** + * Simple object check. + * @param item + * @returns {boolean} + */ +function isObject (item) { + return (item && typeof item === 'object' && !Array.isArray(item)) +} + +/** + * Deep merge two objects. + * @param target + * @param ...sources + */ + +module.exports = function mergeDeep (target, ...sources) { + if (!sources.length) return target + const source = sources.shift() + + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (!target[key]) Object.assign(target, { [key]: {} }) + mergeDeep(target[key], source[key]) + } else { + Object.assign(target, { [key]: source[key] }) + } + } + } + + return mergeDeep(target, ...sources) +} diff --git a/package.json b/package.json index 7db18246..6ebfaef8 100644 --- a/package.json +++ b/package.json @@ -145,4 +145,4 @@ "path": "cz-conventional-changelog" } } -} +} \ No newline at end of file diff --git a/src/event-sources/aws/api-gateway-v2.js b/src/event-sources/aws/api-gateway-v2.js index 5ec4fde0..8ac81efb 100644 --- a/src/event-sources/aws/api-gateway-v2.js +++ b/src/event-sources/aws/api-gateway-v2.js @@ -56,8 +56,8 @@ function getResponseToApiGateway ({ throw new Error('chunked encoding is not supported by API Gateway') } - const cookies = headers["set-cookie"]; - delete headers["set-cookie"]; + const cookies = headers['set-cookie'] + delete headers['set-cookie'] return { statusCode,