'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', async () => 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', async () => { 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', async () => { 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', async () => { 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) }) }) }) })