mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
1468 lines
47 KiB
JavaScript
1468 lines
47 KiB
JavaScript
'use strict';
|
|
|
|
/* eslint-disable no-unused-expressions */
|
|
|
|
const _ = require('lodash');
|
|
const BbPromise = require('bluebird');
|
|
const chai = require('chai');
|
|
const proxyquire = require('proxyquire');
|
|
const sinon = require('sinon');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const overrideEnv = require('process-utils/override-env');
|
|
|
|
const AwsProvider = require('./awsProvider');
|
|
const Serverless = require('../../../Serverless');
|
|
const { replaceEnv } = require('../../../../tests/utils/misc');
|
|
const { getTmpFilePath } = require('../../../../tests/utils/fs');
|
|
|
|
chai.use(require('chai-as-promised'));
|
|
chai.use(require('sinon-chai'));
|
|
|
|
const expect = chai.expect;
|
|
|
|
describe('AwsProvider', () => {
|
|
let awsProvider;
|
|
let serverless;
|
|
let restoreEnv;
|
|
const options = {
|
|
stage: 'dev',
|
|
region: 'us-east-1',
|
|
};
|
|
|
|
beforeEach(() => {
|
|
({ restoreEnv } = overrideEnv());
|
|
serverless = new Serverless(options);
|
|
serverless.cli = new serverless.classes.CLI();
|
|
awsProvider = new AwsProvider(serverless, options);
|
|
});
|
|
afterEach(() => restoreEnv());
|
|
|
|
describe('#getProviderName()', () => {
|
|
it('should return the provider name', () => {
|
|
expect(AwsProvider.getProviderName()).to.equal('aws');
|
|
});
|
|
});
|
|
|
|
describe('#constructor()', () => {
|
|
it('should set Serverless instance', () => {
|
|
expect(typeof awsProvider.serverless).to.not.equal('undefined');
|
|
});
|
|
|
|
it('should set AWS instance', () => {
|
|
expect(typeof awsProvider.sdk).to.not.equal('undefined');
|
|
});
|
|
|
|
it('should set the provider property', () => {
|
|
expect(awsProvider.provider).to.equal(awsProvider);
|
|
});
|
|
|
|
it('should have no AWS logger', () => {
|
|
expect(awsProvider.sdk.config.logger).to.be.null;
|
|
});
|
|
|
|
it('should set AWS logger', () => {
|
|
process.env.SLS_DEBUG = 'true';
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(typeof newAwsProvider.sdk.config.logger).to.not.equal('undefined');
|
|
});
|
|
|
|
it('should set AWS proxy', () => {
|
|
process.env.proxy = 'http://a.b.c.d:n';
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(typeof newAwsProvider.sdk.config.httpOptions.agent).to.not.equal('undefined');
|
|
});
|
|
|
|
it('should set AWS timeout', () => {
|
|
process.env.AWS_CLIENT_TIMEOUT = '120000';
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(typeof newAwsProvider.sdk.config.httpOptions.timeout).to.not.equal('undefined');
|
|
});
|
|
|
|
describe('stage name validation', () => {
|
|
const stages = ['myStage', 'my-stage', 'my_stage', "${opt:stage, 'prod'}"];
|
|
stages.forEach(stage => {
|
|
it(`should not throw an error before variable population
|
|
even if http event is present and stage is ${stage}`, () => {
|
|
const config = {
|
|
stage,
|
|
};
|
|
serverless = new Serverless(config);
|
|
|
|
const serverlessYml = {
|
|
service: 'new-service',
|
|
provider: {
|
|
name: 'aws',
|
|
stage,
|
|
},
|
|
functions: {
|
|
first: {
|
|
events: [
|
|
{
|
|
http: {
|
|
path: 'foo',
|
|
method: 'GET',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
serverless.service = new serverless.classes.Service(serverless, serverlessYml);
|
|
expect(() => new AwsProvider(serverless, config)).to.not.throw(Error);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('certificate authority - environment variable', () => {
|
|
it('should set AWS ca single', () => {
|
|
process.env.ca = '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----';
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(typeof newAwsProvider.sdk.config.httpOptions.agent).to.not.equal('undefined');
|
|
});
|
|
|
|
it('should set AWS ca single and proxy', () => {
|
|
process.env.ca = '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----';
|
|
process.env.proxy = 'http://a.b.c.d:n';
|
|
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(typeof newAwsProvider.sdk.config.httpOptions.agent).to.not.equal('undefined');
|
|
});
|
|
|
|
it('should set AWS ca multiple', () => {
|
|
const certContents = '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----';
|
|
process.env.ca = `${certContents},${certContents}`;
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(typeof newAwsProvider.sdk.config.httpOptions.agent).to.not.equal('undefined');
|
|
});
|
|
});
|
|
|
|
describe('certificate authority - file', () => {
|
|
const certContents = '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----';
|
|
const tmpdir = os.tmpdir();
|
|
let file1 = null;
|
|
let file2 = null;
|
|
|
|
beforeEach('Create CA Files and env vars', () => {
|
|
file1 = path.join(tmpdir, 'ca1.txt');
|
|
file2 = path.join(tmpdir, 'ca2.txt');
|
|
fs.writeFileSync(file1, certContents);
|
|
fs.writeFileSync(file2, certContents);
|
|
});
|
|
|
|
afterEach('CA File Cleanup', () => {
|
|
// delete files
|
|
fs.unlinkSync(file1);
|
|
fs.unlinkSync(file2);
|
|
});
|
|
|
|
it('should set AWS cafile single', () => {
|
|
process.env.cafile = file1;
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(typeof newAwsProvider.sdk.config.httpOptions.agent).to.not.equal('undefined');
|
|
});
|
|
|
|
it('should set AWS cafile multiple', () => {
|
|
process.env.cafile = `${file1},${file2}`;
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(typeof newAwsProvider.sdk.config.httpOptions.agent).to.not.equal('undefined');
|
|
});
|
|
|
|
it('should set AWS ca and cafile', () => {
|
|
process.env.ca = certContents;
|
|
process.env.cafile = file1;
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(typeof newAwsProvider.sdk.config.httpOptions.agent).to.not.equal('undefined');
|
|
});
|
|
});
|
|
|
|
describe('deploymentBucket configuration', () => {
|
|
it('should do nothing if not defined', () => {
|
|
serverless.service.provider.deploymentBucket = undefined;
|
|
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(newAwsProvider.serverless.service.provider.deploymentBucket).to.equal(undefined);
|
|
});
|
|
|
|
it('should do nothing if the value is a string', () => {
|
|
serverless.service.provider.deploymentBucket = 'my.deployment.bucket';
|
|
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(newAwsProvider.serverless.service.provider.deploymentBucket).to.equal(
|
|
'my.deployment.bucket'
|
|
);
|
|
});
|
|
|
|
it('should save a given object and use name from it', () => {
|
|
const deploymentBucketObject = {
|
|
name: 'my.deployment.bucket',
|
|
serverSideEncryption: 'AES256',
|
|
};
|
|
serverless.service.provider.deploymentBucket = deploymentBucketObject;
|
|
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(newAwsProvider.serverless.service.provider.deploymentBucket).to.equal(
|
|
'my.deployment.bucket'
|
|
);
|
|
expect(newAwsProvider.serverless.service.provider.deploymentBucketObject).to.deep.equal(
|
|
deploymentBucketObject
|
|
);
|
|
});
|
|
|
|
it('should save a given object and nullify the name if one is not provided', () => {
|
|
const deploymentBucketObject = {
|
|
serverSideEncryption: 'AES256',
|
|
};
|
|
serverless.service.provider.deploymentBucket = deploymentBucketObject;
|
|
|
|
const newAwsProvider = new AwsProvider(serverless, options);
|
|
|
|
expect(newAwsProvider.serverless.service.provider.deploymentBucket).to.equal(null);
|
|
expect(newAwsProvider.serverless.service.provider.deploymentBucketObject).to.deep.equal(
|
|
deploymentBucketObject
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#request()', () => {
|
|
beforeEach(() => {
|
|
const originalSetTimeout = setTimeout;
|
|
sinon
|
|
.stub(global, 'setTimeout')
|
|
.callsFake((cb, timeout) => originalSetTimeout(cb, Math.min(timeout || 0, 10)));
|
|
});
|
|
|
|
afterEach(() => {
|
|
global.setTimeout.restore();
|
|
});
|
|
|
|
it('should call correct aws method', () => {
|
|
// mocking S3 for testing
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
putObject() {
|
|
return {
|
|
send: cb => cb(null, { called: true }),
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider.serverless.service.environment = {
|
|
vars: {},
|
|
stages: {
|
|
dev: {
|
|
vars: {
|
|
profile: 'default',
|
|
},
|
|
regions: {},
|
|
},
|
|
},
|
|
};
|
|
|
|
return awsProvider.request('S3', 'putObject', {}).then(data => {
|
|
expect(data.called).to.equal(true);
|
|
});
|
|
});
|
|
|
|
it('should call correct aws method with a promise', () => {
|
|
// mocking API Gateway for testing
|
|
class FakeAPIGateway {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
getRestApis() {
|
|
return {
|
|
promise: () => BbPromise.resolve({ called: true }),
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
APIGateway: FakeAPIGateway,
|
|
};
|
|
awsProvider.serverless.service.environment = {
|
|
vars: {},
|
|
stages: {
|
|
dev: {
|
|
vars: {
|
|
profile: 'default',
|
|
},
|
|
regions: {},
|
|
},
|
|
},
|
|
};
|
|
|
|
return awsProvider.request('APIGateway', 'getRestApis', {}).then(data => {
|
|
expect(data.called).to.equal(true);
|
|
});
|
|
});
|
|
|
|
it('should request to the specified region if region in options set', () => {
|
|
// mocking S3 for testing
|
|
class FakeCloudForamtion {
|
|
constructor(config) {
|
|
this.config = config;
|
|
}
|
|
|
|
describeStacks() {
|
|
return {
|
|
send: cb =>
|
|
cb(null, {
|
|
region: this.config.region,
|
|
}),
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
CloudFormation: FakeCloudForamtion,
|
|
};
|
|
awsProvider.serverless.service.environment = {
|
|
vars: {},
|
|
stages: {
|
|
dev: {
|
|
vars: {
|
|
profile: 'default',
|
|
},
|
|
regions: {},
|
|
},
|
|
},
|
|
};
|
|
expect(awsProvider.getCredentials().region).to.eql(options.region);
|
|
|
|
return awsProvider
|
|
.request(
|
|
'CloudFormation',
|
|
'describeStacks',
|
|
{ StackName: 'foo' },
|
|
{ region: 'ap-northeast-1' }
|
|
)
|
|
.then(data => {
|
|
expect(data).to.eql({ region: 'ap-northeast-1' });
|
|
// Requesting different region should not affect region in credentials
|
|
expect(awsProvider.getCredentials().region).to.eql(options.region);
|
|
});
|
|
});
|
|
|
|
it('should retry if error code is 429', done => {
|
|
const error = {
|
|
statusCode: 429,
|
|
retryable: true,
|
|
message: 'Testing retry',
|
|
};
|
|
const sendFake = {
|
|
send: sinon.stub(),
|
|
};
|
|
sendFake.send.onFirstCall().yields(error);
|
|
sendFake.send.yields(undefined, {});
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
error() {
|
|
return sendFake;
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider
|
|
.request('S3', 'error', {})
|
|
.then(data => {
|
|
expect(data).to.exist;
|
|
expect(sendFake.send).to.have.been.calledTwice;
|
|
done();
|
|
})
|
|
.catch(done);
|
|
});
|
|
|
|
it('should retry if error code is 429 and retryable is set to false', done => {
|
|
const error = {
|
|
statusCode: 429,
|
|
retryable: false,
|
|
message: 'Testing retry',
|
|
};
|
|
const sendFake = {
|
|
send: sinon.stub(),
|
|
};
|
|
sendFake.send.onFirstCall().yields(error);
|
|
sendFake.send.yields(undefined, {});
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
error() {
|
|
return sendFake;
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider
|
|
.request('S3', 'error', {})
|
|
.then(data => {
|
|
expect(data).to.exist;
|
|
expect(sendFake.send).to.have.been.calledTwice;
|
|
done();
|
|
})
|
|
.catch(done);
|
|
});
|
|
|
|
it('should not retry if error code is 403 and retryable is set to true', done => {
|
|
const error = {
|
|
statusCode: 403,
|
|
retryable: true,
|
|
message: 'Testing retry',
|
|
};
|
|
const sendFake = {
|
|
send: sinon.stub(),
|
|
};
|
|
sendFake.send.onFirstCall().yields(error);
|
|
sendFake.send.yields(undefined, {});
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
error() {
|
|
return sendFake;
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider
|
|
.request('S3', 'error', {})
|
|
.then(() => done('Should not succeed'))
|
|
.catch(() => {
|
|
expect(sendFake.send).to.have.been.calledOnce;
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should reject errors', done => {
|
|
const error = {
|
|
statusCode: 500,
|
|
message: 'Some error message',
|
|
};
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
error() {
|
|
return {
|
|
send(cb) {
|
|
cb(error);
|
|
},
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider
|
|
.request('S3', 'error', {})
|
|
.then(() => done('Should not succeed'))
|
|
.catch(() => done());
|
|
});
|
|
|
|
it('should use error message if it exists', done => {
|
|
const awsErrorResponse = {
|
|
message: 'Something went wrong...',
|
|
code: 'Forbidden',
|
|
region: null,
|
|
time: '2019-01-24T00:29:01.780Z',
|
|
requestId: 'DAF12C1111A62C6',
|
|
extendedRequestId: '1OnSExiLCOsKrsdjjyds31w=',
|
|
statusCode: 403,
|
|
retryable: false,
|
|
retryDelay: 13.433158364430508,
|
|
};
|
|
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
error() {
|
|
return {
|
|
send(cb) {
|
|
cb(awsErrorResponse);
|
|
},
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider
|
|
.request('S3', 'error', {})
|
|
.then(() => done('Should not succeed'))
|
|
.catch(err => {
|
|
expect(err.message).to.eql(awsErrorResponse.message);
|
|
done();
|
|
})
|
|
.catch(done);
|
|
});
|
|
|
|
it('should default to error code if error message is non-existent', done => {
|
|
const awsErrorResponse = {
|
|
message: null,
|
|
code: 'Forbidden',
|
|
region: null,
|
|
time: '2019-01-24T00:29:01.780Z',
|
|
requestId: 'DAF12C1111A62C6',
|
|
extendedRequestId: '1OnSExiLCOsKrsdjjyds31w=',
|
|
statusCode: 403,
|
|
retryable: false,
|
|
retryDelay: 13.433158364430508,
|
|
};
|
|
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
error() {
|
|
return {
|
|
send(cb) {
|
|
cb(awsErrorResponse);
|
|
},
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider
|
|
.request('S3', 'error', {})
|
|
.then(() => done('Should not succeed'))
|
|
.catch(err => {
|
|
expect(err.message).to.eql(awsErrorResponse.code);
|
|
done();
|
|
})
|
|
.catch(done);
|
|
});
|
|
|
|
it('should return ref to docs for missing credentials', done => {
|
|
const error = {
|
|
statusCode: 403,
|
|
message: 'Missing credentials in config',
|
|
};
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
error() {
|
|
return {
|
|
send(cb) {
|
|
cb(error);
|
|
},
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider
|
|
.request('S3', 'error', {})
|
|
.then(() => done('Should not succeed'))
|
|
.catch(err => {
|
|
expect(err.message).to.contain('in our docs here:');
|
|
done();
|
|
})
|
|
.catch(done);
|
|
});
|
|
|
|
it('should not retry for missing credentials', done => {
|
|
const error = {
|
|
statusCode: 403,
|
|
message: 'Missing credentials in config',
|
|
};
|
|
const sendFake = {
|
|
send: sinon.stub().yields(error),
|
|
};
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
error() {
|
|
return sendFake;
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider
|
|
.request('S3', 'error', {})
|
|
.then(() => done('Should not succeed'))
|
|
.catch(err => {
|
|
expect(sendFake.send).to.have.been.calledOnce;
|
|
expect(err.message).to.contain('in our docs here:');
|
|
done();
|
|
})
|
|
.catch(done);
|
|
});
|
|
|
|
it('should enable S3 acceleration if CLI option is provided', () => {
|
|
// mocking S3 for testing
|
|
class FakeS3 {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
putObject() {
|
|
return {
|
|
send: cb => cb(null, { called: true }),
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
S3: FakeS3,
|
|
};
|
|
awsProvider.serverless.service.environment = {
|
|
vars: {},
|
|
stages: {
|
|
dev: {
|
|
vars: {
|
|
profile: 'default',
|
|
},
|
|
regions: {},
|
|
},
|
|
},
|
|
};
|
|
|
|
const enableS3TransferAccelerationStub = sinon
|
|
.stub(awsProvider, 'enableS3TransferAcceleration')
|
|
.resolves();
|
|
|
|
awsProvider.options['aws-s3-accelerate'] = true;
|
|
return awsProvider.request('S3', 'putObject', {}).then(() => {
|
|
expect(enableS3TransferAccelerationStub.calledOnce).to.equal(true);
|
|
});
|
|
});
|
|
|
|
describe('using the request cache', () => {
|
|
it('should call correct aws method', () => {
|
|
// mocking CF for testing
|
|
class FakeCF {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
describeStacks() {
|
|
return {
|
|
send: cb => cb(null, { called: true }),
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
CloudFormation: FakeCF,
|
|
};
|
|
awsProvider.serverless.service.environment = {
|
|
vars: {},
|
|
stages: {
|
|
dev: {
|
|
vars: {
|
|
profile: 'default',
|
|
},
|
|
regions: {},
|
|
},
|
|
},
|
|
};
|
|
|
|
return awsProvider
|
|
.request('CloudFormation', 'describeStacks', {}, { useCache: true })
|
|
.then(data => {
|
|
expect(data.called).to.equal(true);
|
|
});
|
|
});
|
|
|
|
it('should request if same service, method and params but different region in option', () => {
|
|
const expectedResult = { called: true };
|
|
const sendStub = sinon.stub().yields(null, { called: true });
|
|
const requestSpy = sinon.spy(awsProvider, 'request');
|
|
class FakeCF {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
describeStacks() {
|
|
return {
|
|
send: sendStub,
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
CloudFormation: FakeCF,
|
|
};
|
|
const executeRequestWithRegion = region =>
|
|
awsProvider.request(
|
|
'CloudFormation',
|
|
'describeStacks',
|
|
{ StackName: 'same-stack' },
|
|
{
|
|
useCache: true,
|
|
region,
|
|
}
|
|
);
|
|
const requests = [];
|
|
requests.push(BbPromise.try(() => executeRequestWithRegion('us-east-1')));
|
|
requests.push(BbPromise.try(() => executeRequestWithRegion('ap-northeast-1')));
|
|
|
|
return BbPromise.all(requests)
|
|
.then(results => {
|
|
expect(_.size(results, 2));
|
|
_.forEach(results, result => {
|
|
expect(result).to.deep.equal(expectedResult);
|
|
});
|
|
return expect(sendStub.callCount).to.equal(2);
|
|
})
|
|
.finally(() => {
|
|
requestSpy.restore();
|
|
});
|
|
});
|
|
|
|
it('should resolve to the same response with multiple parallel requests', () => {
|
|
const expectedResult = { called: true };
|
|
const sendStub = sinon.stub().yields(null, { called: true });
|
|
const requestSpy = sinon.spy(awsProvider, 'request');
|
|
class FakeCF {
|
|
constructor(credentials) {
|
|
this.credentials = credentials;
|
|
}
|
|
|
|
describeStacks() {
|
|
return {
|
|
send: sendStub,
|
|
};
|
|
}
|
|
}
|
|
awsProvider.sdk = {
|
|
CloudFormation: FakeCF,
|
|
};
|
|
|
|
awsProvider.serverless.service.environment = {
|
|
vars: {},
|
|
stages: {
|
|
dev: {
|
|
vars: {
|
|
profile: 'default',
|
|
},
|
|
regions: {},
|
|
},
|
|
},
|
|
};
|
|
|
|
const numTests = 1000;
|
|
const executeRequest = () =>
|
|
awsProvider.request('CloudFormation', 'describeStacks', {}, { useCache: true });
|
|
const requests = [];
|
|
for (let n = 0; n < numTests; n++) {
|
|
requests.push(BbPromise.try(() => executeRequest()));
|
|
}
|
|
|
|
return BbPromise.all(requests)
|
|
.then(results => {
|
|
expect(_.size(results, numTests));
|
|
_.forEach(results, result => {
|
|
expect(result).to.deep.equal(expectedResult);
|
|
});
|
|
return BbPromise.join(
|
|
expect(sendStub).to.have.been.calledOnce,
|
|
expect(requestSpy).to.have.callCount(numTests)
|
|
);
|
|
})
|
|
.finally(() => {
|
|
requestSpy.restore();
|
|
});
|
|
});
|
|
|
|
describe('STS tokens', () => {
|
|
let newAwsProvider;
|
|
let originalProviderProfile;
|
|
let originalEnvironmentVariables;
|
|
const relevantEnvironment = {
|
|
AWS_SHARED_CREDENTIALS_FILE: getTmpFilePath('credentials'),
|
|
};
|
|
|
|
beforeEach(() => {
|
|
originalProviderProfile = serverless.service.provider.profile;
|
|
originalEnvironmentVariables = replaceEnv(relevantEnvironment);
|
|
serverless.utils.writeFileSync(
|
|
relevantEnvironment.AWS_SHARED_CREDENTIALS_FILE,
|
|
'[default]\n' +
|
|
'aws_access_key_id = 1111\n' +
|
|
'aws_secret_access_key = 22222\n' +
|
|
'\n' +
|
|
'[async]\n' +
|
|
'role_arn = arn:123\n' +
|
|
'source_profile = default'
|
|
);
|
|
newAwsProvider = new AwsProvider(serverless, options);
|
|
});
|
|
|
|
afterEach(() => {
|
|
replaceEnv(originalEnvironmentVariables);
|
|
serverless.service.provider.profile = originalProviderProfile;
|
|
});
|
|
|
|
it('should retain reference to STS tokens when updated via SDK', () => {
|
|
const expectedToken = '123';
|
|
|
|
serverless.service.provider.profile = 'async';
|
|
const startToken = newAwsProvider.getCredentials().credentials.sessionToken;
|
|
expect(startToken).to.not.equal(expectedToken);
|
|
|
|
class FakeCloudFormation {
|
|
constructor(credentials) {
|
|
// Not sure where the the SDK resolves the STS, so for the test it's here
|
|
this.credentials = credentials;
|
|
this.credentials.credentials.sessionToken = expectedToken;
|
|
}
|
|
|
|
describeStacks() {
|
|
return {
|
|
send: cb => cb(null, {}),
|
|
};
|
|
}
|
|
}
|
|
|
|
newAwsProvider.sdk = {
|
|
CloudFormation: FakeCloudFormation,
|
|
};
|
|
|
|
return newAwsProvider
|
|
.request(
|
|
'CloudFormation',
|
|
'describeStacks',
|
|
{ StackName: 'foo' },
|
|
{ region: 'ap-northeast-1' }
|
|
)
|
|
.then(() => {
|
|
// STS token is resolved after SDK call
|
|
const actualToken = newAwsProvider.getCredentials().credentials.sessionToken;
|
|
expect(expectedToken).to.eql(actualToken);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#getCredentials()', () => {
|
|
const awsStub = sinon.stub().returns();
|
|
const AwsProviderProxyquired = proxyquire('./awsProvider.js', {
|
|
'aws-sdk': awsStub,
|
|
});
|
|
|
|
// add environment variables here if you want them cleared prior to your test and restored
|
|
// after it has completed. Any environment variable that might alter credentials loading
|
|
// should be added here
|
|
const relevantEnvironment = {
|
|
AWS_ACCESS_KEY_ID: 'undefined',
|
|
AWS_SECRET_ACCESS_KEY: 'undefined',
|
|
AWS_SESSION_TOKEN: 'undefined',
|
|
AWS_TESTSTAGE_ACCESS_KEY_ID: 'undefined',
|
|
AWS_TESTSTAGE_SECRET_ACCESS_KEY: 'undefined',
|
|
AWS_TESTSTAGE_SESSION_TOKEN: 'undefined',
|
|
AWS_SHARED_CREDENTIALS_FILE: getTmpFilePath('credentials'),
|
|
AWS_PROFILE: 'undefined',
|
|
AWS_TESTSTAGE_PROFILE: 'undefined',
|
|
};
|
|
|
|
let newAwsProvider;
|
|
const newOptions = {
|
|
stage: 'teststage',
|
|
region: 'testregion',
|
|
};
|
|
const fakeCredentials = {
|
|
accessKeyId: 'AABBCCDDEEFF',
|
|
secretAccessKey: '0123456789876543',
|
|
sessionToken: '981237917391273918273918723987129837129873',
|
|
roleArn: 'a:role:arn',
|
|
sourceProfile: 'notDefaultTemporary',
|
|
};
|
|
|
|
let originalProviderCredentials;
|
|
let originalProviderProfile;
|
|
let originalEnvironmentVariables;
|
|
|
|
beforeEach(() => {
|
|
originalProviderCredentials = serverless.service.provider.credentials;
|
|
originalProviderProfile = serverless.service.provider.profile;
|
|
originalEnvironmentVariables = replaceEnv(relevantEnvironment);
|
|
// make temporary credentials file
|
|
serverless.utils.writeFileSync(
|
|
relevantEnvironment.AWS_SHARED_CREDENTIALS_FILE,
|
|
'[notDefault]\n' +
|
|
`aws_access_key_id = ${fakeCredentials.accessKeyId}\n` +
|
|
`aws_secret_access_key = ${fakeCredentials.secretAccessKey}\n` +
|
|
'\n' +
|
|
'[notDefaultTemporary]\n' +
|
|
`aws_access_key_id = ${fakeCredentials.accessKeyId}\n` +
|
|
`aws_secret_access_key = ${fakeCredentials.secretAccessKey}\n` +
|
|
`aws_session_token = ${fakeCredentials.sessionToken}\n` +
|
|
'\n' +
|
|
'[notDefaultAsync]\n' +
|
|
`role_arn = ${fakeCredentials.roleArn}\n` +
|
|
`source_profile = ${fakeCredentials.sourceProfile}\n`
|
|
);
|
|
newAwsProvider = new AwsProviderProxyquired(serverless, newOptions);
|
|
});
|
|
|
|
afterEach(() => {
|
|
replaceEnv(originalEnvironmentVariables);
|
|
serverless.service.provider.profile = originalProviderProfile;
|
|
serverless.service.provider.credentials = originalProviderCredentials;
|
|
});
|
|
|
|
it('should set region for credentials', () => {
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.region).to.equal(newOptions.region);
|
|
});
|
|
|
|
it('should not set credentials if credentials is an empty object', () => {
|
|
serverless.service.provider.credentials = {};
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials).to.eql({ region: newOptions.region });
|
|
});
|
|
|
|
it('should not set credentials if credentials has undefined values', () => {
|
|
serverless.service.provider.credentials = {
|
|
accessKeyId: undefined,
|
|
secretAccessKey: undefined,
|
|
sessionToken: undefined,
|
|
};
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials).to.eql({ region: newOptions.region });
|
|
});
|
|
|
|
it('should not set credentials if credentials has empty string values', () => {
|
|
serverless.service.provider.credentials = {
|
|
accessKeyId: '',
|
|
secretAccessKey: '',
|
|
sessionToken: '',
|
|
};
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials).to.eql({ region: newOptions.region });
|
|
});
|
|
|
|
it('should get credentials from provider declared credentials', () => {
|
|
serverless.service.provider.credentials = {
|
|
accessKeyId: 'accessKeyId',
|
|
secretAccessKey: 'secretAccessKey',
|
|
sessionToken: 'sessionToken',
|
|
};
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials).to.deep.eql(serverless.service.provider.credentials);
|
|
});
|
|
|
|
it('should load profile credentials from AWS_SHARED_CREDENTIALS_FILE', () => {
|
|
serverless.service.provider.profile = 'notDefault';
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.profile).to.equal(serverless.service.provider.profile);
|
|
expect(credentials.credentials.accessKeyId).to.equal(fakeCredentials.accessKeyId);
|
|
expect(credentials.credentials.secretAccessKey).to.equal(fakeCredentials.secretAccessKey);
|
|
expect(credentials.credentials.sessionToken).to.equal(undefined);
|
|
});
|
|
|
|
it('should load async profiles properly', () => {
|
|
serverless.service.provider.profile = 'notDefaultAsync';
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.roleArn).to.equal(fakeCredentials.roleArn);
|
|
});
|
|
|
|
it('should throw an error if a non-existent profile is set', () => {
|
|
serverless.service.provider.profile = 'not-a-defined-profile';
|
|
expect(() => {
|
|
newAwsProvider.getCredentials();
|
|
}).to.throw(Error);
|
|
});
|
|
|
|
it('should not set credentials if empty profile is set', () => {
|
|
serverless.service.provider.profile = '';
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials).to.eql({ region: newOptions.region });
|
|
});
|
|
|
|
it('should not set credentials if profile is not set', () => {
|
|
serverless.service.provider.profile = undefined;
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials).to.eql({ region: newOptions.region });
|
|
});
|
|
|
|
it('should not set credentials if empty profile is set', () => {
|
|
serverless.service.provider.profile = '';
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials).to.eql({ region: newOptions.region });
|
|
});
|
|
|
|
it('should get credentials from provider declared temporary profile', () => {
|
|
serverless.service.provider.profile = 'notDefaultTemporary';
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.profile).to.equal(serverless.service.provider.profile);
|
|
expect(credentials.credentials.accessKeyId).to.equal(fakeCredentials.accessKeyId);
|
|
expect(credentials.credentials.secretAccessKey).to.equal(fakeCredentials.secretAccessKey);
|
|
expect(credentials.credentials.sessionToken).to.equal(fakeCredentials.sessionToken);
|
|
});
|
|
|
|
it('should get credentials from environment declared for-all-stages credentials', () => {
|
|
const testVal = {
|
|
accessKeyId: 'accessKeyId',
|
|
secretAccessKey: 'secretAccessKey',
|
|
sessionToken: 'sessionToken',
|
|
};
|
|
process.env.AWS_ACCESS_KEY_ID = testVal.accessKeyId;
|
|
process.env.AWS_SECRET_ACCESS_KEY = testVal.secretAccessKey;
|
|
process.env.AWS_SESSION_TOKEN = testVal.sessionToken;
|
|
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.accessKeyId).to.equal(testVal.accessKeyId);
|
|
expect(credentials.credentials.secretAccessKey).to.equal(testVal.secretAccessKey);
|
|
expect(credentials.credentials.sessionToken).to.equal(testVal.sessionToken);
|
|
});
|
|
|
|
it('should get credentials from environment declared stage specific credentials', () => {
|
|
const testVal = {
|
|
accessKeyId: 'accessKeyId',
|
|
secretAccessKey: 'secretAccessKey',
|
|
sessionToken: 'sessionToken',
|
|
};
|
|
process.env.AWS_TESTSTAGE_ACCESS_KEY_ID = testVal.accessKeyId;
|
|
process.env.AWS_TESTSTAGE_SECRET_ACCESS_KEY = testVal.secretAccessKey;
|
|
process.env.AWS_TESTSTAGE_SESSION_TOKEN = testVal.sessionToken;
|
|
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.accessKeyId).to.equal(testVal.accessKeyId);
|
|
expect(credentials.credentials.secretAccessKey).to.equal(testVal.secretAccessKey);
|
|
expect(credentials.credentials.sessionToken).to.equal(testVal.sessionToken);
|
|
});
|
|
|
|
it('should get credentials from environment declared for-all-stages profile', () => {
|
|
process.env.AWS_PROFILE = 'notDefault';
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.profile).to.equal('notDefault');
|
|
});
|
|
|
|
it('should get credentials from environment declared stage-specific profile', () => {
|
|
process.env.AWS_TESTSTAGE_PROFILE = 'notDefault';
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.profile).to.equal('notDefault');
|
|
});
|
|
|
|
it('should get credentials when profile is provied via --aws-profile option', () => {
|
|
process.env.AWS_PROFILE = 'notDefaultTemporary';
|
|
newAwsProvider.options['aws-profile'] = 'notDefault';
|
|
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.profile).to.equal('notDefault');
|
|
});
|
|
|
|
it('should get credentials when profile is provied via --aws-profile option even if profile is defined in serverless.yml', () => {
|
|
// eslint-disable-line max-len
|
|
process.env.AWS_PROFILE = 'notDefaultTemporary';
|
|
newAwsProvider.options['aws-profile'] = 'notDefault';
|
|
|
|
serverless.service.provider.profile = 'notDefaultTemporary2';
|
|
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.profile).to.equal('notDefault');
|
|
});
|
|
|
|
it('should get credentials when profile is provied via process.env.AWS_PROFILE even if profile is defined in serverless.yml', () => {
|
|
// eslint-disable-line max-len
|
|
process.env.AWS_PROFILE = 'notDefault';
|
|
|
|
serverless.service.provider.profile = 'notDefaultTemporary';
|
|
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.credentials.profile).to.equal('notDefault');
|
|
});
|
|
|
|
it('should set the signatureVersion to v4 if the serverSideEncryption is aws:kms', () => {
|
|
newAwsProvider.serverless.service.provider.deploymentBucketObject = {
|
|
serverSideEncryption: 'aws:kms',
|
|
};
|
|
|
|
const credentials = newAwsProvider.getCredentials();
|
|
expect(credentials.signatureVersion).to.equal('v4');
|
|
});
|
|
});
|
|
|
|
describe('values', () => {
|
|
const obj = {
|
|
a: 'b',
|
|
c: {
|
|
d: 'e',
|
|
f: {
|
|
g: 'h',
|
|
},
|
|
},
|
|
};
|
|
const paths = [['a'], ['c', 'd'], ['c', 'f', 'g']];
|
|
const getExpected = [
|
|
{ path: paths[0], value: obj.a },
|
|
{ path: paths[1], value: obj.c.d },
|
|
{ path: paths[2], value: obj.c.f.g },
|
|
];
|
|
describe('#getValues', () => {
|
|
it('should return an array of values given paths to them', () => {
|
|
expect(awsProvider.getValues(obj, paths)).to.eql(getExpected);
|
|
});
|
|
});
|
|
describe('#firstValue', () => {
|
|
it("should ignore entries without a 'value' attribute", () => {
|
|
const input = _.cloneDeep(getExpected);
|
|
delete input[0].value;
|
|
delete input[2].value;
|
|
expect(awsProvider.firstValue(input)).to.eql(getExpected[1]);
|
|
});
|
|
it("should ignore entries with an undefined 'value' attribute", () => {
|
|
const input = _.cloneDeep(getExpected);
|
|
input[0].value = undefined;
|
|
input[2].value = undefined;
|
|
expect(awsProvider.firstValue(input)).to.eql(getExpected[1]);
|
|
});
|
|
it('should return the first value', () => {
|
|
expect(awsProvider.firstValue(getExpected)).to.equal(getExpected[0]);
|
|
});
|
|
it('should return the middle value', () => {
|
|
const input = _.cloneDeep(getExpected);
|
|
delete input[0].value;
|
|
delete input[2].value;
|
|
expect(awsProvider.firstValue(input)).to.equal(input[1]);
|
|
});
|
|
it('should return the last value', () => {
|
|
const input = _.cloneDeep(getExpected);
|
|
delete input[0].value;
|
|
delete input[1].value;
|
|
expect(awsProvider.firstValue(input)).to.equal(input[2]);
|
|
});
|
|
it('should return the last object if none have valid values', () => {
|
|
const input = _.cloneDeep(getExpected);
|
|
delete input[0].value;
|
|
delete input[1].value;
|
|
delete input[2].value;
|
|
expect(awsProvider.firstValue(input)).to.equal(input[2]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#getRegion()', () => {
|
|
let newAwsProvider;
|
|
|
|
it('should prefer options over config or provider', () => {
|
|
const newOptions = {
|
|
region: 'optionsRegion',
|
|
};
|
|
const config = {
|
|
region: 'configRegion',
|
|
};
|
|
serverless = new Serverless(config);
|
|
serverless.service.provider.region = 'providerRegion';
|
|
newAwsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(newAwsProvider.getRegion()).to.equal(newOptions.region);
|
|
});
|
|
|
|
it('should prefer config over provider in lieu of options', () => {
|
|
const newOptions = {};
|
|
const config = {
|
|
region: 'configRegion',
|
|
};
|
|
serverless = new Serverless(config);
|
|
serverless.service.provider.region = 'providerRegion';
|
|
newAwsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(newAwsProvider.getRegion()).to.equal(config.region);
|
|
});
|
|
|
|
it('should use provider in lieu of options and config', () => {
|
|
const newOptions = {};
|
|
const config = {};
|
|
serverless = new Serverless(config);
|
|
serverless.service.provider.region = 'providerRegion';
|
|
newAwsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(newAwsProvider.getRegion()).to.equal(serverless.service.provider.region);
|
|
});
|
|
|
|
it('should use the default us-east-1 in lieu of options, config, and provider', () => {
|
|
const newOptions = {};
|
|
const config = {};
|
|
serverless = new Serverless(config);
|
|
newAwsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(newAwsProvider.getRegion()).to.equal('us-east-1');
|
|
});
|
|
});
|
|
|
|
describe('#getProfile()', () => {
|
|
let newAwsProvider;
|
|
|
|
it('should prefer options over config or provider', () => {
|
|
const newOptions = {
|
|
profile: 'optionsProfile',
|
|
};
|
|
const config = {
|
|
profile: 'configProfile',
|
|
};
|
|
serverless = new Serverless(config);
|
|
serverless.service.provider.profile = 'providerProfile';
|
|
newAwsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(newAwsProvider.getProfile()).to.equal(newOptions.profile);
|
|
});
|
|
|
|
it('should prefer config over provider in lieu of options', () => {
|
|
const newOptions = {};
|
|
const config = {
|
|
profile: 'configProfile',
|
|
};
|
|
serverless = new Serverless(config);
|
|
serverless.service.provider.profile = 'providerProfile';
|
|
newAwsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(newAwsProvider.getProfile()).to.equal(config.profile);
|
|
});
|
|
|
|
it('should use provider in lieu of options and config', () => {
|
|
const newOptions = {};
|
|
const config = {};
|
|
serverless = new Serverless(config);
|
|
serverless.service.provider.profile = 'providerProfile';
|
|
newAwsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(newAwsProvider.getProfile()).to.equal(serverless.service.provider.profile);
|
|
});
|
|
});
|
|
|
|
describe('#getServerlessDeploymentBucketName()', () => {
|
|
it('should return the name of the serverless deployment bucket', () => {
|
|
const describeStackResourcesStub = sinon.stub(awsProvider, 'request').resolves({
|
|
StackResourceDetail: {
|
|
PhysicalResourceId: 'serverlessDeploymentBucketName',
|
|
},
|
|
});
|
|
|
|
return awsProvider.getServerlessDeploymentBucketName().then(bucketName => {
|
|
expect(bucketName).to.equal('serverlessDeploymentBucketName');
|
|
expect(describeStackResourcesStub.calledOnce).to.be.equal(true);
|
|
expect(
|
|
describeStackResourcesStub.calledWithExactly('CloudFormation', 'describeStackResource', {
|
|
StackName: awsProvider.naming.getStackName(),
|
|
LogicalResourceId: awsProvider.naming.getDeploymentBucketLogicalId(),
|
|
})
|
|
).to.be.equal(true);
|
|
awsProvider.request.restore();
|
|
});
|
|
});
|
|
|
|
it('should return the name of the custom deployment bucket', () => {
|
|
awsProvider.serverless.service.provider.deploymentBucket = 'custom-bucket';
|
|
|
|
const describeStackResourcesStub = sinon.stub(awsProvider, 'request').resolves({
|
|
StackResourceDetail: {
|
|
PhysicalResourceId: 'serverlessDeploymentBucketName',
|
|
},
|
|
});
|
|
|
|
return awsProvider.getServerlessDeploymentBucketName().then(bucketName => {
|
|
expect(describeStackResourcesStub.called).to.be.equal(false);
|
|
expect(bucketName).to.equal('custom-bucket');
|
|
awsProvider.request.restore();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#getDeploymentPrefix()', () => {
|
|
it('should return custom deployment prefix if defined', () => {
|
|
serverless.service.provider.deploymentPrefix = 'providerPrefix';
|
|
|
|
expect(awsProvider.getDeploymentPrefix()).to.equal(
|
|
serverless.service.provider.deploymentPrefix
|
|
);
|
|
});
|
|
|
|
it('should use the default serverless if not defined', () => {
|
|
serverless.service.provider.deploymentPrefix = undefined;
|
|
|
|
expect(awsProvider.getDeploymentPrefix()).to.equal('serverless');
|
|
});
|
|
|
|
it('should support no prefix', () => {
|
|
serverless.service.provider.deploymentPrefix = '';
|
|
|
|
expect(awsProvider.getDeploymentPrefix()).to.equal('');
|
|
});
|
|
});
|
|
|
|
describe('#getStage()', () => {
|
|
it('should prefer options over config or provider', () => {
|
|
const newOptions = {
|
|
stage: 'optionsStage',
|
|
};
|
|
const config = {
|
|
stage: 'configStage',
|
|
};
|
|
serverless = new Serverless(config);
|
|
serverless.service.provider.stage = 'providerStage';
|
|
awsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(awsProvider.getStage()).to.equal(newOptions.stage);
|
|
});
|
|
|
|
it('should prefer config over provider in lieu of options', () => {
|
|
const newOptions = {};
|
|
const config = {
|
|
stage: 'configStage',
|
|
};
|
|
serverless = new Serverless(config);
|
|
serverless.service.provider.stage = 'providerStage';
|
|
awsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(awsProvider.getStage()).to.equal(config.stage);
|
|
});
|
|
|
|
it('should use provider in lieu of options and config', () => {
|
|
const newOptions = {};
|
|
const config = {};
|
|
serverless = new Serverless(config);
|
|
serverless.service.provider.stage = 'providerStage';
|
|
awsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(awsProvider.getStage()).to.equal(serverless.service.provider.stage);
|
|
});
|
|
|
|
it('should use the default dev in lieu of options, config, and provider', () => {
|
|
const newOptions = {};
|
|
const config = {};
|
|
serverless = new Serverless(config);
|
|
awsProvider = new AwsProvider(serverless, newOptions);
|
|
|
|
expect(awsProvider.getStage()).to.equal('dev');
|
|
});
|
|
});
|
|
|
|
describe('#getAccountInfo()', () => {
|
|
it('should return the AWS account id and partition', () => {
|
|
const accountId = '12345678';
|
|
const partition = 'aws';
|
|
|
|
const stsGetCallerIdentityStub = sinon.stub(awsProvider, 'request').resolves({
|
|
ResponseMetadata: { RequestId: '12345678-1234-1234-1234-123456789012' },
|
|
UserId: 'ABCDEFGHIJKLMNOPQRSTU:VWXYZ',
|
|
Account: accountId,
|
|
Arn: 'arn:aws:sts::123456789012:assumed-role/ROLE-NAME/VWXYZ',
|
|
});
|
|
|
|
return awsProvider.getAccountInfo().then(result => {
|
|
expect(stsGetCallerIdentityStub.calledOnce).to.equal(true);
|
|
expect(result.accountId).to.equal(accountId);
|
|
expect(result.partition).to.equal(partition);
|
|
awsProvider.request.restore();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#getAccountId()', () => {
|
|
it('should return the AWS account id', () => {
|
|
const accountId = '12345678';
|
|
|
|
const stsGetCallerIdentityStub = sinon.stub(awsProvider, 'request').resolves({
|
|
ResponseMetadata: { RequestId: '12345678-1234-1234-1234-123456789012' },
|
|
UserId: 'ABCDEFGHIJKLMNOPQRSTU:VWXYZ',
|
|
Account: accountId,
|
|
Arn: 'arn:aws:sts::123456789012:assumed-role/ROLE-NAME/VWXYZ',
|
|
});
|
|
|
|
return awsProvider.getAccountId().then(result => {
|
|
expect(stsGetCallerIdentityStub.calledOnce).to.equal(true);
|
|
expect(result).to.equal(accountId);
|
|
awsProvider.request.restore();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#isS3TransferAccelerationEnabled()', () => {
|
|
it('should return false by default', () => {
|
|
awsProvider.options['aws-s3-accelerate'] = undefined;
|
|
return expect(awsProvider.isS3TransferAccelerationEnabled()).to.equal(false);
|
|
});
|
|
it('should return true when CLI option is provided', () => {
|
|
awsProvider.options['aws-s3-accelerate'] = true;
|
|
return expect(awsProvider.isS3TransferAccelerationEnabled()).to.equal(true);
|
|
});
|
|
});
|
|
|
|
describe('#canUseS3TransferAcceleration()', () => {
|
|
it('should return false by default with any input', () => {
|
|
awsProvider.options['aws-s3-accelerate'] = undefined;
|
|
return expect(
|
|
awsProvider.canUseS3TransferAcceleration('lambda', 'updateFunctionCode')
|
|
).to.equal(false);
|
|
});
|
|
it('should return false by default with S3.upload too', () => {
|
|
awsProvider.options['aws-s3-accelerate'] = undefined;
|
|
return expect(awsProvider.canUseS3TransferAcceleration('S3', 'upload')).to.equal(false);
|
|
});
|
|
it('should return false by default with S3.putObject too', () => {
|
|
awsProvider.options['aws-s3-accelerate'] = undefined;
|
|
return expect(awsProvider.canUseS3TransferAcceleration('S3', 'putObject')).to.equal(false);
|
|
});
|
|
it('should return false when CLI option is provided but not an S3 upload', () => {
|
|
awsProvider.options['aws-s3-accelerate'] = true;
|
|
return expect(
|
|
awsProvider.canUseS3TransferAcceleration('lambda', 'updateFunctionCode')
|
|
).to.equal(false);
|
|
});
|
|
it('should return true when CLI option is provided for S3.upload', () => {
|
|
awsProvider.options['aws-s3-accelerate'] = true;
|
|
return expect(awsProvider.canUseS3TransferAcceleration('S3', 'upload')).to.equal(true);
|
|
});
|
|
it('should return true when CLI option is provided for S3.putObject', () => {
|
|
awsProvider.options['aws-s3-accelerate'] = true;
|
|
return expect(awsProvider.canUseS3TransferAcceleration('S3', 'putObject')).to.equal(true);
|
|
});
|
|
});
|
|
|
|
describe('#enableS3TransferAcceleration()', () => {
|
|
it('should update the given credentials object to enable S3 acceleration', () => {
|
|
const credentials = {};
|
|
awsProvider.enableS3TransferAcceleration(credentials);
|
|
return expect(credentials.useAccelerateEndpoint).to.equal(true);
|
|
});
|
|
});
|
|
|
|
describe('#disableTransferAccelerationForCurrentDeploy()', () => {
|
|
it('should remove the corresponding option for the current deploy', () => {
|
|
awsProvider.options['aws-s3-accelerate'] = true;
|
|
awsProvider.disableTransferAccelerationForCurrentDeploy();
|
|
return expect(awsProvider.options['aws-s3-accelerate']).to.be.undefined;
|
|
});
|
|
});
|
|
});
|