guvnor
Version:
A node process manager that isn't spanners all the way down
518 lines (403 loc) • 14.1 kB
JavaScript
var expect = require('chai').expect,
sinon = require('sinon'),
ProcessInfo = require('../../../../lib/daemon/domain/ProcessInfo'),
posix = require('posix'),
semver = require('semver'),
path = require('path'),
os = require('os')
describe('ProcessInfo', function () {
var processInfo
beforeEach(function() {
processInfo = new ProcessInfo({
script: 'foo.js'
})
processInfo._config = {
guvnor: {
logdir: 'foo'
},
debug: {
cluster: false
}
}
processInfo._logger = {
info: sinon.stub(),
warn: sinon.stub(),
error: sinon.stub(),
debug: sinon.stub(),
log: sinon.stub(),
remove: sinon.stub(),
add: sinon.stub()
}
processInfo._child_process = {}
processInfo._posix = {
getpwnam: sinon.stub(),
getgrnam: sinon.stub()
}
processInfo._fs = {
stat: sinon.stub(),
chown: sinon.stub(),
exists: sinon.stub()
}
processInfo._fileSystem = {
getLogDir: sinon.stub()
}
processInfo._semver = {
gt: sinon.stub()
}
})
it('should serialize and deserialize', function () {
processInfo.script = '/foo/bar/baz.js'
processInfo.cwd = '/foo/bar'
processInfo._posix.getpwnam.returns({name: 'foo'})
processInfo._posix.getgrnam.returns({name: 'bar'})
var otherProcessInfo = new ProcessInfo(JSON.parse(JSON.stringify(processInfo)))
otherProcessInfo._posix = {
getpwnam: sinon.stub(),
getgrnam: sinon.stub()
}
otherProcessInfo._fs = {
stat: sinon.stub()
}
otherProcessInfo._config = {
guvnor: {
logdir: 'foo'
}
}
otherProcessInfo._fileSystem = {
getLogDir: sinon.stub()
}
otherProcessInfo._posix.getpwnam.returns({name: 'foo'})
otherProcessInfo._posix.getgrnam.returns({name: 'bar'})
for (var key in processInfo) {
if (key == 'id' || key.substring(0, 1) == '_') {
continue
}
expect(processInfo[key]).to.deep.equal(otherProcessInfo[key])
}
})
it('should remove debug flags', function () {
var processInfo = new ProcessInfo({
script: '/foo/bar/baz.js',
execArgv: [
'--debug',
'--debug=2398',
'--debug-brk',
'--debug-brk=3298'
]
})
expect(processInfo.execArgv).to.be.empty
})
it('should have default options', function () {
delete processInfo.name
processInfo._posix.getpwnam.returns({name: 'foo'})
processInfo._posix.getgrnam.returns({name: 'bar'})
processInfo._fs.stat.withArgs('/foo/bar/baz.js').returns({
isDirectory: function () {
return false
}
})
processInfo.setOptions({
script: '/foo/bar/baz.js'
})
expect(processInfo.name).to.equal('baz.js')
expect(processInfo.restartOnError).to.be.true
expect(processInfo.restartRetries).to.equal(5)
expect(processInfo.argv).to.be.empty
expect(processInfo.execArgv).to.be.empty
expect(processInfo.env).to.be.ok
expect(processInfo.debug).to.be.false
expect(processInfo.instances).to.equal(1)
expect(processInfo.cluster).to.be.false
expect(processInfo.getProcessOptions().env.GUVNOR_SCRIPT).to.equal('/foo/bar/baz.js')
expect(processInfo.getProcessOptions().env.GUVNOR_PROCESS_NAME).to.equal('baz.js')
})
it('should remove the old debug port', function () {
processInfo.debug = true
processInfo.debugPort = 6
processInfo.execArgv = ['--debug=5', '--debug-brk=5']
expect(processInfo.getProcessOptions().execArgv.indexOf('--debug=5')).to.equal(-1)
expect(processInfo.getProcessOptions().execArgv.indexOf('--debug-brk=5')).to.equal(-1)
})
it('should update the debug port for processes', function () {
processInfo.debug = true
processInfo.debugPort = 5
expect(processInfo.getProcessOptions().execArgv.indexOf('--debug-brk=5')).to.equal(0)
})
it('should not debug-brk cluster manager when config says not to', function () {
processInfo._config.debug.cluster = false
processInfo.debug = true
processInfo.instances = 5
processInfo.debugPort = 5
processInfo.cluster = true
expect(processInfo.getProcessOptions().execArgv).to.contain('--debug=5')
expect(processInfo.getProcessOptions().execArgv).to.not.contain('--debug-brk=5')
})
it('should debug-brk cluster manager when config says to', function () {
processInfo._config.debug.cluster = true
processInfo.debug = true
processInfo.instances = 5
processInfo.debugPort = 5
processInfo.cluster = true
expect(processInfo.getProcessOptions().execArgv).to.not.contain('--debug=5')
expect(processInfo.getProcessOptions().execArgv).to.contain('--debug-brk=5')
})
it('should propagate environmental variables', function () {
processInfo.env = {
FOO: 'bar'
}
expect(processInfo.getProcessOptions().env.FOO).to.equal('bar')
})
it('should return args for process', function () {
processInfo.argv = 'argv'
expect(processInfo.getProcessArgs()).to.equal('argv')
})
it('should remove debug port from existing execArgv', function () {
processInfo.execArgv = ['--debug', '--debug=5858', '--debug 5858', '--debug-brk=5858', '--debug-brk 5858', '--debug-port=5858', '--debug-port 5858']
expect(processInfo.getProcessExecArgs().length).to.equal(1)
expect(processInfo.getProcessExecArgs()).to.contain('--expose_gc')
})
it('should use --debug-port if on 0.11 or above', function () {
processInfo.execArgv = []
processInfo.debugPort = 5
processInfo._semver.gt.returns(true)
expect(processInfo.getProcessExecArgs().length).to.equal(2)
expect(processInfo.getProcessExecArgs()).to.contain('--expose_gc')
expect(processInfo.getProcessExecArgs()).to.contain('--debug-port=5')
})
it('should use --debug if on 0.10 or below', function () {
processInfo.execArgv = []
processInfo.debugPort = 5
processInfo._semver.gt.returns(false)
expect(processInfo.getProcessExecArgs().length).to.equal(2)
expect(processInfo.getProcessExecArgs()).to.contain('--expose_gc')
expect(processInfo.getProcessExecArgs()).to.contain('--debug=5')
})
it('should find a name from a package.json file', function () {
expect(processInfo._findName(path.resolve(__dirname + '/../../../../index.js'))).to.equal('guvnor')
})
it('should set pid from process object', function () {
var proc = {
pid: 5,
on: sinon.stub()
}
expect(processInfo.pid).to.not.exist
processInfo.process = proc
expect(processInfo.process).to.equal(proc)
expect(processInfo.pid).to.equal(proc.pid)
})
it('should set process from worker object', function () {
var worker = {
process: {
pid: 5,
on: sinon.stub()
}
}
expect(processInfo.pid).to.not.exist
processInfo.worker = worker
expect(processInfo.process).to.equal(worker.process)
expect(processInfo.pid).to.equal(worker.process.pid)
expect(processInfo.worker).to.equal(worker)
})
it('should remove pid when removing process object', function () {
var proc = {
pid: 5,
on: sinon.stub()
}
expect(processInfo.pid).to.not.exist
processInfo.process = proc
expect(processInfo.pid).to.equal(proc.pid)
processInfo.process = undefined
expect(processInfo.pid).to.not.exist
})
it('should propagate process message events', function () {
var proc = {
pid: 5,
on: sinon.stub(),
emit: sinon.stub()
}
var event = {
event: 'foo',
args: ['bar']
}
processInfo.process = proc
expect(proc.emit.called).to.be.false
expect(proc.on.getCall(0).args[0]).to.equal('message')
proc.on.getCall(0).args[1](event)
expect(proc.emit.called).to.be.true
expect(proc.emit.getCall(0).args).to.deep.equal(['foo', 'bar'])
})
it('should propagate process message events without args array', function () {
var proc = {
pid: 5,
on: sinon.stub(),
emit: sinon.stub()
}
var event = {
event: 'foo'
}
processInfo.process = proc
expect(proc.emit.called).to.be.false
expect(proc.on.getCall(0).args[0]).to.equal('message')
proc.on.getCall(0).args[1](event)
expect(proc.emit.called).to.be.true
expect(proc.emit.getCall(0).args).to.deep.equal(['foo'])
})
it('should ignore process message events without event object', function () {
var proc = {
pid: 5,
on: sinon.stub(),
emit: sinon.stub()
}
var event = 'foo'
processInfo.process = proc
expect(proc.on.getCall(0).args[0]).to.equal('message')
proc.on.getCall(0).args[1](event)
expect(proc.emit.called).to.be.false
})
it('should report as running if status is uninitialised, starting, started, running, restarting or stopping', function () {
processInfo.status = 'foo'
expect(processInfo.running).to.be.false
processInfo.status = 'uninitialised'
expect(processInfo.running).to.be.true
processInfo.status = 'starting'
expect(processInfo.running).to.be.true
processInfo.status = 'started'
expect(processInfo.running).to.be.true
processInfo.status = 'running'
expect(processInfo.running).to.be.true
processInfo.status = 'restarting'
expect(processInfo.running).to.be.true
processInfo.status = 'stopping'
expect(processInfo.running).to.be.true
processInfo.status = 'stopped'
expect(processInfo.running).to.be.false
})
it('should populate user and group via posix from passed parameters', function (done) {
var user = 'foo'
var group = 'bar'
var userDetails = {
name: user,
uid: 5
}
var groupDetails = {
name: group,
gid: 6
}
processInfo.user = user
processInfo.group = group
processInfo._posix.getpwnam.withArgs(user).returns(userDetails)
processInfo._posix.getgrnam.withArgs(group).returns(groupDetails)
processInfo._checkUserAndGroup(function () {
expect(processInfo.user).to.equal(userDetails.name)
expect(processInfo.uid).to.equal(userDetails.uid)
expect(processInfo.group).to.equal(groupDetails.name)
expect(processInfo.gid).to.equal(groupDetails.gid)
done()
})
})
it('should take user and group via posix from process if parameters not passed', function (done) {
var user = 'foo'
var group = 'bar'
var userDetails = {
name: user,
uid: 5
}
var groupDetails = {
name: group,
gid: 6
}
processInfo.user = undefined
processInfo.group = undefined
processInfo._posix.getpwnam.withArgs(process.getuid()).returns(userDetails)
processInfo._posix.getgrnam.withArgs(process.getgid()).returns(groupDetails)
processInfo._checkUserAndGroup(function () {
expect(processInfo.user).to.equal(userDetails.name)
expect(processInfo.uid).to.equal(userDetails.uid)
expect(processInfo.group).to.equal(groupDetails.name)
expect(processInfo.gid).to.equal(groupDetails.gid)
done()
})
})
it('should propagate error if getting user and group via posix fails', function (done) {
var error = new Error('Urk!')
processInfo._posix.getpwnam.throws(error)
processInfo._checkUserAndGroup(function (er) {
expect(er).to.equal(error)
expect(er.code).to.equal('INVALID')
done()
})
})
it('should update a logger', function (done) {
processInfo.logger.add = function(logger) {
logger.emit('open')
}
processInfo._fileSystem.getLogDir.returns(os.tmpdir())
processInfo._fs.chown.callsArgAsync(3)
processInfo._updateLogger(done)
})
it('should check that the script exists', function (done) {
processInfo.script = '/foo/bar.js'
processInfo._fs.exists.withArgs(processInfo.script).callsArgWith(1, true)
processInfo._checkScriptExists(function (error) {
expect(error).to.not.exist
done()
})
})
it('should pass back error if script does not exist', function (done) {
processInfo.script = '/foo/bar.js'
processInfo._fs.exists.withArgs(processInfo.script).callsArgWith(1, false)
processInfo._checkScriptExists(function (error) {
expect(error.code).to.equal('INVALID')
done()
})
})
it('should take cwd from script dirname if is not a directory', function (done) {
processInfo.script = '/foo/bar.js'
processInfo._fs.stat.withArgs(processInfo.script).callsArgWith(1, undefined, {
isDirectory: sinon.stub().returns(false)
})
processInfo._takeCwdFromScript(function () {
expect(processInfo.cwd).to.equal('/foo')
done()
})
})
it('should set cwd as script if is a directory', function (done) {
processInfo.script = '/foo'
processInfo._fs.stat.withArgs(processInfo.script).callsArgWith(1, undefined, {
isDirectory: sinon.stub().returns(true)
})
processInfo._takeCwdFromScript(function () {
expect(processInfo.cwd).to.equal('/foo')
done()
})
})
it('should propagate error if one occurs during cwd stat', function (done) {
var error = new Error('Urk!')
processInfo.script = '/foo'
processInfo._fs.stat.withArgs(processInfo.script).callsArgWith(1, error)
processInfo._takeCwdFromScript(function (er) {
expect(er).to.equal(error)
done()
})
})
it('should not overwrite cwd if already set', function (done) {
processInfo.cwd = '/bar'
processInfo._takeCwdFromScript(function () {
expect(processInfo.cwd).to.equal('/bar')
done()
})
})
it('should check that cwd exists', function (done) {
processInfo.cwd = '/bar'
processInfo._fs.exists.withArgs(processInfo.cwd).callsArgWith(1, true)
processInfo._checkCwdExists(done)
})
it('should pass back error if cwd does not exist', function (done) {
processInfo.cwd = '/bar'
processInfo._fs.exists.withArgs(processInfo.cwd).callsArgWith(1, false)
processInfo._checkCwdExists(function (error) {
expect(error.code).to.equal('INVALID')
done()
})
})
})