From 264ef6eeb68e1f9e1c780b20aeb4872d90589bb3 Mon Sep 17 00:00:00 2001 From: Dominic Bosch Date: Mon, 17 Feb 2014 23:27:26 +0100 Subject: [PATCH] Huge refactoring of architecture started --- coffee/config.coffee | 67 +++---- coffee/http_listener.coffee | 9 +- coffee/new_logging.coffee | 59 ++++++ coffee/persistence.coffee | 4 +- coffee/request_handler.coffee | 23 +-- coffee/server.coffee | 133 ++++++++----- js-coffee/config.js | 68 ++++--- js-coffee/engine.js | 9 +- js-coffee/{eventpoller.js => event_poller.js} | 38 ++-- js-coffee/http_listener.js | 7 +- js-coffee/logging.js | 3 + js-coffee/module_loader.js | 81 -------- js-coffee/module_manager.js | 174 ++++++++---------- js-coffee/new_logging.js | 66 +++++++ js-coffee/request_handler.js | 25 +-- js-coffee/server.js | 152 ++++++++++----- js/logging.js | 28 ++- package.json | 4 +- run_server.sh | 2 +- run_tests.sh | 15 +- testing/js/test_logging.js | 5 + testing/js/test_persistence.js | 87 +++++++++ testing/test_config.coffee | 21 ++- testing/test_logging.coffee | 68 +++++++ testing/test_persistence.coffee | 5 +- 25 files changed, 742 insertions(+), 411 deletions(-) create mode 100644 coffee/new_logging.coffee rename js-coffee/{eventpoller.js => event_poller.js} (82%) delete mode 100644 js-coffee/module_loader.js create mode 100644 js-coffee/new_logging.js create mode 100644 testing/js/test_logging.js create mode 100644 testing/js/test_persistence.js create mode 100644 testing/test_logging.coffee diff --git a/coffee/config.coffee b/coffee/config.coffee index db9fda2..105f275 100644 --- a/coffee/config.coffee +++ b/coffee/config.coffee @@ -7,26 +7,26 @@ Configuration ### # **Requires:** - -# - [Logging](logging.html) -log = require './logging' - # - Node.js Modules: [fs](http://nodejs.org/api/fs.html) and # [path](http://nodejs.org/api/path.html) fs = require 'fs' path = require 'path' ### -##Module call +Module call +----------- + +Calling the module as a function will act as a constructor and load the config file. +It is possible to hand an args object with the properties nolog (true if no outputs shall +be generated) and configPath for a custom configuration file path. -Calling the module as a function will make it look for the `configPath` property in the -args object and then try to load a config file from that relative path. @param {Object} args ### -exports = module.exports = ( args ) -> +exports = module.exports = ( args ) => args = args ? {} - log args - if typeof args.configPath is 'string' + if args.nolog + @nolog = true + if args.configPath loadConfigFile args.configPath else loadConfigFile path.join 'config', 'config.json' @@ -41,20 +41,25 @@ Reads the config file synchronously from the file system and try to parse it. ### loadConfigFile = ( configPath ) => @config = null + confProperties = [ + 'log' + 'http-port' + 'db-port' + 'crypto-key' + ] + #TODO Try to get rid of crypto key try @config = JSON.parse fs.readFileSync path.resolve __dirname, '..', configPath - if @config and @config.http_port and @config.db_port and - @config.crypto_key and @config.session_secret - log.print 'CF', 'config file loaded successfully' - else - log.error 'CF', new Error """Missing property in config file, requires: - - http_port - - db_port - - crypto_key - - session_secret""" + isReady = true + for prop in confProperties + if !@config[prop] + isReady = false + if not isReady and not @nolog + console.error """Missing property in config file, requires: + - #{ confProperties.join "\n -" }""" catch e - e.addInfo = 'no config ready' - log.error 'CF', e + if not @nolog + console.error "Failed loading config file: #{ e.message }" ### @@ -77,25 +82,25 @@ exports.isReady = => @config? @public getHttpPort() ### -exports.getHttpPort = -> fetchProp 'http_port' +exports.getHttpPort = -> fetchProp 'http-port' ### ***Returns*** the DB port* @public getDBPort() ### -exports.getDBPort = -> fetchProp 'db_port' +exports.getDBPort = -> fetchProp 'db-port' + +### +***Returns*** the log conf object + +@public getLogConf() +### +exports.getLogConf = -> fetchProp 'log' ### ***Returns*** the crypto key @public getCryptoKey() ### -exports.getCryptoKey = -> fetchProp 'crypto_key' - -### -***Returns*** the session secret - -@public getSessionSecret() -### -exports.getSessionSecret = -> fetchProp 'session_secret' +exports.getCryptoKey = -> fetchProp 'crypto-key' diff --git a/coffee/http_listener.coffee b/coffee/http_listener.coffee index 6bac9ff..f0c4418 100644 --- a/coffee/http_listener.coffee +++ b/coffee/http_listener.coffee @@ -15,6 +15,7 @@ log = require './logging' # - [Config](config.html) config = require './config' +# TODO remove config # - [User Handler](user_handler.html) requestHandler = require './request_handler' @@ -53,10 +54,14 @@ Adds the shutdown handler to the admin commands. @public addHandlers( *fShutDown* ) ### exports.addHandlers = ( fShutDown ) -> - requestHandler.addHandlers fShutDown + requestHandler.addShutdownHandler fShutDown # Add cookie support for session handling. app.use express.cookieParser() - app.use express.session { secret: config.getSessionSecret() } + #TODO This needs to be fixed! + sess_sec = "149u*y8C:@kmN/520Gt\\v'+KFBnQ!\\r<>5X/xRI`sT + emptylog = + { + info: () -> + warn: () -> + error: () -> + } + args = args ? {} + if args.nolog + emptylog + else + try + opt = + name: "webapi-eca" + if args['mode'] is 'development' + opt.src = true + if args['file-path'] + @logPath = path.resolve __dirname, '..', 'logs', args['file-path'] + else + @logPath = path.resolve __dirname, '..', 'logs', 'server.log' + opt.streams = [ + { + level: args['io-level'] + stream: process.stdout + }, + { + level: args['file-level'] + type: 'rotating-file' + path: @logPath + period: '1d' + count: 3 + } + ] + bunyan.createLogger opt + catch e + console.error e + emptylog \ No newline at end of file diff --git a/coffee/persistence.coffee b/coffee/persistence.coffee index e69ba85..1564e8b 100644 --- a/coffee/persistence.coffee +++ b/coffee/persistence.coffee @@ -55,7 +55,7 @@ exports = module.exports = ( args ) => @ai = new IndexedModules( 'action-invoker', @db ) else log.error 'DB', 'Initialization failed because of missing config file!' - + ### Checks whether the db is connected and passes either an error on failure after ten attempts within five seconds, or nothing on success to the callback(err). @@ -845,4 +845,4 @@ Shuts down the db link. @public shutDown() ### -exports.shutDown = => @db.quit() +exports.shutDown = () => @db.quit() diff --git a/coffee/request_handler.coffee b/coffee/request_handler.coffee index 265b83d..cd537c8 100644 --- a/coffee/request_handler.coffee +++ b/coffee/request_handler.coffee @@ -28,14 +28,6 @@ qs = require 'querystring' # [crypto-js](https://github.com/evanvosberg/crypto-js) mustache = require 'mustache' crypto = require 'crypto-js' - -# Prepare the admin command handlers which are invoked via HTTP requests. -objAdminCmds = - 'loadrules': mm.loadRulesFromFS - 'loadaction': mm.loadActionModuleFromFS - 'loadactions': mm.loadActionModulesFromFS - 'loadevent': mm.loadEventModuleFromFS - 'loadevents': mm.loadEventModulesFromFS # Prepare the user command handlers which are invoked via HTTP requests. objUserCmds = @@ -57,15 +49,14 @@ exports = module.exports = ( args ) -> module.exports ### -This allows the parent to add handlers. The event handler will receive -the events that were received. The shutdown function will be called if the -admin command shutdown is issued. +This allows the parent to add the shutdown handler. +The shutdown function will be called if the admin command shutdown is issued. -@public addHandlers( *fShutdown* ) +@public addShutdownHandler( *fShutdown* ) @param {function} fShutdown ### -exports.addHandlers = ( fShutdown ) => - objAdminCmds.shutdown = ( args, answerHandler ) -> +exports.addShutdownHandler = ( fShutdown ) => + @objAdminCmds.shutdown = ( args, answerHandler ) -> answerHandler.answerSuccess 'Shutting down... BYE!' setTimeout fShutdown, 500 @@ -284,13 +275,13 @@ objects.* @public handleAdmin( *req, resp* ) ### -exports.handleAdmin = ( req, resp ) -> +exports.handleAdmin = ( req, resp ) => if req.session and req.session.user if req.session.user.isAdmin is "true" q = req.query log.print 'RH', 'Received admin request: ' + req.originalUrl if q.cmd - objAdminCmds[q.cmd]? q, answerHandler req, resp, true + @objAdminCmds[q.cmd]? q, answerHandler req, resp, true else resp.send 404, 'Command unknown!' else diff --git a/coffee/server.coffee b/coffee/server.coffee index 4a9ea8a..ac8c5a5 100644 --- a/coffee/server.coffee +++ b/coffee/server.coffee @@ -5,26 +5,15 @@ Server >This is the main module that is used to run the whole server: > -> node server [log_type http_port] +> node server [opt] > ->Valid `log_type`'s are: -> ->- `0`: standard I/O output (default) ->- `1`: log file (server.log) ->- `2`: silent -> ->`http_port` can be set to use another port, than defined in the ->[config](config.html) file, to listen to, e.g. used by the test suite. -> -> - -TODO how about we allow spawning child processes with servers based on address? +> See below in the optimist CLI preparation for allowed optional parameters ### # **Requires:** # - [Logging](logging.html) -log = require './logging' +logger = require './new_logging' # - [Configuration](config.html) conf = require './config' @@ -38,9 +27,54 @@ engine = require './engine' # - [HTTP Listener](http_listener.html) http_listener = require './http_listener' -args = {} +# - Node.js Modules: [fs](http://nodejs.org/api/fs.html), +# [path](http://nodejs.org/api/path.html) +# and [child_process](http://nodejs.org/api/child_process.html) +fs = require 'fs' +path = require 'path' +cp = require 'child_process' + +# - External Module: [optimist](https://github.com/substack/node-optimist) +optimist = require 'optimist' + procCmds = {} +### +Let's prepare the optimist CLI +### +usage = 'This runs your webapi-based ECA engine' +opt = + 'h': + alias : 'help', + describe: 'Display this' + 'c': + alias : 'config-path', + describe: 'Specify a path to a custom configuration file, other than "config/config.json"' + 'w': + alias : 'http-port', + describe: 'Specify a HTTP port for the web server' + 'd': + alias : 'db-port', + describe: 'Specify a port for the redis DB' + 'm': + alias : 'log-mode', + describe: 'Specify a log mode: [development|productive]' + 'i': + alias : 'log-io-level', + describe: 'Specify the log level for the I/O' + 'f': + alias : 'log-file-level', + describe: 'Specify the log level for the log file' + 'p': + alias : 'log-file-path', + describe: 'Specify the path to the log file within the "logs" folder' + 'n': + alias : 'nolog', + describe: 'Set this if no output shall be generated' +argv = optimist.usage( usage ).options( opt ).argv +if argv.help + console.log optimist.help() + process.exit() ### Error handling of the express port listener requires special attention, @@ -50,7 +84,7 @@ the port is already in use. process.on 'uncaughtException', ( err ) -> switch err.errno when 'EADDRINUSE' - err.addInfo = 'http_port already in use, shutting down!' + err.addInfo = 'http-port already in use, shutting down!' log.error 'RS', err shutDown() # else log.error 'RS', err @@ -61,50 +95,63 @@ This function is invoked right after the module is loaded and starts the server. @private init() ### init = -> - log.print 'RS', 'STARTING SERVER' - conf args + conf argv.c # > Check whether the config file is ready, which is required to start the server. if !conf.isReady() - log.error 'RS', 'Config file not ready!' + console.error 'FAIL: Config file not ready! Shutting down...' process.exit() - - # > Fetch the `log_type` argument and post a log about which log type is used. - if process.argv.length > 2 - args.logType = parseInt(process.argv[2]) || 0 - switch args.logType - when 0 then log.print 'RS', 'Log type set to standard I/O output' - when 1 then log.print 'RS', 'Log type set to file output' - when 2 then log.print 'RS', 'Log type set to silent' - else log.print 'RS', 'Unknown log type, using standard I/O' - log args - else - log.print 'RS', 'No log method argument provided, using standard I/O' - - # > Fetch the `http_port` argument - if process.argv.length > 3 then args.http_port = parseInt process.argv[3] - else log.print 'RS', 'No HTTP port passed, using standard port from config file' + + logconf = conf.getLogConf() + if argv.m + logconf[ 'mode' ] = argv.m + if argv.i + logconf[ 'io-level' ] = argv.i + if argv.f + logconf[ 'file-level' ] = argv.f + if argv.p + logconf[ 'file-path' ] = argv.p + if argv.n + logconf[ 'nolog' ] = argv.n + try + fs.unlinkSync path.resolve __dirname, '..', 'logs', logconf[ 'file-path' ] + log = logger logconf + log.info 'RS | STARTING SERVER' + + args = + logger: log + logconf: logconf + # > Fetch the `http-port` argument + args[ 'http-port' ] = parseInt argv.w || conf.getHttpPort() - log.print 'RS', 'Initialzing DB' + log.info 'RS | Initialzing DB' db args # > We only proceed with the initialization if the DB is ready db.isConnected ( err, result ) -> if !err # > Initialize all required modules with the args object. - log.print 'RS', 'Initialzing engine' + log.info 'RS | Initialzing engine' engine args - log.print 'RS', 'Initialzing http listener' + log.info 'RS | Initialzing http listener' http_listener args # > Distribute handlers between modules to link the application. - log.print 'RS', 'Passing handlers to engine' + log.info 'RS | Passing handlers to engine' engine.addPersistence db - log.print 'RS', 'Passing handlers to http listener' + log.info 'RS | Passing handlers to http listener' #TODO engine pushEvent needs to go into redis queue - http_listener.addHandlers shutDown - #log.print 'RS', 'Passing handlers to module manager' + http_listener.addShutdownHandler shutDown #TODO loadAction and addRule will be removed #mm.addHandlers db, engine.loadActionModule, engine.addRule + log.info 'RS | For e child process for the event poller' + cliArgs = [ + args.logconf['mode'] + args.logconf['io-level'] + args.logconf['file-level'] + args.logconf['file-path'] + args.logconf['nolog'] + ] + poller = cp.fork path.resolve( __dirname, 'event_poller' ), cliArgs ### Shuts down the server. @@ -112,7 +159,7 @@ Shuts down the server. @private shutDown() ### shutDown = -> - log.print 'RS', 'Received shut down command!' + log.warn 'RS | Received shut down command!' engine?.shutDown() http_listener?.shutDown() diff --git a/js-coffee/config.js b/js-coffee/config.js index d5d3740..789b924 100644 --- a/js-coffee/config.js +++ b/js-coffee/config.js @@ -8,28 +8,31 @@ Configuration (function() { - var exports, fetchProp, fs, loadConfigFile, log, path, + var exports, fetchProp, fs, loadConfigFile, path, _this = this; - log = require('./logging'); - fs = require('fs'); path = require('path'); /* - ##Module call + Module call + ----------- + + Calling the module as a function will act as a constructor and load the config file. + It is possible to hand an args object with the properties nolog (true if no outputs shall + be generated) and configPath for a custom configuration file path. - Calling the module as a function will make it look for the `configPath` property in the - args object and then try to load a config file from that relative path. @param {Object} args */ exports = module.exports = function(args) { args = args != null ? args : {}; - log(args); - if (typeof args.configPath === 'string') { + if (args.nolog) { + _this.nolog = true; + } + if (args.configPath) { loadConfigFile(args.configPath); } else { loadConfigFile(path.join('config', 'config.json')); @@ -47,19 +50,26 @@ Configuration loadConfigFile = function(configPath) { - var e; + var confProperties, e, isReady, prop, _i, _len; _this.config = null; + confProperties = ['log', 'http-port', 'db-port', 'crypto-key']; try { _this.config = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', configPath))); - if (_this.config && _this.config.http_port && _this.config.db_port && _this.config.crypto_key && _this.config.session_secret) { - return log.print('CF', 'config file loaded successfully'); - } else { - return log.error('CF', new Error("Missing property in config file, requires:\n- http_port\n- db_port\n- crypto_key\n- session_secret")); + isReady = true; + for (_i = 0, _len = confProperties.length; _i < _len; _i++) { + prop = confProperties[_i]; + if (!_this.config[prop]) { + isReady = false; + } + } + if (!isReady && !_this.nolog) { + return console.error("Missing property in config file, requires: \n- " + (confProperties.join("\n -"))); } } catch (_error) { e = _error; - e.addInfo = 'no config ready'; - return log.error('CF', e); + if (!_this.nolog) { + return console.error("Failed loading config file: " + e.message); + } } }; @@ -95,7 +105,7 @@ Configuration exports.getHttpPort = function() { - return fetchProp('http_port'); + return fetchProp('http-port'); }; /* @@ -106,7 +116,18 @@ Configuration exports.getDBPort = function() { - return fetchProp('db_port'); + return fetchProp('db-port'); + }; + + /* + ***Returns*** the log conf object + + @public getLogConf() + */ + + + exports.getLogConf = function() { + return fetchProp('log'); }; /* @@ -117,18 +138,7 @@ Configuration exports.getCryptoKey = function() { - return fetchProp('crypto_key'); - }; - - /* - ***Returns*** the session secret - - @public getSessionSecret() - */ - - - exports.getSessionSecret = function() { - return fetchProp('session_secret'); + return fetchProp('crypto-key'); }; }).call(this); diff --git a/js-coffee/engine.js b/js-coffee/engine.js index 78babcc..9e20ac1 100644 --- a/js-coffee/engine.js +++ b/js-coffee/engine.js @@ -1,23 +1,18 @@ 'use strict'; var path = require('path'), - cp = require('child_process'), log = require('./logging'), - qEvents = new (require('./queue')).Queue(), //TODO export queue into redis + // qEvents = new (require('./queue')).Queue(), //TODO export queue into redis regex = /\$X\.[\w\.\[\]]*/g, // find properties of $X listRules = {}, listActionModules = {}, isRunning = true, ml, poller, db; -exports = module.exports = function(args) { +exports = module.exports = function( args ) { args = args || {}; log(args); ml = require('./module_loader')(args); - poller = cp.fork(path.resolve(__dirname, 'eventpoller'), [log.getLogType()]); - poller.on('message', function(evt) { - exports.pushEvent(evt); - }); return module.exports; }; diff --git a/js-coffee/eventpoller.js b/js-coffee/event_poller.js similarity index 82% rename from js-coffee/eventpoller.js rename to js-coffee/event_poller.js index 19c00d5..52f6c70 100644 --- a/js-coffee/eventpoller.js +++ b/js-coffee/event_poller.js @@ -2,14 +2,13 @@ 'use strict'; -var fs = require('fs'), - path = require('path'), - log = require('./logging'), +var logger = require('./new_logging'), listMessageActions = {}, listAdminCommands = {}, listEventModules = {}, listPoll = {}, //TODO this will change in the future because it could have //several parameterized (user-specific) instances of each event module + log, isRunning = true, eId = 0, db, ml; @@ -18,13 +17,26 @@ var fs = require('fs'), function init() { - if(process.argv.length > 2) log({ logType: parseInt(process.argv[2]) || 0 }); - var args = { logType: log.getLogType() }; - (ml = require('./module_loader'))(args); - (db = require('./db_interface'))(args); + if(process.argv.length < 7){ + console.error('Not all arguments have been passed!'); + process.exit(); + } + + var logconf = {} + logconf['mode'] = process.argv[2] + logconf['io-level'] = process.argv[3] + logconf['file-level'] = process.argv[4] + logconf['file-path'] = process.argv[5] + logconf['nolog'] = process.argv[6] + + log = logger(logconf); + var args = { logger: log }; + (ml = require('./module_manager'))(args); + (db = require('./persistence'))(args); initAdminCommands(); initMessageActions(); pollLoop(); + log.info('Event Poller instantiated'); }; @@ -128,11 +140,13 @@ function checkRemotes() { err.additionalInfo = 'module: ' + p; log.error('EP', err); } else { - process.send({ - event: p, - eventid: 'polled_' + eId++, - payload: obj - }); + // FIXME this needs to be pushed into the db not passed to the process! + console.error('eventpoller needs to push event into db queue') + // process.send({ + // event: p, + // eventid: 'polled_' + eId++, + // payload: obj + // }); } }; })(prop) diff --git a/js-coffee/http_listener.js b/js-coffee/http_listener.js index 3efb1a1..14e8344 100644 --- a/js-coffee/http_listener.js +++ b/js-coffee/http_listener.js @@ -54,11 +54,12 @@ HTTP Listener exports.addHandlers = function(fShutDown) { - var e, http_port; - requestHandler.addHandlers(fShutDown); + var e, http_port, sess_sec; + requestHandler.addShutdownHandler(fShutDown); app.use(express.cookieParser()); + sess_sec = "149u*y8C:@kmN/520Gt\\v'+KFBnQ!\\r<>5X/xRI`sT logTypes.length - 1) logType = 0; + // winston.add(winston.transports.File, { filename: 'somefile.log' }); + // winston.remove(winston.transports.Console); return module.exports; }; diff --git a/js-coffee/module_loader.js b/js-coffee/module_loader.js deleted file mode 100644 index 795f7f4..0000000 --- a/js-coffee/module_loader.js +++ /dev/null @@ -1,81 +0,0 @@ -'use strict'; - -var fs = require('fs'), - path = require('path'), - log = require('./logging'); - -exports = module.exports = function(args) { - args = args || {}; - log(args); - return module.exports; -}; - -exports.requireFromString = function(src, name, dir) { - if(!dir) dir = __dirname; - var id = path.resolve(dir, name, name + '.vm'); - var vm = require('vm'), - // FIXME not log but debug module is required to provide information to the user - sandbox = { - id: id, // use this to gather kill info - needle: require('needle'), - log: log, - exports: {} - }; - //TODO child_process to run module! - // Define max runtime per loop as 10 seconds, after that the child will be killed - // it can still be active after that if there was a timing function or a callback used... - // kill the child each time? how to determine whether there's still a token in the module? - try { - var mod = vm.runInNewContext(src, sandbox, id); - - } catch (err) { - log.error('ML', 'Error running module in sandbox: ' + err.message); - } - return sandbox.exports; -}; - -exports.loadModule = function(directory, name, callback) { - try { - fs.readFile(path.resolve(__dirname, '..', directory, name, name + '.js'), 'utf8', function (err, data) { - if (err) { - log.error('LM', 'Loading module file!'); - return; - } - var mod = exports.requireFromString(data, name, directory); - if(mod && fs.existsSync(path.resolve(__dirname, '..', directory, name, 'credentials.json'))) { - fs.readFile(path.resolve(__dirname, '..', directory, name, 'credentials.json'), 'utf8', function (err, auth) { - if (err) { - log.error('LM', 'Loading credentials file for "' + name + '"!'); - callback(name, data, mod, null); - return; - } - if(mod.loadCredentials) mod.loadCredentials(JSON.parse(auth)); - callback(name, data, mod, auth); - }); - } else { - // Hand back the name, the string contents and the compiled module - callback(name, data, mod, null); - } - }); - } catch(err) { - log.error('LM', 'Failed loading module "' + name + '"'); - } -}; - -exports.loadModules = function(directory, callback) { - fs.readdir(path.resolve(__dirname, '..', directory), function (err, list) { - if (err) { - log.error('LM', 'loading modules directory: ' + err); - return; - } - log.print('LM', 'Loading ' + list.length + ' modules from "' + directory + '"'); - list.forEach(function (file) { - fs.stat(path.resolve(__dirname, '..', directory, file), function (err, stat) { - if (stat && stat.isDirectory()) { - exports.loadModule(directory, file, callback); - } - }); - }); - }); -}; - diff --git a/js-coffee/module_manager.js b/js-coffee/module_manager.js index 03fae94..2ec67f3 100644 --- a/js-coffee/module_manager.js +++ b/js-coffee/module_manager.js @@ -12,12 +12,11 @@ var fs = require('fs'), path = require('path'), log = require('./logging'), - ml, db, funcLoadAction, funcLoadRule; + db, funcLoadAction, funcLoadRule; exports = module.exports = function(args) { args = args || {}; log(args); - ml = require('./module_loader')(args); return module.exports; }; @@ -25,12 +24,82 @@ exports.addDBLink = function(db_link) { db = db_link; }; +exports.requireFromString = function(src, name, dir) { + if(!dir) dir = __dirname; + var id = path.resolve(dir, name, name + '.vm'); + var vm = require('vm'), + // FIXME not log but debug module is required to provide information to the user + sandbox = { + id: id, // use this to gather kill info + needle: require('needle'), //https://github.com/tomas/needle + log: log, + exports: {} + }; + //TODO child_process to run module! + // Define max runtime per loop as 10 seconds, after that the child will be killed + // it can still be active after that if there was a timing function or a callback used... + // kill the child each time? how to determine whether there's still a token in the module? + try { + var mod = vm.runInNewContext(src, sandbox, id); + + } catch (err) { + log.error('ML', 'Error running module in sandbox: ' + err.message); + } + return sandbox.exports; +}; + +exports.loadModule = function(directory, name, callback) { + try { + fs.readFile(path.resolve(__dirname, '..', directory, name, name + '.js'), 'utf8', function (err, data) { + if (err) { + log.error('LM', 'Loading module file!'); + return; + } + var mod = exports.requireFromString(data, name, directory); + if(mod && fs.existsSync(path.resolve(__dirname, '..', directory, name, 'credentials.json'))) { + fs.readFile(path.resolve(__dirname, '..', directory, name, 'credentials.json'), 'utf8', function (err, auth) { + if (err) { + log.error('LM', 'Loading credentials file for "' + name + '"!'); + callback(name, data, mod, null); + return; + } + if(mod.loadCredentials) mod.loadCredentials(JSON.parse(auth)); + callback(name, data, mod, auth); + }); + } else { + // Hand back the name, the string contents and the compiled module + callback(name, data, mod, null); + } + }); + } catch(err) { + log.error('LM', 'Failed loading module "' + name + '"'); + } +}; + +exports.loadModules = function(directory, callback) { + fs.readdir(path.resolve(__dirname, '..', directory), function (err, list) { + if (err) { + log.error('LM', 'loading modules directory: ' + err); + return; + } + log.print('LM', 'Loading ' + list.length + ' modules from "' + directory + '"'); + list.forEach(function (file) { + fs.stat(path.resolve(__dirname, '..', directory, file), function (err, stat) { + if (stat && stat.isDirectory()) { + exports.loadModule(directory, file, callback); + } + }); + }); + }); +}; + + exports.storeEventModule = function (objUser, obj, answHandler) { try { // TODO in the future we might want to link the modules close to the user // and allow for e.g. private modules // we need a child process to run this code and kill it after invocation - var m = ml.requireFromString(obj.data, obj.id); + var m = exports.requireFromString(obj.data, obj.id); obj.methods = Object.keys(m); answHandler.answerSuccess('Thank you for the event module!'); db.storeEventModule(obj.id, obj); @@ -48,7 +117,7 @@ exports.getAllEventModules = function ( objUser, obj, answHandler ) { }; exports.storeActionModule = function (objUser, obj, answHandler) { - var m = ml.requireFromString(obj.data, obj.id); + var m = exports.requireFromString(obj.data, obj.id); obj.methods = Object.keys(m); answHandler.answerSuccess('Thank you for the action module!'); db.storeActionModule(obj.id, obj); @@ -111,100 +180,3 @@ exports.storeRule = function (objUser, obj, answHandler) { } }; - -// FIXME REMOVE -/* - * Legacy file system loaders - */ - - -/* - * Load Rules from fs - * ------------------ - */ -exports.loadRulesFromFS = function(args, answHandler) { - if(!args) args = {}; - if(!args.name) args.name = 'rules'; - if(!funcLoadRule) log.error('ML', 'no rule loader function available'); - else { - fs.readFile(path.resolve(__dirname, '..', 'rules', args.name + '.json'), 'utf8', function (err, data) { - if (err) { - log.error('ML', 'Loading rules file: ' + args.name + '.json'); - return; - } - try { - var arr = JSON.parse(data), txt = ''; - log.print('ML', 'Loading ' + arr.length + ' rules:'); - for(var i = 0; i < arr.length; i++) { - txt += arr[i].id + ', '; - db.storeRule(arr[i].id, 'james-t', JSON.stringify(arr[i])); - // funcLoadRule(arr[i]); - } - answHandler.answerSuccess('Yep, loaded rules: ' + txt); - } catch (e) { - log.error('ML', 'rules file was corrupt! (' + args.name + '.json)'); - } - }); - } -}; - -/* - * Load Action Modules from fs - * --------------------------- - */ - -/** - * - * @param {Object} name - * @param {Object} data - * @param {Object} mod - * @param {String} [auth] The string representation of the auth json - */ -function loadActionCallback(name, data, mod, auth) { - db.storeActionModule(name, data); // store module in db - // funcLoadAction(name, mod); // hand back compiled module - if(auth) db.storeActionModuleAuth(name, auth); -} - -exports.loadActionModuleFromFS = function (args, answHandler) { - if(ml) { - if(args && args.name) { - answHandler.answerSuccess('Loading action module ' + args.name + '...'); - ml.loadModule('mod_actions', args.name, loadActionCallback); - } else log.error('MM', 'Action Module name not provided!'); - } -}; - -exports.loadActionModulesFromFS = function(args, answHandler) { - if(ml) { - answHandler.answerSuccess('Loading action modules...'); - ml.loadModules('mod_actions', loadActionCallback); - } -}; - -/* - * Load Event Modules from fs - * -------------------------- - */ - -function loadEventCallback(name, data, mod, auth) { - if(db) { - db.storeEventModule(name, data); // store module in db - if(auth) db.storeEventModuleAuth(name, auth); - } -} - -exports.loadEventModuleFromFS = function(args, answHandler) { - if(ml) { - if(args && args.name) { - answHandler.answerSuccess('Loading event module ' + args.name + '...'); - ml.loadModule('mod_events', args.name, loadEventCallback); - } else log.error('MM', 'Event Module name not provided!'); - } -}; - -exports.loadEventModulesFromFS = function(args, answHandler) { - answHandler.answerSuccess('Loading event moules...'); - ml.loadModules('mod_actions', loadEventCallback); -}; - diff --git a/js-coffee/new_logging.js b/js-coffee/new_logging.js new file mode 100644 index 0000000..7585b5a --- /dev/null +++ b/js-coffee/new_logging.js @@ -0,0 +1,66 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + var bunyan, exports, path, + _this = this; + + path = require('path'); + + bunyan = require('bunyan'); + + /* + Module call + ----------- + + Calling the module as a function will act as a constructor and load the config file. + It is possible to hand an args object with the properties nolog (true if no outputs shall + be generated) and configPath for a custom configuration file path. + + @param {Object} args + */ + + + exports = module.exports = function(args) { + var e, emptylog, opt; + emptylog = { + info: function() {}, + warn: function() {}, + error: function() {} + }; + args = args != null ? args : {}; + if (args.nolog) { + return emptylog; + } else { + try { + opt = { + name: "webapi-eca" + }; + if (args['mode'] === 'development') { + opt.src = true; + } + if (args['file-path']) { + _this.logPath = path.resolve(__dirname, '..', 'logs', args['file-path']); + } else { + _this.logPath = path.resolve(__dirname, '..', 'logs', 'server.log'); + } + opt.streams = [ + { + level: args['io-level'], + stream: process.stdout + }, { + level: args['file-level'], + type: 'rotating-file', + path: _this.logPath, + period: '1d', + count: 3 + } + ]; + return bunyan.createLogger(opt); + } catch (_error) { + e = _error; + console.error(e); + return emptylog; + } + } + }; + +}).call(this); diff --git a/js-coffee/request_handler.js b/js-coffee/request_handler.js index e46bae5..30d7ab4 100644 --- a/js-coffee/request_handler.js +++ b/js-coffee/request_handler.js @@ -8,7 +8,7 @@ Request Handler (function() { - var answerHandler, crypto, db, exports, fs, getHandlerFileAsString, getHandlerPath, getIncludeFileAsString, log, mm, mustache, objAdminCmds, objUserCmds, path, qs, renderPage, sendLoginOrPage, + var answerHandler, crypto, db, exports, fs, getHandlerFileAsString, getHandlerPath, getIncludeFileAsString, log, mm, mustache, objUserCmds, path, qs, renderPage, sendLoginOrPage, _this = this; log = require('./logging'); @@ -27,14 +27,6 @@ Request Handler crypto = require('crypto-js'); - objAdminCmds = { - 'loadrules': mm.loadRulesFromFS, - 'loadaction': mm.loadActionModuleFromFS, - 'loadactions': mm.loadActionModulesFromFS, - 'loadevent': mm.loadEventModuleFromFS, - 'loadevents': mm.loadEventModulesFromFS - }; - objUserCmds = { 'store_action': mm.storeActionModule, 'get_actionmodules': mm.getAllActionModules, @@ -59,17 +51,16 @@ Request Handler }; /* - This allows the parent to add handlers. The event handler will receive - the events that were received. The shutdown function will be called if the - admin command shutdown is issued. + This allows the parent to add the shutdown handler. + The shutdown function will be called if the admin command shutdown is issued. - @public addHandlers( *fShutdown* ) + @public addShutdownHandler( *fShutdown* ) @param {function} fShutdown */ - exports.addHandlers = function(fShutdown) { - return objAdminCmds.shutdown = function(args, answerHandler) { + exports.addShutdownHandler = function(fShutdown) { + return _this.objAdminCmds.shutdown = function(args, answerHandler) { answerHandler.answerSuccess('Shutting down... BYE!'); return setTimeout(fShutdown, 500); }; @@ -344,13 +335,13 @@ Request Handler exports.handleAdmin = function(req, resp) { - var q, _name; + var q, _base, _name; if (req.session && req.session.user) { if (req.session.user.isAdmin === "true") { q = req.query; log.print('RH', 'Received admin request: ' + req.originalUrl); if (q.cmd) { - return typeof objAdminCmds[_name = q.cmd] === "function" ? objAdminCmds[_name](q, answerHandler(req, resp, true)) : void 0; + return typeof (_base = _this.objAdminCmds)[_name = q.cmd] === "function" ? _base[_name](q, answerHandler(req, resp, true)) : void 0; } else { return resp.send(404, 'Command unknown!'); } diff --git a/js-coffee/server.js b/js-coffee/server.js index 737de63..a70c34d 100644 --- a/js-coffee/server.js +++ b/js-coffee/server.js @@ -6,27 +6,16 @@ Server >This is the main module that is used to run the whole server: > -> node server [log_type http_port] +> node server [opt] > ->Valid `log_type`'s are: -> ->- `0`: standard I/O output (default) ->- `1`: log file (server.log) ->- `2`: silent -> ->`http_port` can be set to use another port, than defined in the ->[config](config.html) file, to listen to, e.g. used by the test suite. -> -> - -TODO how about we allow spawning child processes with servers based on address? +> See below in the optimist CLI preparation for allowed optional parameters */ (function() { - var args, conf, db, engine, http_listener, init, log, procCmds, shutDown; + var argv, conf, cp, db, engine, fs, http_listener, init, logger, opt, optimist, path, procCmds, shutDown, usage; - log = require('./logging'); + logger = require('./new_logging'); conf = require('./config'); @@ -36,10 +25,69 @@ TODO how about we allow spawning child processes with servers based on address? http_listener = require('./http_listener'); - args = {}; + fs = require('fs'); + + path = require('path'); + + cp = require('child_process'); + + optimist = require('optimist'); procCmds = {}; + /* + Let's prepare the optimist CLI + */ + + + usage = 'This runs your webapi-based ECA engine'; + + opt = { + 'h': { + alias: 'help', + describe: 'Display this' + }, + 'c': { + alias: 'config-path', + describe: 'Specify a path to a custom configuration file, other than "config/config.json"' + }, + 'w': { + alias: 'http-port', + describe: 'Specify a HTTP port for the web server' + }, + 'd': { + alias: 'db-port', + describe: 'Specify a port for the redis DB' + }, + 'm': { + alias: 'log-mode', + describe: 'Specify a log mode: [development|productive]' + }, + 'i': { + alias: 'log-io-level', + describe: 'Specify the log level for the I/O' + }, + 'f': { + alias: 'log-file-level', + describe: 'Specify the log level for the log file' + }, + 'p': { + alias: 'log-file-path', + describe: 'Specify the path to the log file within the "logs" folder' + }, + 'n': { + alias: 'nolog', + describe: 'Set this if no output shall be generated' + } + }; + + argv = optimist.usage(usage).options(opt).argv; + + if (argv.help) { + console.log(optimist.help()); + process.exit(); + } + /* Error handling of the express port listener requires special attention, thus we have to catch the process error, which is issued if @@ -50,7 +98,7 @@ TODO how about we allow spawning child processes with servers based on address? process.on('uncaughtException', function(err) { switch (err.errno) { case 'EADDRINUSE': - err.addInfo = 'http_port already in use, shutting down!'; + err.addInfo = 'http-port already in use, shutting down!'; log.error('RS', err); return shutDown(); default: @@ -66,48 +114,54 @@ TODO how about we allow spawning child processes with servers based on address? init = function() { - log.print('RS', 'STARTING SERVER'); - conf(args); + var args, log, logconf; + conf(argv.c); if (!conf.isReady()) { - log.error('RS', 'Config file not ready!'); + console.error('FAIL: Config file not ready! Shutting down...'); process.exit(); } - if (process.argv.length > 2) { - args.logType = parseInt(process.argv[2]) || 0; - switch (args.logType) { - case 0: - log.print('RS', 'Log type set to standard I/O output'); - break; - case 1: - log.print('RS', 'Log type set to file output'); - break; - case 2: - log.print('RS', 'Log type set to silent'); - break; - default: - log.print('RS', 'Unknown log type, using standard I/O'); - } - log(args); - } else { - log.print('RS', 'No log method argument provided, using standard I/O'); + logconf = conf.getLogConf(); + if (argv.m) { + logconf['mode'] = argv.m; } - if (process.argv.length > 3) { - args.http_port = parseInt(process.argv[3]); - } else { - log.print('RS', 'No HTTP port passed, using standard port from config file'); + if (argv.i) { + logconf['io-level'] = argv.i; } - log.print('RS', 'Initialzing DB'); + if (argv.f) { + logconf['file-level'] = argv.f; + } + if (argv.p) { + logconf['file-path'] = argv.p; + } + if (argv.n) { + logconf['nolog'] = argv.n; + } + try { + fs.unlinkSync(path.resolve(__dirname, '..', 'logs', logconf['file-path'])); + } catch (_error) {} + log = logger(logconf); + log.info('RS | STARTING SERVER'); + args = { + logger: log, + logconf: logconf + }; + args['http-port'] = parseInt(argv.w || conf.getHttpPort()); + log.info('RS | Initialzing DB'); db(args); return db.isConnected(function(err, result) { + var cliArgs, poller; if (!err) { - log.print('RS', 'Initialzing engine'); + log.info('RS | Initialzing engine'); engine(args); - log.print('RS', 'Initialzing http listener'); + log.info('RS | Initialzing http listener'); http_listener(args); - log.print('RS', 'Passing handlers to engine'); + log.info('RS | Passing handlers to engine'); engine.addPersistence(db); - log.print('RS', 'Passing handlers to http listener'); - return http_listener.addHandlers(shutDown); + log.info('RS | Passing handlers to http listener'); + http_listener.addShutdownHandler(shutDown); + log.info('RS | For e child process for the event poller'); + cliArgs = [args.logconf['mode'], args.logconf['io-level'], args.logconf['file-level'], args.logconf['file-path'], args.logconf['nolog']]; + return poller = cp.fork(path.resolve(__dirname, 'event_poller'), cliArgs); } }); }; @@ -120,7 +174,7 @@ TODO how about we allow spawning child processes with servers based on address? shutDown = function() { - log.print('RS', 'Received shut down command!'); + log.warn('RS | Received shut down command!'); if (engine != null) { engine.shutDown(); } diff --git a/js/logging.js b/js/logging.js index abed35d..df44534 100644 --- a/js/logging.js +++ b/js/logging.js @@ -9,7 +9,9 @@ * - 1 file * - 2 silent */ + //TODO dynamic log file names (especially to track unit test logs) var fs = require('fs'), + wst = require('winston'), logTypes = [ flushToConsole, flushToFile, null], logFile = require('path').resolve(__dirname, '..', 'server.log'), logType = 0; @@ -19,6 +21,8 @@ exports = module.exports = function(args) { if(args.logType) logType = parseInt(args.logType) || 0; if(logType == 1) fs.truncateSync(logFile, 0); if(logType > logTypes.length - 1) logType = 0; + // winston.add(winston.transports.File, { filename: 'somefile.log' }); + // winston.remove(winston.transports.Console); return module.exports; }; @@ -29,8 +33,10 @@ function flush(err, msg) { } function flushToConsole(err, msg) { - if(err) console.error(msg); + if(err) console.error("\033[31m" + msg + "\033[0m"); else console.log(msg); + // if(err) console.error(msg); + // else console.log(msg); } function flushToFile(err, msg) { @@ -63,13 +69,13 @@ function printError(module, err, isSevere) { // if(module) flush(true, ts + module + ' | ERROR AND BAD HANDLING: ' + err + '\n' + e.stack); // else flush(true, ts + '!N/A! | ERROR, BAD HANDLING AND NO MODULE NAME: ' + err + '\n' + e.stack); // } else if(err) { - if(err.addInfo) ai = ' (' + err.addInfo + ')'; - if(!err.message) err.message = 'UNKNOWN REASON!\n' + err.stack; - if(module) { - var msg = ts + module + ' | ERROR'+ai+': ' + err.message; - if(isSevere) msg += '\n' + err.stack; - flush(true, msg); - } else flush(true, ts + '!N/A! | ERROR AND NO MODULE NAME'+ai+': ' + err.message + '\n' + err.stack); + if(err.addInfo) ai = ' (' + err.addInfo + ')'; + if(!err.message) err.message = 'UNKNOWN REASON!\n' + err.stack; + if(module) { + var msg = ts + module + ' | ERROR'+ai+': ' + err.message; + if(isSevere) msg += '\n' + err.stack; + flush(true, msg); + } else flush(true, ts + '!N/A! | ERROR AND NO MODULE NAME'+ai+': ' + err.message + '\n' + err.stack); // } else { // var e = new Error('Unexpected error'); // flush(true, e.message + ': \n' + e.stack); @@ -93,3 +99,9 @@ exports.error = function(module, err) { exports.severe = function(module, err) { printError(module, err, true); }; + +exports.obj = function (varname, obj) { + var arrS = (new Error).stack.split('\n'); + console.log('Dumping object "' + varname + '"' + arrS[2]); + console.log(obj); +}; diff --git a/package.json b/package.json index df58ad8..53237d0 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "nodeunit": "0.8.4", "redis": "0.10.0", "request": "2.33.0", - "coffee-script": "1.6.3" + "coffee-script": "1.6.3", + "bunyan": "0.22.1", + "optimist": "0.6.1" }, "__comment": { "dependencies": { diff --git a/run_server.sh b/run_server.sh index 2e17819..e343a6a 100755 --- a/run_server.sh +++ b/run_server.sh @@ -1,3 +1,3 @@ #!/bin/bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -node $DIR/js-coffee/server +node $DIR/js-coffee/server | $DIR/node_modules/bunyan/bin/bunyan diff --git a/run_tests.sh b/run_tests.sh index d3232bf..9cefa03 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,2 +1,15 @@ #!/usr/bin/env node -require('nodeunit').reporters.default.run(['testing']); \ No newline at end of file +process.chdir(__dirname); +var fs = require('fs'); +var path = require('path'); +var args = process.argv.slice(2); +if( args[0] !== undefined ) { + var fl = path.join('testing', args[0]); + if (fs.existsSync(fl)) { + require('nodeunit').reporters.default.run([fl]); + } else { + console.error('File not found!!'); + } +} else { + require('nodeunit').reporters.default.run(['testing']); +} diff --git a/testing/js/test_logging.js b/testing/js/test_logging.js new file mode 100644 index 0000000..9e5d05e --- /dev/null +++ b/testing/js/test_logging.js @@ -0,0 +1,5 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + + +}).call(this); diff --git a/testing/js/test_persistence.js b/testing/js/test_persistence.js new file mode 100644 index 0000000..ce82688 --- /dev/null +++ b/testing/js/test_persistence.js @@ -0,0 +1,87 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + var _this = this; + + exports.setUp = function(cb) { + _this.path = require('path'); + _this.db = require(path.join('..', 'js-coffee', 'persistence')); + console.log('setup'); + console.log(_this.db); + _this.db({ + logType: 2 + }); + return cb(); + }; + + exports.tearDown = function(cb) { + _this.db.shutDown(); + return cb(); + }; + + /* + # Test AVAILABILITY + */ + + + exports.Availability = { + setUp: function(cb) { + _this.path = require('path'); + _this.db = require(path.join('..', 'js-coffee', 'persistence')); + console.log('setup'); + console.log(_this.db); + _this.db({ + logType: 2 + }); + return cb(); + }, + testRequire: function(test) { + test.expect(1); + console.log('setup'); + test.ok(_this.db, 'DB interface loaded'); + return test.done(); + }, + testConnect: function(test) { + test.expect(1); + return _this.db.isConnected(function(err) { + test.ifError(err, 'Connection failed!'); + return test.done(); + }); + }, + testNoConfig: function(test) { + test.expect(1); + _this.db({ + configPath: 'nonexistingconf.file' + }); + return _this.db.isConnected(function(err) { + test.ok(err, 'Still connected!?'); + return test.done(); + }); + }, + testWrongConfig: function(test) { + test.expect(1); + _this.db({ + configPath: _this.path.join('testing', 'jsonWrongConfig.json') + }); + return _this.db.isConnected(function(err) { + test.ok(err, 'Still connected!?'); + return test.done(); + }); + }, + testPurgeQueue: function(test) { + var evt; + test.expect(2); + evt = { + eventid: '1', + event: 'mail' + }; + _this.db.pushEvent(evt); + _this.db.purgeEventQueue(); + return _this.db.popEvent(function(err, obj) { + test.ifError(err, 'Error during pop after purging!'); + test.strictEqual(obj, null, 'There was an event in the queue!?'); + return test.done(); + }); + } + }; + +}).call(this); diff --git a/testing/test_config.coffee b/testing/test_config.coffee index 2e853c5..4bebcb2 100644 --- a/testing/test_config.coffee +++ b/testing/test_config.coffee @@ -3,7 +3,7 @@ path = require 'path' exports.setUp = ( cb ) => @conf = require path.join '..', 'js-coffee', 'config' @conf - logType: 2 + nolog: true cb() exports.testRequire = ( test ) => @@ -12,23 +12,34 @@ exports.testRequire = ( test ) => test.done() exports.testParameters = ( test ) => - test.expect 4 + reqProp = [ + 'mode' + 'io-level' + 'file-level' + 'file-path' + ] + test.expect 4 + reqProp.length test.ok @conf.getHttpPort(), 'HTTP port does not exist!' test.ok @conf.getDBPort(), 'DB port does not exist!' test.ok @conf.getCryptoKey(), 'Crypto key does not exist!' - test.ok @conf.getSessionSecret(), 'Session Secret does not exist!' + logconf = @conf.getLogConf() + test.ok logconf, 'Log config does not exist!' + for prop in reqProp + test.ok logconf[prop], "Log conf property #{ prop } does not exist!" test.done() exports.testDifferentConfigFile = ( test ) => test.expect 1 - @conf + @conf + nolog: true configPath: path.join 'testing', 'files', 'jsonWrongConfig.json' test.ok @conf.isReady(), 'Different path not loaded!' test.done() exports.testNoConfigFile = ( test ) => test.expect 1 - @conf + @conf + nolog: true configPath: 'wrongpath.file' test.strictEqual @conf.isReady(), false, 'Wrong path still loaded!' test.done() diff --git a/testing/test_logging.coffee b/testing/test_logging.coffee new file mode 100644 index 0000000..ea521a7 --- /dev/null +++ b/testing/test_logging.coffee @@ -0,0 +1,68 @@ + +exports.setUp = ( cb ) => + @fs = require 'fs' + @path = require 'path' + @stdPath = @path.resolve __dirname, '..', 'logs', 'server.log' + try + @fs.unlinkSync @stdPath + catch e + @log = require @path.join '..', 'js-coffee', 'new_logging' + cb() + +exports.tearDown = ( cb ) => + cb() + +exports.testInitIO = ( test ) => + test.expect 1 + conf = + logType: 0 + @log.configure conf + @log.info 'TL', 'testInitIO - info' + @log.warn 'TL', 'testInitIO - warn' + @log.error 'TL', 'testInitIO - error' + test.ok !@fs.existsSync @stdPath + test.done() + +exports.testInitFile = ( test ) => + test.expect 1 + + conf = + logType: 1 + @log.configure conf + @log.info 'UT', 'test 1' + + fWait = () => + @log.info 'UT', 'test 2' + test.ok @fs.existsSync @stdPath + test.done() + + setTimeout fWait, 100 + +# exports.testInitSilent = ( test ) => +# test.expect 1 + +# conf = +# logType: 2 +# @log.configure conf +# @log.info 'test 3' + +# test.ok true, 'yay' +# test.done() + +# exports.testInitPath = ( test ) => +# test.expect 1 + +# conf = +# logType: 1 +# logPath: 'testing/log-initPath.log' +# @log.configure conf +# @log.info 'test 3' + +# test.ok true, 'yay' +# test.done() + +# exports.testPrint = ( test ) => +# test.expect 1 +# test.ok true, 'yay' +# @log.info 'test 3' +# test.done() \ No newline at end of file diff --git a/testing/test_persistence.coffee b/testing/test_persistence.coffee index d198eac..f54cf42 100644 --- a/testing/test_persistence.coffee +++ b/testing/test_persistence.coffee @@ -1,6 +1,7 @@ exports.setUp = ( cb ) => - @db = require '../js-coffee/persistence' + @path = require 'path' + @db = require @path.join '..', 'js-coffee', 'persistence' @db logType: 2 cb() @@ -38,7 +39,7 @@ exports.Availability = testWrongConfig: ( test ) => test.expect 1 - @db { configPath: 'testing/jsonWrongConfig.json' } + @db { configPath: @path.join 'testing', 'jsonWrongConfig.json' } @db.isConnected ( err ) -> test.ok err, 'Still connected!?' test.done()