diff --git a/.gitignore b/.gitignore index 3c3629e64..fb0da4cd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +!node_modules/nodeunit diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..699e144ea --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "node_modules/nodeunit"] + path = node_modules/nodeunit + url = git://github.com/godsflaw/nodeunit.git diff --git a/config/log.syslog.ini b/config/log.syslog.ini index bf8cf9b87..90b81593a 100644 --- a/config/log.syslog.ini +++ b/config/log.syslog.ini @@ -6,3 +6,4 @@ odelay=1 cons=0 ndelay=0 nowait=0 +always_ok=false diff --git a/docs/plugins/log.syslog.md b/docs/plugins/log.syslog.md index 68fb0b6d1..20975ec56 100644 --- a/docs/plugins/log.syslog.md +++ b/docs/plugins/log.syslog.md @@ -61,3 +61,11 @@ chosen for you. Don't wait for child processes that may have been created while logging the message. + + +* log.syslog.general.always_ok (default: false) + + If false, then this plugin will return with just next() allowing other + plugins that have registered for the log hook to run. To speed things up, + if no other log hooks need to run (daemon), then one can make this true. + This will case the plugin to always call next(OK). diff --git a/node_modules/nodeunit b/node_modules/nodeunit new file mode 160000 index 000000000..bdb457a6e --- /dev/null +++ b/node_modules/nodeunit @@ -0,0 +1 @@ +Subproject commit bdb457a6ed100b73329ca38047d41b6ee38fea29 diff --git a/plugins/log.syslog.js b/plugins/log.syslog.js index a1d9e8411..aad004e52 100644 --- a/plugins/log.syslog.js +++ b/plugins/log.syslog.js @@ -1,16 +1,20 @@ // send logs to syslog -var Syslog = require('node-syslog'); +var Syslog = exports.Syslog = require('node-syslog'); exports.register = function() { - var options = 0; - var ini = this.config.get('log.syslog.ini'); - var name = ini.general && (ini.general['name'] || 'haraka'); - var facility = ini.general && (ini.general['facility'] || 'MAIL'); - var pid = ini.general && (ini.general['log_pid'] || 1); - var odelay = ini.general && (ini.general['log_odelay'] || 1); - var cons = ini.general && (ini.general['log_cons'] || 0); - var ndelay = ini.general && (ini.general['log_ndelay'] || 0); - var nowait = ini.general && (ini.general['log_nowait'] || 0); + var options = 0; + var ini = this.config.get('log.syslog.ini'); + ini.general = ini.general || {}; + var name = ini.general['name'] || 'haraka'; + var facility = ini.general['facility'] || 'MAIL'; + var pid = ini.general['log_pid'] || 1; + var odelay = ini.general['log_odelay'] || 1; + var cons = ini.general['log_cons'] || 0; + var ndelay = ini.general['log_ndelay'] || 0; + var nowait = ini.general['log_nowait'] || 0; + var always_ok = ini.general['always_ok'] || false; + + this.always_ok = always_ok; if (pid) options |= Syslog.LOG_PID; @@ -84,9 +88,11 @@ exports.register = function() { } this.register_hook('log', 'syslog'); -} +}; exports.syslog = function (next, logger, log) { + var plugin = this; + switch(log.level.toUpperCase()) { case 'INFO': Syslog.log(Syslog.LOG_INFO, log.data); @@ -116,5 +122,10 @@ exports.syslog = function (next, logger, log) { Syslog.log(Syslog.LOG_DEBUG, log.data); } - return next(); -} + if (plugin.always_ok) { + return next(OK); + } + else { + return next(); + } +}; diff --git a/run_tests b/run_tests new file mode 100755 index 000000000..e4f03db8b --- /dev/null +++ b/run_tests @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +require.paths.unshift(__dirname); + +try { + var reporter = require('nodeunit').reporters.default; +} +catch(e) { + console.log("Error: " + e.message); + console.log(""); + console.log("Cannot find nodeunit module."); + console.log("You can download submodules for this project by doing:"); + console.log(""); + console.log(" git submodule init"); + console.log(" git submodule update"); + console.log(""); + process.exit(); +} + +process.chdir(__dirname); +reporter.run(['tests/plugins']); diff --git a/tests/fixtures/stub.js b/tests/fixtures/stub.js new file mode 100644 index 000000000..ec1af39d4 --- /dev/null +++ b/tests/fixtures/stub.js @@ -0,0 +1,12 @@ +module.exports = function (returnValue) { + function stub() { + stub.called = true; + stub.args = arguments; + stub.thisArg = this; + return returnValue; + } + + stub.called = false; + + return stub; +}; diff --git a/tests/fixtures/stub_connection.js b/tests/fixtures/stub_connection.js new file mode 100644 index 000000000..479b6e94a --- /dev/null +++ b/tests/fixtures/stub_connection.js @@ -0,0 +1,24 @@ +"use strict"; + +var stub = require('tests/fixtures/stub'); + +var connection = exports; + +function Connection(client, server) { + this.client = client; + this.server = server; + this.relaying = false; +} + +connection.createConnection = function(client, server) { + if (typeof(client) === 'undefined') { + client = {}; + } + + if (typeof(server) === 'undefined') { + server = {}; + } + + var obj = new Connection(client, server); + return obj; +}; diff --git a/tests/fixtures/stub_logger.js b/tests/fixtures/stub_logger.js new file mode 100644 index 000000000..dff8aad25 --- /dev/null +++ b/tests/fixtures/stub_logger.js @@ -0,0 +1,13 @@ +"use strict"; + +var stub = require('tests/fixtures/stub'); + +var logger = exports; + +function Logger() { +} + +logger.createLogger = function() { + var obj = new Logger(); + return obj; +}; diff --git a/tests/fixtures/stub_plugin.js b/tests/fixtures/stub_plugin.js new file mode 100644 index 000000000..d74ce5485 --- /dev/null +++ b/tests/fixtures/stub_plugin.js @@ -0,0 +1,21 @@ +"use strict"; + +var stub = require('tests/fixtures/stub'); + +var plugin = exports; + +function Plugin(name) { +} + +plugin.createPlugin = function(name) { + var obj = new Plugin(name); + var plug = require(name); + + for (var k in plug) { + if (plug.hasOwnProperty(k)) { + obj[k] = plug[k]; + } + } + + return obj; +}; diff --git a/tests/plugins/log.syslog.js b/tests/plugins/log.syslog.js new file mode 100644 index 000000000..d20e2affd --- /dev/null +++ b/tests/plugins/log.syslog.js @@ -0,0 +1,159 @@ +var stub = require('tests/fixtures/stub'), + constants = require('../../constants'), + Logger = require('tests/fixtures/stub_logger'), + Plugin = require('tests/fixtures/stub_plugin'); + +// huge hack here, but plugin tests need constants +constants.import(global); + +function _set_up(callback) { + this.backup = {}; + + // needed for tests + this.plugin = Plugin.createPlugin('plugins/log.syslog'); + this.logger = Logger.createLogger(); + + // backup modifications + this.backup.plugin = {}; + this.backup.plugin.Syslog = {}; + this.backup.plugin.register_hook = this.plugin.register_hook; + + // stub out functions + this.plugin.register_hook = stub(); + this.plugin.config = stub(); + this.log = stub(); + this.log.level = 'info'; + this.log.data = "this is a test log message"; + + // some test data + this.configfile = { + general : { + name : 'haraka', + facility : 'MAIL', + log_pid : 1, + log_odelay : 1, + log_cons : 0, + log_ndelay : 0, + log_nowait : 0, + always_ok : false + } + }; + this.plugin.config.get = function (file) { + return this.configfile; + }.bind(this); + + // going to need these in multiple tests + this.plugin.register(); + + callback(); +} + +function _tear_down(callback) { + // restore backed up functions + this.plugin.register_hook = this.backup.plugin.register_hook; + + callback(); +} + +exports.log_syslog = { + setUp : _set_up, + tearDown : _tear_down, + 'should have register function' : function (test) { + test.expect(2); + test.isNotNull(this.plugin); + test.isFunction(this.plugin.register); + test.done(); + }, + 'register function should call register_hook()' : function (test) { + test.expect(1); + test.ok(this.plugin.register_hook.called); + test.done(); + }, + 'register_hook() should register for propper hook' : function (test) { + test.expect(1); + test.equals(this.plugin.register_hook.args[0], 'log'); + test.done(); + }, + 'register_hook() should register available function' : function (test) { + test.expect(3); + test.equals(this.plugin.register_hook.args[1], 'syslog'); + test.isNotNull(this.plugin.syslog); + test.isFunction(this.plugin.syslog); + test.done(); + }, + 'register calls Syslog.init()' : function (test) { + // local setup + this.backup.plugin.Syslog.init = this.plugin.Syslog.init; + this.plugin.Syslog.init = stub(); + this.plugin.register(); + + test.expect(1); + test.ok(this.plugin.Syslog.init.called); + test.done(); + + // local teardown + this.plugin.Syslog.init = this.backup.plugin.Syslog.init; + }, + 'register calls Syslog.init() with correct args' : function (test) { + // local setup + this.backup.plugin.Syslog.init = this.plugin.Syslog.init; + this.plugin.Syslog.init = stub(); + this.plugin.register(); + + test.expect(4); + test.ok(this.plugin.Syslog.init.called); + test.equals(this.plugin.Syslog.init.args[0], + this.plugin.config.get("test").general.name); + test.equals(this.plugin.Syslog.init.args[1], + this.plugin.Syslog.LOG_PID | this.plugin.Syslog.LOG_ODELAY); + test.equals(this.plugin.Syslog.init.args[2], + this.plugin.Syslog.LOG_MAIL); + test.done(); + + // local teardown + this.plugin.Syslog.init = this.backup.plugin.Syslog.init; + }, + 'hook returns just next() if configured to do so' : function (test) { + var next = function (action) { + test.expect(1); + test.isUndefined(action); + test.done(); + }; + + this.plugin.syslog(next, this.logger, this.log); + }, + 'hook returns next(OK) if configured to do so' : function (test) { + // local setup + this.backup.configfile = this.configfile; + this.configfile.general.always_ok = true; + this.plugin.register(); + + var next = function (action) { + test.expect(1); + test.equals(action, constants.ok); + test.done(); + }; + + this.plugin.syslog(next, this.logger, this.log); + + // local teardown + this.configfile = this.backup.configfile; + }, + 'syslog hook logs correct thing' : function (test) { + // local setup + var next = stub(); + this.backup.plugin.Syslog.log = this.plugin.Syslog.log; + this.plugin.Syslog.log = stub(); + this.plugin.syslog(next, this.logger, this.log); + + test.expect(3); + test.ok(this.plugin.Syslog.log.called); + test.equals(this.plugin.Syslog.log.args[0], + this.plugin.Syslog.LOG_INFO); + test.equals(this.plugin.Syslog.log.args[1], this.log.data); + test.done(); + + // local teardown + this.plugin.Syslog.log = this.backup.plugin.Syslog.log; + } +}; diff --git a/tests/plugins/relay_all.js b/tests/plugins/relay_all.js new file mode 100644 index 000000000..08ef62009 --- /dev/null +++ b/tests/plugins/relay_all.js @@ -0,0 +1,82 @@ +var stub = require('tests/fixtures/stub'), + constants = require('../../constants'), + Connection = require('tests/fixtures/stub_connection'), + Plugin = require('tests/fixtures/stub_plugin'); + +// huge hack here, but plugin tests need constants +constants.import(global); + +function _set_up(callback) { + this.backup = {}; + + // needed for tests + this.plugin = Plugin.createPlugin('plugins/relay_all'); + this.connection = Connection.createConnection(); + this.params = ['foo@bar.com']; + + // backup modifications + this.backup.plugin = {}; + this.backup.plugin.register_hook = this.plugin.register_hook; + + // stub out functions + this.plugin.register_hook = stub(); + this.connection.loginfo = stub(); + + // going to need these in multiple tests + this.plugin.register(); + + callback(); +} + +function _tear_down(callback) { + // restore backed up functions + this.plugin.register_hook = this.backup.plugin.register_hook; + + callback(); +} + +exports.relay_all = { + setUp : _set_up, + tearDown : _tear_down, + 'should have register function' : function (test) { + test.expect(2); + test.isNotNull(this.plugin); + test.isFunction(this.plugin.register); + test.done(); + }, + 'register function should call register_hook()' : function (test) { + test.expect(1); + test.ok(this.plugin.register_hook.called); + test.done(); + }, + 'register_hook() should register for propper hook' : function (test) { + test.expect(1); + test.equals(this.plugin.register_hook.args[0], 'rcpt'); + test.done(); + }, + 'register_hook() should register available function' : function (test) { + test.expect(3); + test.equals(this.plugin.register_hook.args[1], 'confirm_all'); + test.isNotNull(this.plugin.confirm_all); + test.isFunction(this.plugin.confirm_all); + test.done(); + }, + 'confirm_all hook always returns OK' : function (test) { + var next = function (action) { + test.expect(1); + test.equals(action, constants.ok); + test.done(); + }; + + this.plugin.confirm_all(next, this.connection, this.params); + }, + 'confirm_all hook always sets connection.relaying to 1' : function (test) { + var next = function (action) { + test.expect(1); + test.equals(this.connection.relaying, 1); + test.done(); + }.bind(this); + + this.plugin.confirm_all(next, this.connection, this.params); + } +};