navy
Version:
Quick and powerful development environments using Docker and Docker Compose
239 lines (235 loc) • 7.91 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _chai = require("chai");
var _sinon = _interopRequireDefault(require("sinon"));
var _proxyquire = _interopRequireDefault(require("proxyquire"));
var _errors = require("../../errors");
/* eslint-env mocha */
describe('cli/develop', function () {
let sandbox;
let getNavyStub;
let navyStub;
let getNavyRcStub;
let dockerStub;
let containerObj;
let consoleLogStub;
let originalCwd;
let developCli;
beforeEach(function () {
sandbox = _sinon.default.createSandbox();
navyStub = {
name: 'env-1',
getState: sandbox.stub().resolves({}),
saveState: sandbox.stub().resolves(),
emitAsync: sandbox.stub().resolves(),
kill: sandbox.stub().resolves(),
launch: sandbox.stub().resolves(),
ps: sandbox.stub().resolves([{
name: 'web',
id: 'abc123'
}])
};
getNavyStub = sandbox.stub().returns(navyStub);
getNavyRcStub = sandbox.stub().resolves({
services: ['web'],
develop: {
mounts: {
'./src': '/app/src'
},
command: 'npm start'
}
});
containerObj = {
attach: sandbox.stub().resolves('stream'),
modem: {
demuxStream: sandbox.stub()
}
};
dockerStub = {
getContainer: sandbox.stub().returns(containerObj)
};
consoleLogStub = sandbox.stub(console, 'log');
originalCwd = process.cwd;
sandbox.stub(process, 'cwd').returns('/work');
developCli = _proxyquire.default.noCallThru()('../develop', {
'../': {
getNavy: getNavyStub
},
'../errors': {
NavyError: _errors.NavyError
},
'../util/docker-client': dockerStub,
'../util/navyrc': getNavyRcStub
});
});
afterEach(function () {
process.cwd = originalCwd;
sandbox.restore();
});
describe('default export', function () {
it('should throw a NavyError when navyrc is null', async function () {
getNavyRcStub.resolves(null);
let caught;
try {
await developCli('web', {
navy: 'env-1'
});
} catch (err) {
caught = err;
}
(0, _chai.expect)(caught).to.be.instanceof(_errors.NavyError);
(0, _chai.expect)(caught.message).to.contain('No valid .navyrc');
});
it('should throw a NavyError when navyrc has no services', async function () {
getNavyRcStub.resolves({
services: undefined
});
let caught;
try {
await developCli('web', {
navy: 'env-1'
});
} catch (err) {
caught = err;
}
(0, _chai.expect)(caught).to.be.instanceof(_errors.NavyError);
});
it('should throw a NavyError when develop key is missing', async function () {
getNavyRcStub.resolves({
services: ['web']
});
let caught;
try {
await developCli('web', {
navy: 'env-1'
});
} catch (err) {
caught = err;
}
(0, _chai.expect)(caught).to.be.instanceof(_errors.NavyError);
(0, _chai.expect)(caught.message).to.contain('No develop mounts');
});
it('should throw when multiple services are listed and none specified', async function () {
getNavyRcStub.resolves({
services: ['web', 'api'],
develop: {
mounts: {},
command: 'x'
}
});
let caught;
try {
await developCli(undefined, {
navy: 'env-1'
});
} catch (err) {
caught = err;
}
(0, _chai.expect)(caught).to.be.instanceof(_errors.NavyError);
(0, _chai.expect)(caught.message).to.contain('Multiple service mappings');
});
it('should infer the only service when one is listed', async function () {
await developCli(undefined, {
navy: 'env-1'
});
(0, _chai.expect)(navyStub.kill.calledWith(['web'])).to.equal(true);
});
it('should throw when the service is not in the navyrc list', async function () {
let caught;
try {
await developCli('unknown', {
navy: 'env-1'
});
} catch (err) {
caught = err;
}
(0, _chai.expect)(caught).to.be.instanceof(_errors.NavyError);
(0, _chai.expect)(caught.message).to.contain('not a valid development target');
});
it('should resolve mount keys to absolute paths and persist them in state', async function () {
await developCli('web', {
navy: 'env-1'
});
(0, _chai.expect)(navyStub.saveState.calledOnce).to.equal(true);
const newState = navyStub.saveState.firstCall.args[0];
const mountKeys = Object.keys(newState.services.web._develop.mounts);
(0, _chai.expect)(mountKeys[0].endsWith('src')).to.equal(true);
(0, _chai.expect)(newState.services.web._develop.command).to.equal('npm start');
});
it('should preserve existing state.services entries on save', async function () {
navyStub.getState.resolves({
services: {
api: {
_tag: 'v1'
}
}
});
await developCli('web', {
navy: 'env-1'
});
const newState = navyStub.saveState.firstCall.args[0];
(0, _chai.expect)(newState.services.api).to.eql({
_tag: 'v1'
});
});
it('should default to {} when navy.getState returns null', async function () {
navyStub.getState.resolves(null);
await developCli('web', {
navy: 'env-1'
});
const newState = navyStub.saveState.firstCall.args[0];
// eslint-disable-next-line no-unused-expressions
(0, _chai.expect)(newState.services.web._develop).to.exist;
});
it('should kill, then launch the service in develop mode', async function () {
await developCli('web', {
navy: 'env-1'
});
(0, _chai.expect)(navyStub.emitAsync.calledWith('cli.develop.beforeLaunch')).to.equal(true);
(0, _chai.expect)(navyStub.kill.calledOnce).to.equal(true);
(0, _chai.expect)(navyStub.launch.calledOnce).to.equal(true);
(0, _chai.expect)(navyStub.launch.firstCall.args[0]).to.eql(['web']);
(0, _chai.expect)(navyStub.launch.firstCall.args[1]).to.eql({
noDeps: true
});
});
it('should attach to the container and demux the stream', async function () {
await developCli('web', {
navy: 'env-1'
});
(0, _chai.expect)(dockerStub.getContainer.calledWith('abc123')).to.equal(true);
(0, _chai.expect)(containerObj.attach.calledOnce).to.equal(true);
// Wait for the .then callback to fire on the attach promise
await new Promise(resolve => setImmediate(resolve));
(0, _chai.expect)(containerObj.modem.demuxStream.calledOnce).to.equal(true);
(0, _chai.expect)(containerObj.modem.demuxStream.firstCall.args[0]).to.equal('stream');
});
it('should log a graceful exit message when attach rejects', async function () {
containerObj.attach.rejects(new Error('attach failed'));
await developCli('web', {
navy: 'env-1'
});
// Wait microtask flush
await new Promise(resolve => setImmediate(resolve));
await new Promise(resolve => setImmediate(resolve));
const printed = consoleLogStub.getCalls().map(c => c.args[0] || '').join('\n');
(0, _chai.expect)(printed).to.contain('exited');
});
it('should throw an invariant violation when no container is found for the service', async function () {
navyStub.ps.resolves([{
name: 'other',
id: 'xyz'
}]);
let caught;
try {
await developCli('web', {
navy: 'env-1'
});
} catch (err) {
caught = err;
}
(0, _chai.expect)(caught).to.be.an('error');
(0, _chai.expect)(caught.message).to.contain('DEVELOP_NO_CONTAINER_ID');
});
});
});