Comparing version 1.2.7 to 1.2.8
@@ -10,5 +10,6 @@ 'use strict'; | ||
var async = require('async'); | ||
var workers; | ||
var workers = []; | ||
var repl; | ||
var shutdown; | ||
var shuttingDown = false; | ||
@@ -20,3 +21,2 @@ opts = merge({ | ||
restarts: false, | ||
// see https://github.com/dshaw/replify#options | ||
@@ -26,14 +26,16 @@ repl: false | ||
if (opts.restarts === -1) { | ||
opts.restarts = Infinity | ||
} | ||
if (opts.restarts === -1) | ||
opts.restarts = Infinity; | ||
if (opts.repl && opts.repl.path) { | ||
if (opts.repl && opts.repl.path) | ||
opts.repl.path = path.resolve(opts.repl.path); | ||
} | ||
// a master is a worker with a specific behavior | ||
var masterWorker = require('./worker.js')('master process', { | ||
start: async.series.bind(async, [opts.start, start]), | ||
stop: async.series.bind(async, [opts.stop, stop]) | ||
start: function (cb) { | ||
async.series([opts.start, start], cb) | ||
}, | ||
stop: function (cb) { | ||
async.series([opts.stop, stop], cb) | ||
} | ||
}); | ||
@@ -44,3 +46,3 @@ | ||
function start(cb) { | ||
debug('starting master %j', toFork); | ||
debug('starting master with forks: %j', toFork); | ||
@@ -54,13 +56,11 @@ workers = toFork.map(function(what) { | ||
toFork: what, | ||
title: 'no title yet', | ||
title: '[' + what + ']', | ||
status: 'unknown', | ||
start: function start(cb) { | ||
debug('starting clusterWorker %s', what); | ||
var clusterWorker = this; | ||
debug('starting clusterWorker "%s"', clusterWorker.title); | ||
if (!cb) { | ||
cb = function() {} | ||
} | ||
if (!cb) | ||
cb = function() {}; | ||
var clusterWorker = this; | ||
if (forks[clusterWorker.id] !== null) { | ||
@@ -78,5 +78,6 @@ var err = new Error('Cannot start a started worker'); | ||
forks[clusterWorker.id].once('exit', function(code, signal) { | ||
if (shutdown) { | ||
debug('worker "%s" exit detected', clusterWorker.title); | ||
if (shuttingDown) | ||
return; | ||
} | ||
@@ -100,2 +101,3 @@ // if `signal` is defined, it means | ||
debug('attempting to restart worker "%s"...', clusterWorker.title); | ||
clearTimeout(autoRestart); | ||
@@ -110,8 +112,8 @@ autoRestart = setTimeout(function restart() { | ||
}); | ||
} else { | ||
forks[clusterWorker.id].once('exit', function workerStopped(code, signal) { | ||
if (shutdown) { | ||
debug('worker "%s" exit detected', clusterWorker.title); | ||
if (shuttingDown) | ||
return; | ||
} | ||
@@ -134,5 +136,3 @@ masterWorker.emit('worker stopped', clusterWorker, { | ||
], function workerStarted(err) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
if (err) return cb(err); | ||
@@ -148,13 +148,12 @@ clusterWorker.status = 'started'; | ||
stop: function stop(cb) { | ||
if (!cb) { | ||
cb = function() { } | ||
} | ||
var clusterWorker = this; | ||
debug('stopping clusterWorker "%s"', clusterWorker.title); | ||
var clusterWorker = this; | ||
if (!cb) | ||
cb = function() { }; | ||
var err; | ||
if (forks[clusterWorker.id] === null) { | ||
if (shutdown) { | ||
err = null; | ||
} else { | ||
if (!shuttingDown) { | ||
err = new Error('Cannot stop a stopped worker'); | ||
@@ -170,5 +169,3 @@ masterWorker.emit('worker error', clusterWorker, err); | ||
kill(clusterWorker, function killed(err, code, signal) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
if (err) return cb(err); | ||
@@ -182,3 +179,3 @@ forks[clusterWorker.id] = null; | ||
} else { | ||
debug('worker stopped at kill', code, signal); | ||
debug('worker "%s" stopped at kill', clusterWorker.title, code, signal); | ||
masterWorker.emit('worker stopped', clusterWorker); | ||
@@ -191,3 +188,7 @@ } | ||
restart: function restart(cb) { | ||
var clusterWorker = this; | ||
debug('REstarting clusterWorker "%s"', clusterWorker.title); | ||
if (manualRestart) { | ||
// Note : should call back ?? | ||
return; | ||
@@ -198,3 +199,2 @@ } | ||
var clusterWorker = this; | ||
clusterWorker.stop(function(err) { | ||
@@ -227,17 +227,21 @@ clusterWorker.start(cb); | ||
function stop(cb) { | ||
shutdown = true; | ||
debug('stopping master...'); | ||
shuttingDown = true; | ||
async.each(workers, function stopWorker(clusterWorker, cb) { | ||
debug('ordering worker "%s" to stop...', clusterWorker.title); | ||
clusterWorker.stop(cb); | ||
}, function workedStopped(err) { | ||
if(err) | ||
debug('An error happened while stopping all workers : ' + err.message); | ||
else | ||
debug('all workers should have stopped.'); | ||
if (repl && repl.close) { | ||
// when repl failed to start, dont fail at closing it | ||
// when repl failed to start, don't fail at closing it | ||
try { repl.close() } catch (er) {} | ||
} | ||
if (err) { | ||
return cb(err); | ||
} | ||
cb(null); | ||
return cb(err); | ||
}); | ||
@@ -253,4 +257,6 @@ } | ||
// http://nodejs.org/api/cluster.html | ||
debug('forking "%s" as cluster...', clusterWorker.title); | ||
forked = clusterWorker.toFork.fork(); | ||
} else { | ||
debug('forking "%s" as child process...', clusterWorker.title); | ||
forked = fork(clusterWorker.toFork); | ||
@@ -263,2 +269,3 @@ } | ||
function waitForReady(clusterWorker, cb) { | ||
debug('Waiting for worker "%s" readiness...', clusterWorker.title); | ||
var forked = forks[clusterWorker.id]; | ||
@@ -277,5 +284,3 @@ | ||
forked.on('message', function waitForReady(msg) { | ||
if (!msg.graceful) { | ||
return; | ||
} | ||
if (!msg.graceful) return; | ||
@@ -288,2 +293,3 @@ if (msg.graceful.status === 'ready') { | ||
forked.removeListener('message', waitForReady); | ||
debug('Worker "%s[%s]" signaled its readiness.', clusterWorker.title, clusterWorker.toFork); | ||
masterWorker.emit('worker ready', clusterWorker); | ||
@@ -296,2 +302,4 @@ cb(null); | ||
function kill(clusterWorker, cb) { | ||
debug('Killing worker "%s"...', clusterWorker.title); | ||
var forked = forks[clusterWorker.id]; | ||
@@ -322,2 +330,3 @@ var forkedProcess = forked.process || forked; | ||
forkedProcess.once('exit', function waitForExit(code, signal) { | ||
debug('worker "%s" reported its exit.', clusterWorker.title); | ||
clearTimeout(killTimeout); | ||
@@ -329,2 +338,4 @@ cb(null, code, signal); | ||
function startWorker(clusterWorker, cb) { | ||
debug('Starting worker "%s"...', clusterWorker.title); | ||
var forked = forks[clusterWorker.id]; | ||
@@ -335,2 +346,3 @@ var startTimeout = setTimeout(cb.bind(null, new Error('Worker could not be started')), 10 * 1000); | ||
function cancel(code, signal) { | ||
debug('worker "%s" reported its exit while we were starting it !', clusterWorker.title); | ||
clearTimeout(startTimeout); | ||
@@ -341,5 +353,4 @@ | ||
// - before we get the started message | ||
if (signal !== null) { | ||
if (signal !== null) | ||
return; | ||
} | ||
@@ -353,5 +364,3 @@ // worker failed to start and exited immediately, continue | ||
forked.on('message', function waitForStart(msg) { | ||
if (!msg.graceful) { | ||
return; | ||
} | ||
if (!msg.graceful) return; | ||
@@ -362,2 +371,3 @@ if (msg.graceful.status === 'started') { | ||
forked.removeListener('message', waitForStart); | ||
debug('Worker "%s" signaled it stated.', clusterWorker.title); | ||
cb(null); | ||
@@ -371,2 +381,2 @@ } | ||
return masterWorker; | ||
} | ||
} |
@@ -0,1 +1,3 @@ | ||
'use strict'; | ||
module.exports = repl; | ||
@@ -55,2 +57,2 @@ | ||
return server; | ||
} | ||
} |
@@ -16,5 +16,5 @@ 'use strict'; | ||
var EventEmitter = require('events').EventEmitter; | ||
var emitter = new EventEmitter(); | ||
var working; | ||
var isWorking; | ||
var exitAsked; | ||
@@ -25,3 +25,3 @@ | ||
fns.start(function() { | ||
debug('starting %d', process.pid); | ||
debug('"%s"/%d starting...', title, process.pid); | ||
if (process.send) { | ||
@@ -35,3 +35,3 @@ process.send({graceful: {status: 'started', title: title}}); | ||
stop: function() { | ||
debug('stopping %d', process.pid); | ||
debug('"%s"/%d stopping...', title, process.pid); | ||
teardown(); | ||
@@ -69,5 +69,4 @@ fns.stop(function() { | ||
function teardown() { | ||
if (process.send) { | ||
if (process.send) | ||
process.removeListener('message', handleMasterMessage); | ||
} | ||
exitSignals.forEach(unsubscribe); | ||
@@ -77,3 +76,3 @@ } | ||
function subscribe(signal) { | ||
debug('subscribing to signal %s %d', signal, process.pid); | ||
debug('"%s"/%d subscribing to signal %s', title, process.pid, signal); | ||
process.addListener(signal, exitSignalHandler); | ||
@@ -83,3 +82,3 @@ } | ||
function unsubscribe(signal) { | ||
debug('unsubscribing from signal %s %d', signal, process.pid); | ||
debug('"%s"/%d unsubscribing from signal %s', title, process.pid, signal); | ||
try { | ||
@@ -95,5 +94,5 @@ process.removeListener(signal, exitSignalHandler); | ||
if (!quit) { | ||
debug('received %j from master, %d', msg, process.pid); | ||
debug('"%s"/%d received %j from master', title, process.pid, msg); | ||
} else { | ||
debug('was asked by signal to quit, %d', process.pid); | ||
debug('"%s"/%d was asked by signal to quit', title, process.pid); | ||
} | ||
@@ -107,12 +106,15 @@ | ||
case 'stop': | ||
if (exitAsked) { | ||
if (exitAsked) | ||
return; | ||
} | ||
exitAsked = true; | ||
if (!working) { | ||
if (!isWorking) | ||
graceful.stop(); | ||
} | ||
break; | ||
default: | ||
console.error('Unknown msg.graceful.action ! ' + msg.graceful.action); | ||
debug('"%s"/%d Unknown msg.graceful.action ! ' + msg.graceful.action, title, process.pid); | ||
break; | ||
} | ||
@@ -123,5 +125,5 @@ } | ||
emitter.working = function setWorking(status) { | ||
working = status; | ||
isWorking = status; | ||
if (working === false && exitAsked) { | ||
if (isWorking === false && exitAsked) { | ||
graceful.stop(); | ||
@@ -132,3 +134,3 @@ } | ||
function masterDied() { | ||
console.error('Master process died, forced exit of ' + title); | ||
console.error('"%s"/%d Master process died, forced exit', title, process.pid); | ||
process.nextTick(process.exit.bind(process, 1)); | ||
@@ -138,2 +140,2 @@ } | ||
return emitter; | ||
} | ||
} |
{ | ||
"name": "forkie", | ||
"version": "1.2.7", | ||
"version": "1.2.8", | ||
"description": "forkie likes your forks", | ||
@@ -19,16 +19,16 @@ "main": "index.js", | ||
"devDependencies": { | ||
"mocha": "~1.17.0", | ||
"sandboxed-module": "~0.3.0", | ||
"sinon": "~1.8.1", | ||
"chai": "~1.9.0", | ||
"sinon-chai": "~2.5.0", | ||
"lodash.invoke": "~2.4.1", | ||
"log-prefix": "0.0.0" | ||
"chai": "^3.5.0", | ||
"lodash.invokemap": "^4.1.0", | ||
"log-prefix": "0.0.0", | ||
"mocha": "^2.4.5", | ||
"sandboxed-module": "^0.3.0", | ||
"sinon": "^1.17.3", | ||
"sinon-chai": "^2.8.0" | ||
}, | ||
"dependencies": { | ||
"async": "~0.9.2", | ||
"debug": "~2.2.0", | ||
"deepmerge": "~0.2.7", | ||
"async": "~0.2.10", | ||
"replify": "git://github.com/vvo/replify#2e4b54333e386d1854b5d465095cb0973ca04167", | ||
"debug": "^0.8.0" | ||
"replify": "git://github.com/vvo/replify#2e4b54333e386d1854b5d465095cb0973ca04167" | ||
} | ||
} |
@@ -21,3 +21,3 @@ # Forkie [![Build Status](https://travis-ci.org/vvo/forkie.png?branch=master)](https://travis-ci.org/vvo/forkie) [![Dependency Status](https://david-dm.org/vvo/forkie.png?theme=shields.io)](https://david-dm.org/vvo/forkie) [![devDependency Status](https://david-dm.org/vvo/forkie/dev-status.png?theme=shields.io)](https://david-dm.org/vvo/forkie#info=devDependencies) | ||
A forkie master will forks all the workers you give to him. | ||
Workers must implement the [worker API](#worker API). | ||
Workers must implement the [worker API](#worker-api). | ||
@@ -24,0 +24,0 @@ ```js |
// chai | ||
var chai = require('chai'); | ||
chai.Assertion.includeStack = true; | ||
chai.config.includeStack = true; | ||
chai.use(require('sinon-chai')); | ||
@@ -5,0 +5,0 @@ |
@@ -0,9 +1,12 @@ | ||
'use strict'; | ||
var EventEmitter = require('events').EventEmitter; | ||
var SandboxedModule = require('sandboxed-module'); | ||
var invokemap = require('lodash.invokemap'); | ||
describe('creating a graceful master process', function () { | ||
var EventEmitter = require('events').EventEmitter; | ||
var SandboxedModule = require('sandboxed-module'); | ||
var invoke= require('lodash.invoke'); | ||
var processMock; | ||
var workerMock; | ||
var childProcessMock; | ||
var fakeProcess; | ||
var fakeWorker; | ||
var fakeCp; | ||
var gracefulMaster; | ||
@@ -16,2 +19,3 @@ var master; | ||
var stopCb; | ||
var workerEmit; | ||
@@ -26,4 +30,4 @@ beforeEach(function () { | ||
forks = []; | ||
fakeProcess = new EventEmitter(); | ||
fakeProcess.nextTick = sinon.spy(function(cb) { | ||
processMock = new EventEmitter(); | ||
processMock.nextTick = sinon.spy(function(cb) { | ||
cb(); | ||
@@ -33,5 +37,5 @@ }); | ||
// graceful worker minimal mock for master | ||
fakeWorker = sinon.spy(function(title, fns) { | ||
workerMock = sinon.spy(function(title, fns) { | ||
process.nextTick(fns.start.bind(null, startCb)); | ||
fakeProcess.on('SIGTERM', fns.stop.bind(null, stopCb)); | ||
processMock.on('SIGTERM', fns.stop.bind(null, stopCb)); | ||
return { | ||
@@ -42,3 +46,3 @@ emit: workerEmit | ||
fakeCp = { | ||
childProcessMock = { | ||
fork: sinon.spy(function(what) { | ||
@@ -57,7 +61,7 @@ var fork = new EventEmitter(); | ||
requires: { | ||
'./worker.js': fakeWorker, | ||
'child_process': fakeCp | ||
'./worker.js': workerMock, | ||
'child_process': childProcessMock | ||
}, | ||
globals: { | ||
process: fakeProcess | ||
process: processMock | ||
} | ||
@@ -72,3 +76,3 @@ } | ||
describe('with filenames to fork', function () { | ||
context('with filenames to fork', function () { | ||
@@ -84,9 +88,9 @@ beforeEach(function(done) { | ||
it('forked the first module', function() { | ||
expect(fakeCp.fork).to.be.calledOnce; | ||
expect(fakeCp.fork).to.be.calledWithExactly('a-module.js'); | ||
it('forks the first module', function() { | ||
expect(childProcessMock.fork).to.have.been.calledOnce; | ||
expect(childProcessMock.fork).to.have.been.calledWithExactly('a-module.js'); | ||
expect(forks).to.length(1); | ||
}); | ||
describe('when fork is ready', function () { | ||
context('when fork is ready', function () { | ||
@@ -103,4 +107,4 @@ beforeEach(function () { | ||
it('emits ready event', function() { | ||
expect(workerEmit).to.be.calledOnce; | ||
expect(workerEmit).to.be.calledWithMatch('worker ready', { | ||
expect(workerEmit).to.have.been.calledOnce; | ||
expect(workerEmit).to.have.been.calledWithMatch('worker ready', { | ||
id: 0, | ||
@@ -114,7 +118,7 @@ title: 'omg', | ||
}); | ||
}) | ||
}); | ||
it('asks for process start', function() { | ||
expect(send).to.be.calledOnce; | ||
expect(send).to.be.calledWithExactly({ | ||
expect(send).to.have.been.calledOnce; | ||
expect(send).to.have.been.calledWithExactly({ | ||
graceful: { | ||
@@ -126,3 +130,3 @@ action: 'start' | ||
describe('when fork starts', function () { | ||
context('when fork starts', function () { | ||
beforeEach(function () { | ||
@@ -138,4 +142,4 @@ workerEmit.reset(); | ||
it('emits a started event', function() { | ||
expect(workerEmit).to.be.calledOnce; | ||
expect(workerEmit).to.be.calledWithMatch('worker started', { | ||
expect(workerEmit).to.have.been.calledOnce; | ||
expect(workerEmit).to.have.been.calledWithMatch('worker started', { | ||
id: 0, | ||
@@ -151,3 +155,3 @@ toFork: 'a-module.js', | ||
describe('when second process is started', function() { | ||
context('when second process is started', function() { | ||
beforeEach(function() { | ||
@@ -171,4 +175,4 @@ workerEmit.reset(); | ||
it('starts second worker', function() { | ||
expect(workerEmit).to.be.calledTwice; | ||
expect(workerEmit).to.be.calledWithMatch('worker ready', { | ||
expect(workerEmit).to.have.been.calledTwice; | ||
expect(workerEmit).to.have.been.calledWithMatch('worker ready', { | ||
id: 1, | ||
@@ -182,3 +186,3 @@ toFork: 'another-module.js', | ||
}); | ||
expect(workerEmit).to.be.calledWithMatch('worker started', { | ||
expect(workerEmit).to.have.been.calledWithMatch('worker started', { | ||
id: 1, | ||
@@ -195,6 +199,6 @@ toFork: 'another-module.js', | ||
it('calls startCb', function() { | ||
expect(startCb).to.be.calledOnce; | ||
expect(startCb).to.have.been.calledOnce; | ||
}); | ||
describe('when forks are connected', function () { | ||
context('when forks are connected', function () { | ||
beforeEach(function () { | ||
@@ -209,8 +213,8 @@ forks.forEach(function(fork) { | ||
send.reset(); | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.emit('SIGTERM'); | ||
}); | ||
it('gently ask the forks to stop', function() { | ||
expect(send).to.be.calledTwice; | ||
expect(send).to.be.calledWithExactly({ | ||
expect(send).to.have.been.calledTwice; | ||
expect(send).to.have.been.calledWithExactly({ | ||
graceful: { | ||
@@ -222,14 +226,14 @@ action: 'stop' | ||
describe('when worker stops', function () { | ||
context('when worker stops', function () { | ||
beforeEach(function () { | ||
workerEmit.reset(); | ||
invoke(forks, 'emit', 'exit', 0); | ||
invokemap(forks, 'emit', 'exit', 0); | ||
}); | ||
it('emits a worker stopped event', function() { | ||
expect(workerEmit).to.be.calledTwice; | ||
expect(workerEmit).to.be.calledWithMatch('worker stopped', { | ||
expect(workerEmit).to.have.been.calledTwice; | ||
expect(workerEmit).to.have.been.calledWithMatch('worker stopped', { | ||
title: 'omg' | ||
}); | ||
expect(workerEmit).to.be.calledWithMatch('worker stopped', { | ||
expect(workerEmit).to.have.been.calledWithMatch('worker stopped', { | ||
title: 'oh great' | ||
@@ -240,7 +244,7 @@ }); | ||
it('calls stopCb', function() { | ||
expect(stopCb).to.be.calledOnce; | ||
expect(stopCb).to.have.been.calledOnce; | ||
}); | ||
}); | ||
describe('when worker does not stops fast enough', function () { | ||
context('when worker does not stop fast enough', function () { | ||
beforeEach(function () { | ||
@@ -251,4 +255,4 @@ this.clock.tick(5 * 1000); | ||
it('kills the worker with SIGKILL', function() { | ||
expect(kill).to.be.calledTwice; | ||
expect(kill).to.be.calledWithExactly('SIGKILL'); | ||
expect(kill).to.have.been.calledTwice; | ||
expect(kill).to.have.been.calledWithExactly('SIGKILL'); | ||
}); | ||
@@ -259,11 +263,11 @@ | ||
workerEmit.reset(); | ||
invoke(forks, 'emit', 'exit', 1, 'SIGKILL'); | ||
invokemap(forks, 'emit', 'exit', 1, 'SIGKILL'); | ||
}); | ||
it('emits a worker killed event', function() { | ||
expect(workerEmit).to.be.calledTwice; | ||
expect(workerEmit).to.be.calledWithMatch('worker killed', { | ||
expect(workerEmit).to.have.been.calledTwice; | ||
expect(workerEmit).to.have.been.calledWithMatch('worker killed', { | ||
title: 'omg' | ||
}); | ||
expect(workerEmit).to.be.calledWithMatch('worker killed', { | ||
expect(workerEmit).to.have.been.calledWithMatch('worker killed', { | ||
title: 'oh great' | ||
@@ -277,10 +281,10 @@ }); | ||
describe('when forks are not connected', function () { | ||
describe('and we receive a SIGTERM', function () { | ||
context('when forks are not connected', function () { | ||
context('and we receive a SIGTERM', function () { | ||
beforeEach(function () { | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.emit('SIGTERM'); | ||
}); | ||
it('calls fork.kill', function() { | ||
expect(kill).to.be.calledTwice; | ||
expect(kill).to.have.been.calledTwice; | ||
}); | ||
@@ -290,3 +294,3 @@ }); | ||
describe('when fork failed', function () { | ||
context('when fork failed', function () { | ||
beforeEach(function() { | ||
@@ -296,5 +300,5 @@ forks[0].exitCode = 8; | ||
describe('and we receive a SIGTERM', function () { | ||
context('and we receive a SIGTERM', function () { | ||
beforeEach(function () { | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.emit('SIGTERM'); | ||
forks[1].emit('exit', 0); | ||
@@ -304,3 +308,3 @@ }); | ||
it('reaches stopCb', function() { | ||
expect(stopCb).to.be.calledOnce; | ||
expect(stopCb).to.have.been.calledOnce; | ||
}); | ||
@@ -310,3 +314,3 @@ }); | ||
describe('when cluster fork failed', function () { | ||
context('when cluster fork failed', function () { | ||
beforeEach(function() { | ||
@@ -316,5 +320,5 @@ forks[0].process = { exitCode: 8 }; | ||
describe('and we receive a SIGTERM', function () { | ||
context('and we receive a SIGTERM', function () { | ||
beforeEach(function () { | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.emit('SIGTERM'); | ||
forks[1].emit('exit', 0); | ||
@@ -324,7 +328,6 @@ }); | ||
it('reaches stopCb', function() { | ||
expect(stopCb).to.be.calledOnce; | ||
expect(stopCb).to.have.been.calledOnce; | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -335,3 +338,3 @@ }); | ||
describe('with cluster workers to fork', function () { | ||
context('with cluster workers to fork', function () { | ||
var fork = sinon.stub().returns(new EventEmitter); | ||
@@ -350,7 +353,7 @@ | ||
it('called fork once', function() { | ||
expect(fork).to.be.calledOnce; | ||
expect(fork).to.have.been.calledOnce; | ||
}) | ||
}); | ||
describe('when using a specific start function', function () { | ||
context('when using a specific start function', function () { | ||
var start = sinon.stub().yields(); | ||
@@ -369,9 +372,9 @@ | ||
it('calls functions in the right order', function() { | ||
expect(fakeCp.fork).to.be.calledOnce; | ||
expect(start).to.be.calledOnce; | ||
expect(start).to.be.calledBefore(fakeCp.fork); | ||
expect(childProcessMock.fork).to.have.been.calledOnce; | ||
expect(start).to.have.been.calledOnce; | ||
expect(start).to.have.been.calledBefore(childProcessMock.fork); | ||
}); | ||
}); | ||
describe('when using a specific stop function', function () { | ||
context('when using a specific stop function', function () { | ||
var stop = sinon.stub().yields(); | ||
@@ -391,24 +394,24 @@ | ||
describe('when worker is not connected', function () { | ||
context('when worker is not connected', function () { | ||
beforeEach(function () { | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.emit('SIGTERM'); | ||
}); | ||
it('calls function in the right order', function() { | ||
expect(stop).to.be.calledOnce; | ||
expect(kill).to.be.calledOnce; | ||
expect(stop).to.be.calledBefore(kill); | ||
expect(stop).to.have.been.calledOnce; | ||
expect(kill).to.have.been.calledOnce; | ||
expect(stop).to.have.been.calledBefore(kill); | ||
}); | ||
}); | ||
describe('when worker is connected', function () { | ||
context('when worker is connected', function () { | ||
beforeEach(function () { | ||
forks[0].connected = true; | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.emit('SIGTERM'); | ||
}); | ||
it('calls function in the right order', function() { | ||
expect(stop).to.be.calledOnce; | ||
expect(send).to.be.calledOnce; | ||
expect(stop).to.be.calledBefore(send); | ||
expect(stop).to.have.been.calledOnce; | ||
expect(send).to.have.been.calledOnce; | ||
expect(stop).to.have.been.calledBefore(send); | ||
}); | ||
@@ -456,12 +459,11 @@ }); | ||
// two times: init and restart | ||
expect(fakeCp.fork).to.be.calledTwice; | ||
expect(fakeCp.fork).to.be.calledWithExactly('a-restarted-module.js'); | ||
expect(childProcessMock.fork).to.have.been.calledTwice; | ||
expect(childProcessMock.fork).to.have.been.calledWithExactly('a-restarted-module.js'); | ||
}); | ||
it('sends a restarted event', function() { | ||
expect(workerEmit).to.be.calledWithMatch('worker restarted', { | ||
expect(workerEmit).to.have.been.calledWithMatch('worker restarted', { | ||
restarts: { manual: 0, automatic: 1 } | ||
}); | ||
}); | ||
}); | ||
@@ -477,3 +479,3 @@ | ||
it('do not call fork again', function() { | ||
expect(fakeCp.fork).to.be.calledOnce; | ||
expect(childProcessMock.fork).to.have.been.calledOnce; | ||
}); | ||
@@ -488,3 +490,2 @@ | ||
}); | ||
}); | ||
@@ -498,3 +499,3 @@ | ||
it('does not call fork again', function() { | ||
expect(fakeCp.fork).to.be.calledOnce; | ||
expect(childProcessMock.fork).to.have.been.calledOnce; | ||
}); | ||
@@ -509,5 +510,3 @@ | ||
}); | ||
}); | ||
}); | ||
@@ -525,16 +524,14 @@ | ||
// two times: init and restart | ||
expect(fakeCp.fork).to.be.calledTwice; | ||
expect(fakeCp.fork).to.be.calledWithExactly('a-restarted-module.js'); | ||
expect(childProcessMock.fork).to.have.been.calledTwice; | ||
expect(childProcessMock.fork).to.have.been.calledWithExactly('a-restarted-module.js'); | ||
}); | ||
it('sends a restarted event', function() { | ||
expect(workerEmit).to.be.calledWithMatch('worker restarted', { | ||
expect(workerEmit).to.have.been.calledWithMatch('worker restarted', { | ||
restarts: { manual: 0, automatic: 1 } | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -0,6 +1,8 @@ | ||
'use strict'; | ||
var EventEmitter = require('events').EventEmitter; | ||
var SandboxedModule = require('sandboxed-module'); | ||
describe('creating a graceful process', function() { | ||
var EventEmitter = require('events').EventEmitter; | ||
var SandboxedModule = require('sandboxed-module'); | ||
var fakeProcess; | ||
var processMock; | ||
var graceful; | ||
@@ -11,6 +13,8 @@ var worker; | ||
this.clock = sinon.useFakeTimers(); | ||
fakeProcess = new EventEmitter(); | ||
fakeProcess.version = 'gotohell-2.0'; | ||
fakeProcess.nextTick = sinon.stub().yieldsAsync(); | ||
processMock = new EventEmitter(); | ||
processMock.version = 'gotohell-2.0'; | ||
processMock.nextTick = sinon.stub().yieldsAsync(); | ||
processMock.pid = 99999; // for logs | ||
worker = { | ||
@@ -26,7 +30,7 @@ start: sinon.stub().yields(), | ||
describe('when not started as a fork', function() { | ||
context('when not started as a fork', function() { | ||
beforeEach(function (done) { | ||
var gracefulWorker = SandboxedModule.require( | ||
'../../lib/worker.js', { | ||
globals: {process: fakeProcess} | ||
globals: {process: processMock} | ||
} | ||
@@ -44,14 +48,14 @@ ); | ||
it('calls worker.start', function() { | ||
expect(worker.start).to.be.called.once; | ||
expect(worker.start).to.have.been.called.once; | ||
}); | ||
it('emits a started event', function() { | ||
expect(graceful.emit).to.be.calledWith('started'); | ||
expect(graceful.emit).to.have.been.calledWith('started'); | ||
}); | ||
describe('receiving a SIGTERM while busy', function () { | ||
context('on receiving a SIGTERM while busy', function () { | ||
beforeEach(function (done) { | ||
graceful.working(true); | ||
fakeProcess.once('SIGTERM', done); | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.once('SIGTERM', done); | ||
processMock.emit('SIGTERM'); | ||
}); | ||
@@ -67,3 +71,3 @@ | ||
describe('when we are not busy anymore', function() { | ||
context('when we are not busy anymore', function() { | ||
beforeEach(function () { | ||
@@ -74,3 +78,3 @@ graceful.working(false); | ||
it('calls worker.stop', function() { | ||
expect(worker.stop).to.be.called.once; | ||
expect(worker.stop).to.have.been.called.once; | ||
expect(worker.stop.getCall(0).args[0]).to.be.a.Function; | ||
@@ -80,3 +84,3 @@ }); | ||
it('emits a stopped event', function() { | ||
expect(graceful.emit).to.be.calledWith('stopped'); | ||
expect(graceful.emit).to.have.been.calledWith('stopped'); | ||
}); | ||
@@ -86,10 +90,10 @@ }); | ||
describe('receiving a SIGTERM while not busy', function () { | ||
context('on receiving a SIGTERM while not busy', function () { | ||
beforeEach(function (done) { | ||
fakeProcess.once('SIGTERM', done); | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.once('SIGTERM', done); | ||
processMock.emit('SIGTERM'); | ||
}); | ||
it('calls worker.stop', function() { | ||
expect(worker.stop).to.be.called.once; | ||
expect(worker.stop).to.have.been.called.once; | ||
expect(worker.stop.getCall(0).args[0]).to.be.a.Function; | ||
@@ -100,9 +104,9 @@ }); | ||
describe('when started as a fork', function () { | ||
context('when started as a fork', function () { | ||
beforeEach(function (done) { | ||
fakeProcess.send = sinon.spy(); | ||
fakeProcess.disconnect = sinon.spy(); | ||
processMock.send = sinon.spy(); | ||
processMock.disconnect = sinon.spy(); | ||
var gracefulWorker = SandboxedModule.require( | ||
'../../lib/worker.js', { | ||
globals: {process: fakeProcess} | ||
globals: {process: processMock} | ||
} | ||
@@ -116,7 +120,7 @@ ); | ||
it('emits a ready event', function() { | ||
expect(graceful.emit).to.be.calledWith('ready'); | ||
expect(graceful.emit).to.have.been.calledWith('ready'); | ||
}); | ||
it('sends a ready message to master', function() { | ||
expect(fakeProcess.send).to.be.calledWith({ | ||
expect(processMock.send).to.have.been.calledWith({ | ||
graceful: { | ||
@@ -133,18 +137,18 @@ status: 'ready', | ||
describe('when receiving a SIGTERM', function () { | ||
context('when receiving a SIGTERM', function () { | ||
beforeEach(function (done) { | ||
fakeProcess.once('SIGTERM', done); | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.once('SIGTERM', done); | ||
processMock.emit('SIGTERM'); | ||
}); | ||
it('calls worker.stop', function() { | ||
expect(worker.stop).to.be.called.once; | ||
expect(worker.stop).to.have.been.called.once; | ||
}); | ||
it('emits a stoped event', function() { | ||
expect(graceful.emit).to.be.calledWith('stopped'); | ||
expect(graceful.emit).to.have.been.calledWith('stopped'); | ||
}); | ||
it('informs master through the communication channel', function() { | ||
expect(fakeProcess.send).to.be.calledWith({ | ||
expect(processMock.send).to.have.been.calledWith({ | ||
graceful: { | ||
@@ -158,10 +162,10 @@ status: 'stopped', | ||
describe('when master disconnects while stopping', function() { | ||
context('when master disconnects while stopping', function() { | ||
beforeEach(function (done) { | ||
sinon.stub(console, 'error'); | ||
worker.stop = sinon.stub().yieldsAsync(); | ||
fakeProcess.exit = sinon.spy(); | ||
fakeProcess.emit('SIGTERM'); | ||
processMock.exit = sinon.spy(); | ||
processMock.emit('SIGTERM'); | ||
process.nextTick(function() { | ||
fakeProcess.emit('disconnect') | ||
processMock.emit('disconnect') | ||
}); | ||
@@ -173,5 +177,5 @@ process.nextTick(done); | ||
process.nextTick(function() { | ||
expect(fakeProcess.exit).to.be.called.once; | ||
expect(fakeProcess.exit).to.be.calledWith(1); | ||
expect(console.error).to.be.calledWith('Master process died, forced exit of a forked worker'); | ||
expect(processMock.exit).to.have.been.called.once; | ||
expect(processMock.exit).to.have.been.calledWith(1); | ||
expect(console.error).to.have.been.calledWith('"%s"/%d Master process died, forced exit', 'a forked worker', 99999); | ||
done(); | ||
@@ -187,7 +191,7 @@ }); | ||
describe('when master disconnects', function () { | ||
context('when master disconnects', function () { | ||
beforeEach(function () { | ||
fakeProcess.exit = sinon.spy(); | ||
processMock.exit = sinon.spy(); | ||
sinon.stub(console, 'error'); | ||
fakeProcess.emit('disconnect'); | ||
processMock.emit('disconnect'); | ||
}); | ||
@@ -197,5 +201,5 @@ | ||
process.nextTick(function() { | ||
expect(fakeProcess.exit).to.be.called.once; | ||
expect(fakeProcess.exit).to.be.calledWith(1); | ||
expect(console.error).to.be.calledWith('Master process died, forced exit of a forked worker'); | ||
expect(processMock.exit).to.have.been.called.once; | ||
expect(processMock.exit).to.have.been.calledWith(1); | ||
expect(console.error).to.have.been.calledWith('"%s"/%d Master process died, forced exit', 'a forked worker', 99999); | ||
done(); | ||
@@ -210,19 +214,19 @@ }); | ||
describe('when master asks for start', function () { | ||
context('when master asks for start', function () { | ||
beforeEach(function () { | ||
fakeProcess.emit('message', {graceful: {action: 'start'}}); | ||
processMock.emit('message', {graceful: {action: 'start'}}); | ||
}); | ||
it('calls worker.start', function() { | ||
expect(worker.start).to.be.called.once; | ||
expect(worker.start).to.have.been.called.once; | ||
}); | ||
it('emits a started event', function() { | ||
expect(graceful.emit).to.be.called.once; | ||
expect(graceful.emit).to.be.calledWith('started'); | ||
expect(graceful.emit).to.have.been.called.once; | ||
expect(graceful.emit).to.have.been.calledWith('started'); | ||
}); | ||
it('sends a started message to the master', function() { | ||
expect(fakeProcess.send).to.be.called.once; | ||
expect(fakeProcess.send).to.be.calledWith({ | ||
expect(processMock.send).to.have.been.called.once; | ||
expect(processMock.send).to.have.been.calledWith({ | ||
graceful: { | ||
@@ -236,6 +240,6 @@ status: 'started', | ||
it('make calls in the right order', function() { | ||
expect(worker.start).to.be.calledBefore(fakeProcess.send); | ||
expect(worker.start).to.have.been.calledBefore(processMock.send); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
49980
20
1257
3
+ Addedasync@0.9.2(transitive)
+ Addeddebug@2.2.0(transitive)
+ Addedms@0.7.1(transitive)
- Removedasync@0.2.10(transitive)
- Removeddebug@0.8.1(transitive)
Updatedasync@~0.9.2
Updateddebug@~2.2.0