'use strict'; const expect = require('chai').expect; const sinon = require('sinon'); const Serverless = require('../../../../../lib/serverless'); const AwsProvider = require('../../../../../lib/plugins/aws/provider'); const CLI = require('../../../../../lib/classes/cli'); const proxyquire = require('proxyquire'); describe('AwsRollbackFunction', () => { let serverless; let awsRollbackFunction; let fetchStub; let AwsRollbackFunction; beforeEach(() => { fetchStub = sinon.stub().resolves({ buffer: () => {} }); AwsRollbackFunction = proxyquire('../../../../../lib/plugins/aws/rollback-function.js', { 'node-fetch': fetchStub, }); serverless = new Serverless({ commands: [], options: {} }); serverless.service.functions = { hello: { handler: true, name: 'service-dev-hello', }, }; const options = { stage: 'dev', region: 'us-east-1', function: 'hello', }; serverless.setProvider('aws', new AwsProvider(serverless, options)); serverless.cli = new CLI(serverless); awsRollbackFunction = new AwsRollbackFunction(serverless, options); }); describe('#constructor()', () => { let validateStub; let getFunctionToBeRestoredStub; let fetchFunctionCodeStub; let restoreFunctionStub; beforeEach(() => { validateStub = sinon.stub(awsRollbackFunction, 'validate').resolves(); getFunctionToBeRestoredStub = sinon .stub(awsRollbackFunction, 'getFunctionToBeRestored') .resolves(); fetchFunctionCodeStub = sinon.stub(awsRollbackFunction, 'fetchFunctionCode').resolves(); restoreFunctionStub = sinon.stub(awsRollbackFunction, 'restoreFunction').resolves(); }); afterEach(() => { awsRollbackFunction.validate.restore(); awsRollbackFunction.getFunctionToBeRestored.restore(); awsRollbackFunction.fetchFunctionCode.restore(); awsRollbackFunction.restoreFunction.restore(); }); it('should have hooks', () => expect(awsRollbackFunction.hooks).to.be.not.empty); it('should set the provider variable to an instance of AwsProvider', () => expect(awsRollbackFunction.provider).to.be.instanceof(AwsProvider)); it('should set an empty options object if no options are given', () => { const awsRollbackFunctionWithEmptyOptions = new AwsRollbackFunction(serverless); expect(awsRollbackFunctionWithEmptyOptions.options).to.deep.equal({}); }); it('should run promise chain in order', () => awsRollbackFunction.hooks['rollback:function:rollback']().then(() => { expect(validateStub.calledOnce).to.equal(true); expect(getFunctionToBeRestoredStub.calledAfter(validateStub)).to.equal(true); expect(fetchFunctionCodeStub.calledAfter(getFunctionToBeRestoredStub)).to.equal(true); expect(restoreFunctionStub.calledAfter(fetchFunctionCodeStub)).to.equal(true); })); }); describe('#getFunctionToBeRestored()', () => { describe('when function and version can be found', () => { let getFunctionStub; beforeEach(() => { getFunctionStub = sinon .stub(awsRollbackFunction.provider, 'request') .resolves({ function: 'hello' }); }); afterEach(() => { awsRollbackFunction.provider.request.restore(); }); it('should return the requested function', () => { awsRollbackFunction.options.function = 'hello'; awsRollbackFunction.options['function-version'] = '4711'; return awsRollbackFunction.getFunctionToBeRestored().then((result) => { expect(getFunctionStub.calledOnce).to.equal(true); expect( getFunctionStub.calledWithExactly('Lambda', 'getFunction', { FunctionName: 'service-dev-hello', Qualifier: '4711', }) ).to.equal(true); expect(result).to.deep.equal({ function: 'hello' }); }); }); }); describe('when function or version could not be found', () => { let getFunctionStub; beforeEach(() => { getFunctionStub = sinon .stub(awsRollbackFunction.provider, 'request') .rejects(new Error('function hello not found')); }); afterEach(() => { awsRollbackFunction.provider.request.restore(); }); it('should translate the error message to a custom error message', () => { awsRollbackFunction.options.function = 'hello'; awsRollbackFunction.options['function-version'] = '4711'; return awsRollbackFunction.getFunctionToBeRestored().catch((error) => { expect(error.message.match(/Function "hello" with version "4711" not found/)); expect(getFunctionStub.calledOnce).to.equal(true); expect( getFunctionStub.calledWithExactly('Lambda', 'getFunction', { FunctionName: 'service-dev-hello', Qualifier: '4711', }) ).to.equal(true); }); }); }); describe('when other error occurred', () => { let getFunctionStub; beforeEach(() => { getFunctionStub = sinon .stub(awsRollbackFunction.provider, 'request') .rejects(new Error('something went wrong')); }); afterEach(() => { awsRollbackFunction.provider.request.restore(); }); it('should re-throw the error without translating it to a custom error message', () => { awsRollbackFunction.options.function = 'hello'; awsRollbackFunction.options['function-version'] = '4711'; return awsRollbackFunction.getFunctionToBeRestored().catch((error) => { expect(error.message.match(/something went wrong/)); expect(getFunctionStub.calledOnce).to.equal(true); expect( getFunctionStub.calledWithExactly('Lambda', 'getFunction', { FunctionName: 'service-dev-hello', Qualifier: '4711', }) ).to.equal(true); }); }); }); }); describe('#fetchFunctionCode()', () => { it('should fetch the zip file content of the previously requested function', () => { const func = { Code: { Location: 'https://foo.com/bar', }, }; return awsRollbackFunction.fetchFunctionCode(func).then(() => { expect(fetchStub.called).to.equal(true); }); }); }); describe('#restoreFunction()', () => { let updateFunctionCodeStub; beforeEach(() => { updateFunctionCodeStub = sinon.stub(awsRollbackFunction.provider, 'request').resolves(); }); afterEach(() => { awsRollbackFunction.provider.request.restore(); }); it('should restore the provided function', () => { awsRollbackFunction.options.function = 'hello'; const zipBuffer = Buffer.from(''); return awsRollbackFunction.restoreFunction(zipBuffer).then(() => { expect(updateFunctionCodeStub.calledOnce).to.equal(true); expect( updateFunctionCodeStub.calledWithExactly('Lambda', 'updateFunctionCode', { FunctionName: 'service-dev-hello', ZipFile: zipBuffer, }) ).to.equal(true); }); }); }); });