hapiest-deploy
Version:
A deployment tool. Initially for AWS ElasticBeanstalk but it may expand in scope over time.
321 lines (272 loc) • 17.3 kB
JavaScript
;
const Should = require('should');
const Promise = require('bluebird');
const Path = require('path');
const Sinon = require('sinon');
const Chance = require('chance');
const chance = Chance();
const DeployServiceFactory = require('../../lib/deployServiceFactory');
const DeployService = require('../../lib/deployService');
const logger = require('../helper/logger');
/** @type {DeployServiceFactoryFolders} */
const folders = {
config: Path.resolve(__dirname, '../helper/unit-config'),
apps: Path.resolve(__dirname, '../helper/unit-apps'),
gitRoot: Path.resolve(__dirname, '../helper/unit-repo')
};
describe('DeployService', function() {
describe('deployFromCommandLineArguments', function() {
it('Should throw an error because -a / --application required', function() {
const service = DeployServiceFactory.create(folders, logger);
const deployStub = Sinon.stub(service, 'deploy', (deployRequest) => new Promise(resolve => resolve()));
const argv = ['node', 'someFile.js'];
var error;
return service.deployFromCommandLineArguments(argv)
.catch(err => error = err)
.then(() => {
Should.exist(error);
error.message.should.eql(`Invalid argv - option -a / --application required`);
deployStub.callCount.should.eql(0);
})
});
it('Should throw an error because -e / --environment required', function() {
const service = DeployServiceFactory.create(folders, logger);
const deployStub = Sinon.stub(service, 'deploy', (deployRequest) => new Promise(resolve => resolve()));
const argv = ['node', 'someFile.js', '-a', 'myapp'];
var error;
return service.deployFromCommandLineArguments(argv)
.catch(err => error = err)
.then(() => {
Should.exist(error);
error.message.should.eql(`Invalid argv - option -e / --environment required`);
deployStub.callCount.should.eql(0);
})
});
it('Should throw an error because commit hash is not valid', function() {
const service = DeployServiceFactory.create(folders, logger);
const deployStub = Sinon.stub(service, 'deploy', (deployRequest) => new Promise(resolve => resolve()));
const argv = ['node', 'someFile.js', '--application=myapp', '--environment=production', '-c', 'shortinvalidhash'];
var error;
return service.deployFromCommandLineArguments(argv)
.catch(err => error = err)
.then(() => {
Should.exist(error);
error.message.should.eql(`Invalid argv - option -c / --commit-hash must be a full length git hash of 40 characters`);
deployStub.callCount.should.eql(0);
})
});
it('Should successfully deploy with correct appName = web and envName = production', function() {
const service = DeployServiceFactory.create(folders, logger);
const originalGetDeployExecutionService = service._getDeployExecutionService.bind(service);
let deployExecService;
const getDeployExecutionServiceStub = Sinon.stub(service, '_getDeployExecutionService', (deployRequest) => {
deployExecService = originalGetDeployExecutionService(deployRequest);
deployExecService.deploy = Sinon.stub().returns(Promise.resolve());
deployExecService.getCommitHash = Sinon.stub().returns(Promise.resolve('asdfjklasdfkjlkjknjlvjasnjlnfldnlaseaseh'));
return deployExecService;
});
const argv = ['node', 'someFile.js', '-a', 'web', '-e', 'production'];
var error;
return service.deployFromCommandLineArguments(argv)
.catch(err => error = err)
.then(() => {
Should.not.exist(error);
Should.exist(deployExecService);
deployExecService.deploy.calledOnce.should.be.True();
deployExecService.info.appName.should.eql('web');
deployExecService.info.envName.should.eql('production');
})
;
});
it('Should successfully deploy with correct appName = myapp, envName = production, and commitHash = ab5e9e3a4959bc91adfa3028b09226e47331504d', function() {
const service = DeployServiceFactory.create(folders, logger);
const originalGetDeployExecutionService = service._getDeployExecutionService.bind(service);
let deployExecService;
const getDeployExecutionServiceStub = Sinon.stub(service, '_getDeployExecutionService', (deployRequest) => {
deployExecService = originalGetDeployExecutionService(deployRequest);
deployExecService.deploy = Sinon.stub().returns(Promise.resolve());
return deployExecService;
});
const argv = ['node', 'someFile.js', '--application=web-with-tag', '-e', 'production', '--commit-hash=ab5e9e3a4959bc91adfa3028b09226e47331504d'];
var error;
return service.deployFromCommandLineArguments(argv)
.catch(err => error = err)
.then(() => {
Should.not.exist(error);
Should.exist(deployExecService);
deployExecService.deploy.calledOnce.should.be.True();
deployExecService.info.appName.should.eql('web-with-tag');
deployExecService.info.envName.should.eql('production');
deployExecService.info.commitHash.should.eql('ab5e9e3a4959bc91adfa3028b09226e47331504d');
})
});
it('Should successfully deploy with correct appName = web, envName = production, commitHash = ab5e9e3a4959bc91adfa3028b09226e47331504d, and runPreHook = true', function() {
const preHookStub = Sinon.stub().returns(Promise.resolve());
const service = DeployServiceFactory.create(folders, logger, preHookStub);
const originalGetDeployExecutionService = service._getDeployExecutionService.bind(service);
const deployStub = Sinon.stub(service, 'deploy').returns(Promise.resolve());
let deployExecService;
const getDeployExecutionServiceStub = Sinon.stub(service, '_getDeployExecutionService', (deployRequest) => {
deployExecService = originalGetDeployExecutionService(deployRequest);
deployExecService.deploy = Sinon.stub().returns(Promise.resolve());
return deployExecService;
});
const argv = ['node', 'someFile.js', '--application=web', '-e', 'staging', '--commit-hash=ab5e9e3a4959bc91adfa3028b09226e47331504d', '-p'];
var error;
return service.deployFromCommandLineArguments(argv)
.catch(err => error = err)
.then(() => {
Should.not.exist(error);
preHookStub.calledOnce.should.be.True();
Should.exist(deployExecService);
deployExecService.deploy.calledOnce.should.be.True();
deployExecService.info.appName.should.eql('web');
deployExecService.info.envName.should.eql('staging');
deployExecService.info.commitHash.should.eql('ab5e9e3a4959bc91adfa3028b09226e47331504d');
})
});
it('Should successfully deploy with correct appName = web, envName = production, commitHash = ab5e9e3a4959bc91adfa3028b09226e47331504d, and runPreHook = true (--run-pre-hook)', function() {
const preHookStub = Sinon.stub().returns(Promise.resolve());
const service = DeployServiceFactory.create(folders, logger, preHookStub);
const originalGetDeployExecutionService = service._getDeployExecutionService.bind(service);
let deployExecService;
const getDeployExecutionServiceStub = Sinon.stub(service, '_getDeployExecutionService', (deployRequest) => {
deployExecService = originalGetDeployExecutionService(deployRequest);
deployExecService.deploy = Sinon.stub().returns(Promise.resolve());
return deployExecService;
});
const argv = ['node', 'someFile.js', '--application=web', '-e', 'staging', '--commit-hash=ab5e9e3a4959bc91adfa3028b09226e47331504d', '--run-pre-hook'];
var error;
return service.deployFromCommandLineArguments(argv)
.catch(err => error = err)
.then(() => {
Should.not.exist(error);
preHookStub.calledOnce.should.be.True();
Should.exist(deployExecService);
deployExecService.deploy.calledOnce.should.be.True();
deployExecService.info.appName.should.eql('web');
deployExecService.info.envName.should.eql('staging');
deployExecService.info.commitHash.should.eql('ab5e9e3a4959bc91adfa3028b09226e47331504d');
})
});
it('Should successfully deploy two applications, calling preHookFunction first', function() {
const preHookStub = Sinon.stub().returns(Promise.resolve());
const service = DeployServiceFactory.create(folders, logger, preHookStub);
const originalGetDeployExecutionService = service._getDeployExecutionService.bind(service);
let deployExecServices = [];
const getDeployExecutionServiceStub = Sinon.stub(service, '_getDeployExecutionService', (deployRequest) => {
const deployExecService = originalGetDeployExecutionService(deployRequest);
deployExecService.deploy = Sinon.stub().returns(Promise.resolve());
deployExecServices.push(deployExecService);
return deployExecService;
});
const argv = ['node', 'someFile.js', '--application=web,web-with-tag', '-e', 'staging', '--commit-hash=ab5e9e3a4959bc91adfa3028b09226e47331504d', '--run-pre-hook'];
var error;
return service.deployFromCommandLineArguments(argv)
.catch(err => error = err)
.then(() => {
Should.not.exist(error);
preHookStub.calledOnce.should.be.True();
const expectedInfo = [{
"region": "us-east-1",
"s3Bucket": "myBucket",
"appName": "web",
"ebApplicationName": "testapp-web",
"envName": "staging",
"ebEnvironmentName": "testabb-web-staging",
"ebEnvironmentId": "e-asdf3acjkr",
"gitBranch": "master",
"commitHash": "ab5e9e3a4959bc91adfa3028b09226e47331504d"
},{
"region": "us-east-1",
"s3Bucket": "myBucket",
"appName": "web-with-tag",
"ebApplicationName": "testapp-web",
"envName": "staging",
"ebEnvironmentName": "testabb-web-staging",
"ebEnvironmentId": "e-asdf3acjkr",
"gitBranch": "master",
"commitHash": "ab5e9e3a4959bc91adfa3028b09226e47331504d"
}];
const expectCommitHash = 'ab5e9e3a4959bc91adfa3028b09226e47331504d';
preHookStub.calledWithExactly(expectedInfo, expectCommitHash, logger).should.be.True();
deployExecServices.length.should.eql(2);
deployExecServices[0].deploy.calledOnce.should.be.True();
deployExecServices[1].deploy.calledOnce.should.be.True();
deployExecServices[0].info.appName.should.eql('web');
deployExecServices[0].info.envName.should.eql('staging');
deployExecServices[0].info.commitHash.should.eql('ab5e9e3a4959bc91adfa3028b09226e47331504d');
deployExecServices[1].info.appName.should.eql('web-with-tag');
deployExecServices[1].info.envName.should.eql('staging');
deployExecServices[1].info.commitHash.should.eql('ab5e9e3a4959bc91adfa3028b09226e47331504d');
})
});
it('Should throw an error if Git commit hashes do not match', function() {
const preHookStub = Sinon.stub().returns(Promise.resolve());
const service = DeployServiceFactory.create(folders, logger, preHookStub);
const originalGetDeployExecutionService = service._getDeployExecutionService.bind(service);
let deployExecServices = [];
let commitHashes = [];
const getDeployExecutionServiceStub = Sinon.stub(service, '_getDeployExecutionService', (deployRequest) => {
const deployExecService = originalGetDeployExecutionService(deployRequest);
deployExecService.deploy = Sinon.stub().returns(Promise.resolve());
const commitHash = chance.string({length:40});
commitHashes.push(commitHash);
deployExecService.getCommitHash = Sinon.stub().returns(Promise.resolve(commitHash));
deployExecServices.push(deployExecService);
return deployExecService;
});
const argv = ['node', 'someFile.js', '--application=web,web-with-tag', '-e', 'staging', '--run-pre-hook'];
var error;
return service.deployFromCommandLineArguments(argv)
.catch(err => error = err)
.then(() => {
Should.exist(error);
error.message.should.eql(`All commit hashes must be equal when deploying multiple environments simultaneously (${commitHashes[0]} vs ${commitHashes[1]})`);
preHookStub.calledOnce.should.be.False();
deployExecServices.length.should.eql(2);
deployExecServices[0].deploy.calledOnce.should.be.False();
deployExecServices[1].deploy.calledOnce.should.be.False();
deployExecServices[0].info.appName.should.eql('web');
deployExecServices[0].info.envName.should.eql('staging');
deployExecServices[1].info.appName.should.eql('web-with-tag');
deployExecServices[1].info.envName.should.eql('staging');
})
});
});
describe('deploy', function() {
it('Should successfully kick off a deployment, calling the preHookFunction before', function() {
const preHookStub = Sinon.stub().returns(Promise.resolve());
const deployStub = Sinon.stub().returns(Promise.resolve());
const service = DeployServiceFactory.create(folders, logger, preHookStub);
const originalDeployExecutionService = service._getDeployExecutionService.bind(service);
const getExecServiceStub = Sinon.stub(service, '_getDeployExecutionService', (ebApp, ebEnv, commitHash) => {
const deployExecutionService = originalDeployExecutionService(ebApp, ebEnv, commitHash);
deployExecutionService.deploy = deployStub;
return deployExecutionService;
});
return service.deploy({appName:'web', envName:'staging', commitHash:'ab5e9e3a4959bc91adfa3028b09226e47331504d'}, {runPreHook: true})
.then(() => {
preHookStub.calledOnce.should.be.True();
deployStub.calledOnce.should.be.True();
})
});
it('Should not kick off a deployment if the preHookFunction throws an error', function() {
const deployStub = Sinon.stub().returns(Promise.resolve());
const service = DeployServiceFactory.create(folders, logger, () => Promise.reject(new Error('PreHookFunction failed')));
const originalDeployExecutionService = service._getDeployExecutionService.bind(service);
const getExecServiceStub = Sinon.stub(service, '_getDeployExecutionService', (ebApp, ebEnv, commitHash) => {
const deployExecutionService = originalDeployExecutionService(ebApp, ebEnv, commitHash);
deployExecutionService.deploy = deployStub;
return deployExecutionService;
});
let error;
return service.deploy({appName:'web', envName:'staging', commitHash:'ab5e9e3a4959bc91adfa3028b09226e47331504d'}, {runPreHook: true})
.catch(err => error = err)
.then(() => {
Should.exist(error);
deployStub.notCalled.should.be.True();
})
});
})
});