combohandler
Advanced tools
Comparing version 0.3.4 to 0.3.5
Combo Handler History | ||
===================== | ||
0.3.5 (2013-10-08) | ||
------------------ | ||
* Added `errorHandler` middleware to main export, encapsulating a convenient | ||
way to cache error responses for a different duration than normal responses. | ||
* Ensured that Unicode BOM in the response is removed, if found. | ||
[elementstorm] | ||
* Fixed tests in `node` v0.11.x, adding jshint and sinon to `devDependencies`. | ||
* Updated express dependency to 3.3.x. [Eric Ferraiuolo] | ||
0.3.4 (2013-04-29) | ||
@@ -5,0 +18,0 @@ ------------------ |
@@ -46,3 +46,2 @@ /** | ||
shortHands: shortHands, | ||
/*jshint es5: true */ | ||
get usage() { | ||
@@ -49,0 +48,0 @@ var msg = []; |
@@ -24,2 +24,5 @@ /** | ||
// for test stubbing | ||
this.process = process; | ||
if (this._start) { this.on('start', this._start); } | ||
@@ -38,3 +41,3 @@ if (this._destroy) { this.on('destroy', this._destroy); } | ||
if (cb) { | ||
process.nextTick(cb); | ||
this.process.nextTick(cb); | ||
} | ||
@@ -57,3 +60,2 @@ }; | ||
this.removeAllListeners(); | ||
this._destroyed = true; | ||
@@ -60,0 +62,0 @@ }; |
@@ -23,2 +23,7 @@ /** | ||
function ComboCluster(options, cb) { | ||
// factory constructor | ||
if (!(this instanceof ComboCluster)) { | ||
return new ComboCluster(options, cb); | ||
} | ||
if ("function" === typeof options) { | ||
@@ -29,11 +34,6 @@ cb = options; | ||
// factory constructor | ||
if (!(this instanceof ComboCluster)) { | ||
return new ComboCluster(options); | ||
} | ||
ComboBase.call(this, options); | ||
// this doesn't work in OS X (node-v0.8.x), but whatever | ||
process.title = 'combohandler master'; | ||
this.process.title = 'combohandler master'; | ||
@@ -45,2 +45,5 @@ this.startupTimeout = []; | ||
// for test stubbing | ||
this.cluster = cluster; | ||
this._maybeCallback(cb); | ||
@@ -65,3 +68,3 @@ } | ||
ComboCluster.prototype._destroy = function (cb) { | ||
cluster.disconnect(function () { | ||
this.cluster.disconnect(function () { | ||
this._detachEvents(); | ||
@@ -73,3 +76,3 @@ this._maybeCallback(cb); | ||
ComboCluster.prototype.setupMaster = function (options) { | ||
cluster.setupMaster(options); | ||
this.cluster.setupMaster(options); | ||
}; | ||
@@ -95,15 +98,15 @@ | ||
ComboCluster.prototype._detachEvents = function () { | ||
process.removeAllListeners('SIGINT'); | ||
process.removeAllListeners('SIGTERM'); | ||
process.removeAllListeners('SIGUSR2'); | ||
cluster.removeAllListeners(); | ||
this.emit('cleanup'); | ||
}; | ||
ComboCluster.prototype._setupMasterPidFile = function () { | ||
pidfiles.writePidFile(this.options.pids, 'master', process.pid); | ||
pidfiles.writePidFile(this.options.pids, 'master', this.process.pid); | ||
}; | ||
ComboCluster.prototype._bindProcess = function () { | ||
// bind shutdown helper to access this.options.pids | ||
var self = this; | ||
// bind listeners to access this.options.pids | ||
var boundGracefulShutdown = this.gracefulShutdown.bind(this); | ||
var boundRestartWorkers = this.restartWorkers.bind(this); | ||
@@ -113,13 +116,35 @@ // Signal Event Handlers | ||
// http://en.wikipedia.org/wiki/Unix_signal#List_of_signals | ||
process.on('SIGINT', boundGracefulShutdown); | ||
process.on('SIGTERM', boundGracefulShutdown); | ||
process.on('SIGUSR2', this.restartWorkers); | ||
this.process.on('SIGINT', boundGracefulShutdown) | ||
.on('SIGTERM', boundGracefulShutdown) | ||
.on('SIGUSR2', boundRestartWorkers); | ||
this.once('cleanup', function () { | ||
self.process.removeListener('SIGINT', boundGracefulShutdown) | ||
.removeListener('SIGTERM', boundGracefulShutdown) | ||
.removeListener('SIGUSR2', boundRestartWorkers); | ||
}); | ||
}; | ||
ComboCluster.prototype._bindCluster = function () { | ||
cluster.on('fork', this._workerForked.bind(this)); | ||
cluster.on('online', this._workerOnline.bind(this)); | ||
cluster.on('listening', this._workerListening.bind(this)); | ||
cluster.on('disconnect', this._workerDisconnected.bind(this)); | ||
cluster.on('exit', this._workerExited.bind(this)); | ||
var self = this; | ||
var boundForked = this._workerForked.bind(this); | ||
var boundOnline = this._workerOnline.bind(this); | ||
var boundListening = this._workerListening.bind(this); | ||
var boundDisconnected = this._workerDisconnected.bind(this); | ||
var boundExited = this._workerExited.bind(this); | ||
this.cluster.on('fork', boundForked) | ||
.on('online', boundOnline) | ||
.on('listening', boundListening) | ||
.on('disconnect', boundDisconnected) | ||
.on('exit', boundExited); | ||
this.once('cleanup', function () { | ||
self.cluster.removeListener('fork', boundForked) | ||
.removeListener('online', boundOnline) | ||
.removeListener('listening', boundListening) | ||
.removeListener('disconnect', boundDisconnected) | ||
.removeListener('exit', boundExited); | ||
}); | ||
}; | ||
@@ -179,3 +204,3 @@ | ||
pidfiles.removePidFileSync(this.options.pids, 'master'); | ||
process.exit(1); | ||
this.process.exit(1); | ||
} | ||
@@ -185,3 +210,3 @@ } | ||
console.error('Worker %d died, respawning!', worker.id); | ||
cluster.fork(); | ||
this.cluster.fork(); | ||
} | ||
@@ -193,8 +218,8 @@ }; | ||
ComboCluster.prototype.gracefulShutdown = function () { | ||
console.log('combohandler master %d shutting down...', process.pid); | ||
var dir = this.options.pids; | ||
cluster.disconnect(function () { | ||
process.on('exit', function () { | ||
pidfiles.removePidFileSync(dir, 'master'); | ||
console.log('combohandler master %d finished shutting down!', process.pid); | ||
console.log('combohandler master %d shutting down...', this.process.pid); | ||
var self = this; | ||
this.cluster.disconnect(function () { | ||
self.process.on('exit', function () { | ||
pidfiles.removePidFileSync(self.options.pids, 'master'); | ||
console.log('combohandler master %d finished shutting down!', self.process.pid); | ||
}); | ||
@@ -206,6 +231,6 @@ }); | ||
ComboCluster.prototype.restartWorkers = function () { | ||
console.log('combohandler master %d restarting workers...', process.pid); | ||
for (var id in cluster.workers) { | ||
if (cluster.workers.hasOwnProperty(id)) { | ||
process.kill(cluster.workers[id].process.pid, 'SIGUSR2'); | ||
console.log('combohandler master %d restarting workers...', this.process.pid); | ||
for (var id in this.cluster.workers) { | ||
if (this.cluster.workers.hasOwnProperty(id)) { | ||
this.process.kill(this.cluster.workers[id].process.pid, 'SIGUSR2'); | ||
} | ||
@@ -216,2 +241,3 @@ } | ||
ComboCluster.prototype._signalMaster = function (signal) { | ||
var self = this; | ||
pidfiles.getMasterPid(this.options.pids, function (err, masterPid, dir) { | ||
@@ -222,3 +248,3 @@ if (err) { | ||
console.error('combohandler master not running!'); | ||
process.exit(1); | ||
self.process.exit(1); | ||
} | ||
@@ -236,3 +262,3 @@ else { | ||
// send signal to master process, not necessarily "killing" it | ||
process.kill(masterPid, signal); | ||
self.process.kill(masterPid, signal); | ||
} | ||
@@ -256,2 +282,3 @@ catch (ex) { | ||
ComboCluster.prototype.status = function () { | ||
var self = this; | ||
pidfiles.getMasterPid(this.options.pids, function (err, masterPid, dir) { | ||
@@ -261,3 +288,3 @@ if (err) { | ||
console.error('combohandler master not running!'); | ||
process.exit(1); | ||
self.process.exit(1); | ||
} | ||
@@ -287,3 +314,3 @@ else { | ||
ComboCluster.prototype._stop = function () { | ||
console.error('combohandler master %d stopping abruptly...', process.pid); | ||
console.error('combohandler master %d stopping abruptly...', this.process.pid); | ||
this._signalMaster('SIGKILL'); | ||
@@ -296,8 +323,8 @@ }; | ||
// fork | ||
console.log('Forking workers from combohandler master %d', process.pid); | ||
console.log('Forking workers from combohandler master %d', this.process.pid); | ||
// console.log(this.options); | ||
var workers = this.options.workers; | ||
while (workers--) { | ||
cluster.fork(); | ||
this.cluster.fork(); | ||
} | ||
}; |
@@ -28,7 +28,7 @@ /** | ||
// this doesn't work in OS X (node-v0.8.x), but whatever | ||
process.title = 'combohandler worker'; | ||
this.process.title = 'combohandler worker'; | ||
// aid precise detaching during _destroy | ||
this._boundDispatch = this.dispatch.bind(this); | ||
process.on('message', this._boundDispatch); | ||
this.process.on('message', this._boundDispatch); | ||
} | ||
@@ -56,3 +56,3 @@ | ||
ComboWorker.prototype._destroy = function (cb) { | ||
process.removeListener('message', this._boundDispatch); | ||
this.process.removeListener('message', this._boundDispatch); | ||
@@ -59,0 +59,0 @@ if (this._server) { |
@@ -18,2 +18,4 @@ var fs = require('fs'), | ||
var resolvePathSync = require('./utils').resolvePathSync; | ||
// -- Exported Methods --------------------------------------------------------- | ||
@@ -46,6 +48,3 @@ exports.combine = function (config) { | ||
// error. | ||
rootPathResolved = fs.realpathSync(config.rootPath || ''); | ||
// rootPathResolved should always have a trailing slash | ||
rootPathResolved = path.normalize(rootPathResolved + path.sep); | ||
rootPathResolved = resolvePathSync(config.rootPath); | ||
} | ||
@@ -141,3 +140,3 @@ | ||
body[i] = data; | ||
body[i] = removeBOM(data.toString()); | ||
pending -= 1; | ||
@@ -156,2 +155,35 @@ | ||
exports.errorHandler = function (options) { | ||
if (!options) { | ||
options = {}; | ||
} | ||
// pass null to disable caching | ||
var errorAge = options.errorMaxAge; | ||
if (typeof errorAge === 'undefined') { | ||
errorAge = 300; // five minutes in seconds | ||
} | ||
return function comboErrorHandler(err, req, res, next) { | ||
if (err instanceof BadRequest) { | ||
res.charset = 'utf-8'; | ||
res.type('text/plain'); | ||
if (errorAge !== null) { | ||
res.header('Cache-Control', 'public,max-age=' + errorAge); | ||
res.header('Expires', new Date(Date.now() + (errorAge * 1000)).toUTCString()); | ||
} else { | ||
res.header('Cache-Control', 'private,no-store'); | ||
res.header('Expires', new Date(0).toUTCString()); | ||
res.header('Pragma', 'no-cache'); | ||
} | ||
res.send(400, 'Bad request. ' + err.message); | ||
} else { | ||
next(err); | ||
} | ||
}; | ||
}; | ||
// By convention, this is the last middleware passed to any combo route | ||
@@ -167,2 +199,9 @@ exports.respond = function respondMiddleware(req, res) { | ||
function removeBOM (content) { | ||
if (content.charCodeAt(0) === 0xFEFF) { | ||
content = content.slice(1); | ||
} | ||
return content; | ||
} | ||
/** | ||
@@ -169,0 +208,0 @@ Dedupes an array of strings, returning an array that's guaranteed to contain |
@@ -9,2 +9,3 @@ /** | ||
var BadRequest = require('../error/bad-request'); | ||
var resolvePathSync = require('../utils').resolvePathSync; | ||
@@ -54,7 +55,4 @@ /** | ||
// middleware is initialized, and we want it to throw if there's an error. | ||
rootPath = fs.realpathSync(rootPath); | ||
rootPath = resolvePathSync(rootPath); | ||
// rootPath should always have a trailing slash | ||
rootPath = path.normalize(rootPath + path.sep); | ||
return function dynamicPathMiddleware(req, res, next) { | ||
@@ -61,0 +59,0 @@ // on the off chance this middleware runs twice |
@@ -32,13 +32,6 @@ var combo = require('./combohandler'), | ||
app.use(function (err, req, res, next) { | ||
if (err instanceof combo.BadRequest) { | ||
res.charset = 'utf-8'; | ||
res.type('text/plain'); | ||
res.send(400, 'Bad request. ' + err.message); | ||
} else { | ||
next(err); | ||
} | ||
}); | ||
app.use(combo.errorHandler()); | ||
} | ||
/*jshint forin:false */ | ||
for (route in roots) { | ||
@@ -45,0 +38,0 @@ // pass along all of config, overwriting rootPath |
/** | ||
Shared Utilities | ||
**/ | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
@@ -8,2 +10,3 @@ exports.mix = mix; | ||
exports.memoize = memoize; | ||
exports.resolvePathSync = resolvePathSync; | ||
@@ -43,1 +46,8 @@ function mix(receiver, supplier) { | ||
} | ||
function resolvePathSync(rootPath) { | ||
// Intentionally using the sync method because this only runs when the | ||
// middleware is initialized, and we want it to throw if there's an error. | ||
// The resulting rootPath should always have a trailing slash. | ||
return path.normalize(fs.realpathSync(rootPath || '') + path.sep); | ||
} |
{ | ||
"name" : "combohandler", | ||
"description": "Simple Yahoo!-style combo handler.", | ||
"version" : "0.3.4", | ||
"version" : "0.3.5", | ||
"keywords" : [ | ||
@@ -26,6 +26,18 @@ "combo", "combohandler", "combohandle", "combine", "cdn", "css", "yui" | ||
"jshintConfig": { | ||
"curly" : true, | ||
"eqeqeq" : true, | ||
"forin" : true, | ||
"immed" : true, | ||
"newcap" : true, | ||
"noarg" : true, | ||
"noempty" : true, | ||
"undef" : true, | ||
"node" : true | ||
}, | ||
"dependencies": { | ||
"mkdirp" : "0.3.x", | ||
"nopt" : "2.x", | ||
"express": "3.2.x", | ||
"express": "3.3.x", | ||
"URIjs" : "1.10.1" | ||
@@ -36,6 +48,8 @@ }, | ||
"istanbul": "~0.1.34", | ||
"jshint" : "~2.1.11", | ||
"mocha" : "1.9.0", | ||
"request": "2.x", | ||
"rimraf": "~2.1.4", | ||
"should" : "1.2.x" | ||
"should" : "1.x", | ||
"sinon" : "1.x" | ||
}, | ||
@@ -55,2 +69,3 @@ | ||
"scripts": { | ||
"pretest": "jshint bin lib test/*.js", | ||
"test": "istanbul test --print both ./node_modules/mocha/bin/_mocha", | ||
@@ -57,0 +72,0 @@ "posttest": "istanbul check-coverage --statements 80 --functions 80 --branches 70 --lines 80" |
@@ -101,11 +101,3 @@ Combo Handler | ||
// Return a 400 response if the combo handler generates a BadRequest error. | ||
app.use(function (err, req, res, next) { | ||
if (err instanceof combo.BadRequest) { | ||
res.charset = 'utf-8'; | ||
res.type('text/plain'); | ||
res.send(400, 'Bad request. ' + err.message); | ||
} else { | ||
next(err); | ||
} | ||
}); | ||
app.use(combo.errorHandler()); | ||
@@ -139,2 +131,22 @@ // Given a root path that points to a YUI 3 root folder, this route will | ||
#### `combo.errorHandler` | ||
The `errorHandler` export encapsulates the convention of sending `BadRequest` | ||
errors with an optional `errorMaxAge` config. | ||
By default, `BadRequest` errors are served with a 5 minute `max-age` header. | ||
To explicitly disable caching (via | ||
`Pragma: no-cache` and | ||
`Cache-Control: private,no-store` | ||
headers), pass `null` in the options object: | ||
```js | ||
app.use(combo.errorHandler({ | ||
errorMaxAge: null | ||
})); | ||
``` | ||
Any other value (including zero) for `errorMaxAge` is interpreted as the | ||
desired duration in seconds. | ||
### Creating a server | ||
@@ -141,0 +153,0 @@ |
@@ -1,2 +0,2 @@ | ||
/*global describe, before, after, it */ | ||
/*global describe, before, after, it, sinon */ | ||
var fs = require('fs'); | ||
@@ -14,8 +14,10 @@ var path = require('path'); | ||
process.env['NODE_ENV'] = 'test'; | ||
process.env.NODE_ENV = 'test'; | ||
describe('combohandler', function () { | ||
/*jshint expr:true */ | ||
var app, httpServer; | ||
before(function () { | ||
before(function (done) { | ||
app = server({ | ||
@@ -28,7 +30,7 @@ roots: { | ||
httpServer = app.listen(PORT); | ||
httpServer = app.listen(PORT, done); | ||
}); | ||
after(function () { | ||
httpServer.close(); | ||
after(function (done) { | ||
httpServer.close(done); | ||
}); | ||
@@ -96,3 +98,4 @@ | ||
var expires = new Date(res.headers['expires']); | ||
res.headers.should.have.property('expires'); | ||
var expires = new Date(res.headers.expires); | ||
((expires - Date.now()) / 1000).should.be.within(31535990, 31536000); | ||
@@ -120,3 +123,4 @@ | ||
var expires = new Date(res.headers['expires']); | ||
res.headers.should.have.property('expires'); | ||
var expires = new Date(res.headers.expires); | ||
((expires - Date.now()) / 1000).should.be.within(-5, 5); | ||
@@ -129,2 +133,62 @@ | ||
describe('config: errorMaxAge', function () { | ||
before(function () { | ||
function throwBadRequest(req, res, next) { | ||
next(new combo.BadRequest('errorMaxAge')); | ||
} | ||
app.use('/error-max-age-null', throwBadRequest); | ||
app.use('/error-max-age-null', combo.errorHandler({ | ||
errorMaxAge: null | ||
})); | ||
app.use('/error-max-age-0', throwBadRequest); | ||
app.use('/error-max-age-0', combo.errorHandler({ | ||
errorMaxAge: 0 | ||
})); | ||
}); | ||
it('should default to five minutes', function (done) { | ||
request(BASE_URL + '/js?err.js', function (err, res, body) { | ||
res.should.have.status(400); | ||
res.should.have.header('cache-control', 'public,max-age=300'); | ||
res.headers.should.have.property('expires'); | ||
var expires = new Date(res.headers.expires); | ||
((expires - Date.now()) / 1000).should.be.within(290, 300); | ||
done(); | ||
}); | ||
}); | ||
it('should set private Cache-Control and no-cache headers when errorMaxAge is null', function (done) { | ||
request(BASE_URL + '/error-max-age-null?a.js', function (err, res, body) { | ||
assert.ifError(err); | ||
res.should.have.status(400); | ||
res.should.have.header('cache-control', 'private,no-store'); | ||
res.should.have.header('pragma', 'no-cache'); | ||
res.headers.should.have.property('expires'); | ||
var expires = new Date(res.headers.expires).getTime(); | ||
expires.should.equal(new Date(0).getTime()); | ||
done(); | ||
}); | ||
}); | ||
it('should support an errorMaxAge of 0', function (done) { | ||
request(BASE_URL + '/error-max-age-0?a.js', function (err, res, body) { | ||
assert.ifError(err); | ||
res.should.have.status(400); | ||
res.should.have.header('cache-control', 'public,max-age=0'); | ||
res.headers.should.have.property('expires'); | ||
var expires = new Date(res.headers.expires); | ||
((expires - Date.now()) / 1000).should.be.within(-5, 5); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('config: basePath', function () { | ||
@@ -131,0 +195,0 @@ describe('when absent', function () { |
@@ -1,2 +0,2 @@ | ||
/*global describe, it */ | ||
/*global describe, it, sinon */ | ||
var path = require('path'); | ||
@@ -6,2 +6,4 @@ var args = require('../lib/args'); | ||
describe("args", function () { | ||
/*jshint expr:true */ | ||
describe("parse()", function () { | ||
@@ -8,0 +10,0 @@ it("should consume ad-hoc string 'restart' param as boolean config.restart", function () { |
@@ -1,2 +0,2 @@ | ||
/*global describe, it */ | ||
/*global describe, it, sinon */ | ||
var path = require('path'); | ||
@@ -7,2 +7,3 @@ | ||
describe("cluster base", function () { | ||
/*jshint expr:true */ | ||
@@ -66,20 +67,2 @@ describe("instantiation", function () { | ||
it("should remove all instance listeners", function () { | ||
var instance = new ComboBase(); | ||
instance.on('start', function () {}); | ||
instance.on('listen', function () {}); | ||
instance.on('destroy', function () {}); | ||
instance.listeners('start').should.not.be.empty; | ||
instance.listeners('listen').should.not.be.empty; | ||
instance.listeners('destroy').should.not.be.empty; | ||
instance.destroy(); | ||
instance.listeners('start').should.be.empty; | ||
instance.listeners('listen').should.be.empty; | ||
instance.listeners('destroy').should.be.empty; | ||
}); | ||
it("should call destroy callback directly when no listener for 'destroy' event", function (done) { | ||
@@ -86,0 +69,0 @@ var instance = new ComboBase(); |
@@ -1,2 +0,2 @@ | ||
/*global describe, before, after, it */ | ||
/*global describe, before, after, it, sinon */ | ||
var path = require('path'); | ||
@@ -6,2 +6,4 @@ var defaults = require('../lib/defaults'); | ||
describe("defaults", function () { | ||
/*jshint expr:true */ | ||
var DEFAULT_PORT = parseInt(process.env.npm_package_config_port, 10) || 8000; | ||
@@ -55,2 +57,3 @@ var DEFAULT_SERVER = path.resolve(process.env.npm_package_config_server || | ||
/*jshint laxbreak:true */ | ||
// invert the conditional already tested above | ||
@@ -57,0 +60,0 @@ invertedPrefix = process.env.npm_config_prefix = _npmConfigPrefix |
@@ -1,2 +0,2 @@ | ||
/*global describe, before, after, beforeEach, afterEach, it */ | ||
/*global describe, before, after, beforeEach, afterEach, it, sinon */ | ||
var fs = require('fs'); | ||
@@ -11,3 +11,4 @@ var path = require('path'); | ||
describe("cluster master", function () { | ||
var cluster = require('cluster'); | ||
/*jshint expr:true */ | ||
var PIDS_DIR = 'test/fixtures/pids'; | ||
@@ -60,74 +61,100 @@ | ||
describe("on 'destroy'", function () { | ||
it("should detach events, passing through callback", function (done) { | ||
var instance = new ComboMaster(); | ||
describe("on 'start'", function () { | ||
before(cleanPidsDir); | ||
instance._attachEvents(); | ||
hasAttachedClusterEvents(); | ||
hasAttachedSignalEvents(); | ||
instance.destroy(function () { | ||
hasDetachedClusterEvents(); | ||
hasDetachedSignalEvents(); | ||
done(); | ||
beforeEach(function () { | ||
var instance = new ComboMaster({ | ||
pids: PIDS_DIR | ||
}); | ||
}); | ||
it("should not error when detachEvents callback missing", function () { | ||
var instance = new ComboMaster(); | ||
sinon.spy(instance.cluster, "on"); | ||
sinon.spy(instance.process, "on"); | ||
instance._bindProcess(); | ||
hasAttachedSignalEvents(); | ||
instance.destroy(); | ||
hasDetachedSignalEvents(); | ||
this.instance = instance; | ||
}); | ||
}); | ||
describe("on 'start'", function () { | ||
var master; | ||
afterEach(function () { | ||
var instance = this.instance; | ||
before(cleanPidsDir); | ||
instance.cluster.on.restore(); | ||
instance.process.on.restore(); | ||
beforeEach(function (done) { | ||
master = new ComboMaster({ | ||
pids: PIDS_DIR | ||
}, done); | ||
instance.emit('cleanup'); | ||
this.instance = instance = null; | ||
}); | ||
afterEach(function (done) { | ||
master.destroy(done); | ||
}); | ||
after(cleanPidsDir); | ||
it("should setupMaster with exec config", function (done) { | ||
master.setupMaster = function (options) { | ||
var instance = this.instance; | ||
instance.setupMaster = function (options) { | ||
options.should.have.property('exec'); | ||
options.exec.should.equal(path.resolve(__dirname, '../lib/cluster/worker.js')); | ||
}; | ||
master.emit('start', done); | ||
instance.start(done); | ||
}); | ||
it("should create master.pid", function (done) { | ||
master.emit('start', function () { | ||
fs.readFile(path.join(master.options.pids, 'master.pid'), done); | ||
var instance = this.instance; | ||
instance.start(function () { | ||
fs.readFile(path.join(instance.options.pids, 'master.pid'), done); | ||
}); | ||
}); | ||
it("should attach signal events", function (done) { | ||
master.emit('start', function () { | ||
hasAttachedSignalEvents(); | ||
it("should attach cluster and process events", function (done) { | ||
var instance = this.instance; | ||
instance.start(function () { | ||
verifyCluster(instance.cluster.on); | ||
verifyProcess(instance.process.on); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it("should attach cluster events", function (done) { | ||
master.emit('start', function () { | ||
hasAttachedClusterEvents(); | ||
describe("on 'destroy'", function () { | ||
beforeEach(function () { | ||
var instance = new ComboMaster(); | ||
sinon.spy(instance.cluster, "removeListener"); | ||
sinon.spy(instance.process, "removeListener"); | ||
sinon.stub(instance.cluster, "disconnect"); | ||
this.instance = instance; | ||
}); | ||
afterEach(function () { | ||
var instance = this.instance; | ||
instance.cluster.removeListener.restore(); | ||
instance.process.removeListener.restore(); | ||
instance.cluster.disconnect.restore(); | ||
this.instance = instance = null; | ||
}); | ||
it("should detach cluster and process events", function (done) { | ||
var instance = this.instance; | ||
instance.start(); | ||
instance.destroy(function () { | ||
verifyCluster(instance.cluster.removeListener); | ||
verifyProcess(instance.process.removeListener); | ||
done(); | ||
}); | ||
instance.cluster.disconnect.invokeCallback(); | ||
}); | ||
it("should not error when destroy callback missing", function () { | ||
var instance = this.instance; | ||
/*jshint immed:false */ | ||
(function () { | ||
instance.destroy(); | ||
}).should.not.throwError(); | ||
instance.cluster.disconnect.invokeCallback(); | ||
}); | ||
}); | ||
@@ -322,28 +349,22 @@ | ||
function hasAttachedClusterEvents() { | ||
cluster.listeners('fork' ).should.have.length(1); | ||
cluster.listeners('online' ).should.have.length(1); | ||
cluster.listeners('listening' ).should.have.length(1); | ||
cluster.listeners('disconnect' ).should.have.length(1); | ||
cluster.listeners('exit' ).should.have.length(1); | ||
} | ||
function verifyCluster(spyCluster) { | ||
spyCluster.callCount.should.equal(5); | ||
function hasDetachedClusterEvents() { | ||
cluster.listeners('fork' ).should.be.empty; | ||
cluster.listeners('online' ).should.be.empty; | ||
cluster.listeners('listening' ).should.be.empty; | ||
cluster.listeners('disconnect' ).should.be.empty; | ||
cluster.listeners('exit' ).should.be.empty; | ||
} | ||
spyCluster.getCall(0).calledWith('fork', sinon.match.func).should.be.ok; | ||
spyCluster.getCall(1).calledWith('online', sinon.match.func).should.be.ok; | ||
spyCluster.getCall(2).calledWith('listening', sinon.match.func).should.be.ok; | ||
spyCluster.getCall(3).calledWith('disconnect', sinon.match.func).should.be.ok; | ||
spyCluster.getCall(4).calledWith('exit', sinon.match.func).should.be.ok; | ||
function hasAttachedSignalEvents() { | ||
process.listeners('SIGINT' ).should.have.length(1); | ||
process.listeners('SIGTERM').should.have.length(1); | ||
process.listeners('SIGUSR2').should.have.length(1); | ||
return spyCluster; | ||
} | ||
function hasDetachedSignalEvents() { | ||
process.listeners('SIGINT' ).should.be.empty; | ||
process.listeners('SIGTERM').should.be.empty; | ||
process.listeners('SIGUSR2').should.be.empty; | ||
function verifyProcess(spyProcess) { | ||
spyProcess.callCount.should.equal(3); | ||
spyProcess.getCall(0).calledWith('SIGINT', sinon.match.func).should.be.ok; | ||
spyProcess.getCall(1).calledWith('SIGTERM', sinon.match.func).should.be.ok; | ||
spyProcess.getCall(2).calledWith('SIGUSR2', sinon.match.func).should.be.ok; | ||
return spyProcess; | ||
} | ||
@@ -350,0 +371,0 @@ |
@@ -1,2 +0,2 @@ | ||
/*global describe, it */ | ||
/*global describe, it, sinon */ | ||
@@ -7,2 +7,4 @@ var ComboBase = require('../lib/cluster/base'); | ||
describe("cluster worker", function () { | ||
/*jshint expr:true */ | ||
describe("instantiation", function () { | ||
@@ -32,3 +34,3 @@ it("should support empty options arg with correct defaults", function () { | ||
var instance = new ComboWorker(); | ||
var msgListeners = process.listeners('message').slice(); | ||
var msgListeners = instance.process.listeners('message').slice(); | ||
var dispatcherIndex = msgListeners.indexOf(instance._boundDispatch); | ||
@@ -61,3 +63,3 @@ | ||
instance.destroy(function () { | ||
var msgListeners = process.listeners('message').slice(); | ||
var msgListeners = instance.process.listeners('message').slice(); | ||
var dispatcherIndex = msgListeners.indexOf(instance._boundDispatch); | ||
@@ -64,0 +66,0 @@ |
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
134202
48
2685
454
7
20
+ Addedcommander@1.2.0(transitive)
+ Addedconnect@2.8.8(transitive)
+ Addedexpress@3.3.8(transitive)
+ Addedfresh@0.2.0(transitive)
+ Addedkeypress@0.1.0(transitive)
+ Addedsend@0.1.4(transitive)
+ Addeduid2@0.0.2(transitive)
- Removedcommander@0.6.1(transitive)
- Removedconnect@2.7.11(transitive)
- Removedcookie@0.0.5(transitive)
- Removedexpress@3.2.6(transitive)
- Removedfresh@0.1.0(transitive)
- Removedmime@1.2.6(transitive)
- Removedmkdirp@0.3.4(transitive)
- Removedsend@0.1.00.1.1(transitive)
Updatedexpress@3.3.x