@martinfranek/naught
Version:
zero downtime deployment for your node.js server
1,049 lines (1,022 loc) • 35.4 kB
JavaScript
var fs = require('fs');
var mkdirp = require('mkdirp');
var ncp = require('ncp').ncp;
var rimraf = require('rimraf');
var http = require('http');
var spawn = require('child_process').spawn;
var fork = require('child_process').fork;
var path = require("path");
var assert = require("assert");
var Pend = require("pend");
var zlib = require('zlib');
var assertDeepEqual = require('whynoteq').assertDeepEqual;
var fs = require('fs');
var root = path.join(__dirname, "..");
var test_root = path.join(root, "test");
var NAUGHT_MAIN = path.join(root, "lib", "main.js");
var PORT = 11904;
var HOSTNAME = 'localhost';
var TIMEOUT = 5000;
// naught child process when runnning in no daemon mode
var bin;
var steps = [
{
info: "version command",
fn: function (cb) {
naughtExec(["version"], {}, function(stdout, stderr, code) {
assertEqual(stdout.trim(), require("../package.json").version);
assertEqual(code, 0);
cb();
});
},
},
use("server1.js"),
{
info: "should get error message when starting with invalid path",
fn: function(cb) {
naughtExec(["start", "--ipc-file", "/invalid/path/foo.ipc", "server.js"], {},
function(stdout, stderr, code)
{
assertEqual(stderr, "unable to start daemon: EACCES, mkdir '/invalid'\n");
assertEqual(stdout, "");
assertEqual(code, 1);
cb();
});
},
},
{
info: "should remove an existing ipc file and startup if asked to",
fn: function(cb) {
var ipcFile = path.join(test_root, "tmp.ipc")
fs.writeFileSync(ipcFile, "fake")
naughtExec(["start", "--ipc-file", ipcFile, "--remove-old-ipc", "true", "server.js"], {},
function(stdout, stderr, code)
{
assertEqual(stderr,
"unable to connect to ipc-file `" + ipcFile + "`\n\n" +
"removing the ipc-file and attempting to continue\n" +
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n");
assertEqual(stdout, "workers online: 1\n");
assertEqual(code, 0);
cb();
});
},
},
{
info: "stop the server",
fn: function(cb) {
naughtExec(["stop", path.join(test_root, "tmp.ipc")], {}, cb)
}
},
rm(["naught.log", "stderr.log", "stdout.log"]),
{
info: "ability to start a server",
fn: function (cb) {
naughtExec(["start", "server.js"], {
PORT: PORT,
hi: "sup dawg",
}, function(stdout, stderr, code) {
assertEqual(stderr,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n");
assertEqual(stdout, "workers online: 1\n");
assertEqual(code, 0);
cb();
});
},
},
{
info: "starting a server twice prints the status of the running server",
fn: function (cb) {
naughtExec(["start", "server.js"], {}, function(stdout, stderr, code) {
assertEqual(stderr, "");
assertEqual(stdout, "workers online: 1\n");
assertEqual(code, 1);
cb();
});
},
},
{
info: "ability to query status of a running server",
fn: function (cb) {
naughtExec(["status"], {}, function(stdout, stderr, code) {
assertEqual(stderr, "");
assertEqual(stdout, "workers online: 1\n");
assertEqual(code, 0);
cb();
});
},
},
get("make sure the server is up", "/hi", "server1 sup dawg"),
use("server2.js"),
{
info: "ability to deploy to a running server",
fn: function (cb) {
naughtExec(["deploy"], {hi: "hola"}, function(stdout, stderr, code) {
assertEqual(stderr,
"SpawnNew. booting: 1, online: 1, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 1, dying: 0, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"done\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
{
info: "ability to deploy and increase workers count",
fn: function (cb) {
naughtExec(["deploy", "--worker-count", "2"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"SpawnNew. booting: 1, online: 1, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 2, online: 1, dying: 0, new_online: 0\n" +
"NewOnline. booting: 1, online: 1, dying: 0, new_online: 1\n" +
"NewOnline. booting: 0, online: 1, dying: 0, new_online: 2\n" +
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 2\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 2\n" +
"done\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
{
info: "ability to deploy and decrease workers count",
fn: function (cb) {
naughtExec(["deploy", "--worker-count", "1"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"SpawnNew. booting: 1, online: 2, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 2, dying: 0, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 1, dying: 1, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 0, dying: 2, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 1, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"done\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
get("worker cwd defaults to naught cwd", "/cwd", __dirname),
{
info: "ability to deploy and pass cwd",
fn: function (cb) {
naughtExec(["deploy", "--cwd", '../lib'], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"SpawnNew. booting: 1, online: 1, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 1, dying: 0, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"done\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
get("ability to change environment variables of workers", "/hi", "server2 hola"),
get("ability to change cwd of workers", "/cwd", path.resolve('./lib')),
{
info: "ability to stop a running server",
fn: function (cb) {
naughtExec(["stop"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 0\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
{
info: "stopping a server twice prints helpful output and exits with code 0",
fn: function (cb) {
naughtExec(["stop"], {}, function(stdout, stderr, code) {
assertEqual(stdout, "");
assertEqual(stderr, "server not running\n");
assertEqual(code, 0);
cb();
});
},
},
{
info: "redirect stdout to log file",
fn: function (cb) {
fs.readFile(path.join(test_root, "stdout.log"), "utf8", function (err, contents) {
assertEqual(contents, "server1 attempting to listen\n" +
"server2 attempting to listen\n" +
"server2 attempting to listen\n" +
"server2 attempting to listen\n" +
"server2 attempting to listen\n" +
"server2 attempting to listen\n");
cb();
});
},
},
{
info: "redirect stderr to log file",
fn: function (cb) {
fs.readFile(path.join(test_root, "stderr.log"), "utf8", function (err, contents) {
assertEqual(contents, "server1 listening\n" +
"server2 listening\n" +
"server2 listening\n" +
"server2 listening\n" +
"server2 listening\n" +
"server2 listening\n");
cb();
});
},
},
{
info: "naught log contains events",
fn: function (cb) {
fs.readFile(path.join(test_root, "naught.log"), "utf8", function (err, contents) {
assertEqual(contents, "Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"Ready. booting: 0, online: 1, dying: 0, new_online: 0\n" +
"Status. booting: 0, online: 1, dying: 0, new_online: 0\n" +
"Status. booting: 0, online: 1, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 1, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 1, dying: 0, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"Ready. booting: 0, online: 1, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 1, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 2, online: 1, dying: 0, new_online: 0\n" +
"NewOnline. booting: 1, online: 1, dying: 0, new_online: 1\n" +
"NewOnline. booting: 0, online: 1, dying: 0, new_online: 2\n" +
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 2\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 2\n" +
"Ready. booting: 0, online: 2, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 2, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 2, dying: 0, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 1, dying: 1, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 0, dying: 2, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 1, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"Ready. booting: 0, online: 1, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 1, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 1, dying: 0, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"Ready. booting: 0, online: 1, dying: 0, new_online: 0\n" +
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"Shutdown. booting: 0, online: 0, dying: 0, new_online: 0\n");
cb();
});
},
},
rm(["naught.log", "stderr.log", "stdout.log", "server.js"]),
use("server1.js"),
{
info: "start server in no daemon mode",
fn: function (cb) {
var expectedMessages = [
{
event: 'IpcListening',
},
{
event: 'Bootup',
waitingFor: null,
count: {
booting: 0,
online: 0,
dying: 0,
new_online: 0,
},
},
{
event: 'SpawnNew',
waitingFor: 'new',
count: {
booting: 1,
online: 0,
dying: 0,
new_online: 0,
},
},
{
event: 'NewOnline',
waitingFor: 'new',
count: {
booting: 0,
online: 0,
dying: 0,
new_online: 1,
},
},
];
naughtSpawn(["start", "--daemon-mode", "false", "server.js"], {
PORT: PORT,
hi: "no daemons here",
});
expectMessages(expectedMessages, cb);
},
},
get("make sure the server is up", "/hi", "server1 no daemons here"),
use("server2.js"),
{
info: "SIGHUP in no daemon mode causes a deploy",
fn: function (cb) {
var expectedMessages = [
{
event: 'SpawnNew',
waitingFor: 'new',
count: {
booting: 1,
online: 1,
dying: 0,
new_online: 0,
},
},
{
event: 'NewOnline',
waitingFor: 'new',
count: {
booting: 0,
online: 1,
dying: 0,
new_online: 1,
},
},
{
event: 'ShutdownOld',
waitingFor: 'old',
count: {
booting: 0,
online: 0,
dying: 1,
new_online: 1,
},
},
{
event: 'OldExit',
waitingFor: 'old',
count: {
booting: 0,
online: 0,
dying: 0,
new_online: 1,
},
},
{
event: 'Ready',
waitingFor: null,
count: {
booting: 0,
online: 1,
dying: 0,
new_online: 0,
},
},
];
expectMessages(expectedMessages, cb);
bin.kill('SIGHUP');
},
},
get("make sure the deploy worked", "/hi", "server2 no daemons here"),
{
info: "SIGTERM in no daemon mode causes a stop",
fn: function (cb) {
var expectedMessages = [
{
event: 'ShutdownOld',
waitingFor: 'shutdown',
count: {
booting: 0,
online: 0,
dying: 1,
new_online: 0,
},
},
{
event: 'OldExit',
waitingFor: 'shutdown',
count: {
booting: 0,
online: 0,
dying: 0,
new_online: 0,
},
},
];
var exited = false;
bin.on('exit', function(code) {
assert.strictEqual(code, 0);
exited = true;
done();
});
var gotMessages = false;
expectMessages(expectedMessages, function() {
gotMessages = true;
done();
});
bin.kill('SIGTERM');
function done() {
if (exited && gotMessages) cb();
}
},
},
use("server3.js"),
mkdir("foo"),
{
info: "cli accepts non-default args",
fn: function (cb) {
naughtExec([
"start",
"--worker-count", "5",
"--ipc-file", "some/dir/ipc",
"--log", "log/naught/a.log",
"--stderr", "log/stderr/b",
"--stdout", "log/stdout/c.",
"--max-log-size", "300",
"--cwd", "foo",
"server.js",
"--custom1", "aoeu",
"herp derp",
], {
PORT: PORT,
}, function(stdout, stderr, code) {
assertEqual(stderr,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 2, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 3, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 4, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 5, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 4, online: 0, dying: 0, new_online: 1\n" +
"NewOnline. booting: 3, online: 0, dying: 0, new_online: 2\n" +
"NewOnline. booting: 2, online: 0, dying: 0, new_online: 3\n" +
"NewOnline. booting: 1, online: 0, dying: 0, new_online: 4\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 5\n");
assertEqual(stdout, "workers online: 5\n")
assertEqual(code, 0)
cb();
});
},
},
get("command line arguments passed to server correctly", "/argv", "--custom1,aoeu,herp derp"),
get("multi-worker server responding to get requests", "/stdout", "stdout3"),
get("(test setup) generate log output", "/stderr", "stderr3"),
get("(test setup) generate log output", "/stdout", "stdout3"),
get("(test setup) generate log output", "/stderr", "stderr3"),
{
info: "ability to stop a running server with multiple workers",
fn: function (cb) {
naughtExec(["stop", "some/dir/ipc"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"ShutdownOld. booting: 0, online: 4, dying: 1, new_online: 0\n" +
"ShutdownOld. booting: 0, online: 3, dying: 2, new_online: 0\n" +
"ShutdownOld. booting: 0, online: 2, dying: 3, new_online: 0\n" +
"ShutdownOld. booting: 0, online: 1, dying: 4, new_online: 0\n" +
"ShutdownOld. booting: 0, online: 0, dying: 5, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 4, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 3, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 2, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 1, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 0\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
remove(["foo", "log", "some", "server.js"]),
use("server4.js"),
{
info: "(test setup) starting a server that won't shut down",
fn: function (cb) {
naughtExec(["start", "--worker-count", "2", "server.js"], {
PORT: PORT,
}, function(stdout, stderr, code) {
assertEqual(stderr,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 2, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 1, online: 0, dying: 0, new_online: 1\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 2\n");
assertEqual(stdout, "workers online: 2\n")
assertEqual(code, 0)
cb();
});
},
},
{
info: "ability to stop a hanging server with a timeout",
fn: function (cb) {
naughtExec(["stop", "--timeout", "0.3"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"ShutdownOld. booting: 0, online: 1, dying: 1, new_online: 0\n" +
"ShutdownOld. booting: 0, online: 0, dying: 2, new_online: 0\n" +
"Timeout. booting: 0, online: 0, dying: 2, new_online: 0\n" +
"DestroyOld. booting: 0, online: 0, dying: 2, new_online: 0\n" +
"DestroyOld. booting: 0, online: 0, dying: 2, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 1, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 0\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
rm(["naught.log", "stderr.log", "stdout.log", "server.js"]),
{
info: "ability to pass command line arguments to node",
fn: function (cb) {
naughtExec([
"start",
"--node-args", "--harmony --use-strict",
"--log", "/dev/null",
"--stderr", "/dev/null",
"--stdout", "/dev/null",
"server5.js",
], {
PORT: PORT,
}, function(stdout, stderr, code) {
assertEqual(stderr,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n");
assertEqual(stdout, "workers online: 1\n")
assertEqual(code, 0)
cb();
});
},
},
get("make sure --harmony --use-strict worked", "/hi", "0\n1\n2\nserver5 says hi\n"),
{
info: "(test setup) stopping server",
fn: function (cb) {
naughtExec(["stop"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 0\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
use("server6.js"),
{
info: "(test setup) start server6 up",
fn: function (cb) {
naughtExec(["start", "server.js"], {
PORT: PORT,
hi: "server6 says hi",
}, function(stdout, stderr, code) {
assertEqual(stderr,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n");
assertEqual(stdout, "workers online: 1\n")
assertEqual(code, 0)
cb();
});
}
},
get("(test setup) have server send offline message", "/offline", "Going offline"),
{
info: "make sure offline message spawns a new server",
fn: function (cb) {
// Wait for the server to go offline, spawn a new one, and have the old one die
setTimeout(function() {
fs.readFile(path.join(test_root, "naught.log"), "utf8", function (err, contents) {
assertEqual(contents,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"Ready. booting: 0, online: 1, dying: 0, new_online: 0\n" +
"WorkerOffline. booting: 0, online: 0, dying: 1, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 1, new_online: 0\n" +
"WorkerOnline. booting: 0, online: 1, dying: 1, new_online: 0\n" +
"Ready. booting: 0, online: 1, dying: 1, new_online: 0\n" +
"WorkerDeath. booting: 0, online: 1, dying: 0, new_online: 0\n"
);
cb();
});
}, 600);
},
},
{
info: "(test setup) stopping server",
fn: function (cb) {
naughtExec(["stop"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 0\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
rm(["naught.log", "stderr.log", "stdout.log"]),
{
info: "(test setup) start server6 up",
fn: function (cb) {
naughtExec(["start", "server.js"], {
PORT: PORT,
hi: "server6 says hi",
}, function(stdout, stderr, code) {
assertEqual(stderr,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n");
assertEqual(stdout, "workers online: 1\n")
assertEqual(code, 0)
cb();
});
}
},
get("(test setup) have server send offline message without dying", "/double-offline", "Not really going offline"),
{
info: "make sure double offline message only spawns one new server",
fn: function (cb) {
// Wait for the server to go send both offline messages
setTimeout(function() {
fs.readFile(path.join(test_root, "naught.log"), "utf8", function (err, contents) {
assertEqual(contents,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"Ready. booting: 0, online: 1, dying: 0, new_online: 0\n" +
"WorkerOffline. booting: 0, online: 0, dying: 1, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 1, new_online: 0\n" +
"WorkerOnline. booting: 0, online: 1, dying: 1, new_online: 0\n" +
"Ready. booting: 0, online: 1, dying: 1, new_online: 0\n"
);
cb();
});
}, 600);
},
},
{
info: "(test setup) stopping server",
fn: function (cb) {
naughtExec(["stop"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"ShutdownOld. booting: 0, online: 0, dying: 2, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 1, new_online: 0\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
rm(["naught.log", "stderr.log", "stdout.log", "server.js"]),
use("server7.js"),
{
info: "should exit with error code when fail to start server",
fn: function(cb) {
naughtExec(["start", "server.js"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewDeath. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"The server crashed without booting. Check stderr.log.\n");
assertEqual(stdout, "");
assertEqual(code, 1);
cb();
});
},
},
rm(["naught.log", "stderr.log", "stdout.log", "server.js"]),
use("server8.js"),
{
info: "should keep dying processes when gracefully restarting",
fn: function(cb) {
naughtExec(["start", "server.js"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n");
assertEqual(stdout, "workers online: 1\n");
assertEqual(code, 0);
setTimeout(function(){
naughtExec(["deploy"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"SpawnNew. booting: 1, online: 1, dying: 1, new_online: 0\n" +
"NewOnline. booting: 0, online: 1, dying: 1, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 0, dying: 2, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 1, new_online: 1\n" +
"done\n");
assertEqual(stdout, "");
assertEqual(code, 0);
naughtExec(["stop"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"ShutdownOld. booting: 0, online: 0, dying: 2, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 1, new_online: 0\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
});
}, 1500);
})
},
},
rm(["naught.log", "stderr.log", "stdout.log", "server.js"]),
use("server9.js"),
{
info: "(test setup) starting server",
fn: function(cb) {
naughtExec(["start", "server.js"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"Bootup. booting: 0, online: 0, dying: 0, new_online: 0\n" +
"SpawnNew. booting: 1, online: 0, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 0, dying: 0, new_online: 1\n");
assertEqual(stdout, "workers online: 1\n");
assertEqual(code, 0);
cb();
assertEqual(fs.existsSync(path.join(test_root, "/tmp"), "utf8"), false)
});
}
},
{
info: "make sure tasks in the shutdown message handler are executed",
fn: function(cb) {
naughtExec(["deploy"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"SpawnNew. booting: 1, online: 1, dying: 0, new_online: 0\n" +
"NewOnline. booting: 0, online: 1, dying: 0, new_online: 1\n" +
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 1\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 1\n" +
"done\n");
assertEqual(stdout, "");
assertEqual(code, 0)
assertEqual(fs.readFileSync(path.join(test_root, "/tmp"), "utf8"), "shutdown")
cb();
});
},
},
{
info: "(test setup) stopping server",
fn: function (cb) {
naughtExec(["stop"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"ShutdownOld. booting: 0, online: 0, dying: 1, new_online: 0\n" +
"OldExit. booting: 0, online: 0, dying: 0, new_online: 0\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
}
},
rm(["naught.log", "stderr.log", "stdout.log", "server.js", "tmp"]),
use("server10.js"),
{
info: "Should expose worker number via process.env.NAUGHT_WORKER",
fn: function(cb) {
naughtExec(["start","--worker-count", "4", "server.js"], {}, function(stdout, stderr, code) {
assertEqual(fs.readFileSync("./test/stdout.log", "UTF8").split('\n').sort().splice(1).join('\n'),
"worker #0\n"+
"worker #1\n"+
"worker #2\n"+
"worker #3");
assertEqual(code, 0);
cb();
assertEqual(fs.existsSync(path.join(test_root, "/tmp"), "utf8"), false)
});
}
},
{
info: "numbers should be consistent across deploys",
fn: function(cb) {
naughtExec(["deploy"], {}, function(stdout, stderr, code) {
assertEqual(fs.readFileSync("./test/stdout.log", "UTF8").split('\n').sort().splice(1).join('\n'),
"worker #0\n"+
"worker #0\n"+
"worker #1\n"+
"worker #1\n"+
"worker #2\n"+
"worker #2\n"+
"worker #3\n"+
"worker #3");
assertEqual(code, 0)
cb();
});
},
},
{
info: "(test setup) stopping server",
fn: function (cb) {
naughtExec(["stop"], {}, function(stdout, stderr, code) {
assertEqual(stderr,
"ShutdownOld. booting: 0, online: 3, dying: 1, new_online: 0\n"+
"ShutdownOld. booting: 0, online: 2, dying: 2, new_online: 0\n"+
"ShutdownOld. booting: 0, online: 1, dying: 3, new_online: 0\n"+
"ShutdownOld. booting: 0, online: 0, dying: 4, new_online: 0\n"+
"OldExit. booting: 0, online: 0, dying: 3, new_online: 0\n"+
"OldExit. booting: 0, online: 0, dying: 2, new_online: 0\n"+
"OldExit. booting: 0, online: 0, dying: 1, new_online: 0\n"+
"OldExit. booting: 0, online: 0, dying: 0, new_online: 0\n");
assertEqual(stdout, "");
assertEqual(code, 0)
cb();
});
},
},
rm(["naught.log", "stderr.log", "stdout.log", "server.js"]),
];
var stepCount = steps.length;
cleanUp();
doStep();
function cleanUp() {
console.log("** Cleaning up previous tests content.")
deleteIfPresent(path.join(test_root, "stdout.log"));
deleteIfPresent(path.join(test_root, "stderr.log"));
deleteIfPresent(path.join(test_root, "naught.log"));
console.log("** Done cleaning up.")
}
function deleteIfPresent(filename) {
if (fs.existsSync(filename)) {
fs.unlinkSync(filename);
}
}
function doStep() {
var step = steps.shift();
process.stderr.write(step.info + "...")
var interval = setTimeout(function() {
fs.writeSync(process.stderr.fd, "timeout\n")
process.exit(1);
}, TIMEOUT);
step.fn(function (err) {
assert.ifError(err);
clearTimeout(interval);
process.stderr.write("pass\n");
if (steps.length === 0) {
process.stderr.write(stepCount + " tests passed\n");
} else {
doStep();
}
});
}
function exec(cmd, args, opts, cb){
if (args == null) args = [];
if (opts == null) opts = {};
if (cb == null) cb = noop;
var bin = spawn(cmd, args, opts);
var stdout = ""
bin.stdout.setEncoding('utf8')
bin.stdout.on('data', function(data) {
stdout += data;
});
var stderr = ""
bin.stderr.setEncoding('utf8')
bin.stderr.on('data', function(data) {
stderr += data;
});
bin.on('close', function(code, signal) {
cb(stdout, stderr, code, signal);
});
}
function extend(obj, src) {
for (var key in src) {
obj[key] = src[key];
}
return obj;
}
function naughtSpawn(args, env, cb) {
env = extend(extend({}, process.env), env || {});
bin = fork(NAUGHT_MAIN, args, {
cwd: __dirname,
env: env,
});
}
function naughtExec(args, env, cb) {
env = extend(extend({}, process.env), env || {});
exec(process.execPath, [NAUGHT_MAIN].concat(args), {
cwd: __dirname,
env: env
}, function(stdout, stderr, code, signal) {
cb(stdout, stderr, code);
});
}
function collectLogFiles(test_path, cb) {
fs.readdir(path.join(test_root, test_path), function (err, files) {
if (err) return cb(err);
files.sort();
if (! /\.gz$/.test(files[0])) {
files.push(files.shift());
}
var pend = new Pend();
var results;
files.forEach(function(file) {
pend.go(function(cb) {
fs.readFile(path.join(test_root, test_path, file), function (err, data) {
if (err) return cb(err);
if (/\.gz$/.test(file)) {
zlib.gunzip(data, function (err, data) {
if (err) return cb(err);
results = {file: file, data: data};
cb();
});
} else {
results = {file: file, data: data};
cb();
}
});
});
});
pend.wait(function(err) {
if (err) return cb(err);
var fullData = "";
results.forEach(function(item) {
fullData += item.data.toString();
});
cb(null, results, fullData);
});
});
}
function use(script) {
return {
info: "(test setup) use " + script,
fn: function (cb) {
ncp(path.join(test_root, script), path.join(test_root, "server.js"), cb);
},
};
}
function mkdir(dir) {
return {
info: "(test setup) mkdir " + dir,
fn: function (cb) {
mkdirp(path.join(test_root, dir), cb);
},
};
}
function rm(files) {
return {
info: "(test setup) rm " + files.join(" "),
fn: function (cb) {
var pend = new Pend();
files.forEach(function(file) {
fs.unlink(path.join(test_root, file), pend.hold());
});
pend.wait(cb);
},
}
}
function remove(files) {
return {
info: "(test setup) rm -rf " + files.join(" "),
fn: function (cb) {
var pend = new Pend();
files.forEach(function(file) {
rimraf(path.join(test_root, file), pend.hold());
});
pend.wait(cb);
},
}
}
function get(info, url, expected_resp) {
return {
info: info,
fn: function (cb) {
http.request({
hostname: HOSTNAME,
port: PORT,
path: url,
}, function (res) {
var body;
assertEqual(res.statusCode, 200);
body = ""
res.on('data', function(data) {
body += data;
});
res.on('end', function() {
assertEqual(body, expected_resp);
cb();
});
}).end();
},
};
}
function expectMessages(msgList, cb) {
bin.on('message', onMessage);
function onMessage(message) {
var expected = msgList.shift();
assertDeepEqual(message, expected, "expected: " +
JSON.stringify(expected, null, 2) +
"\ngot: " + JSON.stringify(message, null, 2));
if (msgList.length === 0) {
bin.removeListener('message', onMessage);
cb();
}
}
}
function assertEqual(actual, expected, msg) {
msg = msg || "";
assert(actual === expected, "actual:\n" + actual + "\nexpected:\n" + expected + "\n" + msg);
}
function noop() {}