UNPKG

lambdasync

Version:

Easy sync between local code and AWS lambda functions

357 lines (325 loc) 11.5 kB
'use strict'; const path = require('path'); jest.mock('fs'); const fs = require('fs'); const { promisedExec, mustacheLite, markdown, markdownProperty, addInputDefault, awsPromise, stripLambdaVersion, makeLambdaPolicyArn, parseCommandArgs, functionExists, copyPackageJson, hashPackageDependencies, logger, handleGenericFailure, logMessage, formatTimestamp, delay, startWith, isDate, removeFileExtension, makeAbsolutePath } = require('./util'); describe('util', () => { describe('promisedExec', () => { it('should succeed with result', () => { promisedExec('ls') .then(res => expect(typeof res).toBe('string')); }); it('should fail with error', () => { promisedExec('xcvgdfgfdggfdgdf') .catch(err => expect(typeof err).toBe('object')); }); }); describe('mustacheLite', () => { it('should replace matching params', () => { const result = mustacheLite(`Hello, {{you}}! My name is {{me}}!`, {you:'Foo', me: 'Bar'}); expect(result).toBe(`Hello, Foo! My name is Bar!`); }); it('should not replace unmatched params', () => { const result = mustacheLite(`Hello, {{you}}! My name is {{me}}!`, {me: 'Bar'}); expect(result).toBe(`Hello, {{you}}! My name is Bar!`); }); }); describe('markdown', () => { const markdownPath = path.join(__dirname, '..', 'test', 'markdown.md'); fs.__setMockFiles({ [markdownPath]: `# Markdown test ===================================== Properties ===================================== **First property**: \`{{firstProperty}}\` **Second property**: \`{{secondProperty}}\` ## Next \`\`\` cd {{name}} lambdasync \`\`\` ` }); it('should produce styled markdown output from string', () => { const result = markdown({ templateString: `**Hello {{you}}!** My name is _{{me}}_`, data: {you:'Foo', me: 'Bar'} }); expect(result).toMatchSnapshot(); }); it('should produce styled markdown output from path', () => { const result = markdown({ templatePath: '../test/markdown.md', data: {firstProperty:'Foo', secondProperty: 'Bar', name: 'Baz'} }); expect(result).toMatchSnapshot(); }); }); describe('markdownProperty', () => { it('should produce markdown properties based on the input', () => { const result = markdownProperty( { key: 'you', label: 'You' }, {you:'Foo', me: 'Bar'} ); expect(result).toMatchSnapshot(); }); }); describe('addInputDefault', () => { it('should add defaults to inquirer input', () => { const result = addInputDefault( { profileName: 'lambdasync', lambdaName: 'test' }, { type: 'input', name: 'lambdaName', message: 'Function name' } ); expect(result.default).toMatchSnapshot(); }); }); describe('awsPromise', () => { const mockApi = { getFunctionConfiguration: (params, cb) => { if (params && params.FunctionName === 'test') { return cb(null, {id: 1}); } return cb('Could not find function'); } }; it('should resolve with result', () => { awsPromise(mockApi, 'getFunctionConfiguration', {FunctionName: 'test'}) .then(res => expect(res.id).toBe(1)) }); it('should reject on failure', () => { awsPromise(mockApi, 'getFunctionConfiguration', {FunctionName: 'unknown'}) .catch(err => expect(err).toBe('Could not find function')) awsPromise(null, 'getFunctionConfiguration', {FunctionName: 'unknown'}) .catch(err => expect(err).toBeTruthy()) }); }); describe('stripLambdaVersion', () => { it('should remove :num from strings', () => { const res = stripLambdaVersion('some:String:123'); expect(res).toBe('some:String'); }); it('should leave strings not ending in :num untouched', () => { const res = stripLambdaVersion('some random string '); expect(res).toBe('some random string '); }); }); describe('makeLambdaPolicyArn', () => { it('should make a policy ARN from FunctionArn and gateway id', () => { const res = makeLambdaPolicyArn({ lambdaArn: 'arn:aws:lambda:eu-west-1:202020202020:function:test', apiGatewayId: '4ghl2mfk30' }); expect(res).toMatchSnapshot(); }); }); describe('parseCommandArgs', () => { it('should create an object with the arg array from CLI', () => { const res = parseCommandArgs([ 'timeout=10', 'memory=128' ]); expect(res).toMatchSnapshot(); }); }); describe('functionExists', () => { const validFunctions = ['foo', 'bar']; const mockApi = { getFunction: function getFunction({ FunctionName }, cb) { if (validFunctions.indexOf(FunctionName) !== -1) { return cb(null, FunctionName); } if (FunctionName === 'error') { return cb(new Error('Generic Error')); } return cb(new Error('ResourceNotFoundException')); }, }; it('should return true for existing functions', () => { expect.assertions(2); functionExists(mockApi, 'foo') .then(res => expect(res).toBe(true)); functionExists(mockApi, 'bar') .then(res => expect(res).toBe(true)); }); it('should catch ResourceNotFoundExceptions and return false for non existant functions', () => { expect.assertions(1); functionExists(mockApi, 'baz') .then(res => expect(res).toBe(false)); }); it('should reject the promise on unknown errors', () => { expect.assertions(1); functionExists(mockApi, 'error') .catch(err => expect(err).toBeDefined()); }); }); describe('copyPackageJson', () => { const SOURCE_DIR = path.join(__dirname, '..', 'test'); const TARGET_DIR = path.join(__dirname, '..', 'test', 'new'); it('Should move package.json from source file to target file', () => { const mockJson = JSON.stringify({ name: 'w00t', version: '0.0.1'}); fs.__setMockFiles({ [path.join(SOURCE_DIR, 'package.json')]: mockJson }); copyPackageJson(SOURCE_DIR, TARGET_DIR); expect(fs.readFileSync(path.join(TARGET_DIR, 'package.json'))).toBe(mockJson); }); it('Should replace fields in the JSON with props from the optional third argument', () => { const mockJson = JSON.stringify({ name: '{{name}}', version: '0.0.1'}); const name = 'm00t'; fs.__setMockFiles({ [path.join(SOURCE_DIR, 'package.json')]: mockJson }); copyPackageJson(SOURCE_DIR, TARGET_DIR, { name }); expect(JSON.parse(fs.readFileSync(path.join(TARGET_DIR, 'package.json'))).name).toBe(name); }) }); describe('hashPackageDependencies', () => { const packageJson1 = { name: 'packageJson1', dependencies: { pony: '*', uniwhal: '1.0.0'}}; const packageJson2 = { name: 'packageJson2', dependencies: { pony: '*', uniwhal: '1.0.0'}}; const packageJson3 = { name: 'packageJson3', dependencies: { pony: '0.1.1', uniwhal: '1.0.0'}}; it('creates the same hash given the same input', () => { expect(hashPackageDependencies(packageJson1)).toEqual(hashPackageDependencies(packageJson1)); }); it('creates the same hash given the input with the same dependencies', () => { expect(hashPackageDependencies(packageJson1)).toEqual(hashPackageDependencies(packageJson2)); }); it('creates different hashes given different dependencies', () => { expect(hashPackageDependencies(packageJson1)).not.toEqual(hashPackageDependencies(packageJson3)); }); }); describe('logger', () => { beforeEach(() => { global.console.log = jest.fn(); }); it('should return a function',() => { expect(typeof logger('w00t!')).toBe('function'); }); it('returned function should return it\'s input',() => { expect(logger('w00t!')(1)).toBe(1); }); it('should call console.log',() => { logger('herro')('there'); expect(global.console.log).toHaveBeenCalled(); }); }); describe('handleGenericFailure', () => { const mockedMessage = 'mock'; beforeEach(() => { global.console.log = jest.fn(); fs.__setMockFiles({ [path.join(__dirname, 'markdown', 'generic-fail.md')]: mockedMessage, }); }); it('should console.log the generic error message', () => { handleGenericFailure(); expect(global.console.log).toHaveBeenCalled(); expect(global.console.log.mock.calls[0][0]).toContain(mockedMessage); }) }); describe('logMessage', () => { beforeEach(() => { global.console.log = jest.fn(); }); it('should return a function',() => { expect(typeof logMessage('w00t!')).toBe('function'); }); it('returned function should return it\'s input',() => { expect(logMessage('w00t!')(1)).toBe(1); }); it('should call console.log',() => { const hello = 'hello'; logMessage(hello)('there'); expect(global.console.log).toHaveBeenLastCalledWith(hello); }); }); describe('formatTimestamp', () => { it('Should format a timestamp to ISO date time format', () => { expect(formatTimestamp(new Date())).toMatch(/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/); }) }); describe('delay', () => { const delayTime = 10; const delayFunc = delay(delayTime); it('should return a function',() => { expect(typeof delayFunc).toBe('function'); }); it('returned function should return a Promise',() => { expect(delayFunc('w00t')).toBeInstanceOf(Promise); }); it('promise should resolve with input value after the specified time', () => { const start = new Date().getTime(); delayFunc('💩') .then(val => { const end = new Date().getTime(); expect(val).toBe('💩'); expect(end - start).toBeGreaterThanOrEqual(delayTime); }); }); }); describe('startWith', () => { it('should resolve a promise with the input value', () => { const val = 'arbitrary value'; startWith(val) .then(res => expect(res).toBe(val)); }); }); describe('isDate', () => { it('Should return true for valid dates', () => { expect(isDate(new Date())).toBe(true); }); it('Should return false for invalid dates', () => { expect(isDate(new Date('w00t'))).toBe(false); }); it('should return false for non Date types', () => { expect(isDate('test')).toBe(false); expect(isDate(42)).toBe(false); expect(isDate([])).toBe(false); expect(isDate({})).toBe(false); }); }); describe('removeFileExtension', () => { it('should remove known file extensions', () => { expect(removeFileExtension('./hej/kom/och/hjalp/mig.js')).toBe('./hej/kom/och/hjalp/mig'); expect(removeFileExtension('lambdasync.com')).toBe('lambdasync.com'); expect(removeFileExtension('lambdasync')).toBe('lambdasync'); }); }); describe('makeAbsolutePath', () => { const ABS_PATH = '/Users/lambdasync'; const REL_PATH = './bin/src/file.js'; it('should return absolute paths unchanged', () => { expect(makeAbsolutePath(ABS_PATH)).toBe(ABS_PATH); }); it('should add process.cwd() to relative paths', () => { expect(makeAbsolutePath(REL_PATH)).toBe(path.join(process.cwd(), REL_PATH)); }); }); });