Created
December 4, 2019 15:50
-
-
Save marekkrzynowek/72d4f7bf66f61ef278dc9383688516be to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const _ = require('lodash'); | |
const createEvent = require('aws-event-mocks'); | |
const expect = require('expect'); | |
const yaml = require('js-yaml'); | |
const fs = require('fs'); | |
const dotenv = require('dotenv'); | |
const diff = require('deep-diff'); | |
const fns = {}; | |
/** | |
* This function wraps the message object in SNS context so it simulates amazon SNS payload. | |
* | |
* @param {*} message: Payload to be included in th SNS | |
* @param {string} topicArn: Optional: topic the SNS was sent to | |
* @param {string} sourceArn: Optional: SNS source topic | |
* @returns SNS message | |
*/ | |
fns.generateSNSFixture = function(message, topicArn = 'topic_Arn', sourceArn = 'source_arn') { | |
const body = { | |
Type: 'Notification', | |
MessageId: '694e2d00-afd6-5065-a525-7932afa3652e', | |
TopicArn: topicArn, | |
Message: JSON.stringify(message), | |
Timestamp: new Date().toISOString(), | |
SignatureVersion: '1', | |
Signature: 'K+szeNJUc/q8aQ4LcDzfwBJt1/Q==', | |
SigningCertURL: 'https://sns.us-east-1.amazonaws.com/url', | |
UnsubscribeURL: 'https://sns.us-east-1.amazonaws.com/?url' | |
}; | |
const fixture = { | |
Records: [ | |
{ | |
messageId: 'eccfe821-0a8e-4ab8-aa8c-cb818c0dcc1a', | |
receiptHandle: 'N1254aOMj35YWpAVbUzzx25rvGL39CC3NbL9+ZDaSK43Rgafo3+LDJPjydxF0LkAQ1+e1giDiL0=', | |
body: JSON.stringify(body), | |
attributes: {}, | |
messageAttributes: {}, | |
md5OfBody: 'be61a9f98ddd5dd9ef53e5e0f62b67fe', | |
eventSource: 'aws:sqs', | |
eventSourceArn: sourceArn, | |
awsRegion: 'us-east-1' | |
} | |
] | |
}; | |
return fixture; | |
}; | |
/** | |
* This method is a wrapper fixing the aws-mock implementation which | |
* has a bug that causes modifications to be remembered between calls. | |
* | |
* @param {*} config event parameters | |
* @return {*} event mock | |
*/ | |
fns.createAwsEvent = function(config) { | |
const event = createEvent({ template: config.template }); | |
const eventClone = { ...event }; | |
return _.has(config, 'merge') ? _.merge(eventClone, config.merge) : eventClone; | |
}; | |
/** | |
* Test for existence of headers necessary for the endpoint to support CORS requests. | |
* | |
* @param {*} Response headers | |
*/ | |
fns.checkCorsHeadersPresent = function(headers) { | |
expect(headers['Access-Control-Allow-Origin']).toBe('*'); | |
expect(headers['Access-Control-Allow-Credentials']).toBe(true); | |
}; | |
/** | |
* Test that there are no messages send to unknown SNS topics. | |
* if that test fails usually means your serverless.yml file is missing | |
* SNS topics defined as env variables. Use this test very often. | |
* | |
* @param {Sinon.spy} spy:Sinon spy | |
*/ | |
fns.expectNoUnknownSNS = function(spy) { | |
for (const spyCall of spy.getCalls()) { | |
const params = spyCall.args[0]; | |
expect(params.TopicArn).toBeDefined(); | |
} | |
}; | |
/** | |
* Test the exact number of sns send to a spy with the correlating topics and optionally payloads. | |
* You should construct the 'expected' array to contain all Messages that the test method will fire | |
* Once you pass this array the tests will ensure that all of these messages were fired and only these messages. | |
* It does not test the message firing order. | |
* | |
* @param {*} spy: Sinon spy | |
* @param {*} expected: Array of Messages to test for: | |
* [ | |
* {topic: 'SNS_USER_CREATED', payload: { user }}, | |
* {topic: 'SNS_USER_UPDATED' } | |
* ] | |
*/ | |
fns.expectExactSNSMessages = function(spy, expected) { | |
const snss = spy.getCalls().map(call => { | |
const params = call.args[0]; | |
return { topic: params.TopicArn, payload: JSON.parse(params.Message) }; | |
}); | |
expect(expected.length).toBe(snss.length); | |
for (const msg of expected) { | |
const toRemove = _findSNSIndex(snss, msg); | |
if (toRemove !== undefined && toRemove >= 0) { | |
snss.splice(toRemove, 1); | |
} | |
} | |
expect(snss.length).toBe(0); | |
}; | |
/** | |
* This function parses the serverless.yml file and recreates the environment by setting | |
* all the env variables that will be used after the deployment. | |
* | |
* @param {*} serverlessPath: Path to the serverless.yml file | |
* @param {*} dotEnvPath : Path to the .env file. Any entries in this file will overwrite | |
* whatever values will bbe set in serverless.yml file handy for | |
* setting the DB_URI variable for testing. | |
*/ | |
fns.prepareServerlessEnv = function(serverlessPath, dotEnvPath) { | |
const serverless = yaml.safeLoad(fs.readFileSync(serverlessPath, 'utf8')); | |
const envVars = Object.keys(serverless.provider.environment); | |
for (const envVar of envVars) { | |
process.env[envVar] = envVar.toLocaleLowerCase(); | |
} | |
// Override from .env file | |
const overrides = dotenv.parse(fs.readFileSync(dotEnvPath)); | |
for (const override of Object.keys(overrides)) { | |
process.env[override] = overrides[override]; | |
} | |
}; | |
/** | |
* Returns all the payloads for the topic | |
* | |
* @param {*} spy; Sinon spy | |
* @param {*} topic: SNS topic | |
*/ | |
fns.getSNSPayloads = function(spy, topic) { | |
const payloads = []; | |
for (const spyCall of spy.getCalls()) { | |
const params = spyCall.args[0]; | |
if (params.TopicArn === topic) { | |
payloads.push(JSON.parse(params.Message)); | |
} | |
} | |
return payloads; | |
}; | |
/** | |
* Test that messages was send to the topic. | |
* | |
* @param {*} spy: Sinon spy | |
* @param {string} topic: SNS topic name | |
* @param {int} count: Expected number of messages sent to the topic defaults to 1 | |
*/ | |
fns.expectSNSTopic = function(spy, topic, count = 1) { | |
let found = 0; | |
for (const spyCall of spy.getCalls()) { | |
const params = spyCall.args[0]; | |
if (params.TopicArn === topic) { | |
found += 1; | |
} | |
} | |
expect(found).toBe(count); | |
}; | |
/** | |
* Tests that the exact payload was sent to the topic. | |
* Prints a diff of payloads for all payloads sent to the topic for debugging. | |
* | |
* @param {*} spy: Sinon Spy. | |
* @param {string} topic: SNS topic name. | |
* @param {*} payload: Expected payload sent. | |
*/ | |
fns.expectSNSWithPayload = function(spy, topic, payload) { | |
let found = 0; | |
for (const spyCall of spy.getCalls()) { | |
console.log('=='); | |
const params = spyCall.args[0]; | |
if (params.TopicArn === topic) { | |
const jsonSns = JSON.parse(params.Message); | |
console.log('Found a call for topic:', topic); | |
console.log('-'); | |
console.log('Expected SNS Payload:', payload); | |
console.log('-'); | |
console.log('Acutal Payload:', jsonSns); | |
console.log('-'); | |
const differences = diff(jsonSns, JSON.parse(JSON.stringify(payload))); | |
if (!differences) { | |
console.log('Found a match'); | |
found += 1; | |
} else { | |
console.log('Differences: ', differences); | |
} | |
console.log('=='); | |
} | |
} | |
expect(found).toBe(1); | |
}; | |
/** | |
* Tests that the topic had no messages sent to it. | |
* | |
* @param {*} spy: Sinon spy. | |
* @param {string} topic: SNS topic. | |
*/ | |
fns.expectNoCallToSNSTopic = function(spy, topic) { | |
for (const spyCall of spy.getCalls()) { | |
const params = spyCall.args[0]; | |
expect(params.TopicArn).not.toBe(topic); | |
} | |
}; | |
function _findSNSIndex(snss, msg) { | |
for (let i = 0; i < snss.length; i++) { | |
const sns = snss[i]; | |
if (msg.topic === sns.topic) { | |
if (msg.payload) { | |
const difference = diff(sns.payload, JSON.parse(JSON.stringify(msg.payload))); | |
if (difference) { | |
console.log('Payloads are different:', difference); | |
} | |
return i; | |
} | |
return i; | |
} | |
} | |
} | |
module.exports = fns; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment