From 861bc945eae6c4bb0cd17c3c5a0784390d768432 Mon Sep 17 00:00:00 2001 From: kdnakt Date: Sun, 13 Oct 2019 21:57:22 +0900 Subject: [PATCH 01/50] add testcase for #resolveFilePathsFromPatterns --- .../package/lib/packageService.test.js | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/plugins/package/lib/packageService.test.js b/lib/plugins/package/lib/packageService.test.js index eb47cfc0b..e632ac6a0 100644 --- a/lib/plugins/package/lib/packageService.test.js +++ b/lib/plugins/package/lib/packageService.test.js @@ -604,4 +604,44 @@ describe('#packageService()', () => { ); }); }); + + describe('#resolveFilePathsFromPatterns()', () => { + const servicePath = 'lib/plugins/package/lib/__tests__/'; + + it('should exclude all and include function/handler.js', () => { + const params = { + exclude: ['**'], + include: ['src/function/handler.js'], + }; + const expected = ['src/function/handler.js']; + serverless.config.servicePath = servicePath; + + return expect(packagePlugin.resolveFilePathsFromPatterns(params)) + .to.be.fulfilled.then(actual => expect(actual).to.deep.equal(expected)); + }); + + it('should include file specified with `!` in exclude params', () => { + const params = { + exclude: ['**','!src/utils/utils.js'], + include: ['src/function/handler.js'], + }; + const expected = ['src/function/handler.js','src/utils/utils.js']; + serverless.config.servicePath = servicePath; + + return expect(packagePlugin.resolveFilePathsFromPatterns(params)) + .to.be.fulfilled.then(actual => expect(actual).to.deep.equal(expected)); + }); + + it('should exclude file specified with `!` in include params', () => { + const params = { + exclude: [], + include: ['!src/utils/utils.js'], + }; + const expected = ['src/function/handler.js']; + serverless.config.servicePath = servicePath; + + return expect(packagePlugin.resolveFilePathsFromPatterns(params)) + .to.be.fulfilled.then(actual => expect(actual).to.deep.equal(expected)); + }); + }); }); From 93f015bcd1836d03061e95f60c3142d0053a6c60 Mon Sep 17 00:00:00 2001 From: kdnakt Date: Sun, 13 Oct 2019 21:58:21 +0900 Subject: [PATCH 02/50] add tests files --- lib/plugins/package/lib/__tests__/src/function/handler.js | 0 lib/plugins/package/lib/__tests__/src/utils/utils.js | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/plugins/package/lib/__tests__/src/function/handler.js create mode 100644 lib/plugins/package/lib/__tests__/src/utils/utils.js diff --git a/lib/plugins/package/lib/__tests__/src/function/handler.js b/lib/plugins/package/lib/__tests__/src/function/handler.js new file mode 100644 index 000000000..e69de29bb diff --git a/lib/plugins/package/lib/__tests__/src/utils/utils.js b/lib/plugins/package/lib/__tests__/src/utils/utils.js new file mode 100644 index 000000000..e69de29bb From 3fcd05e4a78e258ceab9d75718e9099becb7f23e Mon Sep 17 00:00:00 2001 From: kdnakt Date: Sun, 13 Oct 2019 22:02:34 +0900 Subject: [PATCH 03/50] prettify --- .../package/lib/packageService.test.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/plugins/package/lib/packageService.test.js b/lib/plugins/package/lib/packageService.test.js index e632ac6a0..5c7fbdf0f 100644 --- a/lib/plugins/package/lib/packageService.test.js +++ b/lib/plugins/package/lib/packageService.test.js @@ -616,20 +616,22 @@ describe('#packageService()', () => { const expected = ['src/function/handler.js']; serverless.config.servicePath = servicePath; - return expect(packagePlugin.resolveFilePathsFromPatterns(params)) - .to.be.fulfilled.then(actual => expect(actual).to.deep.equal(expected)); + return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( + actual => expect(actual).to.deep.equal(expected) + ); }); it('should include file specified with `!` in exclude params', () => { const params = { - exclude: ['**','!src/utils/utils.js'], + exclude: ['**', '!src/utils/utils.js'], include: ['src/function/handler.js'], }; - const expected = ['src/function/handler.js','src/utils/utils.js']; + const expected = ['src/function/handler.js', 'src/utils/utils.js']; serverless.config.servicePath = servicePath; - return expect(packagePlugin.resolveFilePathsFromPatterns(params)) - .to.be.fulfilled.then(actual => expect(actual).to.deep.equal(expected)); + return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( + actual => expect(actual).to.deep.equal(expected) + ); }); it('should exclude file specified with `!` in include params', () => { @@ -640,8 +642,9 @@ describe('#packageService()', () => { const expected = ['src/function/handler.js']; serverless.config.servicePath = servicePath; - return expect(packagePlugin.resolveFilePathsFromPatterns(params)) - .to.be.fulfilled.then(actual => expect(actual).to.deep.equal(expected)); + return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( + actual => expect(actual).to.deep.equal(expected) + ); }); }); }); From 886057864b239e37f2eaa5b73bd0fd0168b14c59 Mon Sep 17 00:00:00 2001 From: Eetu Tuomala Date: Fri, 18 Oct 2019 09:29:42 +0300 Subject: [PATCH 04/50] allow ref in stream arn property --- docs/providers/aws/events/streams.md | 4 +++ .../package/compile/events/stream/index.js | 12 +++++--- .../compile/events/stream/index.test.js | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/docs/providers/aws/events/streams.md b/docs/providers/aws/events/streams.md index 1c06e3f48..a37c659b3 100644 --- a/docs/providers/aws/events/streams.md +++ b/docs/providers/aws/events/streams.md @@ -47,6 +47,10 @@ functions: type: kinesis arn: Fn::ImportValue: MyExportedKinesisStreamArnId + - stream: + type: dynamodb + arn: + Ref: MyDynamoDbTableStreamArn - stream: type: kinesis arn: diff --git a/lib/plugins/aws/package/compile/events/stream/index.js b/lib/plugins/aws/package/compile/events/stream/index.js index b977b1dda..8ef01ecf7 100644 --- a/lib/plugins/aws/package/compile/events/stream/index.js +++ b/lib/plugins/aws/package/compile/events/stream/index.js @@ -72,13 +72,15 @@ class AwsCompileStreamEvents { !( _.has(event.stream.arn, 'Fn::ImportValue') || _.has(event.stream.arn, 'Fn::GetAtt') || + (_.has(event.stream.arn, 'Ref') && + _.has(this.serverless.service.resources.Parameters, event.stream.arn.Ref)) || _.has(event.stream.arn, 'Fn::Join') ) ) { const errorMessage = [ `Bad dynamic ARN property on stream event in function "${functionName}"`, - ' If you use a dynamic "arn" (such as with Fn::GetAtt, Fn::Join', - ' or Fn::ImportValue) there must only be one key (either Fn::GetAtt, Fn::Join', + ' If you use a dynamic "arn" (such as with Fn::GetAtt, Fn::Join, Ref', + ' or Fn::ImportValue) there must only be one key (either Fn::GetAtt, Fn::Join, Ref', ' or Fn::ImportValue) in the arn object. Please check the docs for more info.', ].join(''); throw new this.serverless.classes.Error(errorMessage); @@ -108,6 +110,8 @@ class AwsCompileStreamEvents { return EventSourceArn['Fn::GetAtt'][0]; } else if (EventSourceArn['Fn::ImportValue']) { return EventSourceArn['Fn::ImportValue']; + } else if (EventSourceArn.Ref) { + return EventSourceArn.Ref; } else if (EventSourceArn['Fn::Join']) { // [0] is the used delimiter, [1] is the array with values const name = EventSourceArn['Fn::Join'][1].slice(-1).pop(); @@ -147,9 +151,9 @@ class AwsCompileStreamEvents { ) { dependsOn = `"${funcRole['Fn::GetAtt'][0]}"`; } else if ( - // otherwise, check if we have an import + // otherwise, check if we have an import or parameters ref typeof funcRole === 'object' && - 'Fn::ImportValue' in funcRole + ('Fn::ImportValue' in funcRole || 'Ref' in funcRole) ) { dependsOn = '[]'; } else if (typeof funcRole === 'string') { diff --git a/lib/plugins/aws/package/compile/events/stream/index.test.js b/lib/plugins/aws/package/compile/events/stream/index.test.js index f4feddb64..e34584c9b 100644 --- a/lib/plugins/aws/package/compile/events/stream/index.test.js +++ b/lib/plugins/aws/package/compile/events/stream/index.test.js @@ -234,6 +234,35 @@ describe('AwsCompileStreamEvents', () => { ).to.equal(null); }); + it('should not throw error if IAM role is referenced from cloudformation parameters', () => { + awsCompileStreamEvents.serverless.service.functions = { + first: { + role: { Ref: 'MyStreamArn' }, + events: [ + { + // doesn't matter if DynamoDB or Kinesis stream + stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1', + }, + ], + }, + }; + + // pretend that the default IamRoleLambdaExecution is not in place + awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution = null; + + expect(() => { + awsCompileStreamEvents.compileStreamEvents(); + }).to.not.throw(Error); + expect( + awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources + .FirstEventSourceMappingDynamodbFoo.DependsOn.length + ).to.equal(0); + expect( + awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources + .IamRoleLambdaExecution + ).to.equal(null); + }); + it('should not throw error if IAM role is imported', () => { awsCompileStreamEvents.serverless.service.functions = { first: { From 51aacdfd715279bfa6e3a227b56acd14264ab3ba Mon Sep 17 00:00:00 2001 From: Eetu Tuomala Date: Fri, 18 Oct 2019 12:05:29 +0300 Subject: [PATCH 05/50] add missing tests --- .../compile/events/stream/index.test.js | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/lib/plugins/aws/package/compile/events/stream/index.test.js b/lib/plugins/aws/package/compile/events/stream/index.test.js index e34584c9b..d33187eaa 100644 --- a/lib/plugins/aws/package/compile/events/stream/index.test.js +++ b/lib/plugins/aws/package/compile/events/stream/index.test.js @@ -473,6 +473,14 @@ describe('AwsCompileStreamEvents', () => { }); it('should allow specifying DynamoDB and Kinesis streams as CFN reference types', () => { + awsCompileStreamEvents.serverless.service.resources.Parameters = { + SomeDdbTableStreamArn: { + Type: 'String', + }, + ForeignKinesisStreamArn: { + Type: 'String', + }, + }; awsCompileStreamEvents.serverless.service.functions = { first: { events: [ @@ -510,6 +518,18 @@ describe('AwsCompileStreamEvents', () => { type: 'kinesis', }, }, + { + stream: { + arn: { Ref: 'SomeDdbTableStreamArn' }, + type: 'dynamodb', + }, + }, + { + stream: { + arn: { Ref: 'ForeignKinesisStreamArn' }, + type: 'kinesis', + }, + }, ], }, }; @@ -536,6 +556,9 @@ describe('AwsCompileStreamEvents', () => { { 'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'], }, + { + Ref: 'SomeDdbTableStreamArn', + }, ], }); @@ -566,6 +589,60 @@ describe('AwsCompileStreamEvents', () => { ], ], }); + + expect( + awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate + .Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[1] + ).to.deep.equal({ + Effect: 'Allow', + Action: [ + 'kinesis:GetRecords', + 'kinesis:GetShardIterator', + 'kinesis:DescribeStream', + 'kinesis:ListStreams', + ], + Resource: [ + { + 'Fn::ImportValue': 'ForeignKinesis', + }, + { + 'Fn::Join': [ + ':', + [ + 'arn', + 'aws', + 'kinesis', + { + Ref: 'AWS::Region', + }, + { + Ref: 'AWS::AccountId', + }, + 'stream/MyStream', + ], + ], + }, + { + Ref: 'ForeignKinesisStreamArn', + }, + ], + }); + }); + + it('fails if Ref/dynamic stream ARN is used without defining it to the CF parameters', () => { + awsCompileStreamEvents.serverless.service.functions = { + first: { + events: [ + { + stream: { + arn: { Ref: 'SomeDdbTableStreamArn' }, + }, + }, + ], + }, + }; + + expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error); }); it('fails if Fn::GetAtt/dynamic stream ARN is used without a type', () => { From 7927148f54247787709a1cea2df81a479731915d Mon Sep 17 00:00:00 2001 From: Eetu Tuomala Date: Fri, 18 Oct 2019 13:06:16 +0300 Subject: [PATCH 06/50] fix arn name --- lib/plugins/aws/package/compile/events/stream/index.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/package/compile/events/stream/index.test.js b/lib/plugins/aws/package/compile/events/stream/index.test.js index d33187eaa..7701870c1 100644 --- a/lib/plugins/aws/package/compile/events/stream/index.test.js +++ b/lib/plugins/aws/package/compile/events/stream/index.test.js @@ -237,7 +237,7 @@ describe('AwsCompileStreamEvents', () => { it('should not throw error if IAM role is referenced from cloudformation parameters', () => { awsCompileStreamEvents.serverless.service.functions = { first: { - role: { Ref: 'MyStreamArn' }, + role: { Ref: 'MyStreamRoleArn' }, events: [ { // doesn't matter if DynamoDB or Kinesis stream From 1e8c23a25897da73ef171c9b84a28e9c30bc745e Mon Sep 17 00:00:00 2001 From: kdnakt Date: Sat, 19 Oct 2019 18:07:48 +0900 Subject: [PATCH 07/50] use temporary dir for test --- .../lib/__tests__/src/function/handler.js | 0 .../package/lib/__tests__/src/utils/utils.js | 0 .../package/lib/packageService.test.js | 26 +++++++++++++------ 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 lib/plugins/package/lib/__tests__/src/function/handler.js delete mode 100644 lib/plugins/package/lib/__tests__/src/utils/utils.js diff --git a/lib/plugins/package/lib/__tests__/src/function/handler.js b/lib/plugins/package/lib/__tests__/src/function/handler.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/plugins/package/lib/__tests__/src/utils/utils.js b/lib/plugins/package/lib/__tests__/src/utils/utils.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/plugins/package/lib/packageService.test.js b/lib/plugins/package/lib/packageService.test.js index 5c7fbdf0f..e86d8139f 100644 --- a/lib/plugins/package/lib/packageService.test.js +++ b/lib/plugins/package/lib/packageService.test.js @@ -3,11 +3,13 @@ const _ = require('lodash'); const BbPromise = require('bluebird'); const path = require('path'); +const fse = require('fs-extra'); const chai = require('chai'); const sinon = require('sinon'); const Package = require('../package'); const Serverless = require('../../../Serverless'); const serverlessConfigFileUtils = require('../../../../lib/utils/getServerlessConfigFile'); +const { createTmpDir } = require('../../../../tests/utils/fs'); // Configure chai chai.use(require('chai-as-promised')); @@ -606,14 +608,22 @@ describe('#packageService()', () => { }); describe('#resolveFilePathsFromPatterns()', () => { - const servicePath = 'lib/plugins/package/lib/__tests__/'; + const handlerFile = 'src/function/handler.js'; + const utilsFile = 'src/utils/utils.js'; + let servicePath; + + beforeEach(() => { + servicePath = createTmpDir(); + fse.ensureFileSync(path.join(servicePath, handlerFile)); + fse.ensureFileSync(path.join(servicePath, utilsFile)); + }); it('should exclude all and include function/handler.js', () => { const params = { exclude: ['**'], - include: ['src/function/handler.js'], + include: [handlerFile], }; - const expected = ['src/function/handler.js']; + const expected = [handlerFile]; serverless.config.servicePath = servicePath; return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( @@ -623,10 +633,10 @@ describe('#packageService()', () => { it('should include file specified with `!` in exclude params', () => { const params = { - exclude: ['**', '!src/utils/utils.js'], - include: ['src/function/handler.js'], + exclude: ['**', '!' + utilsFile], + include: [handlerFile], }; - const expected = ['src/function/handler.js', 'src/utils/utils.js']; + const expected = [handlerFile, utilsFile]; serverless.config.servicePath = servicePath; return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( @@ -637,9 +647,9 @@ describe('#packageService()', () => { it('should exclude file specified with `!` in include params', () => { const params = { exclude: [], - include: ['!src/utils/utils.js'], + include: ['!' + utilsFile], }; - const expected = ['src/function/handler.js']; + const expected = [handlerFile]; serverless.config.servicePath = servicePath; return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( From 3e5c680290bcc1997bf6de5f976a028d42312b5b Mon Sep 17 00:00:00 2001 From: kdnakt Date: Sat, 19 Oct 2019 21:32:59 +0900 Subject: [PATCH 08/50] fix lint --- lib/plugins/package/lib/packageService.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins/package/lib/packageService.test.js b/lib/plugins/package/lib/packageService.test.js index e86d8139f..299c13f82 100644 --- a/lib/plugins/package/lib/packageService.test.js +++ b/lib/plugins/package/lib/packageService.test.js @@ -633,7 +633,7 @@ describe('#packageService()', () => { it('should include file specified with `!` in exclude params', () => { const params = { - exclude: ['**', '!' + utilsFile], + exclude: ['**', `!${utilsFile}`], include: [handlerFile], }; const expected = [handlerFile, utilsFile]; @@ -647,7 +647,7 @@ describe('#packageService()', () => { it('should exclude file specified with `!` in include params', () => { const params = { exclude: [], - include: ['!' + utilsFile], + include: [`!${utilsFile}`], }; const expected = [handlerFile]; serverless.config.servicePath = servicePath; From f76a2d83648473cc2c53a6ea36a3b01c681d7308 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Mon, 21 Oct 2019 08:48:55 +0200 Subject: [PATCH 09/50] Explicit file path creation --- lib/plugins/package/lib/packageService.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/plugins/package/lib/packageService.test.js b/lib/plugins/package/lib/packageService.test.js index 299c13f82..d6e4e5877 100644 --- a/lib/plugins/package/lib/packageService.test.js +++ b/lib/plugins/package/lib/packageService.test.js @@ -608,8 +608,8 @@ describe('#packageService()', () => { }); describe('#resolveFilePathsFromPatterns()', () => { - const handlerFile = 'src/function/handler.js'; - const utilsFile = 'src/utils/utils.js'; + const handlerFile = path.join('src', 'function', 'handler.js'); + const utilsFile = path.join('src', 'utils', 'utils.js'); let servicePath; beforeEach(() => { From 80f7e7c206516b5f5e45ba9aa9d0609ae4a748ca Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 3 Sep 2019 10:30:42 +0200 Subject: [PATCH 10/50] Refactor basic integration test to rely on async spawn --- tests/integration-basic/tests.js | 91 ++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/tests/integration-basic/tests.js b/tests/integration-basic/tests.js index 9ea327139..eeb101d5c 100644 --- a/tests/integration-basic/tests.js +++ b/tests/integration-basic/tests.js @@ -3,11 +3,10 @@ const path = require('path'); const fs = require('fs'); const fse = require('fs-extra'); -const BbPromise = require('bluebird'); const AWS = require('aws-sdk'); const stripAnsi = require('strip-ansi'); const { expect } = require('chai'); -const { execSync } = require('../utils/child-process'); +const spawn = require('child-process-ext/spawn'); const { getTmpDirPath } = require('../utils/fs'); const { region, getServiceName } = require('../utils/misc'); @@ -25,29 +24,36 @@ describe('Service Lifecyle Integration Test', function() { before(() => { serviceName = getServiceName(); StackName = `${serviceName}-dev`; + console.info(`Temporary path: ${tmpDir}`); fse.mkdirsSync(tmpDir); }); - it('should create service in tmp directory', () => { - execSync(`${serverlessExec} create --template ${templateName} --name ${serviceName}`, { + it('should create service in tmp directory', async () => { + await spawn(serverlessExec, ['create', '--template', templateName, '--name', serviceName], { cwd: tmpDir, }); expect(fs.existsSync(path.join(tmpDir, 'serverless.yml'))).to.be.equal(true); expect(fs.existsSync(path.join(tmpDir, 'handler.js'))).to.be.equal(true); }); - it('should deploy service to aws', () => { - execSync(`${serverlessExec} deploy`, { cwd: tmpDir }); + it('should deploy service to aws', async () => { + await spawn(serverlessExec, ['deploy'], { cwd: tmpDir }); - return CF.describeStacks({ StackName }) - .promise() - .then(d => expect(d.Stacks[0].StackStatus).to.be.equal('UPDATE_COMPLETE')); + const d = await CF.describeStacks({ StackName }).promise(); + expect(d.Stacks[0].StackStatus).to.be.equal('UPDATE_COMPLETE'); }); - it('should invoke function from aws', () => { - const invoked = execSync(`${serverlessExec} invoke --function hello --noGreeting true`, { - cwd: tmpDir, - }); + it('should invoke function from aws', async () => { + const { stdoutBuffer: invoked } = await spawn( + serverlessExec, + ['invoke', '--function', 'hello', '--noGreeting', 'true'], + { + cwd: tmpDir, + // As in invoke we optionally read stdin, we need to ensure it's closed + // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 + shouldCloseStdin: true, + } + ); const result = JSON.parse(Buffer.from(invoked, 'base64').toString()); // parse it once again because the body is stringified to be LAMBDA-PROXY ready const message = JSON.parse(result.body).message; @@ -64,20 +70,29 @@ describe('Service Lifecyle Integration Test', function() { `; fs.writeFileSync(path.join(tmpDir, 'handler.js'), newHandler); - execSync(`${serverlessExec} deploy`, { cwd: tmpDir }); + return spawn(serverlessExec, ['deploy'], { cwd: tmpDir }); }); - it('should invoke updated function from aws', () => { - const invoked = execSync(`${serverlessExec} invoke --function hello --noGreeting true`, { - cwd: tmpDir, - }); + it('should invoke updated function from aws', async () => { + const { stdoutBuffer: invoked } = await spawn( + serverlessExec, + ['invoke', '--function', 'hello', '--noGreeting', 'true'], + { + cwd: tmpDir, + // As in invoke we optionally read stdin, we need to ensure it's closed + // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 + shouldCloseStdin: true, + } + ); const result = JSON.parse(Buffer.from(invoked, 'base64').toString()); expect(result.message).to.be.equal('Service Update Succeeded'); }); - it('should list existing deployments and roll back to first deployment', () => { + it('should list existing deployments and roll back to first deployment', async () => { let timestamp; - const listDeploys = execSync(`${serverlessExec} deploy list`, { cwd: tmpDir }); + const { stdoutBuffer: listDeploys } = await spawn(serverlessExec, ['deploy', 'list'], { + cwd: tmpDir, + }); const output = stripAnsi(listDeploys.toString()); const match = output.match(new RegExp('Datetime: (.+)')); if (match) { @@ -86,26 +101,36 @@ describe('Service Lifecyle Integration Test', function() { // eslint-disable-next-line no-unused-expressions expect(timestamp).to.not.undefined; - execSync(`${serverlessExec} rollback -t ${timestamp}`, { cwd: tmpDir }); + await spawn(serverlessExec, ['rollback', '-t', timestamp], { cwd: tmpDir }); - const invoked = execSync(`${serverlessExec} invoke --function hello --noGreeting true`, { - cwd: tmpDir, - }); + const { stdoutBuffer: invoked } = await spawn( + serverlessExec, + ['invoke', '--function', 'hello', '--noGreeting', 'true'], + { + cwd: tmpDir, + // As in invoke we optionally read stdin, we need to ensure it's closed + // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 + shouldCloseStdin: true, + } + ); const result = JSON.parse(Buffer.from(invoked, 'base64').toString()); // parse it once again because the body is stringified to be LAMBDA-PROXY ready const message = JSON.parse(result.body).message; expect(message).to.be.equal('Go Serverless v1.0! Your function executed successfully!'); }); - it('should remove service from aws', () => { - execSync(`${serverlessExec} remove`, { cwd: tmpDir }); + it('should remove service from aws', async () => { + await spawn(serverlessExec, ['remove'], { cwd: tmpDir }); - return CF.describeStacks({ StackName }) - .promise() - .then(d => expect(d.Stacks[0].StackStatus).to.be.equal('DELETE_COMPLETE')) - .catch(error => { - if (error.message.indexOf('does not exist') > -1) return BbPromise.resolve(); - throw new Error(error); - }); + const d = await (async () => { + try { + return await CF.describeStacks({ StackName }).promise(); + } catch (error) { + if (error.message.indexOf('does not exist') > -1) return null; + throw error; + } + })(); + if (!d) return; + expect(d.Stacks[0].StackStatus).to.be.equal('DELETE_COMPLETE'); }); }); From 9633cf04ddcf9f826b51052ac0adeca71ae647a0 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 12:33:10 +0200 Subject: [PATCH 11/50] Ensure to remove the stack if some tests are aborted on the way --- tests/integration-basic/tests.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/integration-basic/tests.js b/tests/integration-basic/tests.js index eeb101d5c..ccf17b7f2 100644 --- a/tests/integration-basic/tests.js +++ b/tests/integration-basic/tests.js @@ -28,6 +28,16 @@ describe('Service Lifecyle Integration Test', function() { fse.mkdirsSync(tmpDir); }); + after(async () => { + try { + await CF.describeStacks({ StackName }).promise(); + } catch (error) { + if (error.message.indexOf('does not exist') > -1) return; + throw error; + } + await spawn(serverlessExec, ['remove'], { cwd: tmpDir }); + }); + it('should create service in tmp directory', async () => { await spawn(serverlessExec, ['create', '--template', templateName, '--name', serviceName], { cwd: tmpDir, From 92aaca0eee1e96e782906f37854b719bb95f6e9c Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 13:13:55 +0200 Subject: [PATCH 12/50] Fix output resolution --- tests/integration-basic/tests.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration-basic/tests.js b/tests/integration-basic/tests.js index ccf17b7f2..ea28bb576 100644 --- a/tests/integration-basic/tests.js +++ b/tests/integration-basic/tests.js @@ -64,7 +64,7 @@ describe('Service Lifecyle Integration Test', function() { shouldCloseStdin: true, } ); - const result = JSON.parse(Buffer.from(invoked, 'base64').toString()); + const result = JSON.parse(invoked); // parse it once again because the body is stringified to be LAMBDA-PROXY ready const message = JSON.parse(result.body).message; expect(message).to.be.equal('Go Serverless v1.0! Your function executed successfully!'); @@ -94,7 +94,7 @@ describe('Service Lifecyle Integration Test', function() { shouldCloseStdin: true, } ); - const result = JSON.parse(Buffer.from(invoked, 'base64').toString()); + const result = JSON.parse(invoked); expect(result.message).to.be.equal('Service Update Succeeded'); }); @@ -123,7 +123,7 @@ describe('Service Lifecyle Integration Test', function() { shouldCloseStdin: true, } ); - const result = JSON.parse(Buffer.from(invoked, 'base64').toString()); + const result = JSON.parse(invoked); // parse it once again because the body is stringified to be LAMBDA-PROXY ready const message = JSON.parse(result.body).message; expect(message).to.be.equal('Go Serverless v1.0! Your function executed successfully!'); From 9062f612e25d16117020f60a27cb670c7b179f6e Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 13:14:21 +0200 Subject: [PATCH 13/50] Fix used environment --- tests/integration-basic/tests.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/integration-basic/tests.js b/tests/integration-basic/tests.js index ea28bb576..daf3cf0ff 100644 --- a/tests/integration-basic/tests.js +++ b/tests/integration-basic/tests.js @@ -7,6 +7,7 @@ const AWS = require('aws-sdk'); const stripAnsi = require('strip-ansi'); const { expect } = require('chai'); const spawn = require('child-process-ext/spawn'); +const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); const { getTmpDirPath } = require('../utils/fs'); const { region, getServiceName } = require('../utils/misc'); @@ -18,6 +19,7 @@ describe('Service Lifecyle Integration Test', function() { this.timeout(1000 * 60 * 10); // Involves time-taking deploys const templateName = 'aws-nodejs'; const tmpDir = getTmpDirPath(); + const env = resolveAwsEnv(); let serviceName; let StackName; @@ -41,13 +43,14 @@ describe('Service Lifecyle Integration Test', function() { it('should create service in tmp directory', async () => { await spawn(serverlessExec, ['create', '--template', templateName, '--name', serviceName], { cwd: tmpDir, + env, }); expect(fs.existsSync(path.join(tmpDir, 'serverless.yml'))).to.be.equal(true); expect(fs.existsSync(path.join(tmpDir, 'handler.js'))).to.be.equal(true); }); it('should deploy service to aws', async () => { - await spawn(serverlessExec, ['deploy'], { cwd: tmpDir }); + await spawn(serverlessExec, ['deploy'], { cwd: tmpDir, env }); const d = await CF.describeStacks({ StackName }).promise(); expect(d.Stacks[0].StackStatus).to.be.equal('UPDATE_COMPLETE'); @@ -59,6 +62,7 @@ describe('Service Lifecyle Integration Test', function() { ['invoke', '--function', 'hello', '--noGreeting', 'true'], { cwd: tmpDir, + env, // As in invoke we optionally read stdin, we need to ensure it's closed // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 shouldCloseStdin: true, @@ -89,6 +93,7 @@ describe('Service Lifecyle Integration Test', function() { ['invoke', '--function', 'hello', '--noGreeting', 'true'], { cwd: tmpDir, + env, // As in invoke we optionally read stdin, we need to ensure it's closed // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 shouldCloseStdin: true, @@ -102,6 +107,7 @@ describe('Service Lifecyle Integration Test', function() { let timestamp; const { stdoutBuffer: listDeploys } = await spawn(serverlessExec, ['deploy', 'list'], { cwd: tmpDir, + env, }); const output = stripAnsi(listDeploys.toString()); const match = output.match(new RegExp('Datetime: (.+)')); @@ -111,13 +117,14 @@ describe('Service Lifecyle Integration Test', function() { // eslint-disable-next-line no-unused-expressions expect(timestamp).to.not.undefined; - await spawn(serverlessExec, ['rollback', '-t', timestamp], { cwd: tmpDir }); + await spawn(serverlessExec, ['rollback', '-t', timestamp], { cwd: tmpDir, env }); const { stdoutBuffer: invoked } = await spawn( serverlessExec, ['invoke', '--function', 'hello', '--noGreeting', 'true'], { cwd: tmpDir, + env, // As in invoke we optionally read stdin, we need to ensure it's closed // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 shouldCloseStdin: true, @@ -130,7 +137,7 @@ describe('Service Lifecyle Integration Test', function() { }); it('should remove service from aws', async () => { - await spawn(serverlessExec, ['remove'], { cwd: tmpDir }); + await spawn(serverlessExec, ['remove'], { cwd: tmpDir, env }); const d = await (async () => { try { From 1f080768da5a7e9c68ef0f66d6aae597bab9beb4 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 13:24:40 +0200 Subject: [PATCH 14/50] Ensure debug log output on test crash --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4bc126546..dac3f630c 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "mocha": { "reporter": "./tests/mocha-reporter", "require": [ + "@serverless/test/setup/log", "@serverless/test/setup/async-leaks-detector", "@serverless/test/setup/async-leaks-detector/bluebird-patch", "@serverless/test/setup/mock-homedir", From f9e29d9ed3ffcf749a96f3d10ed26137becaf246 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 13:25:00 +0200 Subject: [PATCH 15/50] Seclude createTestService to dedicated integration tests utils --- tests/integration-all/api-gateway/tests.js | 9 +-- .../cognito-user-pool/tests.js | 8 +-- tests/integration-all/event-bridge/tests.js | 8 +-- tests/integration-all/s3/tests.js | 8 +-- tests/integration-all/schedule/tests.js | 8 +-- tests/integration-all/sns/tests.js | 8 +-- tests/integration-all/sqs/tests.js | 8 +-- tests/integration-all/stream/tests.js | 8 +-- tests/integration-all/websocket/tests.js | 2 +- tests/utils/integration.js | 61 +++++++++++++++++++ tests/utils/misc/index.js | 49 --------------- 11 files changed, 78 insertions(+), 99 deletions(-) create mode 100644 tests/utils/integration.js diff --git a/tests/integration-all/api-gateway/tests.js b/tests/integration-all/api-gateway/tests.js index 79e16db46..7189170e6 100644 --- a/tests/integration-all/api-gateway/tests.js +++ b/tests/integration-all/api-gateway/tests.js @@ -7,13 +7,8 @@ const fetch = require('node-fetch'); const { expect } = require('chai'); const { getTmpDirPath, readYamlFile, writeYamlFile } = require('../../utils/fs'); -const { - region, - confirmCloudWatchLogs, - createTestService, - deployService, - removeService, -} = require('../../utils/misc'); +const { region, confirmCloudWatchLogs, deployService, removeService } = require('../../utils/misc'); +const { createTestService } = require('../../utils/integration'); const { createRestApi, deleteRestApi, getResources } = require('../../utils/api-gateway'); const CF = new AWS.CloudFormation({ region }); diff --git a/tests/integration-all/cognito-user-pool/tests.js b/tests/integration-all/cognito-user-pool/tests.js index 28b1c5215..2a1f9c9ce 100644 --- a/tests/integration-all/cognito-user-pool/tests.js +++ b/tests/integration-all/cognito-user-pool/tests.js @@ -15,12 +15,8 @@ const { setUserPassword, initiateAuth, } = require('../../utils/cognito'); -const { - createTestService, - deployService, - removeService, - waitForFunctionLogs, -} = require('../../utils/misc'); +const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Cognito User Pool Integration Test', function() { diff --git a/tests/integration-all/event-bridge/tests.js b/tests/integration-all/event-bridge/tests.js index f8060739e..62e591b14 100644 --- a/tests/integration-all/event-bridge/tests.js +++ b/tests/integration-all/event-bridge/tests.js @@ -5,12 +5,8 @@ const { expect } = require('chai'); const { getTmpDirPath, readYamlFile, writeYamlFile } = require('../../utils/fs'); const { createEventBus, putEvents, deleteEventBus } = require('../../utils/eventBridge'); -const { - createTestService, - deployService, - removeService, - waitForFunctionLogs, -} = require('../../utils/misc'); +const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Event Bridge Integration Test', function() { diff --git a/tests/integration-all/s3/tests.js b/tests/integration-all/s3/tests.js index d7a4f1df4..53b50a0d0 100644 --- a/tests/integration-all/s3/tests.js +++ b/tests/integration-all/s3/tests.js @@ -6,12 +6,8 @@ const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); const { createBucket, createAndRemoveInBucket, deleteBucket } = require('../../utils/s3'); -const { - createTestService, - deployService, - removeService, - waitForFunctionLogs, -} = require('../../utils/misc'); +const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - S3 Integration Test', function() { diff --git a/tests/integration-all/schedule/tests.js b/tests/integration-all/schedule/tests.js index 004e8f055..e3b15d00d 100644 --- a/tests/integration-all/schedule/tests.js +++ b/tests/integration-all/schedule/tests.js @@ -4,12 +4,8 @@ const path = require('path'); const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); -const { - createTestService, - deployService, - removeService, - waitForFunctionLogs, -} = require('../../utils/misc'); +const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Schedule Integration Test', function() { diff --git a/tests/integration-all/sns/tests.js b/tests/integration-all/sns/tests.js index e64b7bf81..23a14b29d 100644 --- a/tests/integration-all/sns/tests.js +++ b/tests/integration-all/sns/tests.js @@ -6,12 +6,8 @@ const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); const { createSnsTopic, removeSnsTopic, publishSnsMessage } = require('../../utils/sns'); -const { - createTestService, - deployService, - removeService, - waitForFunctionLogs, -} = require('../../utils/misc'); +const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - SNS Integration Test', function() { diff --git a/tests/integration-all/sqs/tests.js b/tests/integration-all/sqs/tests.js index 6939b90de..21305285c 100644 --- a/tests/integration-all/sqs/tests.js +++ b/tests/integration-all/sqs/tests.js @@ -5,12 +5,8 @@ const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); const { createSqsQueue, deleteSqsQueue, sendSqsMessage } = require('../../utils/sqs'); -const { - createTestService, - deployService, - removeService, - waitForFunctionLogs, -} = require('../../utils/misc'); +const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - SQS Integration Test', function() { diff --git a/tests/integration-all/stream/tests.js b/tests/integration-all/stream/tests.js index e28df2da5..f9eba6200 100644 --- a/tests/integration-all/stream/tests.js +++ b/tests/integration-all/stream/tests.js @@ -10,12 +10,8 @@ const { putKinesisRecord, } = require('../../utils/kinesis'); const { putDynamoDbItem } = require('../../utils/dynamodb'); -const { - createTestService, - deployService, - removeService, - waitForFunctionLogs, -} = require('../../utils/misc'); +const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Stream Integration Test', function() { diff --git a/tests/integration-all/websocket/tests.js b/tests/integration-all/websocket/tests.js index 5aceecc4e..0c941279c 100644 --- a/tests/integration-all/websocket/tests.js +++ b/tests/integration-all/websocket/tests.js @@ -10,11 +10,11 @@ const { getTmpDirPath, readYamlFile, writeYamlFile } = require('../../utils/fs') const { region, confirmCloudWatchLogs, - createTestService, deployService, removeService, wait, } = require('../../utils/misc'); +const { createTestService } = require('../../utils/integration'); const { createApi, deleteApi, diff --git a/tests/utils/integration.js b/tests/utils/integration.js new file mode 100644 index 000000000..a1a5d88d8 --- /dev/null +++ b/tests/utils/integration.js @@ -0,0 +1,61 @@ +// Integration tests related utils + +'use strict'; + +const path = require('path'); +const fse = require('fs-extra'); +const { getServiceName } = require('./misc'); +const { readYamlFile, writeYamlFile } = require('./fs'); +const { execSync } = require('./child-process'); + +const serverlessExec = path.resolve(__dirname, '..', '..', 'bin', 'serverless'); + +function createTestService( + tmpDir, + options = { + // Either templateName or templateDir have to be provided + templateName: null, // Generic template to use (e.g. 'aws-nodejs') + templateDir: null, // Path to custom pre-prepared service template + filesToAdd: [], // Array of additional files to add to the service directory + serverlessConfigHook: null, // Eventual hook that allows to customize serverless config + } +) { + const serviceName = getServiceName(); + + fse.mkdirsSync(tmpDir); + + if (options.templateName) { + // create a new Serverless service + execSync(`${serverlessExec} create --template ${options.templateName}`, { cwd: tmpDir }); + } else if (options.templateDir) { + fse.copySync(options.templateDir, tmpDir, { clobber: true, preserveTimestamps: true }); + } else { + throw new Error("Either 'templateName' or 'templateDir' options have to be provided"); + } + + if (options.filesToAdd && options.filesToAdd.length) { + options.filesToAdd.forEach(filePath => { + fse.copySync(filePath, tmpDir, { preserveTimestamps: true }); + }); + } + + const serverlessFilePath = path.join(tmpDir, 'serverless.yml'); + const serverlessConfig = readYamlFile(serverlessFilePath); + // Ensure unique service name + serverlessConfig.service = serviceName; + if (options.serverlessConfigHook) options.serverlessConfigHook(serverlessConfig); + writeYamlFile(serverlessFilePath, serverlessConfig); + + process.env.TOPIC_1 = `${serviceName}-1`; + process.env.TOPIC_2 = `${serviceName}-1`; + process.env.BUCKET_1 = `${serviceName}-1`; + process.env.BUCKET_2 = `${serviceName}-2`; + process.env.COGNITO_USER_POOL_1 = `${serviceName}-1`; + process.env.COGNITO_USER_POOL_2 = `${serviceName}-2`; + + return serverlessConfig; +} + +module.exports = { + createTestService, +}; diff --git a/tests/utils/misc/index.js b/tests/utils/misc/index.js index bd4fdfbac..1de3f88a4 100644 --- a/tests/utils/misc/index.js +++ b/tests/utils/misc/index.js @@ -1,11 +1,9 @@ 'use strict'; const path = require('path'); -const fse = require('fs-extra'); const BbPromise = require('bluebird'); const CloudWatchLogsSdk = require('aws-sdk/clients/cloudwatchlogs'); const { execSync } = require('../child-process'); -const { readYamlFile, writeYamlFile } = require('../fs'); const logger = console; @@ -48,52 +46,6 @@ function replaceEnv(values) { return originals; } -function createTestService( - tmpDir, - options = { - // Either templateName or templateDir have to be provided - templateName: null, // Generic template to use (e.g. 'aws-nodejs') - templateDir: null, // Path to custom pre-prepared service template - filesToAdd: [], // Array of additional files to add to the service directory - serverlessConfigHook: null, // Eventual hook that allows to customize serverless config - } -) { - const serviceName = getServiceName(); - - fse.mkdirsSync(tmpDir); - - if (options.templateName) { - // create a new Serverless service - execSync(`${serverlessExec} create --template ${options.templateName}`, { cwd: tmpDir }); - } else if (options.templateDir) { - fse.copySync(options.templateDir, tmpDir, { clobber: true, preserveTimestamps: true }); - } else { - throw new Error("Either 'templateName' or 'templateDir' options have to be provided"); - } - - if (options.filesToAdd && options.filesToAdd.length) { - options.filesToAdd.forEach(filePath => { - fse.copySync(filePath, tmpDir, { preserveTimestamps: true }); - }); - } - - const serverlessFilePath = path.join(tmpDir, 'serverless.yml'); - const serverlessConfig = readYamlFile(serverlessFilePath); - // Ensure unique service name - serverlessConfig.service = serviceName; - if (options.serverlessConfigHook) options.serverlessConfigHook(serverlessConfig); - writeYamlFile(serverlessFilePath, serverlessConfig); - - process.env.TOPIC_1 = `${serviceName}-1`; - process.env.TOPIC_2 = `${serviceName}-1`; - process.env.BUCKET_1 = `${serviceName}-1`; - process.env.BUCKET_2 = `${serviceName}-2`; - process.env.COGNITO_USER_POOL_1 = `${serviceName}-1`; - process.env.COGNITO_USER_POOL_2 = `${serviceName}-2`; - - return serverlessConfig; -} - function getFunctionLogs(cwd, functionName) { try { const logs = execSync(`${serverlessExec} logs --function ${functionName} --noGreeting true`, { @@ -181,7 +133,6 @@ module.exports = { deployService, removeService, replaceEnv, - createTestService, getFunctionLogs, waitForFunctionLogs, persistentRequest, From 26b98617e50c17b11245302141328ffbb432c66f Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 14:40:45 +0200 Subject: [PATCH 16/50] Ensure to not lint custom resource node_modules --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index dac3f630c..a07f6676e 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,8 @@ "root": true }, "eslintIgnore": [ - "lib/plugins/create/templates/**" + "lib/plugins/create/templates/**", + "lib/plugins/aws/customResources/node_modules/**" ], "mocha": { "reporter": "./tests/mocha-reporter", From c7599d889ae07a8853a9c19ce4427c8a7718e867 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 14:41:01 +0200 Subject: [PATCH 17/50] Fix eslint configuration for test utils --- tests/.eslintrc.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/.eslintrc.js b/tests/.eslintrc.js index 9938cda39..d26dc4ead 100644 --- a/tests/.eslintrc.js +++ b/tests/.eslintrc.js @@ -6,4 +6,18 @@ module.exports = { // console.info allowed to report on long going tasks or valuable debug information 'no-console': ['error', { allow: ['info'] }], }, + overrides: [ + { + files: ['utils/**.js'], + parserOptions: { + ecmaVersion: 2015, + }, + }, + { + files: ['utils/aws-cleanup.js', 'utils/integration.js'], + parserOptions: { + ecmaVersion: 2017, + }, + }, + ], }; From 6b76b983f962163b151d88b6eb07ca5068f4cad6 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 14:42:38 +0200 Subject: [PATCH 18/50] Ensure to rely on preprepared env --- tests/integration-basic/tests.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration-basic/tests.js b/tests/integration-basic/tests.js index daf3cf0ff..25adf5584 100644 --- a/tests/integration-basic/tests.js +++ b/tests/integration-basic/tests.js @@ -37,7 +37,7 @@ describe('Service Lifecyle Integration Test', function() { if (error.message.indexOf('does not exist') > -1) return; throw error; } - await spawn(serverlessExec, ['remove'], { cwd: tmpDir }); + await spawn(serverlessExec, ['remove'], { cwd: tmpDir, env }); }); it('should create service in tmp directory', async () => { @@ -84,7 +84,7 @@ describe('Service Lifecyle Integration Test', function() { `; fs.writeFileSync(path.join(tmpDir, 'handler.js'), newHandler); - return spawn(serverlessExec, ['deploy'], { cwd: tmpDir }); + return spawn(serverlessExec, ['deploy'], { cwd: tmpDir, env }); }); it('should invoke updated function from aws', async () => { From 8a0451e36d97661febf3c6a6df397ea02d2eb1bf Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 14:45:56 +0200 Subject: [PATCH 19/50] Rely on async spawn in createTestService --- tests/integration-all/api-gateway/tests.js | 4 ++-- tests/integration-all/cognito-user-pool/tests.js | 4 ++-- tests/integration-all/event-bridge/tests.js | 4 ++-- tests/integration-all/s3/tests.js | 4 ++-- tests/integration-all/schedule/tests.js | 4 ++-- tests/integration-all/sns/tests.js | 4 ++-- tests/integration-all/sqs/tests.js | 4 ++-- tests/integration-all/stream/tests.js | 4 ++-- tests/integration-all/websocket/tests.js | 4 ++-- tests/utils/integration.js | 6 +++--- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/integration-all/api-gateway/tests.js b/tests/integration-all/api-gateway/tests.js index 7189170e6..4c58ed1d4 100644 --- a/tests/integration-all/api-gateway/tests.js +++ b/tests/integration-all/api-gateway/tests.js @@ -25,11 +25,11 @@ describe('AWS - API Gateway Integration Test', function() { let apiKey; const stage = 'dev'; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); serverlessFilePath = path.join(tmpDirPath, 'serverless.yml'); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), serverlessConfigHook: // Ensure unique API key for each test (to avoid collision among concurrent CI runs) diff --git a/tests/integration-all/cognito-user-pool/tests.js b/tests/integration-all/cognito-user-pool/tests.js index 2a1f9c9ce..5fb249721 100644 --- a/tests/integration-all/cognito-user-pool/tests.js +++ b/tests/integration-all/cognito-user-pool/tests.js @@ -30,10 +30,10 @@ describe('AWS - Cognito User Pool Integration Test', function() { let poolExistingSimpleSetupConfig; const stage = 'dev'; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), filesToAdd: [path.join(__dirname, '..', 'shared')], serverlessConfigHook: diff --git a/tests/integration-all/event-bridge/tests.js b/tests/integration-all/event-bridge/tests.js index 62e591b14..51eac889f 100644 --- a/tests/integration-all/event-bridge/tests.js +++ b/tests/integration-all/event-bridge/tests.js @@ -27,10 +27,10 @@ describe('AWS - Event Bridge Integration Test', function() { }, ]; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), filesToAdd: [path.join(__dirname, '..', 'shared')], serverlessConfigHook: diff --git a/tests/integration-all/s3/tests.js b/tests/integration-all/s3/tests.js index 53b50a0d0..2189e4eeb 100644 --- a/tests/integration-all/s3/tests.js +++ b/tests/integration-all/s3/tests.js @@ -21,10 +21,10 @@ describe('AWS - S3 Integration Test', function() { let bucketExistingComplexSetup; const stage = 'dev'; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), filesToAdd: [path.join(__dirname, '..', 'shared')], serverlessConfigHook: diff --git a/tests/integration-all/schedule/tests.js b/tests/integration-all/schedule/tests.js index e3b15d00d..dacfa451f 100644 --- a/tests/integration-all/schedule/tests.js +++ b/tests/integration-all/schedule/tests.js @@ -15,10 +15,10 @@ describe('AWS - Schedule Integration Test', function() { let tmpDirPath; const stage = 'dev'; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), filesToAdd: [path.join(__dirname, '..', 'shared')], }); diff --git a/tests/integration-all/sns/tests.js b/tests/integration-all/sns/tests.js index 23a14b29d..d802aafc6 100644 --- a/tests/integration-all/sns/tests.js +++ b/tests/integration-all/sns/tests.js @@ -20,10 +20,10 @@ describe('AWS - SNS Integration Test', function() { let existingTopicName; const stage = 'dev'; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), filesToAdd: [path.join(__dirname, '..', 'shared')], serverlessConfigHook: diff --git a/tests/integration-all/sqs/tests.js b/tests/integration-all/sqs/tests.js index 21305285c..e1e9c8f38 100644 --- a/tests/integration-all/sqs/tests.js +++ b/tests/integration-all/sqs/tests.js @@ -17,10 +17,10 @@ describe('AWS - SQS Integration Test', function() { let queueName; const stage = 'dev'; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), filesToAdd: [path.join(__dirname, '..', 'shared')], serverlessConfigHook: diff --git a/tests/integration-all/stream/tests.js b/tests/integration-all/stream/tests.js index f9eba6200..0315b7521 100644 --- a/tests/integration-all/stream/tests.js +++ b/tests/integration-all/stream/tests.js @@ -24,10 +24,10 @@ describe('AWS - Stream Integration Test', function() { const historicStreamMessage = 'Hello from the Kinesis horizon!'; const stage = 'dev'; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), filesToAdd: [path.join(__dirname, '..', 'shared')], serverlessConfigHook: diff --git a/tests/integration-all/websocket/tests.js b/tests/integration-all/websocket/tests.js index 0c941279c..818b3ed15 100644 --- a/tests/integration-all/websocket/tests.js +++ b/tests/integration-all/websocket/tests.js @@ -33,11 +33,11 @@ describe('AWS - API Gateway Websocket Integration Test', function() { let serverlessFilePath; const stage = 'dev'; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); serverlessFilePath = path.join(tmpDirPath, 'serverless.yml'); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), }); serviceName = serverlessConfig.service; diff --git a/tests/utils/integration.js b/tests/utils/integration.js index a1a5d88d8..df2efc743 100644 --- a/tests/utils/integration.js +++ b/tests/utils/integration.js @@ -4,13 +4,13 @@ const path = require('path'); const fse = require('fs-extra'); +const spawn = require('child-process-ext/spawn'); const { getServiceName } = require('./misc'); const { readYamlFile, writeYamlFile } = require('./fs'); -const { execSync } = require('./child-process'); const serverlessExec = path.resolve(__dirname, '..', '..', 'bin', 'serverless'); -function createTestService( +async function createTestService( tmpDir, options = { // Either templateName or templateDir have to be provided @@ -26,7 +26,7 @@ function createTestService( if (options.templateName) { // create a new Serverless service - execSync(`${serverlessExec} create --template ${options.templateName}`, { cwd: tmpDir }); + await spawn(serverlessExec, ['create', '--template', options.templateName], { cwd: tmpDir }); } else if (options.templateDir) { fse.copySync(options.templateDir, tmpDir, { clobber: true, preserveTimestamps: true }); } else { From ec1026fa21e5b68c1072bb322b041f4257ded439 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 14:54:07 +0200 Subject: [PATCH 20/50] Convert deployService and removeService to rely on spawn --- tests/integration-all/api-gateway/tests.js | 20 +++++++++---------- .../cognito-user-pool/tests.js | 10 +++++----- tests/integration-all/event-bridge/tests.js | 10 +++++----- tests/integration-all/s3/tests.js | 10 +++++----- tests/integration-all/schedule/tests.js | 10 +++++----- tests/integration-all/sns/tests.js | 10 +++++----- tests/integration-all/sqs/tests.js | 10 +++++----- tests/integration-all/stream/tests.js | 10 +++++----- tests/integration-all/websocket/tests.js | 18 ++++++----------- tests/utils/integration.js | 10 ++++++++++ tests/utils/misc/index.js | 10 ---------- 11 files changed, 61 insertions(+), 67 deletions(-) diff --git a/tests/integration-all/api-gateway/tests.js b/tests/integration-all/api-gateway/tests.js index 4c58ed1d4..da53db591 100644 --- a/tests/integration-all/api-gateway/tests.js +++ b/tests/integration-all/api-gateway/tests.js @@ -7,8 +7,8 @@ const fetch = require('node-fetch'); const { expect } = require('chai'); const { getTmpDirPath, readYamlFile, writeYamlFile } = require('../../utils/fs'); -const { region, confirmCloudWatchLogs, deployService, removeService } = require('../../utils/misc'); -const { createTestService } = require('../../utils/integration'); +const { region, confirmCloudWatchLogs } = require('../../utils/misc'); +const { createTestService, deployService, removeService } = require('../../utils/integration'); const { createRestApi, deleteRestApi, getResources } = require('../../utils/api-gateway'); const CF = new AWS.CloudFormation({ region }); @@ -41,7 +41,7 @@ describe('AWS - API Gateway Integration Test', function() { serviceName = serverlessConfig.service; stackName = `${serviceName}-${stage}`; console.info(`Deploying "${stackName}" service...`); - deployService(tmpDirPath); + await deployService(tmpDirPath); // create an external REST API const externalRestApiName = `${stage}-${serviceName}-ext-api`; return createRestApi(externalRestApiName) @@ -58,7 +58,7 @@ describe('AWS - API Gateway Integration Test', function() { }); }); - after(() => { + after(async () => { // NOTE: deleting the references to the old, external REST API const serverless = readYamlFile(serverlessFilePath); delete serverless.provider.apiGateway.restApiId; @@ -66,9 +66,9 @@ describe('AWS - API Gateway Integration Test', function() { writeYamlFile(serverlessFilePath, serverless); // NOTE: deploying once again to get the stack into the original state console.info('Redeploying service...'); - deployService(tmpDirPath); + await deployService(tmpDirPath); console.info('Removing service...'); - removeService(tmpDirPath); + await removeService(tmpDirPath); console.info('Deleting external rest API...'); return deleteRestApi(restApiId); }); @@ -219,7 +219,7 @@ describe('AWS - API Gateway Integration Test', function() { }); describe('Using stage specific configuration', () => { - before(() => { + before(async () => { const serverless = readYamlFile(serverlessFilePath); // enable Logs, Tags and Tracing _.merge(serverless.provider, { @@ -235,7 +235,7 @@ describe('AWS - API Gateway Integration Test', function() { }, }); writeYamlFile(serverlessFilePath, serverless); - deployService(tmpDirPath); + await deployService(tmpDirPath); }); it('should update the stage without service interruptions', () => { @@ -256,7 +256,7 @@ describe('AWS - API Gateway Integration Test', function() { // NOTE: this test should be at the very end because we're using an external REST API here describe('when using an existing REST API with stage specific configuration', () => { - before(() => { + before(async () => { const serverless = readYamlFile(serverlessFilePath); // enable Logs, Tags and Tracing _.merge(serverless.provider, { @@ -276,7 +276,7 @@ describe('AWS - API Gateway Integration Test', function() { }, }); writeYamlFile(serverlessFilePath, serverless); - deployService(tmpDirPath); + await deployService(tmpDirPath); }); it('should update the stage without service interruptions', () => { diff --git a/tests/integration-all/cognito-user-pool/tests.js b/tests/integration-all/cognito-user-pool/tests.js index 5fb249721..a25ea8666 100644 --- a/tests/integration-all/cognito-user-pool/tests.js +++ b/tests/integration-all/cognito-user-pool/tests.js @@ -15,8 +15,8 @@ const { setUserPassword, initiateAuth, } = require('../../utils/cognito'); -const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService } = require('../../utils/integration'); +const { waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService, deployService, removeService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Cognito User Pool Integration Test', function() { @@ -63,13 +63,13 @@ describe('AWS - Cognito User Pool Integration Test', function() { createUserPool(poolExistingMultiSetup), ]).then(() => { console.info(`Deploying "${stackName}" service...`); - deployService(tmpDirPath); + return deployService(tmpDirPath); }); }); - after(() => { + after(async () => { console.info('Removing service...'); - removeService(tmpDirPath); + await removeService(tmpDirPath); console.info('Deleting Cognito User Pools'); return BbPromise.all([ deleteUserPool(poolExistingSimpleSetup), diff --git a/tests/integration-all/event-bridge/tests.js b/tests/integration-all/event-bridge/tests.js index 51eac889f..de7db55cf 100644 --- a/tests/integration-all/event-bridge/tests.js +++ b/tests/integration-all/event-bridge/tests.js @@ -5,8 +5,8 @@ const { expect } = require('chai'); const { getTmpDirPath, readYamlFile, writeYamlFile } = require('../../utils/fs'); const { createEventBus, putEvents, deleteEventBus } = require('../../utils/eventBridge'); -const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService } = require('../../utils/integration'); +const { waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService, deployService, removeService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Event Bridge Integration Test', function() { @@ -57,13 +57,13 @@ describe('AWS - Event Bridge Integration Test', function() { writeYamlFile(serverlessFilePath, config); // deploy the service console.info(`Deploying "${stackName}" service...`); - deployService(tmpDirPath); + return deployService(tmpDirPath); }); }); - after(() => { + after(async () => { console.info('Removing service...'); - removeService(tmpDirPath); + await removeService(tmpDirPath); console.info(`Deleting Event Bus "${arnEventBusName}"...`); return deleteEventBus(arnEventBusName); }); diff --git a/tests/integration-all/s3/tests.js b/tests/integration-all/s3/tests.js index 2189e4eeb..745b6e630 100644 --- a/tests/integration-all/s3/tests.js +++ b/tests/integration-all/s3/tests.js @@ -6,8 +6,8 @@ const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); const { createBucket, createAndRemoveInBucket, deleteBucket } = require('../../utils/s3'); -const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService } = require('../../utils/integration'); +const { waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService, deployService, removeService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - S3 Integration Test', function() { @@ -53,13 +53,13 @@ describe('AWS - S3 Integration Test', function() { createBucket(bucketExistingComplexSetup), ]).then(() => { console.info(`Deploying "${stackName}" service...`); - deployService(tmpDirPath); + return deployService(tmpDirPath); }); }); - after(() => { + after(async () => { console.info('Removing service...'); - removeService(tmpDirPath); + await removeService(tmpDirPath); console.info('Deleting S3 buckets'); return BbPromise.all([ deleteBucket(bucketExistingSimpleSetup), diff --git a/tests/integration-all/schedule/tests.js b/tests/integration-all/schedule/tests.js index dacfa451f..6379d5fb0 100644 --- a/tests/integration-all/schedule/tests.js +++ b/tests/integration-all/schedule/tests.js @@ -4,8 +4,8 @@ const path = require('path'); const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); -const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService } = require('../../utils/integration'); +const { waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService, deployService, removeService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Schedule Integration Test', function() { @@ -25,12 +25,12 @@ describe('AWS - Schedule Integration Test', function() { serviceName = serverlessConfig.service; stackName = `${serviceName}-${stage}`; console.info(`Deploying "${stackName}" service...`); - deployService(tmpDirPath); + return deployService(tmpDirPath); }); - after(() => { + after(async () => { console.info('Removing service...'); - removeService(tmpDirPath); + return removeService(tmpDirPath); }); describe('Minimal Setup', () => { diff --git a/tests/integration-all/sns/tests.js b/tests/integration-all/sns/tests.js index d802aafc6..02cdf6956 100644 --- a/tests/integration-all/sns/tests.js +++ b/tests/integration-all/sns/tests.js @@ -6,8 +6,8 @@ const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); const { createSnsTopic, removeSnsTopic, publishSnsMessage } = require('../../utils/sns'); -const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService } = require('../../utils/integration'); +const { waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService, deployService, removeService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - SNS Integration Test', function() { @@ -49,13 +49,13 @@ describe('AWS - SNS Integration Test', function() { console.info(`Creating SNS topic "${existingTopicName}"...`); return createSnsTopic(existingTopicName).then(() => { console.info(`Deploying "${stackName}" service...`); - deployService(tmpDirPath); + return deployService(tmpDirPath); }); }); - after(() => { + after(async () => { console.info('Removing service...'); - removeService(tmpDirPath); + await removeService(tmpDirPath); console.info('Deleting SNS topics'); return removeSnsTopic(existingTopicName); }); diff --git a/tests/integration-all/sqs/tests.js b/tests/integration-all/sqs/tests.js index e1e9c8f38..c0cfabcf1 100644 --- a/tests/integration-all/sqs/tests.js +++ b/tests/integration-all/sqs/tests.js @@ -5,8 +5,8 @@ const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); const { createSqsQueue, deleteSqsQueue, sendSqsMessage } = require('../../utils/sqs'); -const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService } = require('../../utils/integration'); +const { waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService, deployService, removeService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - SQS Integration Test', function() { @@ -37,13 +37,13 @@ describe('AWS - SQS Integration Test', function() { console.info(`Creating SQS queue "${queueName}"...`); return createSqsQueue(queueName).then(() => { console.info(`Deploying "${stackName}" service...`); - deployService(tmpDirPath); + return deployService(tmpDirPath); }); }); - after(() => { + after(async () => { console.info('Removing service...'); - removeService(tmpDirPath); + await removeService(tmpDirPath); console.info('Deleting SQS queue'); return deleteSqsQueue(queueName); }); diff --git a/tests/integration-all/stream/tests.js b/tests/integration-all/stream/tests.js index 0315b7521..5c928e7d4 100644 --- a/tests/integration-all/stream/tests.js +++ b/tests/integration-all/stream/tests.js @@ -10,8 +10,8 @@ const { putKinesisRecord, } = require('../../utils/kinesis'); const { putDynamoDbItem } = require('../../utils/dynamodb'); -const { deployService, removeService, waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService } = require('../../utils/integration'); +const { waitForFunctionLogs } = require('../../utils/misc'); +const { createTestService, deployService, removeService } = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Stream Integration Test', function() { @@ -52,13 +52,13 @@ describe('AWS - Stream Integration Test', function() { console.info( `Deploying "${stackName}" service with DynamoDB table resource "${tableName}"...` ); - deployService(tmpDirPath); + return deployService(tmpDirPath); }); }); - after(() => { + after(async () => { console.info(`Removing service (and DynamoDB table resource "${tableName}")...`); - removeService(tmpDirPath); + await removeService(tmpDirPath); console.info('Deleting Kinesis stream'); return deleteKinesisStream(streamName); }); diff --git a/tests/integration-all/websocket/tests.js b/tests/integration-all/websocket/tests.js index 818b3ed15..efc3faa13 100644 --- a/tests/integration-all/websocket/tests.js +++ b/tests/integration-all/websocket/tests.js @@ -7,14 +7,8 @@ const _ = require('lodash'); const { expect } = require('chai'); const { getTmpDirPath, readYamlFile, writeYamlFile } = require('../../utils/fs'); -const { - region, - confirmCloudWatchLogs, - deployService, - removeService, - wait, -} = require('../../utils/misc'); -const { createTestService } = require('../../utils/integration'); +const { region, confirmCloudWatchLogs, wait } = require('../../utils/misc'); +const { createTestService, deployService, removeService } = require('../../utils/integration'); const { createApi, deleteApi, @@ -43,12 +37,12 @@ describe('AWS - API Gateway Websocket Integration Test', function() { serviceName = serverlessConfig.service; stackName = `${serviceName}-${stage}`; console.info(`Deploying "${stackName}" service...`); - deployService(tmpDirPath); + return deployService(tmpDirPath); }); after(() => { console.info('Removing service...'); - removeService(tmpDirPath); + return removeService(tmpDirPath); }); describe('Minimal Setup', () => { @@ -108,7 +102,7 @@ describe('AWS - API Gateway Websocket Integration Test', function() { }, }); writeYamlFile(serverlessFilePath, serverless); - deployService(tmpDirPath); + return deployService(tmpDirPath); }); after(async () => { @@ -121,7 +115,7 @@ describe('AWS - API Gateway Websocket Integration Test', function() { await deleteStage(websocketApiId, 'dev'); // NOTE: deploying once again to get the stack into the original state console.info('Redeploying service...'); - deployService(tmpDirPath); + await deployService(tmpDirPath); console.info('Deleting external websocket API...'); await deleteApi(websocketApiId); }); diff --git a/tests/utils/integration.js b/tests/utils/integration.js index df2efc743..acdf18e61 100644 --- a/tests/utils/integration.js +++ b/tests/utils/integration.js @@ -56,6 +56,16 @@ async function createTestService( return serverlessConfig; } +async function deployService(cwd) { + return spawn(serverlessExec, ['deploy'], { cwd }); +} + +async function removeService(cwd) { + return spawn(serverlessExec, ['remove'], { cwd }); +} + module.exports = { createTestService, + deployService, + removeService, }; diff --git a/tests/utils/misc/index.js b/tests/utils/misc/index.js index 1de3f88a4..204c557aa 100644 --- a/tests/utils/misc/index.js +++ b/tests/utils/misc/index.js @@ -21,14 +21,6 @@ function getServiceName() { return `${testServiceIdentifier}-${hrtime[1]}`; } -function deployService(cwd) { - execSync(`${serverlessExec} deploy`, { cwd }); -} - -function removeService(cwd) { - execSync(`${serverlessExec} remove`, { cwd }); -} - function replaceEnv(values) { const originals = {}; for (const key of Object.keys(values)) { @@ -130,8 +122,6 @@ module.exports = { serverlessExec, serviceNameRegex, getServiceName, - deployService, - removeService, replaceEnv, getFunctionLogs, waitForFunctionLogs, From ca0b9dfc81524e71123cb5d6ae47b9c847fe42e8 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 14:56:32 +0200 Subject: [PATCH 21/50] Isolate env in which serverless commands are run --- tests/utils/integration.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/utils/integration.js b/tests/utils/integration.js index acdf18e61..0e03fe2bb 100644 --- a/tests/utils/integration.js +++ b/tests/utils/integration.js @@ -5,10 +5,12 @@ const path = require('path'); const fse = require('fs-extra'); const spawn = require('child-process-ext/spawn'); +const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); const { getServiceName } = require('./misc'); const { readYamlFile, writeYamlFile } = require('./fs'); const serverlessExec = path.resolve(__dirname, '..', '..', 'bin', 'serverless'); +const env = resolveAwsEnv(); async function createTestService( tmpDir, @@ -26,7 +28,10 @@ async function createTestService( if (options.templateName) { // create a new Serverless service - await spawn(serverlessExec, ['create', '--template', options.templateName], { cwd: tmpDir }); + await spawn(serverlessExec, ['create', '--template', options.templateName], { + cwd: tmpDir, + env, + }); } else if (options.templateDir) { fse.copySync(options.templateDir, tmpDir, { clobber: true, preserveTimestamps: true }); } else { @@ -57,15 +62,16 @@ async function createTestService( } async function deployService(cwd) { - return spawn(serverlessExec, ['deploy'], { cwd }); + return spawn(serverlessExec, ['deploy'], { cwd, env }); } async function removeService(cwd) { - return spawn(serverlessExec, ['remove'], { cwd }); + return spawn(serverlessExec, ['remove'], { cwd, env }); } module.exports = { createTestService, deployService, removeService, + env, }; From 3520c23ab51eb6391f3919be08563b4b70118d7f Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 15:13:35 +0200 Subject: [PATCH 22/50] Improve scope of external Rest API id test --- tests/integration-all/api-gateway/tests.js | 53 ++++++++++++---------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/tests/integration-all/api-gateway/tests.js b/tests/integration-all/api-gateway/tests.js index da53db591..5cbbdfd08 100644 --- a/tests/integration-all/api-gateway/tests.js +++ b/tests/integration-all/api-gateway/tests.js @@ -42,35 +42,11 @@ describe('AWS - API Gateway Integration Test', function() { stackName = `${serviceName}-${stage}`; console.info(`Deploying "${stackName}" service...`); await deployService(tmpDirPath); - // create an external REST API - const externalRestApiName = `${stage}-${serviceName}-ext-api`; - return createRestApi(externalRestApiName) - .then(restApiMeta => { - restApiId = restApiMeta.id; - return getResources(restApiId); - }) - .then(resources => { - restApiRootResourceId = resources[0].id; - console.info( - 'Created external rest API ' + - `(id: ${restApiId}, root resource id: ${restApiRootResourceId})` - ); - }); }); after(async () => { - // NOTE: deleting the references to the old, external REST API - const serverless = readYamlFile(serverlessFilePath); - delete serverless.provider.apiGateway.restApiId; - delete serverless.provider.apiGateway.restApiRootResourceId; - writeYamlFile(serverlessFilePath, serverless); - // NOTE: deploying once again to get the stack into the original state - console.info('Redeploying service...'); - await deployService(tmpDirPath); console.info('Removing service...'); await removeService(tmpDirPath); - console.info('Deleting external rest API...'); - return deleteRestApi(restApiId); }); beforeEach(() => { @@ -257,6 +233,21 @@ describe('AWS - API Gateway Integration Test', function() { // NOTE: this test should be at the very end because we're using an external REST API here describe('when using an existing REST API with stage specific configuration', () => { before(async () => { + // create an external REST API + const externalRestApiName = `${stage}-${serviceName}-ext-api`; + await createRestApi(externalRestApiName) + .then(restApiMeta => { + restApiId = restApiMeta.id; + return getResources(restApiId); + }) + .then(resources => { + restApiRootResourceId = resources[0].id; + console.info( + 'Created external rest API ' + + `(id: ${restApiId}, root resource id: ${restApiRootResourceId})` + ); + }); + const serverless = readYamlFile(serverlessFilePath); // enable Logs, Tags and Tracing _.merge(serverless.provider, { @@ -276,9 +267,23 @@ describe('AWS - API Gateway Integration Test', function() { }, }); writeYamlFile(serverlessFilePath, serverless); + console.info('Redeploying service (with external Rest API ID)...'); await deployService(tmpDirPath); }); + after(async () => { + // NOTE: deleting the references to the old, external REST API + const serverless = readYamlFile(serverlessFilePath); + delete serverless.provider.apiGateway.restApiId; + delete serverless.provider.apiGateway.restApiRootResourceId; + writeYamlFile(serverlessFilePath, serverless); + // NOTE: deploying once again to get the stack into the original state + console.info('Redeploying service (without external Rest API ID)...'); + await deployService(tmpDirPath); + console.info('Deleting external rest API...'); + return deleteRestApi(restApiId); + }); + it('should update the stage without service interruptions', () => { // re-using the endpoint from the "minimal" test case const testEndpoint = `${endpoint}/minimal-1`; From c171670b6dd723dd3d4901fb97071f812e075013 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 15:33:18 +0200 Subject: [PATCH 23/50] Disable tracking on SLS_TRACKING_DISABLED env var --- lib/utils/isTrackingDisabled.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/isTrackingDisabled.js b/lib/utils/isTrackingDisabled.js index 8095d3011..95f3e659d 100644 --- a/lib/utils/isTrackingDisabled.js +++ b/lib/utils/isTrackingDisabled.js @@ -2,4 +2,4 @@ const configUtils = require('./config'); -module.exports = configUtils.get('trackingDisabled'); +module.exports = process.env.SLS_TRACKING_DISABLED || configUtils.get('trackingDisabled'); From 50189a55ed3993a434fe473f7c0dd2a47e6f985d Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 16:31:37 +0200 Subject: [PATCH 24/50] Move waitForFunction logs to integration utils --- .../cognito-user-pool/tests.js | 8 +++-- tests/integration-all/event-bridge/tests.js | 9 ++++-- tests/integration-all/s3/tests.js | 8 +++-- tests/integration-all/schedule/tests.js | 8 +++-- tests/integration-all/sns/tests.js | 8 +++-- tests/integration-all/sqs/tests.js | 8 +++-- tests/integration-all/stream/tests.js | 8 +++-- tests/utils/integration.js | 30 +++++++++++++++++- tests/utils/misc/index.js | 31 ------------------- 9 files changed, 72 insertions(+), 46 deletions(-) diff --git a/tests/integration-all/cognito-user-pool/tests.js b/tests/integration-all/cognito-user-pool/tests.js index a25ea8666..e431fd98b 100644 --- a/tests/integration-all/cognito-user-pool/tests.js +++ b/tests/integration-all/cognito-user-pool/tests.js @@ -15,8 +15,12 @@ const { setUserPassword, initiateAuth, } = require('../../utils/cognito'); -const { waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService, deployService, removeService } = require('../../utils/integration'); +const { + createTestService, + deployService, + removeService, + waitForFunctionLogs, +} = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Cognito User Pool Integration Test', function() { diff --git a/tests/integration-all/event-bridge/tests.js b/tests/integration-all/event-bridge/tests.js index de7db55cf..a1e43fcd4 100644 --- a/tests/integration-all/event-bridge/tests.js +++ b/tests/integration-all/event-bridge/tests.js @@ -5,8 +5,13 @@ const { expect } = require('chai'); const { getTmpDirPath, readYamlFile, writeYamlFile } = require('../../utils/fs'); const { createEventBus, putEvents, deleteEventBus } = require('../../utils/eventBridge'); -const { waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService, deployService, removeService } = require('../../utils/integration'); + +const { + createTestService, + deployService, + removeService, + waitForFunctionLogs, +} = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Event Bridge Integration Test', function() { diff --git a/tests/integration-all/s3/tests.js b/tests/integration-all/s3/tests.js index 745b6e630..ae4e3331f 100644 --- a/tests/integration-all/s3/tests.js +++ b/tests/integration-all/s3/tests.js @@ -6,8 +6,12 @@ const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); const { createBucket, createAndRemoveInBucket, deleteBucket } = require('../../utils/s3'); -const { waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService, deployService, removeService } = require('../../utils/integration'); +const { + createTestService, + deployService, + removeService, + waitForFunctionLogs, +} = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - S3 Integration Test', function() { diff --git a/tests/integration-all/schedule/tests.js b/tests/integration-all/schedule/tests.js index 6379d5fb0..0dda32f71 100644 --- a/tests/integration-all/schedule/tests.js +++ b/tests/integration-all/schedule/tests.js @@ -4,8 +4,12 @@ const path = require('path'); const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); -const { waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService, deployService, removeService } = require('../../utils/integration'); +const { + createTestService, + deployService, + removeService, + waitForFunctionLogs, +} = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Schedule Integration Test', function() { diff --git a/tests/integration-all/sns/tests.js b/tests/integration-all/sns/tests.js index 02cdf6956..41ab45e5d 100644 --- a/tests/integration-all/sns/tests.js +++ b/tests/integration-all/sns/tests.js @@ -6,8 +6,12 @@ const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); const { createSnsTopic, removeSnsTopic, publishSnsMessage } = require('../../utils/sns'); -const { waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService, deployService, removeService } = require('../../utils/integration'); +const { + createTestService, + deployService, + removeService, + waitForFunctionLogs, +} = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - SNS Integration Test', function() { diff --git a/tests/integration-all/sqs/tests.js b/tests/integration-all/sqs/tests.js index c0cfabcf1..e1d6f5f2b 100644 --- a/tests/integration-all/sqs/tests.js +++ b/tests/integration-all/sqs/tests.js @@ -5,8 +5,12 @@ const { expect } = require('chai'); const { getTmpDirPath } = require('../../utils/fs'); const { createSqsQueue, deleteSqsQueue, sendSqsMessage } = require('../../utils/sqs'); -const { waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService, deployService, removeService } = require('../../utils/integration'); +const { + createTestService, + deployService, + removeService, + waitForFunctionLogs, +} = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - SQS Integration Test', function() { diff --git a/tests/integration-all/stream/tests.js b/tests/integration-all/stream/tests.js index 5c928e7d4..c03fd7f8b 100644 --- a/tests/integration-all/stream/tests.js +++ b/tests/integration-all/stream/tests.js @@ -10,8 +10,12 @@ const { putKinesisRecord, } = require('../../utils/kinesis'); const { putDynamoDbItem } = require('../../utils/dynamodb'); -const { waitForFunctionLogs } = require('../../utils/misc'); -const { createTestService, deployService, removeService } = require('../../utils/integration'); +const { + createTestService, + deployService, + removeService, + waitForFunctionLogs, +} = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - Stream Integration Test', function() { diff --git a/tests/utils/integration.js b/tests/utils/integration.js index 0e03fe2bb..5f7b3e6db 100644 --- a/tests/utils/integration.js +++ b/tests/utils/integration.js @@ -6,7 +6,7 @@ const path = require('path'); const fse = require('fs-extra'); const spawn = require('child-process-ext/spawn'); const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); -const { getServiceName } = require('./misc'); +const { getServiceName, wait } = require('./misc'); const { readYamlFile, writeYamlFile } = require('./fs'); const serverlessExec = path.resolve(__dirname, '..', '..', 'bin', 'serverless'); @@ -69,9 +69,37 @@ async function removeService(cwd) { return spawn(serverlessExec, ['remove'], { cwd, env }); } +async function getFunctionLogs(cwd, functionName) { + let logs; + try { + ({ stdoutBuffer: logs } = await spawn( + serverlessExec, + ['logs', '--function', functionName, '--noGreeting', 'true'], + { + cwd, + env, + } + )); + } catch (_) { + // Attempting to read logs before first invocation will will result in a "No existing streams for the function" error + return null; + } + const logsString = String(logs); + process.stdout.write(logsString); + return logsString; +} + +async function waitForFunctionLogs(cwd, functionName, startMarker, endMarker) { + await wait(2000); + const logs = await getFunctionLogs(cwd, functionName); + if (logs && logs.includes(startMarker) && logs.includes(endMarker)) return logs; + return waitForFunctionLogs(cwd, functionName, startMarker, endMarker); +} + module.exports = { createTestService, deployService, removeService, + waitForFunctionLogs, env, }; diff --git a/tests/utils/misc/index.js b/tests/utils/misc/index.js index 204c557aa..d737e97cc 100644 --- a/tests/utils/misc/index.js +++ b/tests/utils/misc/index.js @@ -3,7 +3,6 @@ const path = require('path'); const BbPromise = require('bluebird'); const CloudWatchLogsSdk = require('aws-sdk/clients/cloudwatchlogs'); -const { execSync } = require('../child-process'); const logger = console; @@ -38,34 +37,6 @@ function replaceEnv(values) { return originals; } -function getFunctionLogs(cwd, functionName) { - try { - const logs = execSync(`${serverlessExec} logs --function ${functionName} --noGreeting true`, { - cwd, - }); - const logsString = Buffer.from(logs, 'base64').toString(); - process.stdout.write(logsString); - return logsString; - } catch (_) { - // Attempting to read logs before first invocation will will result in a "No existing streams for the function" error - return null; - } -} - -function waitForFunctionLogs(cwd, functionName, startMarker, endMarker) { - let logs; - return new BbPromise(resolve => { - const interval = setInterval(() => { - logs = getFunctionLogs(cwd, functionName); - if (logs && logs.includes(startMarker) && logs.includes(endMarker)) { - clearInterval(interval); - return resolve(logs); - } - return null; - }, 2000); - }); -} - /** * Cloudwatch logs when turned on, are usually take some time for being effective * This function allows to confirm that new setting (turned on cloudwatch logs) @@ -123,8 +94,6 @@ module.exports = { serviceNameRegex, getServiceName, replaceEnv, - getFunctionLogs, - waitForFunctionLogs, persistentRequest, wait, }; From a2089c0abc146feaf4264553ad0290bf0483c976 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 18 Oct 2019 16:35:45 +0200 Subject: [PATCH 25/50] Prevent noisy logs output --- tests/utils/integration.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/utils/integration.js b/tests/utils/integration.js index 5f7b3e6db..2a6340ed9 100644 --- a/tests/utils/integration.js +++ b/tests/utils/integration.js @@ -84,9 +84,7 @@ async function getFunctionLogs(cwd, functionName) { // Attempting to read logs before first invocation will will result in a "No existing streams for the function" error return null; } - const logsString = String(logs); - process.stdout.write(logsString); - return logsString; + return String(logs); } async function waitForFunctionLogs(cwd, functionName, startMarker, endMarker) { From e640d613af6120b730b392979e025576cbbf3ea8 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 11:39:48 +0200 Subject: [PATCH 26/50] Reuse options setup --- tests/integration-basic/tests.js | 51 +++++++++++++------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/tests/integration-basic/tests.js b/tests/integration-basic/tests.js index 25adf5584..8c06234b8 100644 --- a/tests/integration-basic/tests.js +++ b/tests/integration-basic/tests.js @@ -20,6 +20,13 @@ describe('Service Lifecyle Integration Test', function() { const templateName = 'aws-nodejs'; const tmpDir = getTmpDirPath(); const env = resolveAwsEnv(); + const spawnOptions = { + cwd: tmpDir, + env, + // As in invoke we optionally read stdin, we need to ensure it's closed + // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 + shouldCloseStdin: true, + }; let serviceName; let StackName; @@ -41,10 +48,11 @@ describe('Service Lifecyle Integration Test', function() { }); it('should create service in tmp directory', async () => { - await spawn(serverlessExec, ['create', '--template', templateName, '--name', serviceName], { - cwd: tmpDir, - env, - }); + await spawn( + serverlessExec, + ['create', '--template', templateName, '--name', serviceName], + spawnOptions + ); expect(fs.existsSync(path.join(tmpDir, 'serverless.yml'))).to.be.equal(true); expect(fs.existsSync(path.join(tmpDir, 'handler.js'))).to.be.equal(true); }); @@ -60,13 +68,7 @@ describe('Service Lifecyle Integration Test', function() { const { stdoutBuffer: invoked } = await spawn( serverlessExec, ['invoke', '--function', 'hello', '--noGreeting', 'true'], - { - cwd: tmpDir, - env, - // As in invoke we optionally read stdin, we need to ensure it's closed - // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 - shouldCloseStdin: true, - } + spawnOptions ); const result = JSON.parse(invoked); // parse it once again because the body is stringified to be LAMBDA-PROXY ready @@ -84,20 +86,14 @@ describe('Service Lifecyle Integration Test', function() { `; fs.writeFileSync(path.join(tmpDir, 'handler.js'), newHandler); - return spawn(serverlessExec, ['deploy'], { cwd: tmpDir, env }); + return spawn(serverlessExec, ['deploy'], spawnOptions); }); it('should invoke updated function from aws', async () => { const { stdoutBuffer: invoked } = await spawn( serverlessExec, ['invoke', '--function', 'hello', '--noGreeting', 'true'], - { - cwd: tmpDir, - env, - // As in invoke we optionally read stdin, we need to ensure it's closed - // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 - shouldCloseStdin: true, - } + spawnOptions ); const result = JSON.parse(invoked); expect(result.message).to.be.equal('Service Update Succeeded'); @@ -105,10 +101,11 @@ describe('Service Lifecyle Integration Test', function() { it('should list existing deployments and roll back to first deployment', async () => { let timestamp; - const { stdoutBuffer: listDeploys } = await spawn(serverlessExec, ['deploy', 'list'], { - cwd: tmpDir, - env, - }); + const { stdoutBuffer: listDeploys } = await spawn( + serverlessExec, + ['deploy', 'list'], + spawnOptions + ); const output = stripAnsi(listDeploys.toString()); const match = output.match(new RegExp('Datetime: (.+)')); if (match) { @@ -122,13 +119,7 @@ describe('Service Lifecyle Integration Test', function() { const { stdoutBuffer: invoked } = await spawn( serverlessExec, ['invoke', '--function', 'hello', '--noGreeting', 'true'], - { - cwd: tmpDir, - env, - // As in invoke we optionally read stdin, we need to ensure it's closed - // See https://github.com/sindresorhus/get-stdin/issues/13#issuecomment-279234249 - shouldCloseStdin: true, - } + spawnOptions ); const result = JSON.parse(invoked); // parse it once again because the body is stringified to be LAMBDA-PROXY ready From 389e61d3e8b5bfaa5716e73075aaf3f64ae23d11 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 11:45:58 +0200 Subject: [PATCH 27/50] Reconfigure to rely on spawn and env resolver --- lib/classes/PluginManager.test.js | 38 +++++++++++-------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/lib/classes/PluginManager.test.js b/lib/classes/PluginManager.test.js index 9f04a22ad..4abfdc06c 100644 --- a/lib/classes/PluginManager.test.js +++ b/lib/classes/PluginManager.test.js @@ -5,6 +5,8 @@ const chai = require('chai'); const overrideEnv = require('process-utils/override-env'); const cjsResolve = require('ncjsm/resolve/sync'); +const spawn = require('child-process-ext/spawn'); +const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); const Serverless = require('../../lib/Serverless'); const CLI = require('../../lib/classes/CLI'); const Create = require('../../lib/plugins/create/create'); @@ -19,7 +21,6 @@ const sinon = require('sinon'); const proxyquire = require('proxyquire'); const BbPromise = require('bluebird'); const getCacheFilePath = require('../utils/getCacheFilePath'); -const { execSync } = require('child_process'); const { installPlugin } = require('../../tests/utils/plugins'); const { getTmpDirPath } = require('../../tests/utils/fs'); @@ -29,6 +30,7 @@ chai.use(require('sinon-chai')); const expect = chai.expect; describe('PluginManager', () => { + const env = resolveAwsEnv(); let pluginManager; let serverless; @@ -2005,9 +2007,8 @@ describe('PluginManager', () => { }); describe('Plugin / CLI integration', function() { - this.timeout(0); + this.timeout(1000 * 60 * 10); - const cwd = process.cwd(); let serverlessInstance; let serviceDir; let serverlessExec; @@ -2024,16 +2025,11 @@ describe('PluginManager', () => { const tmpDir = getTmpDirPath(); serviceDir = path.join(tmpDir, 'service'); fse.mkdirsSync(serviceDir); - process.chdir(serviceDir); - try { - execSync(`${serverlessExec} create --template aws-nodejs`); - } catch (error) { - // Expose process output in case of crash - process.stdout.write(error.stdout); - process.stderr.write(error.stderr); - throw error; - } + return spawn(serverlessExec, ['create', '--template', 'aws-nodejs'], { + env, + cwd: serviceDir, + }); }); }); @@ -2057,22 +2053,14 @@ describe('PluginManager', () => { 'plugins:\n - local-plugin\n - parent-plugin' ); - let output; - try { - output = execSync(serverlessExec); - } catch (error) { - process.stdout.write(error.stdout); - process.stdout.write(error.stderr); - throw error; - } - const stringifiedOutput = Buffer.from(output, 'base64').toString(); - expect(stringifiedOutput).to.contain('SynchronousPluginMock'); - expect(stringifiedOutput).to.contain('PromisePluginMock'); + return spawn(serverlessExec, [], { env, cwd: serviceDir }).then(({ stdoutBuffer }) => { + const stringifiedOutput = String(stdoutBuffer); + expect(stringifiedOutput).to.contain('SynchronousPluginMock'); + expect(stringifiedOutput).to.contain('PromisePluginMock'); + }); }); afterEach(() => { - // eslint-disable-line prefer-arrow-callback - process.chdir(cwd); try { fse.removeSync(serviceDir); } catch (e) { From b33e96dbcea7c22c1504b15617f509f3a212fa74 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 11:58:30 +0200 Subject: [PATCH 28/50] Allow to customize serverless binary path --- lib/classes/CLI.test.js | 16 ++++------------ lib/classes/PluginManager.test.js | 6 +----- tests/integration-basic/tests.js | 2 +- .../integration-package/cloudformation.tests.js | 2 +- tests/integration-package/lambda-files.tests.js | 2 +- tests/serverless-binary.js | 8 ++++++++ tests/utils/integration.js | 3 ++- tests/utils/misc/index.js | 4 ---- 8 files changed, 18 insertions(+), 25 deletions(-) create mode 100644 tests/serverless-binary.js diff --git a/lib/classes/CLI.test.js b/lib/classes/CLI.test.js index 2bfa8bd84..7308e8e16 100644 --- a/lib/classes/CLI.test.js +++ b/lib/classes/CLI.test.js @@ -626,6 +626,7 @@ describe('CLI', () => { describe('Integration tests', function() { this.timeout(0); const that = this; + const serverlessExec = require('../../tests/serverless-binary'); before(() => { const tmpDir = getTmpDirPath(); @@ -634,15 +635,6 @@ describe('CLI', () => { fse.mkdirsSync(tmpDir); process.chdir(tmpDir); - - serverless = new Serverless(); - return serverless.init().then(() => { - // Cannot rely on shebang in severless.js to invoke script using NodeJS on Windows. - const execPrefix = os.platform() === 'win32' ? 'node ' : ''; - - that.serverlessExec = - execPrefix + path.join(serverless.config.serverlessPath, '..', 'bin', 'serverless'); - }); }); after(() => { @@ -650,7 +642,7 @@ describe('CLI', () => { }); it('should print general --help to stdout', done => { - exec(`${this.serverlessExec} --help`, (err, stdout, stderr) => { + exec(`${serverlessExec} --help`, (err, stdout, stderr) => { if (err) { process.stdout.write(stdout); process.stderr.write(stderr); @@ -664,7 +656,7 @@ describe('CLI', () => { }); it('should print command --help to stdout', done => { - exec(`${this.serverlessExec} deploy --help`, (err, stdout, stderr) => { + exec(`${serverlessExec} deploy --help`, (err, stdout, stderr) => { if (err) { process.stdout.write(stdout); process.stderr.write(stderr); @@ -679,7 +671,7 @@ describe('CLI', () => { }); it('should print help --verbose to stdout', done => { - exec(`${this.serverlessExec} help --verbose`, (err, stdout, stderr) => { + exec(`${serverlessExec} help --verbose`, (err, stdout, stderr) => { if (err) { process.stdout.write(stdout); process.stderr.write(stderr); diff --git a/lib/classes/PluginManager.test.js b/lib/classes/PluginManager.test.js index 4abfdc06c..cc6b56def 100644 --- a/lib/classes/PluginManager.test.js +++ b/lib/classes/PluginManager.test.js @@ -2011,17 +2011,13 @@ describe('PluginManager', () => { let serverlessInstance; let serviceDir; - let serverlessExec; + const serverlessExec = require('../../tests/serverless-binary'); beforeEach(() => { // eslint-disable-line prefer-arrow-callback serverlessInstance = new Serverless(); return serverlessInstance.init().then(() => { // Cannot rely on shebang in severless.js to invoke script using NodeJS on Windows. - const execPrefix = os.platform() === 'win32' ? 'node ' : ''; - serverlessExec = - execPrefix + - path.join(serverlessInstance.config.serverlessPath, '..', 'bin', 'serverless'); const tmpDir = getTmpDirPath(); serviceDir = path.join(tmpDir, 'service'); fse.mkdirsSync(serviceDir); diff --git a/tests/integration-basic/tests.js b/tests/integration-basic/tests.js index 8c06234b8..6b2b14421 100644 --- a/tests/integration-basic/tests.js +++ b/tests/integration-basic/tests.js @@ -11,7 +11,7 @@ const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); const { getTmpDirPath } = require('../utils/fs'); const { region, getServiceName } = require('../utils/misc'); -const serverlessExec = path.join(__dirname, '..', '..', 'bin', 'serverless'); +const serverlessExec = require('../serverless-binary'); const CF = new AWS.CloudFormation({ region }); diff --git a/tests/integration-package/cloudformation.tests.js b/tests/integration-package/cloudformation.tests.js index 420149a84..0adc13fdc 100644 --- a/tests/integration-package/cloudformation.tests.js +++ b/tests/integration-package/cloudformation.tests.js @@ -5,7 +5,7 @@ const path = require('path'); const { expect } = require('chai'); const fse = require('fs-extra'); const { execSync } = require('../utils/child-process'); -const { serverlessExec } = require('../utils/misc'); +const serverlessExec = require('../serverless-binary'); const { getTmpDirPath } = require('../utils/fs'); const fixturePaths = { diff --git a/tests/integration-package/lambda-files.tests.js b/tests/integration-package/lambda-files.tests.js index b05107604..0f12d1cd8 100644 --- a/tests/integration-package/lambda-files.tests.js +++ b/tests/integration-package/lambda-files.tests.js @@ -4,7 +4,7 @@ const path = require('path'); const { expect } = require('chai'); const fse = require('fs-extra'); const { execSync } = require('../utils/child-process'); -const { serverlessExec } = require('../utils/misc'); +const serverlessExec = require('../serverless-binary'); const { getTmpDirPath, listZipFiles } = require('../utils/fs'); const fixturePaths = { diff --git a/tests/serverless-binary.js b/tests/serverless-binary.js new file mode 100644 index 000000000..180765a6a --- /dev/null +++ b/tests/serverless-binary.js @@ -0,0 +1,8 @@ +'use strict'; + +const path = require('path'); + +module.exports = (() => { + if (process.env.SERVERLESS_BINARY_PATH) return path.resolve(process.env.SERVERLESS_BINARY_PATH); + return path.join(__dirname, '../bin/serverless.js'); +})(); diff --git a/tests/utils/integration.js b/tests/utils/integration.js index 2a6340ed9..8fed8fe8f 100644 --- a/tests/utils/integration.js +++ b/tests/utils/integration.js @@ -9,7 +9,8 @@ const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); const { getServiceName, wait } = require('./misc'); const { readYamlFile, writeYamlFile } = require('./fs'); -const serverlessExec = path.resolve(__dirname, '..', '..', 'bin', 'serverless'); +const serverlessExec = require('../serverless-binary'); + const env = resolveAwsEnv(); async function createTestService( diff --git a/tests/utils/misc/index.js b/tests/utils/misc/index.js index d737e97cc..85e42956b 100644 --- a/tests/utils/misc/index.js +++ b/tests/utils/misc/index.js @@ -1,6 +1,5 @@ 'use strict'; -const path = require('path'); const BbPromise = require('bluebird'); const CloudWatchLogsSdk = require('aws-sdk/clients/cloudwatchlogs'); @@ -11,8 +10,6 @@ const cloudWatchLogsSdk = new CloudWatchLogsSdk({ region }); const testServiceIdentifier = 'integ-test'; -const serverlessExec = path.resolve(__dirname, '..', '..', '..', 'bin', 'serverless'); - const serviceNameRegex = new RegExp(`${testServiceIdentifier}-d+`); function getServiceName() { @@ -90,7 +87,6 @@ module.exports = { region, confirmCloudWatchLogs, testServiceIdentifier, - serverlessExec, serviceNameRegex, getServiceName, replaceEnv, From c43c3f5e62a8bf9a46e5d4264a9369d9c2898c15 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 11:58:54 +0200 Subject: [PATCH 29/50] Improve timeout setup --- lib/classes/CLI.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/CLI.test.js b/lib/classes/CLI.test.js index 7308e8e16..f5042d958 100644 --- a/lib/classes/CLI.test.js +++ b/lib/classes/CLI.test.js @@ -624,7 +624,7 @@ describe('CLI', () => { }); describe('Integration tests', function() { - this.timeout(0); + this.timeout(1000 * 60 * 10); const that = this; const serverlessExec = require('../../tests/serverless-binary'); From 2c26e6f423f6570ba1eedfae34162a008ecbab79 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 11:59:26 +0200 Subject: [PATCH 30/50] Lint fixes --- lib/classes/CLI.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/classes/CLI.test.js b/lib/classes/CLI.test.js index f5042d958..04d658058 100644 --- a/lib/classes/CLI.test.js +++ b/lib/classes/CLI.test.js @@ -5,10 +5,8 @@ const chai = require('chai'); const sinon = require('sinon'); const CLI = require('../../lib/classes/CLI'); -const os = require('os'); const fse = require('fs-extra'); const exec = require('child_process').exec; -const path = require('path'); const stripAnsi = require('strip-ansi'); const Serverless = require('../../lib/Serverless'); const { getTmpDirPath } = require('../../tests/utils/fs'); From 2c836580d39601aee96c617000f0a582ab671b83 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 12:00:28 +0200 Subject: [PATCH 31/50] Lint fix --- lib/classes/PluginManager.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/classes/PluginManager.test.js b/lib/classes/PluginManager.test.js index cc6b56def..fc20a8508 100644 --- a/lib/classes/PluginManager.test.js +++ b/lib/classes/PluginManager.test.js @@ -16,7 +16,6 @@ const path = require('path'); const fs = require('fs'); const fse = require('fs-extra'); const mockRequire = require('mock-require'); -const os = require('os'); const sinon = require('sinon'); const proxyquire = require('proxyquire'); const BbPromise = require('bluebird'); From 16fa70c20913139511b819e40ddc36ac966e3b23 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 12:03:36 +0200 Subject: [PATCH 32/50] Improve CLI integration tests --- lib/classes/CLI.test.js | 54 ++++++++++++----------------------------- 1 file changed, 15 insertions(+), 39 deletions(-) diff --git a/lib/classes/CLI.test.js b/lib/classes/CLI.test.js index 04d658058..bdcf87de5 100644 --- a/lib/classes/CLI.test.js +++ b/lib/classes/CLI.test.js @@ -6,7 +6,8 @@ const chai = require('chai'); const sinon = require('sinon'); const CLI = require('../../lib/classes/CLI'); const fse = require('fs-extra'); -const exec = require('child_process').exec; +const spawn = require('child-process-ext/spawn'); +const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); const stripAnsi = require('strip-ansi'); const Serverless = require('../../lib/Serverless'); const { getTmpDirPath } = require('../../tests/utils/fs'); @@ -625,6 +626,7 @@ describe('CLI', () => { this.timeout(1000 * 60 * 10); const that = this; const serverlessExec = require('../../tests/serverless-binary'); + const env = resolveAwsEnv(); before(() => { const tmpDir = getTmpDirPath(); @@ -639,47 +641,21 @@ describe('CLI', () => { process.chdir(that.cwd); }); - it('should print general --help to stdout', done => { - exec(`${serverlessExec} --help`, (err, stdout, stderr) => { - if (err) { - process.stdout.write(stdout); - process.stderr.write(stderr); - done(err); - return; - } - - expect(stdout).to.contain('contextual help'); - done(); - }); - }); - - it('should print command --help to stdout', done => { - exec(`${serverlessExec} deploy --help`, (err, stdout, stderr) => { - if (err) { - process.stdout.write(stdout); - process.stderr.write(stderr); - done(err); - return; - } + it('should print general --help to stdout', () => + spawn(serverlessExec, ['--help'], { env }).then(({ stdoutBuffer }) => + expect(String(stdoutBuffer)).to.contain('contextual help') + )); + it('should print command --help to stdout', () => + spawn(serverlessExec, ['deploy', '--help'], { env }).then(({ stdoutBuffer }) => { + const stdout = String(stdoutBuffer); expect(stdout).to.contain('deploy'); expect(stdout).to.contain('--stage'); - done(); - }); - }); + })); - it('should print help --verbose to stdout', done => { - exec(`${serverlessExec} help --verbose`, (err, stdout, stderr) => { - if (err) { - process.stdout.write(stdout); - process.stderr.write(stderr); - done(err); - return; - } - - expect(stdout).to.contain('Commands by plugin'); - done(); - }); - }); + it('should print help --verbose to stdout', () => + spawn(serverlessExec, ['help', '--verbose'], { env }).then(({ stdoutBuffer }) => + expect(String(stdoutBuffer)).to.contain('Commands by plugin') + )); }); }); From 7fce1c77a31269a1818c79493200de30855bc02f Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 17:42:09 +0200 Subject: [PATCH 33/50] Remove obsolete comment --- lib/classes/CLI.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/classes/CLI.test.js b/lib/classes/CLI.test.js index bdcf87de5..4648c6b21 100644 --- a/lib/classes/CLI.test.js +++ b/lib/classes/CLI.test.js @@ -1,7 +1,5 @@ 'use strict'; -/* eslint-disable no-unused-expressions */ - const chai = require('chai'); const sinon = require('sinon'); const CLI = require('../../lib/classes/CLI'); From f3017894c8031c307c29539ccb6ecf5bb88e6a67 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 17:42:50 +0200 Subject: [PATCH 34/50] Update @serverless/test dependency --- lib/classes/CLI.test.js | 2 +- lib/classes/PluginManager.test.js | 2 +- package.json | 2 +- tests/integration-basic/tests.js | 2 +- tests/utils/integration.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/classes/CLI.test.js b/lib/classes/CLI.test.js index 4648c6b21..0527f296a 100644 --- a/lib/classes/CLI.test.js +++ b/lib/classes/CLI.test.js @@ -5,7 +5,7 @@ const sinon = require('sinon'); const CLI = require('../../lib/classes/CLI'); const fse = require('fs-extra'); const spawn = require('child-process-ext/spawn'); -const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); +const resolveAwsEnv = require('@serverless/test/resolve-env'); const stripAnsi = require('strip-ansi'); const Serverless = require('../../lib/Serverless'); const { getTmpDirPath } = require('../../tests/utils/fs'); diff --git a/lib/classes/PluginManager.test.js b/lib/classes/PluginManager.test.js index fc20a8508..5c2a2377c 100644 --- a/lib/classes/PluginManager.test.js +++ b/lib/classes/PluginManager.test.js @@ -6,7 +6,7 @@ const chai = require('chai'); const overrideEnv = require('process-utils/override-env'); const cjsResolve = require('ncjsm/resolve/sync'); const spawn = require('child-process-ext/spawn'); -const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); +const resolveAwsEnv = require('@serverless/test/resolve-env'); const Serverless = require('../../lib/Serverless'); const CLI = require('../../lib/classes/CLI'); const Create = require('../../lib/plugins/create/create'); diff --git a/package.json b/package.json index a07f6676e..7ef85e9fe 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ }, "devDependencies": { "@serverless/eslint-config": "^1.2.0", - "@serverless/test": "^2.1.0", + "@serverless/test": "^2.2.0", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-ext": "^2.1.0", diff --git a/tests/integration-basic/tests.js b/tests/integration-basic/tests.js index 6b2b14421..a28870550 100644 --- a/tests/integration-basic/tests.js +++ b/tests/integration-basic/tests.js @@ -7,7 +7,7 @@ const AWS = require('aws-sdk'); const stripAnsi = require('strip-ansi'); const { expect } = require('chai'); const spawn = require('child-process-ext/spawn'); -const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); +const resolveAwsEnv = require('@serverless/test/resolve-aws-env'); const { getTmpDirPath } = require('../utils/fs'); const { region, getServiceName } = require('../utils/misc'); diff --git a/tests/utils/integration.js b/tests/utils/integration.js index 8fed8fe8f..ffd53a269 100644 --- a/tests/utils/integration.js +++ b/tests/utils/integration.js @@ -5,7 +5,7 @@ const path = require('path'); const fse = require('fs-extra'); const spawn = require('child-process-ext/spawn'); -const resolveAwsEnv = require('@serverless/test/lib/resolve-aws-env'); +const resolveAwsEnv = require('@serverless/test/resolve-aws-env'); const { getServiceName, wait } = require('./misc'); const { readYamlFile, writeYamlFile } = require('./fs'); From ddf994f3e3ba44c1bfe26e2320d508080da1f187 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Mon, 21 Oct 2019 17:44:01 +0200 Subject: [PATCH 35/50] Update up to recent changes --- tests/integration-all/iot/tests.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration-all/iot/tests.js b/tests/integration-all/iot/tests.js index 9349bb790..24e64d350 100644 --- a/tests/integration-all/iot/tests.js +++ b/tests/integration-all/iot/tests.js @@ -10,7 +10,7 @@ const { deployService, removeService, waitForFunctionLogs, -} = require('../../utils/misc'); +} = require('../../utils/integration'); const { getMarkers } = require('../shared/utils'); describe('AWS - IoT Integration Test', function() { @@ -21,10 +21,10 @@ describe('AWS - IoT Integration Test', function() { let tmpDirPath; const stage = 'dev'; - before(() => { + before(async () => { tmpDirPath = getTmpDirPath(); console.info(`Temporary path: ${tmpDirPath}`); - const serverlessConfig = createTestService(tmpDirPath, { + const serverlessConfig = await createTestService(tmpDirPath, { templateDir: path.join(__dirname, 'service'), filesToAdd: [path.join(__dirname, '..', 'shared')], serverlessConfigHook: @@ -43,7 +43,7 @@ describe('AWS - IoT Integration Test', function() { after(() => { // Topics are ephemeral and IoT endpoint is part of the account console.info('Removing service...'); - removeService(tmpDirPath); + return removeService(tmpDirPath); }); describe('Basic Setup', () => { From 5372117607ccb4bafe1069643a3b0da55fd31f60 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 22 Oct 2019 10:33:04 +0200 Subject: [PATCH 36/50] Fix tests --- lib/plugins/package/lib/packageService.test.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/plugins/package/lib/packageService.test.js b/lib/plugins/package/lib/packageService.test.js index d6e4e5877..82571286a 100644 --- a/lib/plugins/package/lib/packageService.test.js +++ b/lib/plugins/package/lib/packageService.test.js @@ -623,11 +623,10 @@ describe('#packageService()', () => { exclude: ['**'], include: [handlerFile], }; - const expected = [handlerFile]; serverless.config.servicePath = servicePath; return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( - actual => expect(actual).to.deep.equal(expected) + actual => expect(actual).to.deep.equal(['src/function/handler.js']) ); }); @@ -636,11 +635,10 @@ describe('#packageService()', () => { exclude: ['**', `!${utilsFile}`], include: [handlerFile], }; - const expected = [handlerFile, utilsFile]; serverless.config.servicePath = servicePath; return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( - actual => expect(actual).to.deep.equal(expected) + actual => expect(actual).to.deep.equal(['src/function/handler.js', 'src/utils/utils.js']) ); }); From c1e64578c1b20d4fe1b4baec060f9c0dd2e535ea Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 22 Oct 2019 11:15:34 +0200 Subject: [PATCH 37/50] Ensure alphabetical order --- tests/utils/integration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils/integration.js b/tests/utils/integration.js index ffd53a269..afe5edc2b 100644 --- a/tests/utils/integration.js +++ b/tests/utils/integration.js @@ -98,7 +98,7 @@ async function waitForFunctionLogs(cwd, functionName, startMarker, endMarker) { module.exports = { createTestService, deployService, + env, removeService, waitForFunctionLogs, - env, }; From 377249665e6b4418e285780f36e0135ec848a273 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 22 Oct 2019 12:00:54 +0200 Subject: [PATCH 38/50] Provide logs for fetch calls --- package.json | 1 + tests/integration-all/api-gateway/tests.js | 8 +++++-- tests/utils/integration.js | 28 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7ef85e9fe..63fb5160f 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "eslint": "^6.5.1", "eslint-plugin-import": "^2.18.2", "git-list-updated": "^1.2.1", + "log": "^6.0.0", "mocha": "^6.2.1", "mocha-lcov-reporter": "^1.3.0", "mock-require": "^3.0.3", diff --git a/tests/integration-all/api-gateway/tests.js b/tests/integration-all/api-gateway/tests.js index 5cbbdfd08..c51982321 100644 --- a/tests/integration-all/api-gateway/tests.js +++ b/tests/integration-all/api-gateway/tests.js @@ -3,12 +3,16 @@ const path = require('path'); const AWS = require('aws-sdk'); const _ = require('lodash'); -const fetch = require('node-fetch'); const { expect } = require('chai'); const { getTmpDirPath, readYamlFile, writeYamlFile } = require('../../utils/fs'); const { region, confirmCloudWatchLogs } = require('../../utils/misc'); -const { createTestService, deployService, removeService } = require('../../utils/integration'); +const { + createTestService, + deployService, + removeService, + fetch, +} = require('../../utils/integration'); const { createRestApi, deleteRestApi, getResources } = require('../../utils/api-gateway'); const CF = new AWS.CloudFormation({ region }); diff --git a/tests/utils/integration.js b/tests/utils/integration.js index afe5edc2b..9cdbdee9b 100644 --- a/tests/utils/integration.js +++ b/tests/utils/integration.js @@ -5,6 +5,8 @@ const path = require('path'); const fse = require('fs-extra'); const spawn = require('child-process-ext/spawn'); +const nodeFetch = require('node-fetch'); +const logFetch = require('log').get('fetch'); const resolveAwsEnv = require('@serverless/test/resolve-aws-env'); const { getServiceName, wait } = require('./misc'); const { readYamlFile, writeYamlFile } = require('./fs'); @@ -95,10 +97,36 @@ async function waitForFunctionLogs(cwd, functionName, startMarker, endMarker) { return waitForFunctionLogs(cwd, functionName, startMarker, endMarker); } +let lastRequestId = 0; +async function fetch(url, options) { + const requestId = ++lastRequestId; + logFetch.debug('[%d] %s %o', requestId, url, options); + + let response; + try { + response = await nodeFetch(url, options); + } catch (error) { + logFetch.error('[%d] request error: %o', requestId, error); + throw error; + } + + /* eslint-disable no-underscore-dangle */ + logFetch.debug('[%d] %d %j', requestId, response.status, response.headers._headers); + const responseDecodeResult = response._decode(); + response._decode = () => responseDecodeResult; + /* eslint-enable */ + responseDecodeResult.then( + buffer => logFetch.debug('[%d] %s', requestId, String(buffer)), + error => logFetch.error('[%d] response resolution error: %o', requestId, error) + ); + return response; +} + module.exports = { createTestService, deployService, env, + fetch, removeService, waitForFunctionLogs, }; From 626bf2995a18ce22665883e14c52608bc2d9f708 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 22 Oct 2019 12:39:10 +0200 Subject: [PATCH 39/50] Log cognito AWS SDK requests --- tests/utils/cognito/index.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/utils/cognito/index.js b/tests/utils/cognito/index.js index 594594c8c..b25efd46a 100644 --- a/tests/utils/cognito/index.js +++ b/tests/utils/cognito/index.js @@ -1,6 +1,7 @@ 'use strict'; const AWS = require('aws-sdk'); +const log = require('log').get('aws'); const { region, persistentRequest } = require('../misc'); function createUserPool(name, config = {}) { @@ -30,6 +31,7 @@ function deleteUserPool(name) { } function findUserPoolByName(name) { + log.debug('find cognito user pool by name %s', name); const cognito = new AWS.CognitoIdentityServiceProvider({ region }); const params = { @@ -42,6 +44,7 @@ function findUserPoolByName(name) { .listUserPools(params) .promise() .then(result => { + log.debug('cognito.listUserPools %j', result); const matches = result.UserPools.filter(pool => pool.Name === name); if (matches.length) { return matches.shift(); @@ -57,7 +60,13 @@ function findUserPoolByName(name) { function describeUserPool(userPoolId) { const cognito = new AWS.CognitoIdentityServiceProvider({ region }); - return cognito.describeUserPool({ UserPoolId: userPoolId }).promise(); + return cognito + .describeUserPool({ UserPoolId: userPoolId }) + .promise() + .then(result => { + log.debug('cognito.describeUserPool %s %j', userPoolId, result); + return result; + }); } function createUser(userPoolId, username, password) { From 52a0c00dbc5d0e473f6868faab163004773b0d3f Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 22 Oct 2019 12:50:08 +0200 Subject: [PATCH 40/50] Upgrade process-utils --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 63fb5160f..afaebfa2c 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "mock-require": "^3.0.3", "nyc": "^14.1.1", "prettier": "^1.18.2", - "process-utils": "^2.5.0", + "process-utils": "^3.0.0", "proxyquire": "^2.1.3", "sinon": "^7.5.0", "sinon-chai": "^3.3.0", From f56c85089fb874cdadfd6eacf91905795f6692cf Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 22 Oct 2019 13:35:24 +0200 Subject: [PATCH 41/50] Fix tests --- lib/plugins/package/lib/packageService.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/plugins/package/lib/packageService.test.js b/lib/plugins/package/lib/packageService.test.js index 82571286a..9c591075e 100644 --- a/lib/plugins/package/lib/packageService.test.js +++ b/lib/plugins/package/lib/packageService.test.js @@ -608,8 +608,10 @@ describe('#packageService()', () => { }); describe('#resolveFilePathsFromPatterns()', () => { - const handlerFile = path.join('src', 'function', 'handler.js'); - const utilsFile = path.join('src', 'utils', 'utils.js'); + // NOTE: the path.join in `beforeEach` will take care of OS + // independent file paths + const handlerFile = 'src/function/handler.js'; + const utilsFile = 'src/utils/utils.js'; let servicePath; beforeEach(() => { @@ -626,7 +628,7 @@ describe('#packageService()', () => { serverless.config.servicePath = servicePath; return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( - actual => expect(actual).to.deep.equal(['src/function/handler.js']) + actual => expect(actual).to.deep.equal([handlerFile]) ); }); @@ -638,7 +640,7 @@ describe('#packageService()', () => { serverless.config.servicePath = servicePath; return expect(packagePlugin.resolveFilePathsFromPatterns(params)).to.be.fulfilled.then( - actual => expect(actual).to.deep.equal(['src/function/handler.js', 'src/utils/utils.js']) + actual => expect(actual).to.deep.equal([handlerFile, utilsFile]) ); }); From 979e7fbdc8ba20a563271a09427ae150bbe32f42 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 22 Oct 2019 13:44:58 +0200 Subject: [PATCH 42/50] Bump dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index afaebfa2c..db9846f61 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ }, "devDependencies": { "@serverless/eslint-config": "^1.2.0", - "@serverless/test": "^2.2.0", + "@serverless/test": "^2.3.0", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-ext": "^2.1.0", @@ -104,7 +104,7 @@ "mock-require": "^3.0.3", "nyc": "^14.1.1", "prettier": "^1.18.2", - "process-utils": "^3.0.0", + "process-utils": "^3.0.1", "proxyquire": "^2.1.3", "sinon": "^7.5.0", "sinon-chai": "^3.3.0", From 45be518d477cae0ed7d4165e5db9bb56748f2612 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 22 Oct 2019 13:55:20 +0200 Subject: [PATCH 43/50] Ensure boolean result --- lib/utils/isTrackingDisabled.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/isTrackingDisabled.js b/lib/utils/isTrackingDisabled.js index 95f3e659d..a0262443e 100644 --- a/lib/utils/isTrackingDisabled.js +++ b/lib/utils/isTrackingDisabled.js @@ -2,4 +2,4 @@ const configUtils = require('./config'); -module.exports = process.env.SLS_TRACKING_DISABLED || configUtils.get('trackingDisabled'); +module.exports = Boolean(process.env.SLS_TRACKING_DISABLED || configUtils.get('trackingDisabled')); From 9e6bab2e42a9091b292fe37273537981d883f43b Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 22 Oct 2019 13:56:29 +0200 Subject: [PATCH 44/50] Fix tabCompletion/isSupported test --- lib/utils/tabCompletion/isSupported.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/tabCompletion/isSupported.test.js b/lib/utils/tabCompletion/isSupported.test.js index 6516f28dc..d7bc3f2ea 100644 --- a/lib/utils/tabCompletion/isSupported.test.js +++ b/lib/utils/tabCompletion/isSupported.test.js @@ -2,8 +2,8 @@ const { expect } = require('chai'); -const isTabtabCompletionSuported = require('../isTrackingDisabled'); +const isSuported = require('./isSupported'); describe('isTabtabCompletionSuported', () => { - it('Should resolve boolean', () => expect(typeof isTabtabCompletionSuported).to.equal('boolean')); + it('Should resolve boolean', () => expect(typeof isSuported).to.equal('boolean')); }); From ae1696084bbb6789c92e55f4ae274116fc922282 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 22 Oct 2019 16:59:28 +0200 Subject: [PATCH 45/50] Reorganize custom role setup --- lib/plugins/aws/customResources/index.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/plugins/aws/customResources/index.js b/lib/plugins/aws/customResources/index.js index b3b9b4f99..12d34edf3 100644 --- a/lib/plugins/aws/customResources/index.js +++ b/lib/plugins/aws/customResources/index.js @@ -123,6 +123,7 @@ function addCustomResourceToService(awsProvider, resourceName, iamRoleStatements ], }, }; + Resources[customResourcesRoleLogicalId] = customResourceRole; if (shouldWriteLogs) { const logGroupsPrefix = awsProvider.naming.getLogGroupName(funcPrefix); @@ -177,11 +178,7 @@ function addCustomResourceToService(awsProvider, resourceName, iamRoleStatements }, DependsOn: [customResourcesRoleLogicalId], }; - - Object.assign(Resources, { - [customResourceFunctionLogicalId]: customResourceFunction, - [customResourcesRoleLogicalId]: customResourceRole, - }); + Resources[customResourceFunctionLogicalId] = customResourceFunction; if (shouldWriteLogs) { const customResourceLogGroupLogicalId = awsProvider.naming.getLogGroupLogicalId( From 88f60a48ab6780479aa1b597a265d7e7a075b97c Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Tue, 22 Oct 2019 17:13:17 +0200 Subject: [PATCH 46/50] Honor cfnRole in custom resources handling --- lib/plugins/aws/customResources/index.js | 149 ++++++++++-------- lib/plugins/aws/customResources/index.test.js | 110 +++++++++++++ 2 files changed, 189 insertions(+), 70 deletions(-) diff --git a/lib/plugins/aws/customResources/index.js b/lib/plugins/aws/customResources/index.js index 12d34edf3..67ca6f6b5 100644 --- a/lib/plugins/aws/customResources/index.js +++ b/lib/plugins/aws/customResources/index.js @@ -86,79 +86,83 @@ function addCustomResourceToService(awsProvider, resourceName, iamRoleStatements const s3FileName = outputFilePath.split(path.sep).pop(); const S3Key = `${s3Folder}/${s3FileName}`; - let customResourceRole = Resources[customResourcesRoleLogicalId]; - if (!customResourceRole) { - customResourceRole = { - Type: 'AWS::IAM::Role', - Properties: { - AssumeRolePolicyDocument: { - Version: '2012-10-17', - Statement: [ - { - Effect: 'Allow', - Principal: { - Service: ['lambda.amazonaws.com'], - }, - Action: ['sts:AssumeRole'], - }, - ], - }, - Policies: [ - { - PolicyName: { - 'Fn::Join': [ - '-', - [ - awsProvider.getStage(), - awsProvider.serverless.service.service, - 'custom-resources-lambda', - ], - ], - }, - PolicyDocument: { - Version: '2012-10-17', - Statement: [], - }, - }, - ], - }, - }; - Resources[customResourcesRoleLogicalId] = customResourceRole; + const cfnRoleArn = serverless.service.provider.cfnRole; - if (shouldWriteLogs) { - const logGroupsPrefix = awsProvider.naming.getLogGroupName(funcPrefix); - customResourceRole.Properties.Policies[0].PolicyDocument.Statement.push( - { - Effect: 'Allow', - Action: ['logs:CreateLogStream'], - Resource: [ + if (!cfnRoleArn) { + let customResourceRole = Resources[customResourcesRoleLogicalId]; + if (!customResourceRole) { + customResourceRole = { + Type: 'AWS::IAM::Role', + Properties: { + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Effect: 'Allow', + Principal: { + Service: ['lambda.amazonaws.com'], + }, + Action: ['sts:AssumeRole'], + }, + ], + }, + Policies: [ { - 'Fn::Sub': - 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' + - `:log-group:${logGroupsPrefix}*:*`, + PolicyName: { + 'Fn::Join': [ + '-', + [ + awsProvider.getStage(), + awsProvider.serverless.service.service, + 'custom-resources-lambda', + ], + ], + }, + PolicyDocument: { + Version: '2012-10-17', + Statement: [], + }, }, ], }, - { - Effect: 'Allow', - Action: ['logs:PutLogEvents'], - Resource: [ - { - 'Fn::Sub': - 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' + - `:log-group:${logGroupsPrefix}*:*:*`, - }, - ], - } - ); + }; + Resources[customResourcesRoleLogicalId] = customResourceRole; + + if (shouldWriteLogs) { + const logGroupsPrefix = awsProvider.naming.getLogGroupName(funcPrefix); + customResourceRole.Properties.Policies[0].PolicyDocument.Statement.push( + { + Effect: 'Allow', + Action: ['logs:CreateLogStream'], + Resource: [ + { + 'Fn::Sub': + 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' + + `:log-group:${logGroupsPrefix}*:*`, + }, + ], + }, + { + Effect: 'Allow', + Action: ['logs:PutLogEvents'], + Resource: [ + { + 'Fn::Sub': + 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' + + `:log-group:${logGroupsPrefix}*:*:*`, + }, + ], + } + ); + } } + const { Statement } = customResourceRole.Properties.Policies[0].PolicyDocument; + iamRoleStatements.forEach(newStmt => { + if (!Statement.find(existingStmt => existingStmt.Resource === newStmt.Resource)) { + Statement.push(newStmt); + } + }); } - const { Statement } = customResourceRole.Properties.Policies[0].PolicyDocument; - iamRoleStatements.forEach(newStmt => { - if (!Statement.find(existingStmt => existingStmt.Resource === newStmt.Resource)) { - Statement.push(newStmt); - } - }); const customResourceFunction = { Type: 'AWS::Lambda::Function', @@ -170,16 +174,21 @@ function addCustomResourceToService(awsProvider, resourceName, iamRoleStatements FunctionName: absoluteFunctionName, Handler, MemorySize: 1024, - Role: { - 'Fn::GetAtt': [customResourcesRoleLogicalId, 'Arn'], - }, Runtime: 'nodejs10.x', Timeout: 180, }, - DependsOn: [customResourcesRoleLogicalId], }; Resources[customResourceFunctionLogicalId] = customResourceFunction; + if (cfnRoleArn) { + customResourceFunction.Properties.Role = cfnRoleArn; + } else { + customResourceFunction.Properties.Role = { + 'Fn::GetAtt': [customResourcesRoleLogicalId, 'Arn'], + }; + customResourceFunction.DependsOn = [customResourcesRoleLogicalId]; + } + if (shouldWriteLogs) { const customResourceLogGroupLogicalId = awsProvider.naming.getLogGroupLogicalId( functionName diff --git a/lib/plugins/aws/customResources/index.test.js b/lib/plugins/aws/customResources/index.test.js index ceb5a328d..ae0ac7f11 100644 --- a/lib/plugins/aws/customResources/index.test.js +++ b/lib/plugins/aws/customResources/index.test.js @@ -221,6 +221,116 @@ describe('#addCustomResourceToService()', () => { }); }); + it('Should not setup new IAM role, when cfnRole is provided', () => { + const cfnRoleArn = (serverless.service.provider.cfnRole = + 'arn:aws:iam::999999999999:role/some-role'); + return expect( + BbPromise.all([ + // add the custom S3 resource + addCustomResourceToService(provider, 's3', [ + ...iamRoleStatements, + { + Effect: 'Allow', + Resource: 'arn:aws:s3:::some-bucket', + Action: ['s3:PutBucketNotification', 's3:GetBucketNotification'], + }, + ]), + // add the custom Cognito User Pool resource + addCustomResourceToService(provider, 'cognitoUserPool', [ + ...iamRoleStatements, + { + Effect: 'Allow', + Resource: '*', + Action: [ + 'cognito-idp:ListUserPools', + 'cognito-idp:DescribeUserPool', + 'cognito-idp:UpdateUserPool', + ], + }, + ]), + // add the custom Event Bridge resource + addCustomResourceToService(provider, 'eventBridge', [ + ...iamRoleStatements, + { + Effect: 'Allow', + Resource: 'arn:aws:events:*:*:rule/some-rule', + Action: [ + 'events:PutRule', + 'events:RemoveTargets', + 'events:PutTargets', + 'events:DeleteRule', + ], + }, + { + Action: ['events:CreateEventBus', 'events:DeleteEventBus'], + Effect: 'Allow', + Resource: 'arn:aws:events:*:*:event-bus/some-event-bus', + }, + ]), + ]) + ).to.be.fulfilled.then(() => { + const { Resources } = serverless.service.provider.compiledCloudFormationTemplate; + const customResourcesZipFilePath = path.join( + tmpDirPath, + '.serverless', + 'custom-resources.zip' + ); + + expect(execAsyncStub).to.have.callCount(3); + expect(fs.existsSync(customResourcesZipFilePath)).to.equal(true); + // S3 Lambda Function + expect(Resources.CustomDashresourceDashexistingDashs3LambdaFunction).to.deep.equal({ + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: { Ref: 'ServerlessDeploymentBucket' }, + S3Key: 'artifact-dir-name/custom-resources.zip', + }, + FunctionName: `${serviceName}-dev-custom-resource-existing-s3`, + Handler: 's3/handler.handler', + MemorySize: 1024, + Role: cfnRoleArn, + Runtime: 'nodejs10.x', + Timeout: 180, + }, + }); + // Cognito User Pool Lambda Function + expect(Resources.CustomDashresourceDashexistingDashcupLambdaFunction).to.deep.equal({ + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: { Ref: 'ServerlessDeploymentBucket' }, + S3Key: 'artifact-dir-name/custom-resources.zip', + }, + FunctionName: `${serviceName}-dev-custom-resource-existing-cup`, + Handler: 'cognitoUserPool/handler.handler', + MemorySize: 1024, + Role: cfnRoleArn, + Runtime: 'nodejs10.x', + Timeout: 180, + }, + }); + // Event Bridge Lambda Function + expect(Resources.CustomDashresourceDasheventDashbridgeLambdaFunction).to.deep.equal({ + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: { Ref: 'ServerlessDeploymentBucket' }, + S3Key: 'artifact-dir-name/custom-resources.zip', + }, + FunctionName: `${serviceName}-dev-custom-resource-event-bridge`, + Handler: 'eventBridge/handler.handler', + MemorySize: 1024, + Role: cfnRoleArn, + Runtime: 'nodejs10.x', + Timeout: 180, + }, + }); + // Iam Role + expect(Resources.IamRoleCustomResourcesLambdaExecution).to.be.undefined; + }); + }); + it('should setup CloudWatch Logs when logs.frameworkLambda is true', () => { serverless.service.provider.logs = { frameworkLambda: true }; return BbPromise.all([ From ce6d1559488cb0feafec290e59faa18dbad0c5af Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Wed, 23 Oct 2019 11:10:02 +0200 Subject: [PATCH 47/50] Fix DependsOn handling --- lib/plugins/aws/customResources/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/plugins/aws/customResources/index.js b/lib/plugins/aws/customResources/index.js index 67ca6f6b5..3b84d4663 100644 --- a/lib/plugins/aws/customResources/index.js +++ b/lib/plugins/aws/customResources/index.js @@ -177,6 +177,7 @@ function addCustomResourceToService(awsProvider, resourceName, iamRoleStatements Runtime: 'nodejs10.x', Timeout: 180, }, + DependsOn: [], }; Resources[customResourceFunctionLogicalId] = customResourceFunction; @@ -186,7 +187,7 @@ function addCustomResourceToService(awsProvider, resourceName, iamRoleStatements customResourceFunction.Properties.Role = { 'Fn::GetAtt': [customResourcesRoleLogicalId, 'Arn'], }; - customResourceFunction.DependsOn = [customResourcesRoleLogicalId]; + customResourceFunction.DependsOn.push(customResourcesRoleLogicalId); } if (shouldWriteLogs) { From b749213029e3bca7514798ba569218058140d818 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Wed, 23 Oct 2019 11:49:58 +0200 Subject: [PATCH 48/50] Fix custom resource tests --- lib/plugins/aws/customResources/index.test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/plugins/aws/customResources/index.test.js b/lib/plugins/aws/customResources/index.test.js index ae0ac7f11..639f25cd8 100644 --- a/lib/plugins/aws/customResources/index.test.js +++ b/lib/plugins/aws/customResources/index.test.js @@ -293,6 +293,7 @@ describe('#addCustomResourceToService()', () => { Runtime: 'nodejs10.x', Timeout: 180, }, + DependsOn: [], }); // Cognito User Pool Lambda Function expect(Resources.CustomDashresourceDashexistingDashcupLambdaFunction).to.deep.equal({ @@ -309,6 +310,7 @@ describe('#addCustomResourceToService()', () => { Runtime: 'nodejs10.x', Timeout: 180, }, + DependsOn: [], }); // Event Bridge Lambda Function expect(Resources.CustomDashresourceDasheventDashbridgeLambdaFunction).to.deep.equal({ @@ -325,6 +327,7 @@ describe('#addCustomResourceToService()', () => { Runtime: 'nodejs10.x', Timeout: 180, }, + DependsOn: [], }); // Iam Role expect(Resources.IamRoleCustomResourcesLambdaExecution).to.be.undefined; From 0130139ebd4408801aaa3091a1e65ca70647a130 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Wed, 23 Oct 2019 12:18:24 +0200 Subject: [PATCH 49/50] Bump dependencies --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index c73e4cb33..274af88c8 100644 --- a/package.json +++ b/package.json @@ -89,17 +89,17 @@ }, "devDependencies": { "@serverless/eslint-config": "^1.2.0", - "@serverless/test": "^2.3.0", + "@serverless/test": "^2.4.0", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-ext": "^2.1.0", "cli-progress-footer": "^1.1.1", - "coveralls": "^3.0.6", + "coveralls": "^3.0.7", "eslint": "^6.5.1", "eslint-plugin-import": "^2.18.2", "git-list-updated": "^1.2.1", "log": "^6.0.0", - "mocha": "^6.2.1", + "mocha": "^6.2.2", "mocha-lcov-reporter": "^1.3.0", "mock-require": "^3.0.3", "nyc": "^14.1.1", @@ -109,15 +109,15 @@ "sinon": "^7.5.0", "sinon-chai": "^3.3.0", "strip-ansi": "^5.2.0", - "ws": "^7.1.2" + "ws": "^7.2.0" }, "dependencies": { - "@serverless/cli": "^1.2.3", + "@serverless/cli": "^1.4.0", "@serverless/enterprise-plugin": "^3.1.2", "archiver": "^1.3.0", "async": "^1.5.2", - "aws-sdk": "^2.545.0", - "bluebird": "^3.7.0", + "aws-sdk": "^2.554.0", + "bluebird": "^3.7.1", "cachedir": "^2.2.0", "chalk": "^2.4.2", "ci-info": "^1.6.0", From b91c382308ec7ef6cca59da51caa0fa0314ef83c Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Wed, 23 Oct 2019 12:30:03 +0200 Subject: [PATCH 50/50] Release v1.55.0 --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73414341f..8d238b713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +# 1.55.0 (2019-10-23) + +- [Allow empty arrays in overrides](https://github.com/serverless/serverless/pull/6813) +- [Make question mark available as variables fallback](https://github.com/serverless/serverless/pull/6808) +- [Improve plugins resolution and initialization flow](https://github.com/serverless/serverless/pull/6814) +- [Azure Python template](https://github.com/serverless/serverless/pull/6822) +- [Chore - stop using deprecated 'new Buffer()' method.](https://github.com/serverless/serverless/pull/6829) +- [AWS - adding naming function for S3 compiled template file name.](https://github.com/serverless/serverless/pull/6828) +- [Span docs! and full `serverless_sdk` docs](https://github.com/serverless/serverless/pull/6809) +- [Fix perms with several CloudWatch log subscriptions](https://github.com/serverless/serverless/pull/6827) +- [Fixing an Azure docs broken link](https://github.com/serverless/serverless/pull/6838) +- [Adding note to Azure nodejs template](https://github.com/serverless/serverless/pull/6839) +- [Updated Azure Functions documentation](https://github.com/serverless/serverless/pull/6840) +- [Support for NotAction and NotResource in IAM role statements](https://github.com/serverless/serverless/pull/6842) +- [added frontmatter to sdk docs](https://github.com/serverless/serverless/pull/6845) +- [Setup completion via CLI command and interactive CLI step](https://github.com/serverless/serverless/pull/6835) +- [Upgrade gradle version](https://github.com/serverless/serverless/pull/6855) +- [Update Google provider documentation for functions](https://github.com/serverless/serverless/pull/6854) +- [SNS integration tests](https://github.com/serverless/serverless/pull/6846) +- [SQS integration tests](https://github.com/serverless/serverless/pull/6847) +- [Streams integration tests](https://github.com/serverless/serverless/pull/6848) +- [Improvements on SQS docs as suggested on #6516](https://github.com/serverless/serverless/pull/6853) +- [Schedule integration tests](https://github.com/serverless/serverless/pull/6851) +- [Update event documentation](https://github.com/serverless/serverless/pull/6857) +- [Upgrade groovy/gradle/plugin versions and dependencies (aws-groovy-gradle)](https://github.com/serverless/serverless/pull/6862) +- [Upgrade gradle/plugins version and dependencies (aws-clojure-gradle)](https://github.com/serverless/serverless/pull/6861) +- [IoT integration tests](https://github.com/serverless/serverless/pull/6837) +- [Update https-proxy-agent dependency](https://github.com/serverless/serverless/pull/6866) +- [Allow to use Ref in stream arn property](https://github.com/serverless/serverless/pull/6856) +- [Add Tests for resolveFilePathsFromPatterns()](https://github.com/serverless/serverless/pull/6825) +- [Integration tests improvements and fixes](https://github.com/serverless/serverless/pull/6867) +- [Honor cfnRole in custom resources](https://github.com/serverless/serverless/pull/6871) + +## Meta + +- [Comparison since last release](https://github.com/serverless/serverless/compare/v1.54.0...v1.55.0) + # 1.54.0 (2019-10-09) - [Fixing typos in variable names](https://github.com/serverless/serverless/pull/6746) diff --git a/package.json b/package.json index 274af88c8..a23069b4b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless", - "version": "1.54.0", + "version": "1.55.0", "engines": { "node": ">=6.0" },