From c070c9e741f5ffec6c8811ed0b219e7b938f3ad7 Mon Sep 17 00:00:00 2001 From: Spencer Alger Date: Wed, 4 Dec 2013 12:49:39 -0700 Subject: [PATCH] more tests, simplified the standard tests for the loggers --- LICENSE.md | 2 +- package.json | 2 +- src/lib/loggers/console.js | 51 +++-- src/lib/loggers/file.js | 1 - src/lib/loggers/stdio.js | 13 +- src/lib/loggers/stream.js | 5 + src/lib/loggers/tracer.js | 1 - src/lib/serializers/json.js | 34 ++-- src/lib/transport.js | 2 +- src/lib/utils.js | 32 +--- .../browser_yaml_suite/yaml_tests.js | 175 +++++++++--------- test/integration/yaml_suite/yaml_file.js | 3 - test/mocks/incomming_message.js | 2 +- test/mocks/old_writable_stream.js | 22 +++ test/mocks/request.js | 5 + test/mocks/writable_stream.js | 6 +- test/unit/auto_release_stub.js | 23 +++ ...test_logger.js => generic_logger_tests.js} | 137 +++++++------- test/unit/test_abstract_logger.js | 35 ++++ test/unit/test_client_action.js | 2 +- test/unit/test_connection_abstract.js | 14 +- test/unit/test_console_logger.js | 28 +++ test/unit/test_file_logger.js | 29 +++ test/unit/test_host.js | 9 +- test/unit/test_http_connector.js | 38 ++-- test/unit/test_json_serializer.js | 99 ++++++++++ test/unit/test_stdio_logger.js | 166 ++--------------- test/unit/test_stream_logger.js | 67 ++----- test/unit/test_transport.js | 12 +- test/unit/test_utils.js | 25 ++- 30 files changed, 535 insertions(+), 505 deletions(-) create mode 100644 test/mocks/old_writable_stream.js create mode 100644 test/unit/auto_release_stub.js rename test/unit/{test_logger.js => generic_logger_tests.js} (64%) create mode 100644 test/unit/test_abstract_logger.js create mode 100644 test/unit/test_console_logger.js create mode 100644 test/unit/test_file_logger.js create mode 100644 test/unit/test_json_serializer.js diff --git a/LICENSE.md b/LICENSE.md index b2ba7010b..eff617818 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright 2012-2013 Elasticsearch BV +Copyright 2013 Elasticsearch BV Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may diff --git a/package.json b/package.json index 4774d5002..357863d2a 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "chalk": "~0.3.0" }, "scripts": { - "test": "node scripts/run_tests.js", + "test": "node scripts/run_tests.js --integration --unit --browsers=chrome,safari,firefox,opera", "coverage": "mocha test/unit/test_*.js --require blanket -R html-cov > coverage.html && open -a \"Google Chrome\" ./coverage.html", "build_clients": "grunt", "generate": "node scripts/generate/js_api && node scripts/generate/yaml_tests", diff --git a/src/lib/loggers/console.js b/src/lib/loggers/console.js index 0553edad4..237b4b2be 100644 --- a/src/lib/loggers/console.js +++ b/src/lib/loggers/console.js @@ -16,7 +16,6 @@ var LoggerAbstract = require('../logger'); var _ = require('../utils'); function Console(log, config) { - // call my super LoggerAbstract.call(this, log, config); // config/state @@ -30,18 +29,17 @@ _.inherits(Console, LoggerAbstract); * @param {Array} levels - The levels that we should be listeneing for */ Console.prototype.setupListeners = function (levels) { - // since some of our functions are bound a bit differently (to the console) - // create some of the bound properties manually - this.bound.onError = this.onError; - this.bound.onWarning = this.onWarning; - this.bound.onInfo = this.onInfo; - this.bound.onDebug = this.onDebug; - this.bound.onTrace = this.onTrace; - // call the super method LoggerAbstract.prototype.setupListeners.call(this, levels); }; +Console.prototype.write = function (label, message, to) { + /* jshint browser:true */ + if (window.console && window.console[to]) { + window.console[to](this.format(label, message)); + } +}; + /** * Handler for the bridges "error" event * @@ -50,13 +48,10 @@ Console.prototype.setupListeners = function (levels) { * @param {Error} e - The Error object to log * @return {undefined} */ -Console.prototype.onError = function (e) { - if (console.error && console.trace) { - console.error(e.name === 'Error' ? 'ERROR' : e.name, e.stack || e.message); - } else { - console.log(e.name === 'Error' ? 'ERROR' : e.name, e.stack || e.message); - } -}; +Console.prototype.onError = _.handler(function (e) { + var to = console.error ? 'error' : 'log'; + this.write(e.name === 'Error' ? 'ERROR' : e.name, e.stack || e.message, to); +}); /** * Handler for the bridges "warning" event @@ -66,9 +61,9 @@ Console.prototype.onError = function (e) { * @param {String} msg - The message to be logged * @return {undefined} */ -Console.prototype.onWarning = function (msg) { - console[console.warn ? 'warn' : 'log']('WARNING', msg); -}; +Console.prototype.onWarning = _.handler(function (msg) { + this.write('WARNING', msg, console.warn ? 'warn' : 'log'); +}); /** * Handler for the bridges "info" event @@ -78,9 +73,9 @@ Console.prototype.onWarning = function (msg) { * @param {String} msg - The message to be logged * @return {undefined} */ -Console.prototype.onInfo = function (msg) { - console[console.warn ? 'info' : 'log']('INFO', msg); -}; +Console.prototype.onInfo = _.handler(function (msg) { + this.write('INFO', msg, console.info ? 'info' : 'log'); +}); /** * Handler for the bridges "debug" event @@ -90,9 +85,9 @@ Console.prototype.onInfo = function (msg) { * @param {String} msg - The message to be logged * @return {undefined} */ -Console.prototype.onDebug = function (msg) { - console[console.debug ? 'debug' : 'log']('DEBUG', msg); -}; +Console.prototype.onDebug = _.handler(function (msg) { + this.write('DEBUG', msg, console.debug ? 'debug' : 'log'); +}); /** * Handler for the bridges "trace" event * @@ -100,6 +95,6 @@ Console.prototype.onDebug = function (msg) { * @private * @return {undefined} */ -Console.prototype.onTrace = function (message, curlCall) { - console.log('TRACE:\n' + curlCall + '\n' + message); -}; +Console.prototype.onTrace = _.handler(function (msg, curlCall) { + this.write('TRACE', curlCall + '\n' + msg, 'log'); +}); diff --git a/src/lib/loggers/file.js b/src/lib/loggers/file.js index d4c43fc94..7f098f341 100755 --- a/src/lib/loggers/file.js +++ b/src/lib/loggers/file.js @@ -27,7 +27,6 @@ function File(log, config) { encoding: 'utf8' }); - // call my super StreamLogger.call(this, log, config); } _.inherits(File, StreamLogger); diff --git a/src/lib/loggers/stdio.js b/src/lib/loggers/stdio.js index 32b8bf81c..7c24c1140 100755 --- a/src/lib/loggers/stdio.js +++ b/src/lib/loggers/stdio.js @@ -28,7 +28,6 @@ var defaultColors = { }; function Stdio(log, config) { - // call my super LoggerAbstract.call(this, log, config); // config/state @@ -50,7 +49,7 @@ _.inherits(Stdio, LoggerAbstract); * @param {*} what - The message to log * @return {undefined} */ -Stdio.prototype.write = function (to, label, colorize, message) { +Stdio.prototype.write = function (label, message, to, colorize) { if (this.color) { label = colorize(label); } @@ -66,7 +65,7 @@ Stdio.prototype.write = function (to, label, colorize, message) { * @return {undefined} */ Stdio.prototype.onError = _.handler(function (e) { - this.write(process.stderr, e.name === 'Error' ? 'ERROR' : e.name, this.colors.error, e.stack); + this.write(e.name === 'Error' ? 'ERROR' : e.name, e.stack, process.stderr, this.colors.error); }); /** @@ -78,7 +77,7 @@ Stdio.prototype.onError = _.handler(function (e) { * @return {undefined} */ Stdio.prototype.onWarning = _.handler(function (msg) { - this.write(process.stderr, 'WARNING', this.colors.warning, msg); + this.write('WARNING', msg, process.stderr, this.colors.warning); }); /** @@ -90,7 +89,7 @@ Stdio.prototype.onWarning = _.handler(function (msg) { * @return {undefined} */ Stdio.prototype.onInfo = _.handler(function (msg) { - this.write(process.stdout, 'INFO', this.colors.info, msg); + this.write('INFO', msg, process.stdout, this.colors.info); }); /** @@ -102,7 +101,7 @@ Stdio.prototype.onInfo = _.handler(function (msg) { * @return {undefined} */ Stdio.prototype.onDebug = _.handler(function (msg) { - this.write(process.stdout, 'DEBUG', this.colors.debug, msg); + this.write('DEBUG', msg, process.stdout, this.colors.debug); }); /** @@ -113,5 +112,5 @@ Stdio.prototype.onDebug = _.handler(function (msg) { * @return {undefined} */ Stdio.prototype.onTrace = _.handler(function (message, curlCall) { - this.write(process.stdout, 'TRACE', this.colors.trace, curlCall + '\n' + message); + this.write('TRACE', curlCall + '\n' + message, process.stdout, this.colors.trace); }); diff --git a/src/lib/loggers/stream.js b/src/lib/loggers/stream.js index cd5f3eb4e..fac924154 100755 --- a/src/lib/loggers/stream.js +++ b/src/lib/loggers/stream.js @@ -28,6 +28,11 @@ function Stream(log, config) { } _.inherits(Stream, LoggerAbstract); +Stream.prototype.cleanUpListeners = _.handler(function () { + process.removeListener('exit', this.bound.onProcessExit); + LoggerAbstract.prototype.cleanUpListeners.call(this); +}); + // flush the write buffer to stderr synchronously Stream.prototype.onProcessExit = _.handler(function () { // process is dying, lets manually flush the buffer synchronously to stderr. diff --git a/src/lib/loggers/tracer.js b/src/lib/loggers/tracer.js index e33e929a7..9597878c0 100755 --- a/src/lib/loggers/tracer.js +++ b/src/lib/loggers/tracer.js @@ -16,7 +16,6 @@ var FileLogger = require('./file'); var _ = require('../utils'); function Tracer(log, config) { - // call my super FileLogger.call(this, log, config); } _.inherits(Tracer, FileLogger); diff --git a/src/lib/serializers/json.js b/src/lib/serializers/json.js index 2ecc21bef..47799b9e5 100755 --- a/src/lib/serializers/json.js +++ b/src/lib/serializers/json.js @@ -8,26 +8,36 @@ var _ = require('../utils'); function Json() {} +/** + * Converts a value into a string, or an error + * @param {*} val - Any value, methods are stripped and + * see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify about other params + * @return {String|Error} - A string is always returned, unless an error occured. then it will be that error. + */ Json.prototype.serialize = function (val, replacer, spaces) { - if (val == null) { - return; - } - else if (typeof val === 'string') { + switch (typeof val) { + case 'string': return val; - } else { - return JSON.stringify(val, replacer, spaces); + case 'object': + if (val) { + return JSON.stringify(val, replacer, spaces); + } + /* falls through */ + default: + return; } }; -Json.prototype.unserialize = function (str) { +/** + * Parse a JSON string, if it is already parsed it is ignored + * @param {String} str - the string to parse + * @return {[type]} + */ +Json.prototype.deserialize = function (str) { if (typeof str === 'string') { try { return JSON.parse(str); - } catch (e) { - return; - } - } else { - return str; + } catch (e) {} } }; diff --git a/src/lib/transport.js b/src/lib/transport.js index 3ef86fc8d..7f09c70a8 100644 --- a/src/lib/transport.js +++ b/src/lib/transport.js @@ -148,7 +148,7 @@ Transport.prototype.request = function (params, cb) { var parsedBody; if (!err && body) { - parsedBody = self.serializer.unserialize(body); + parsedBody = self.serializer.deserialize(body); if (parsedBody == null) { err = new errors.Serialization(); } diff --git a/src/lib/utils.js b/src/lib/utils.js index 99c256a51..2a346d266 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -21,32 +21,6 @@ _ = utils; */ utils.joinPath = path.join; -/** - * Recursively re-key an object, applying "transform" to each key - * @param {Object} obj - The object to re-key - * @param {Function} transform - The transformation function to apply to each key - * @param {Boolean} [recursive=true] - Should this act recursively? - * @param {Object} out - used primarily for recursion, allows you to specify the object which new keys will be written to - * @return {Object} - */ -utils.reKey = function (obj, transform, recursive) { - // defaults - if (recursive === void 0) { recursive = true; } - if (typeof transform !== 'function') { throw new TypeError('invalid transform function'); } - - var out = {}; - - _.each(obj, function (prop, name) { - if (recursive && _.isPlainObject(prop)) { - out[transform(name)] = utils.reKey(prop, transform, recursive); - } else { - out[transform(name)] = prop; - } - }); - - return out; -}; - /** * Recursively merge two objects, walking into each object and concating arrays. If both to and from have a value at a * key, but the values' types don't match to's value is left unmodified. Only Array and Object values are merged - that @@ -146,7 +120,7 @@ function adjustWordCase(firstWordCap, otherWordsCap, sep) { words.push(word); } // add the leading underscore back to strings the had it originally - if (words.lenth && string.charAt(0) === '_') { + if (words.length && string.charAt(0) === '_') { words[0] = '_' + words[0]; } return words.join(sep); @@ -399,10 +373,10 @@ _.funcEnum = function (config, name, opts, def) { case 0: break; case 1: - err += 'or ' + _.keys(opts)[0]; + err += ' or ' + _.keys(opts)[0]; break; default: - err += 'or one of ' + _.keys(opts).join(', '); + err += ' or one of ' + _.keys(opts).join(', '); break; } throw new TypeError(err); diff --git a/test/integration/browser_yaml_suite/yaml_tests.js b/test/integration/browser_yaml_suite/yaml_tests.js index 3a9361283..6b75e1db8 100644 --- a/test/integration/browser_yaml_suite/yaml_tests.js +++ b/test/integration/browser_yaml_suite/yaml_tests.js @@ -3553,7 +3553,7 @@ function Buffer(subject, encoding, offset) { if (encoding == "base64" && typeof subject == "string") { subject = stringtrim(subject); while (subject.length % 4 != 0) { - subject = subject + "="; + subject = subject + "="; } } @@ -4661,7 +4661,7 @@ Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { function b64ToByteArray(b64) { var i, j, l, tmp, placeHolders, arr; - + if (b64.length % 4 > 0) { throw 'Invalid string. Length must be a multiple of 4'; } @@ -4852,7 +4852,7 @@ function Buffer(subject, encoding, offset) { if (encoding == "base64" && typeof subject == "string") { subject = stringtrim(subject); while (subject.length % 4 != 0) { - subject = subject + "="; + subject = subject + "="; } } @@ -5962,7 +5962,7 @@ module.exports=require('q9TxCC'); function b64ToByteArray(b64) { var i, j, l, tmp, placeHolders, arr; - + if (b64.length % 4 > 0) { throw 'Invalid string. Length must be a multiple of 4'; } @@ -23436,7 +23436,7 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) { } // no match was found. // However, in partial mode, we can't say this is necessarily over. - // If there's more *pattern* left, then + // If there's more *pattern* left, then if (partial) { // ran out of file // console.error("\n>>> no match, partial?", file, fr, pattern, pr) @@ -23838,7 +23838,7 @@ var exports = module.exports = Argv; function Argv (processArgs, cwd) { var self = {}; if (!cwd) cwd = process.cwd(); - + self.$0 = process.argv .slice(0,2) .map(function (x) { @@ -23848,30 +23848,30 @@ function Argv (processArgs, cwd) { }) .join(' ') ; - + if (process.env._ != undefined && process.argv[1] == process.env._) { self.$0 = process.env._.replace( path.dirname(process.execPath) + '/', '' ); } - + var options = { boolean: [], string: [], alias: {}, default: [] }; - + self.boolean = function (bools) { options.boolean.push.apply(options.boolean, [].concat(bools)); return self; }; - + self.string = function (strings) { options.string.push.apply(options.string, [].concat(strings)); return self; }; - + self.default = function (key, value) { if (typeof key === 'object') { Object.keys(key).forEach(function (k) { @@ -23883,7 +23883,7 @@ function Argv (processArgs, cwd) { } return self; }; - + self.alias = function (x, y) { if (typeof x === 'object') { Object.keys(x).forEach(function (key) { @@ -23895,7 +23895,7 @@ function Argv (processArgs, cwd) { } return self; }; - + var demanded = {}; self.demand = function (keys) { if (typeof keys == 'number') { @@ -23910,36 +23910,36 @@ function Argv (processArgs, cwd) { else { demanded[keys] = true; } - + return self; }; - + var usage; self.usage = function (msg, opts) { if (!opts && typeof msg === 'object') { opts = msg; msg = null; } - + usage = msg; - + if (opts) self.options(opts); - + return self; }; - + function fail (msg) { self.showHelp(); if (msg) console.error(msg); process.exit(1); } - + var checks = []; self.check = function (f) { checks.push(f); return self; }; - + var descriptions = {}; self.describe = function (key, desc) { if (typeof key === 'object') { @@ -23952,11 +23952,11 @@ function Argv (processArgs, cwd) { } return self; }; - + self.parse = function (args) { return parseArgs(args); }; - + self.option = self.options = function (key, opt) { if (typeof key === 'object') { Object.keys(key).forEach(function (k) { @@ -23969,34 +23969,34 @@ function Argv (processArgs, cwd) { if (typeof opt.default !== 'undefined') { self.default(key, opt.default); } - + if (opt.boolean || opt.type === 'boolean') { self.boolean(key); } if (opt.string || opt.type === 'string') { self.string(key); } - + var desc = opt.describe || opt.description || opt.desc; if (desc) { self.describe(key, desc); } } - + return self; }; - + var wrap = null; self.wrap = function (cols) { wrap = cols; return self; }; - + self.showHelp = function (fn) { if (!fn) fn = console.error; fn(self.help()); }; - + self.help = function () { var keys = Object.keys( Object.keys(descriptions) @@ -24007,13 +24007,13 @@ function Argv (processArgs, cwd) { return acc; }, {}) ); - + var help = keys.length ? [ 'Options:' ] : []; - + if (usage) { help.unshift(usage.replace(/\$0/g, self.$0), ''); } - + var switches = keys.reduce(function (acc, key) { acc[key] = [ key ].concat(options.alias[key] || []) .map(function (sw) { @@ -24023,42 +24023,42 @@ function Argv (processArgs, cwd) { ; return acc; }, {}); - + var switchlen = longest(Object.keys(switches).map(function (s) { return switches[s] || ''; })); - - var desclen = longest(Object.keys(descriptions).map(function (d) { + + var desclen = longest(Object.keys(descriptions).map(function (d) { return descriptions[d] || ''; })); - + keys.forEach(function (key) { var kswitch = switches[key]; var desc = descriptions[key] || ''; - + if (wrap) { desc = wordwrap(switchlen + 4, wrap)(desc) .slice(switchlen + 4) ; } - + var spadding = new Array( Math.max(switchlen - kswitch.length + 3, 0) ).join(' '); - + var dpadding = new Array( Math.max(desclen - desc.length + 1, 0) ).join(' '); - + var type = null; - + if (options.boolean[key]) type = '[boolean]'; if (options.string[key]) type = '[string]'; - + if (!wrap && dpadding.length > 0) { desc += dpadding; } - + var prelude = ' ' + kswitch + spadding; var extra = [ type, @@ -24071,14 +24071,14 @@ function Argv (processArgs, cwd) { : null , ].filter(Boolean).join(' '); - + var body = [ desc, extra ].filter(Boolean).join(' '); - + if (wrap) { var dlines = desc.split('\n'); var dlen = dlines.slice(-1)[0].length + (dlines.length === 1 ? prelude.length : 0) - + body = desc + (dlen + extra.length > wrap - 2 ? '\n' + new Array(wrap - extra.length + 1).join(' ') @@ -24087,38 +24087,38 @@ function Argv (processArgs, cwd) { + extra ); } - + help.push(prelude + body); }); - + help.push(''); return help.join('\n'); }; - + Object.defineProperty(self, 'argv', { get : function () { return parseArgs(processArgs) }, enumerable : true, }); - + function parseArgs (args) { var argv = minimist(args, options); argv.$0 = self.$0; - + if (demanded._ && argv._.length < demanded._) { fail('Not enough non-option arguments: got ' + argv._.length + ', need at least ' + demanded._ ); } - + var missing = []; Object.keys(demanded).forEach(function (key) { if (!argv[key]) missing.push(key); }); - + if (missing.length) { fail('Missing required arguments: ' + missing.join(', ')); } - + checks.forEach(function (f) { try { if (f(argv) === false) { @@ -24129,17 +24129,17 @@ function Argv (processArgs, cwd) { fail(err) } }); - + return argv; } - + function longest (xs) { return Math.max.apply( null, xs.map(function (x) { return x.length }) ); } - + return self; }; @@ -24149,10 +24149,10 @@ exports.rebase = rebase; function rebase (base, dir) { var ds = path.normalize(dir).split('/').slice(1); var bs = path.normalize(base).split('/').slice(1); - + for (var i = 0; ds[i] && ds[i] == bs[i]; i++); ds.splice(0, i); bs.splice(0, i); - + var p = path.normalize( bs.map(function () { return '..' }).concat(ds).join('/') ).replace(/\/$/,'').replace(/^$/, '.'); @@ -24162,17 +24162,17 @@ function rebase (base, dir) { },{"__browserify_process":15,"minimist":55,"path":7,"wordwrap":56}],55:[function(require,module,exports){ module.exports = function (args, opts) { if (!opts) opts = {}; - + var flags = { bools : {}, strings : {} }; - + [].concat(opts['boolean']).filter(Boolean).forEach(function (key) { flags.bools[key] = true; }); - + [].concat(opts.string).filter(Boolean).forEach(function (key) { flags.strings[key] = true; }); - + var aliases = {}; Object.keys(opts.alias || {}).forEach(function (key) { aliases[key] = [].concat(opts.alias[key]); @@ -24182,14 +24182,14 @@ module.exports = function (args, opts) { })); }); }); - + var defaults = opts['default'] || {}; - + var argv = { _ : [] }; Object.keys(flags.bools).forEach(function (key) { setArg(key, defaults[key] === undefined ? false : defaults[key]); }); - + var notFlags = []; if (args.indexOf('--') !== -1) { @@ -24202,15 +24202,15 @@ module.exports = function (args, opts) { ? Number(val) : val ; setKey(argv, key.split('.'), value); - + (aliases[key] || []).forEach(function (x) { setKey(argv, x.split('.'), value); }); } - + for (var i = 0; i < args.length; i++) { var arg = args[i]; - + if (arg.match(/^--.+=/)) { // Using [\s\S] instead of . because js doesn't support the // 'dotall' regex modifier. See: @@ -24241,7 +24241,7 @@ module.exports = function (args, opts) { } else if (arg.match(/^-[^-]+/)) { var letters = arg.slice(1,-1).split(''); - + var broken = false; for (var j = 0; j < letters.length; j++) { var next = arg.slice(j+2); @@ -24251,19 +24251,19 @@ module.exports = function (args, opts) { broken = true; break; } - + if (next === '-') { setArg(letters[j], next) continue; } - + if (/[A-Za-z]/.test(letters[j]) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { setArg(letters[j], next); broken = true; break; } - + if (letters[j+1] && letters[j+1].match(/\W/)) { setArg(letters[j], arg.slice(j+2)); broken = true; @@ -24273,7 +24273,7 @@ module.exports = function (args, opts) { setArg(letters[j], true); } } - + var key = arg.slice(-1)[0]; if (!broken && key !== '-') { if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) @@ -24297,17 +24297,17 @@ module.exports = function (args, opts) { ); } } - + Object.keys(defaults).forEach(function (key) { if (!hasKey(argv, key.split('.'))) { setKey(argv, key.split('.'), defaults[key]); - + (aliases[key] || []).forEach(function (x) { setKey(argv, x.split('.'), defaults[key]); }); } }); - + notFlags.forEach(function(key) { argv._.push(key); }); @@ -24331,7 +24331,7 @@ function setKey (obj, keys, value) { if (o[key] === undefined) o[key] = {}; o = o[key]; }); - + var key = keys[keys.length - 1]; if (o[key] === undefined || typeof o[key] === 'boolean') { o[key] = value; @@ -24361,22 +24361,22 @@ var wordwrap = module.exports = function (start, stop, params) { start = params.start; stop = params.stop; } - + if (typeof stop === 'object') { params = stop; start = start || params.start; stop = undefined; } - + if (!stop) { stop = start; start = 0; } - + if (!params) params = {}; var mode = params.mode || 'soft'; var re = mode === 'hard' ? /\b/ : /(\S+\s+)/; - + return function (text) { var chunks = text.toString() .split(re) @@ -24390,16 +24390,16 @@ var wordwrap = module.exports = function (start, stop, params) { return acc; }, []) ; - + return chunks.reduce(function (lines, rawChunk) { if (rawChunk === '') return lines; - + var chunk = rawChunk.replace(/\t/g, ' '); - + var i = lines.length - 1; if (lines[i].length + chunk.length > stop) { lines[i] = lines[i].replace(/\s+$/, ''); - + chunk.split(/\n/).forEach(function (c) { lines.push( new Array(start + 1).join(' ') @@ -24420,7 +24420,7 @@ var wordwrap = module.exports = function (start, stop, params) { else { lines[i] += chunk; } - + return lines; }, [ new Array(start + 1).join(' ') ]).join('\n'); }; @@ -31762,7 +31762,6 @@ function YamlFile(filename, docs) { }); } else { it(doc.description, function (done) { - // console.log('test doc'); async.series(_.pluck(doc._actions, 'testable'), done); }); } diff --git a/test/integration/yaml_suite/yaml_file.js b/test/integration/yaml_suite/yaml_file.js index 23a96e3f0..f09ca2db7 100644 --- a/test/integration/yaml_suite/yaml_file.js +++ b/test/integration/yaml_suite/yaml_file.js @@ -20,19 +20,16 @@ function YamlFile(filename, docs) { doc = new YamlDoc(doc, file); if (doc.description === 'setup') { beforeEach(/* doc */function (done) { - console.log('setting up:', filename); async.series(_.pluck(doc._actions, 'testable'), done); }); } else { it(doc.description, function (done) { - console.log('test doc'); async.series(_.pluck(doc._actions, 'testable'), done); }); } }); afterEach(/* doc */function (done) { - console.log('clearing indices'); clientManager.get().indices.delete({ index: '*', ignore: 404 diff --git a/test/mocks/incomming_message.js b/test/mocks/incomming_message.js index 58b9ecf6b..52e7d4622 100644 --- a/test/mocks/incomming_message.js +++ b/test/mocks/incomming_message.js @@ -2,7 +2,7 @@ * Simple Mock of the http.IncommingMessage. Just implmenents the methods the methods * we use * - * @type {[type]} + * @type {Constuctor} */ module.exports = MockIncommingMessage; diff --git a/test/mocks/old_writable_stream.js b/test/mocks/old_writable_stream.js new file mode 100644 index 000000000..e5dd15c71 --- /dev/null +++ b/test/mocks/old_writable_stream.js @@ -0,0 +1,22 @@ +/** + * Just a buffer really, but one that implements just a few methods similar + * to the old writable streams, but without the same internals as streams 2.0 + * Writables. + * + * @type {Constuctor} + */ +module.exports = MockOldWritableStream; + +var util = require('util'); + +function MockOldWritableStream(opts) { + var queue = []; + + this.write = function (chunk) { + queue.push(chunk); + }; + + this.end = function () { + queue.push(null); + }; +} diff --git a/test/mocks/request.js b/test/mocks/request.js index 14d799f2e..b2b2da7b8 100644 --- a/test/mocks/request.js +++ b/test/mocks/request.js @@ -1,3 +1,8 @@ +/** + * Extended version of http.ClientRequest with a few methods stubbed + * + * @type {Constructor} + */ module.exports = MockRequest; var sinon = require('sinon'); diff --git a/test/mocks/writable_stream.js b/test/mocks/writable_stream.js index 0c1ece237..284adc498 100644 --- a/test/mocks/writable_stream.js +++ b/test/mocks/writable_stream.js @@ -1,6 +1,6 @@ /** * Just a buffer really, but one that implements the Writeable class - * @type {WritableStream} + * @type {Constuctor} */ module.exports = MockWritableStream; @@ -10,9 +10,7 @@ var util = require('util'); function MockWritableStream(opts) { Writable.call(this, opts); - this._write = function (chunk, encoding, cb) { - - }; + this._write = function (chunk, encoding, cb) {}; } util.inherits(MockWritableStream, Writable); diff --git a/test/unit/auto_release_stub.js b/test/unit/auto_release_stub.js new file mode 100644 index 000000000..53e1fb3da --- /dev/null +++ b/test/unit/auto_release_stub.js @@ -0,0 +1,23 @@ + +var sinon = require('sinon'); + +exports.make = function () { + var log = []; + afterEach(function () { + var stub; + while (stub = log.pop()) { + stub.restore(); + } + }); + var stubber = function () { + log.push(sinon.stub.apply(sinon, arguments)); + }; + + stubber.autoRelease = function (item) { + if (item.restore && !~log.indexOf(item)) { + log.push(item); + } + }; + + return stubber; +}; \ No newline at end of file diff --git a/test/unit/test_logger.js b/test/unit/generic_logger_tests.js similarity index 64% rename from test/unit/test_logger.js rename to test/unit/generic_logger_tests.js index 67970dbc9..977b7d4c9 100644 --- a/test/unit/test_logger.js +++ b/test/unit/generic_logger_tests.js @@ -1,79 +1,65 @@ -describe('Logger Abstract', function () { +var Log = require('../../src/lib/log'); +var LoggerAbstract = require('../../src/lib/logger'); +var now = new Date('2013-03-01T00:00:00Z'); +var sinon = require('sinon'); - var sinon = require('sinon'); - var now = new Date('2013-03-01T00:00:00Z'); - var Log = require('../../src/lib/log'); - var LoggerAbstract = require('../../src/lib/logger'); - - var parentLog; - var stubs = []; - - function stub() { - stubs.push(sinon.stub.apply(sinon, arguments)); - } - - function makeLogger(levels) { - return new LoggerAbstract(parentLog, { - levels: levels || [] - }); - } - - beforeEach(function () { - parentLog = new Log(); - }); +module.exports = function (makeLogger) { + var stub = require('./auto_release_stub').make(); + var parent = new Log(); afterEach(function () { - var stub; - while (stub = stubs.pop()) { - stub.restore(); - } - - parentLog.close(); + parent.close(); }); describe('Constuctor', function () { - it('calls setupListeners', function () { - stub(LoggerAbstract.prototype, 'setupListeners'); - var logger = makeLogger(); + it('calls setupListeners, passes its new levels', function () { + var logger = makeLogger(parent); + stub(logger.constructor.prototype, 'setupListeners'); + parent.close(); + + logger = makeLogger(parent); logger.setupListeners.callCount.should.eql(1); }); it('listens for the loggers\' "closing" event', function () { - var logger = makeLogger(); - parentLog.listenerCount('closing').should.eql(1); + var logger = makeLogger(parent); + parent.listenerCount('closing').should.eql(1); }); }); - describe('#setupListeners', function () { - it('calls cleanUpListeners', function () { + describe('listening levels', function () { + it('calls cleanUpListeners when the listeners are being setup', function () { var logger = makeLogger(); stub(logger, 'cleanUpListeners'); logger.setupListeners([]); logger.cleanUpListeners.callCount.should.eql(1); }); - it('explicitly listens for the events specified', function () { - var logger = makeLogger(); - logger.setupListeners(['error']); - parentLog.listenerCount('error').should.eql(1); - parentLog.listenerCount('warning').should.eql(0); - parentLog.listenerCount('info').should.eql(0); - parentLog.listenerCount('debug').should.eql(0); - parentLog.listenerCount('trace').should.eql(0); + it('listens to just error when log is explicitly error', function () { + var logger = makeLogger(parent, 'error'); + parent.listenerCount('error').should.eql(1); + parent.listenerCount('warning').should.eql(0); + parent.listenerCount('info').should.eql(0); + parent.listenerCount('debug').should.eql(0); + parent.listenerCount('trace').should.eql(0); + }); - logger.setupListeners(['warning', 'trace']); - parentLog.listenerCount('error').should.eql(0); - parentLog.listenerCount('warning').should.eql(1); - parentLog.listenerCount('info').should.eql(0); - parentLog.listenerCount('debug').should.eql(0); - parentLog.listenerCount('trace').should.eql(1); + it('listens for all the events when level is "trace"', function () { + var logger = makeLogger(parent, 'trace'); + parent.listenerCount('error').should.eql(1); + parent.listenerCount('warning').should.eql(1); + parent.listenerCount('info').should.eql(1); + parent.listenerCount('debug').should.eql(1); + parent.listenerCount('trace').should.eql(1); + }); - logger.setupListeners(['debug', 'debug']); - parentLog.listenerCount('error').should.eql(0); - parentLog.listenerCount('warning').should.eql(0); - parentLog.listenerCount('info').should.eql(0); - parentLog.listenerCount('debug').should.eql(2); - parentLog.listenerCount('trace').should.eql(0); + it('listens for specific events when level is an array', function () { + var logger = makeLogger(parent, ['error', 'trace']); + parent.listenerCount('error').should.eql(1); + parent.listenerCount('warning').should.eql(0); + parent.listenerCount('info').should.eql(0); + parent.listenerCount('debug').should.eql(0); + parent.listenerCount('trace').should.eql(1); }); it('sets the logLevel property to the new levels', function () { @@ -86,7 +72,6 @@ describe('Logger Abstract', function () { logger.setupListeners(levels); logger.listeningLevels.should.eql(levels).and.not.be.exactly(levels); - levels = ['debug', 'debug']; logger.setupListeners(levels); logger.listeningLevels.should.eql(levels).and.not.be.exactly(levels); @@ -98,19 +83,39 @@ describe('Logger Abstract', function () { logger.setupListeners(['scream']); }).should.throw(/unable to listen/i); }); + + it('emits events because something is listening', function () { + var logger = makeLogger(parent, 'trace'); + stub(parent, 'emit'); + + parent.error(new Error('error message')); + parent.emit.lastCall.args[0].should.eql('error'); + + parent.warning('warning'); + parent.emit.lastCall.args[0].should.eql('warning'); + + parent.info('info'); + parent.emit.lastCall.args[0].should.eql('info'); + + parent.debug('debug'); + parent.emit.lastCall.args[0].should.eql('debug'); + + parent.trace('GET', {}, '', '', 200); + parent.emit.lastCall.args[0].should.eql('trace'); + }); }); describe('#timestamp', function () { it('returns in the right format', function () { - stubs.push(sinon.useFakeTimers(now.getTime())); + stub.autoRelease(sinon.useFakeTimers(now.getTime())); var logger = makeLogger(); logger.timestamp().should.eql('2013-03-01T00:00:00Z'); }); }); - describe('#formate', function () { + describe('#format', function () { it('returns a single string with the message indented', function () { - stubs.push(sinon.useFakeTimers(now.getTime())); + stub.autoRelease(sinon.useFakeTimers(now.getTime())); var logger = makeLogger(); logger.format('LABEL', 'MSG').should.eql( 'LABEL: 2013-03-01T00:00:00Z\n' + @@ -120,7 +125,7 @@ describe('Logger Abstract', function () { }); it('properly indents multi-line messages', function () { - stubs.push(sinon.useFakeTimers(now.getTime())); + stub.autoRelease(sinon.useFakeTimers(now.getTime())); var logger = makeLogger(); logger.format('LABEL', 'MSG\nwith\nseveral lines').should.eql( 'LABEL: 2013-03-01T00:00:00Z\n' + @@ -132,15 +137,6 @@ describe('Logger Abstract', function () { }); }); - describe('#write', function () { - it('requires that it is overwritten', function () { - (function () { - var logger = makeLogger(); - logger.write(); - }).should.throw(/overwritten/); - }); - }); - describe('#onError', function () { it('uses the Error name when it is not just "Error"', function () { var logger = makeLogger(); @@ -246,5 +242,4 @@ describe('Logger Abstract', function () { logger.write.callCount.should.eql(1); }); }); - -}); \ No newline at end of file +}; \ No newline at end of file diff --git a/test/unit/test_abstract_logger.js b/test/unit/test_abstract_logger.js new file mode 100644 index 000000000..0f4bbca7e --- /dev/null +++ b/test/unit/test_abstract_logger.js @@ -0,0 +1,35 @@ +describe('Logger Abstract', function () { + + var sinon = require('sinon'); + var Log = require('../../src/lib/log'); + var LoggerAbstract = require('../../src/lib/logger'); + + var parentLog; + var stub = require('./auto_release_stub').make(); + + function makeLogger(parent, levels) { + return new LoggerAbstract(parent || parentLog, { + levels: Log.parseLevels(levels || []) + }); + } + + beforeEach(function () { + parentLog = new Log(); + }); + + afterEach(function () { + parentLog.close(); + }); + + describe('#write', function () { + it('requires that it is overwritten', function () { + (function () { + var logger = makeLogger(); + logger.write(); + }).should.throw(/overwritten/); + }); + }); + + require('./generic_logger_tests')(makeLogger); + +}); \ No newline at end of file diff --git a/test/unit/test_client_action.js b/test/unit/test_client_action.js index 518023914..839e83a07 100644 --- a/test/unit/test_client_action.js +++ b/test/unit/test_client_action.js @@ -515,7 +515,7 @@ describe('Client Action runner', function () { params.query.two.should.equal('-69'); params.query.three.should.equal('15'); params.query.four.should.equal('' + now.getTime()); - params.query.five.should.equal('-11162941200000'); + params.query.five.should.equal('-11162948400000'); done(); }); }); diff --git a/test/unit/test_connection_abstract.js b/test/unit/test_connection_abstract.js index 9a1a8868c..8a3523d5f 100644 --- a/test/unit/test_connection_abstract.js +++ b/test/unit/test_connection_abstract.js @@ -3,13 +3,7 @@ var Host = require('../../src/lib/host'); var sinon = require('sinon'); var _ = require('lodash'); -var stubs = []; -afterEach(function () { - var stub; - while (stub = stubs.pop()) { - stub.restore(); - } -}); +var stub = require('./auto_release_stub').make(); describe('Connection Abstract', function () { var host = new Host('localhost:9200'); @@ -83,7 +77,7 @@ describe('Connection Abstract', function () { it('sets a timeout when set to dead, and removed when alive', function () { var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout'); - stubs.push(clock); + stub.autoRelease(clock); var conn = new ConnectionAbstract(host); var start = _.size(clock.timeouts); @@ -112,7 +106,7 @@ describe('Connection Abstract', function () { it('should ping the connection after the deadTimeout, and set the status to "alive" on pong', function (done) { var conn = new ConnectionAbstract(host); var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout'); - stubs.push(clock); + stub.autoRelease(clock); // schedules the resuscitate conn.setStatus('dead'); @@ -138,7 +132,7 @@ describe('Connection Abstract', function () { it('should ping the connection after the deadTimeout, and set the status to "dead" on error', function (done) { var conn = new ConnectionAbstract(host); var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout'); - stubs.push(clock); + stub.autoRelease(clock); // schedules the resuscitate conn.setStatus('dead'); diff --git a/test/unit/test_console_logger.js b/test/unit/test_console_logger.js new file mode 100644 index 000000000..132004da2 --- /dev/null +++ b/test/unit/test_console_logger.js @@ -0,0 +1,28 @@ +var Log = require('../../src/lib/log'); +var StdioLogger = require('../../src/lib/loggers/console'); +var sinon = require('sinon'); +var parentLog; + +beforeEach(function () { + parentLog = new Log(); +}); + +afterEach(function () { + parentLog.close(); +}); + +function makeLogger(parent, levels) { + parent = parent || parentLog; + var config = { + levels: Log.parseLevels(levels || 'trace') + }; + return new StdioLogger(parent, config); +} + +var stub = require('./auto_release_stub').make(); + +describe('Console Logger', function () { + + require('./generic_logger_tests')(makeLogger); + +}); diff --git a/test/unit/test_file_logger.js b/test/unit/test_file_logger.js new file mode 100644 index 000000000..43de8721d --- /dev/null +++ b/test/unit/test_file_logger.js @@ -0,0 +1,29 @@ +var Log = require('../../src/lib/log'); +var FileLogger = require('../../src/lib/loggers/file'); +var sinon = require('sinon'); +var parentLog; + +beforeEach(function () { + parentLog = new Log(); +}); + +afterEach(function () { + parentLog.close(); +}); + +function makeLogger(parent, levels, path) { + parent = parent || parentLog; + var config = { + levels: Log.parseLevels(levels || 'trace'), + path: path === void 0 ? 'elasticsearch.log' : path + }; + return new FileLogger(parent, config); +} + +var stub = require('./auto_release_stub').make(); + +describe('File Logger', function () { + + require('./generic_logger_tests')(makeLogger); + +}); diff --git a/test/unit/test_host.js b/test/unit/test_host.js index 10fd946b1..47c3871c2 100644 --- a/test/unit/test_host.js +++ b/test/unit/test_host.js @@ -154,7 +154,14 @@ describe('Host class', function () { }); describe('#toString', function () { - // just calls makeUrl without any params + it('produces the same output as makeUrl when it is called without params', function () { + var host = new Host({ + path: '/pasta', + host: 'google.com' + }); + + host.toString().should.eql(host.makeUrl()); + }); }); }); diff --git a/test/unit/test_http_connector.js b/test/unit/test_http_connector.js index 998d5f0d8..cde7355e0 100644 --- a/test/unit/test_http_connector.js +++ b/test/unit/test_http_connector.js @@ -17,14 +17,7 @@ describe('Http Connector', function () { nock.disableNetConnect(); - afterEach(function () { - if (http.request.restore) { - http.request.restore(); - } - if (https.request.restore) { - https.request.restore(); - } - }); + var stub = require('./auto_release_stub').make(); function makeStubReqMethod(prep) { return function (params, cb) { @@ -195,8 +188,8 @@ describe('Http Connector', function () { describe('#request', function () { beforeEach(function () { - sinon.stub(http, 'request', makeStubReqMethod(whereReqDies())); - sinon.stub(https, 'request', makeStubReqMethod(whereReqDies())); + stub(http, 'request', makeStubReqMethod(whereReqDies())); + stub(https, 'request', makeStubReqMethod(whereReqDies())); }); it('calls http based on the host', function (done) { @@ -220,10 +213,14 @@ describe('Http Connector', function () { it('logs error events, and sets the connection to dead when an error occurs', function (done) { var con = new HttpConnection(new Host('http://google.com')); - sinon.stub(con.log); + stub(con.log, 'error'); + stub(con.log, 'trace'); + stub(con.log, 'info'); + stub(con.log, 'warning'); + stub(con.log, 'debug'); http.request.restore(); - sinon.stub(http, 'request', makeStubReqMethod(whereReqDies(new Error('actual error')))); + stub(http, 'request', makeStubReqMethod(whereReqDies(new Error('actual error')))); con.request({}, function (err) { // error should have been sent to the @@ -246,10 +243,9 @@ describe('Http Connector', function () { it('logs error events, and sets the connection to dead', function (done) { var con = new HttpConnection(new Host('http://google.com')); - sinon.stub(con.log); + stub(con.log, 'error'); - http.request.restore(); - sinon.stub(http, 'request', makeStubReqMethod(whereReqDies(new Error('actual error')))); + http.request.func = makeStubReqMethod(whereReqDies(new Error('actual error'))); con.request({}, function (err) { // error should have been sent to the @@ -274,8 +270,8 @@ describe('Http Connector', function () { it('logs error event', function (done) { var con = new HttpConnection(new Host('https://google.com')); - sinon.stub(con.log, 'error'); - sinon.stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody()); + stub(con.log, 'error'); + stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody()); con.request({}, function (err, resp, status) { con.log.error.callCount.should.eql(1); @@ -285,7 +281,7 @@ describe('Http Connector', function () { it('and sets the connection to dead', function (done) { var con = new HttpConnection(new Host('https://google.com')); - sinon.stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody()); + stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody()); con.request({}, function (err, resp, status) { con.status.should.eql('dead'); @@ -295,7 +291,7 @@ describe('Http Connector', function () { it('passes the original error on', function (done) { var con = new HttpConnection(new Host('https://google.com')); - sinon.stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody(new Error('no more message :('))); + stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody(new Error('no more message :('))); con.request({}, function (err, resp, status) { should.exist(err); @@ -306,7 +302,7 @@ describe('Http Connector', function () { it('does not pass the partial body along', function (done) { var con = new HttpConnection(new Host('https://google.com')); - sinon.stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody()); + stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody()); con.request({}, function (err, resp, status) { should.not.exist(resp); @@ -316,7 +312,7 @@ describe('Http Connector', function () { it('does not pass the status code along', function (done) { var con = new HttpConnection(new Host('https://google.com')); - sinon.stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody()); + stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody()); con.request({}, function (err, resp, status) { should.not.exist(status); diff --git a/test/unit/test_json_serializer.js b/test/unit/test_json_serializer.js new file mode 100644 index 000000000..b227f8c38 --- /dev/null +++ b/test/unit/test_json_serializer.js @@ -0,0 +1,99 @@ +var JsonSerializer = require('../../src/lib/serializers/json'); +var should = require('should'); + +describe('JSON serializer', function () { + var stub = require('./auto_release_stub').make(); + + function makeSerializer() { + return new JsonSerializer(); + } + + describe('#serialize', function () { + it('defers to JSON.stringify', function () { + stub(JSON, 'stringify'); + var ser = makeSerializer(); + ser.serialize({ some: 'object' }); + JSON.stringify.callCount.should.eql(1); + }); + + it('does not modify strings', function () { + var ser = makeSerializer(); + var thing = 'pretend that I am serialized'; + ser.serialize(thing).should.be.exactly(thing); + }); + + it('returns nothing for invalid values', function () { + var ser = makeSerializer(); + + should.not.exist(ser.serialize(null)); + should.not.exist(ser.serialize(false)); + }); + + it('throws serialization errors', function () { + var ser = makeSerializer(); + var thing = { name: 'thing' }; + thing.self = thing; + + (function () { + ser.serialize(thing); + }).should.throw(); + }); + }); + + describe('#deserialize', function () { + it('defers to JSON.parse', function () { + stub(JSON, 'parse'); + var ser = makeSerializer(); + ser.deserialize('{ "some": "JSON" }'); + JSON.parse.callCount.should.eql(1); + }); + + it('ignores non string values', function () { + var ser = makeSerializer(); + var thing = ['pretend that I am not here']; + should.not.exist(ser.deserialize(thing)); + should.not.exist(ser.deserialize(null)); + should.not.exist(ser.deserialize(false)); + }); + + it('catches serialization errors, returns nothing', function () { + var ser = makeSerializer(); + var thing = '{ name: \'thing\' }'; + + should.not.exist(ser.deserialize(thing)); + }); + }); + + describe('#bulkBody', function () { + var body = [ + { index: 'thing' }, + { document: 'hi' } + ]; + var bulk = '{"index":"thing"}\n{"document":"hi"}\n'; + + it('creates a string out of an array of obejcts', function () { + var ser = makeSerializer(); + ser.bulkBody(body).should.eql(bulk); + }); + + it('adds a newline to the end of strings', function () { + var ser = makeSerializer(); + ser.bulkBody(bulk.substr(0, bulk.length - 1)).should.eql(bulk); + }); + + it('throws an error for anything else', function () { + var ser = makeSerializer(); + (function () { + ser.bulkBody({}); + }).should.throw(); + + (function () { + ser.bulkBody(null); + }).should.throw(); + + (function () { + ser.bulkBody(false); + }).should.throw(); + }); + }); +}); \ No newline at end of file diff --git a/test/unit/test_stdio_logger.js b/test/unit/test_stdio_logger.js index 5d9da6089..8ad333ca2 100644 --- a/test/unit/test_stdio_logger.js +++ b/test/unit/test_stdio_logger.js @@ -1,7 +1,6 @@ var Log = require('../../src/lib/log'); var StdioLogger = require('../../src/lib/loggers/stdio'); var sinon = require('sinon'); -var stubs = []; var parentLog; beforeEach(function () { @@ -9,60 +8,22 @@ beforeEach(function () { }); afterEach(function () { - var stub; - while (stub = stubs.pop()) { - stub.restore(); - } parentLog.close(); }); -function stub() { - stubs.push(sinon.stub.apply(sinon, arguments)); +function makeLogger(parent, levels) { + parent = parent || parentLog; + var config = { + levels: Log.parseLevels(levels || 'trace') + }; + return new StdioLogger(parent, config); } -function makeLogger(colors) { - var config = { - levels: Log.parseLevels('trace') - }; - if (colors !== void 0) { - config.colors = colors; - } - return new StdioLogger(parentLog, config); -} +var stub = require('./auto_release_stub').make(); describe('Stdio Logger', function () { - describe('pays attention to the level setting', function () { - beforeEach(function () { - var logger = makeLogger(); - stub(parentLog, 'emit'); - }); - - it('listens for all the events', function () { - parentLog.listenerCount('error').should.eql(1); - parentLog.listenerCount('warning').should.eql(1); - parentLog.listenerCount('info').should.eql(1); - parentLog.listenerCount('debug').should.eql(1); - parentLog.listenerCount('trace').should.eql(1); - }); - - it('emits events because something is listening', function () { - parentLog.error(new Error('error message')); - parentLog.emit.lastCall.args[0].should.eql('error'); - - parentLog.warning('warning'); - parentLog.emit.lastCall.args[0].should.eql('warning'); - - parentLog.info('info'); - parentLog.emit.lastCall.args[0].should.eql('info'); - - parentLog.debug('debug'); - parentLog.emit.lastCall.args[0].should.eql('debug'); - - parentLog.trace('GET', {}, '', '', 200); - parentLog.emit.lastCall.args[0].should.eql('trace'); - }); - }); + require('./generic_logger_tests')(makeLogger); describe('colorizing', function () { var chalk = require('chalk'); @@ -72,7 +33,7 @@ describe('Stdio Logger', function () { var clock; beforeEach(function () { - stubs.push(sinon.useFakeTimers(nowTime)); + stub.autoRelease(sinon.useFakeTimers(nowTime)); }); it('uses colors when it\'s supported', function () { @@ -92,7 +53,8 @@ describe('Stdio Logger', function () { }); it('obeys the logger.color === true', function () { - var logger = makeLogger(false); + var logger = makeLogger(); + stub(process.stdout, 'write'); var withoutColor = 'TRACE: ' + now + '\n curl\n msg\n\n'; @@ -103,110 +65,4 @@ describe('Stdio Logger', function () { }); }); - - describe('#onError', function () { - it('uses the Error name when it is not just "Error"', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - label.should.eql('TypeError'); - }); - - logger.onError(new TypeError('Typerr')); - logger.write.callCount.should.eql(1); - }); - - it('uses "ERROR" when the error name is "Error"', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - label.should.eql('ERROR'); - }); - - logger.onError(new Error('thing')); - logger.write.callCount.should.eql(1); - }); - }); - - describe('#onWarning', function () { - it('uses the "WARNING" label', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - label.should.eql('WARNING'); - }); - logger.onWarning('message'); - logger.write.callCount.should.eql(1); - }); - - it('echos the message', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - msg.should.eql('message'); - }); - - logger.onWarning('message'); - logger.write.callCount.should.eql(1); - }); - }); - - describe('#onInfo', function () { - it('uses the "INFO" label', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - label.should.eql('INFO'); - }); - logger.onInfo('message'); - logger.write.callCount.should.eql(1); - }); - - it('echos the message', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - msg.should.eql('message'); - }); - - logger.onInfo('message'); - logger.write.callCount.should.eql(1); - }); - }); - - describe('#onDebug', function () { - it('uses the "DEBUG" label', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - label.should.eql('DEBUG'); - }); - logger.onDebug('message'); - logger.write.callCount.should.eql(1); - }); - - it('echos the message', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - msg.should.eql('message'); - }); - - logger.onDebug('message'); - logger.write.callCount.should.eql(1); - }); - }); - - describe('#onTrace', function () { - it('uses the "TRACE" label', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - label.should.eql('TRACE'); - }); - logger.onTrace('message'); - logger.write.callCount.should.eql(1); - }); - - it('joins the message and curl call with a newline', function () { - var logger = makeLogger(); - stub(logger, 'write', function (to, label, colorize, msg) { - msg.should.eql('curlcall\nmessage'); - }); - - logger.onTrace('message', 'curlcall'); - logger.write.callCount.should.eql(1); - }); - }); }); diff --git a/test/unit/test_stream_logger.js b/test/unit/test_stream_logger.js index 1e5f535a9..0e80a8785 100644 --- a/test/unit/test_stream_logger.js +++ b/test/unit/test_stream_logger.js @@ -1,76 +1,37 @@ var Log = require('../../src/lib/log'); var StreamLogger = require('../../src/lib/loggers/stream'); var MockWritableStream = require('../mocks/writable_stream'); +var MockOldWritableStream = require('../mocks/old_writable_stream'); var once = require('events').EventEmitter.prototype.once; var write = require('stream').Writable.prototype.write; var sinon = require('sinon'); -var stubs = []; -var parentLog; var stream = new MockWritableStream(); var _ = require('lodash'); var util = require('util'); +var parentLog; + +var stub = require('./auto_release_stub').make(); beforeEach(function () { parentLog = new Log(); - stub(stream, 'write', function () { console.log('stubbed write'); }); - stub(stream, 'end', function () { console.log('stubbed close'); }); + stub(stream, 'write'); + stub(stream, 'end'); }); afterEach(function () { parentLog.close(); - - var stub; - while (stub = stubs.pop()) { - stub.restore(); - } }); -function stub() { - stubs.push(sinon.stub.apply(sinon, arguments)); -} - -function makeLogger() { +function makeLogger(parent, levels) { + parent = parent || parentLog; var config = { - levels: Log.parseLevels('trace'), + levels: Log.parseLevels(levels || 'trace'), stream: stream }; - return new StreamLogger(parentLog, config); + return new StreamLogger(parent, config); } describe('Stream Logger', function () { - describe('pays attention to the level setting', function () { - - beforeEach(function () { - var logger = makeLogger(); - stub(parentLog, 'emit'); - }); - - it('listens for all the events', function () { - parentLog.listenerCount('error').should.eql(1); - parentLog.listenerCount('warning').should.eql(1); - parentLog.listenerCount('info').should.eql(1); - parentLog.listenerCount('debug').should.eql(1); - parentLog.listenerCount('trace').should.eql(1); - }); - - it('emits events because something is listening', function () { - parentLog.error(new Error('error message')); - parentLog.emit.lastCall.args[0].should.eql('error'); - - parentLog.warning('warning'); - parentLog.emit.lastCall.args[0].should.eql('warning'); - - parentLog.info('info'); - parentLog.emit.lastCall.args[0].should.eql('info'); - - parentLog.debug('debug'); - parentLog.emit.lastCall.args[0].should.eql('debug'); - - parentLog.trace('GET', {}, '', '', 200); - parentLog.emit.lastCall.args[0].should.eql('trace'); - }); - }); - describe('buffer flushing', function () { it('writes everything in the buffer to console.error', function () { var onExitCallback; @@ -99,8 +60,14 @@ describe('Stream Logger', function () { // empty the buffer manually stream._writableState.buffer.splice(0); - flushedOutput.match(new RegExp(line, 'g')).length.should.above(0); + // the first line is stuck "in writing" and there is nothing we can do about that + flushedOutput.match(new RegExp(line, 'g')).length.should.eql(9); }); + + it('works with older streams'); }); + + require('./generic_logger_tests')(makeLogger); + }); diff --git a/test/unit/test_transport.js b/test/unit/test_transport.js index fc62600a8..3a13862ce 100644 --- a/test/unit/test_transport.js +++ b/test/unit/test_transport.js @@ -4,17 +4,7 @@ var Host = require('../../src/lib/host'); var sinon = require('sinon'); var nodeList = require('../fixtures/short_node_list.json'); -var stubs = []; -function stub() { - stubs.push(sinon.stub.apply(sinon, arguments)); -} -afterEach(function () { - var stub; - while (stub = stubs.pop()) { - stub.restore(); - } -}); - +var stub = require('./auto_release_stub').make(); describe('Transport Class', function () { diff --git a/test/unit/test_utils.js b/test/unit/test_utils.js index ade066df7..c78d1ad68 100644 --- a/test/unit/test_utils.js +++ b/test/unit/test_utils.js @@ -134,8 +134,8 @@ describe('Utils', function () { _.camelCase('Json_parser').should.eql('jsonParser'); }); - it('handles trailing _', function () { - _.camelCase('_thing_one_').should.eql('thingOne'); + it('handles leading _', function () { + _.camelCase('_thing_one_').should.eql('_thingOne'); }); }); @@ -148,8 +148,8 @@ describe('Utils', function () { _.studlyCase('Json_parser').should.eql('JsonParser'); }); - it('handles trailing _', function () { - _.studlyCase('_thing_one_').should.eql('ThingOne'); + it('handles leading _', function () { + _.studlyCase('_thing_one_').should.eql('_ThingOne'); }); }); @@ -162,8 +162,8 @@ describe('Utils', function () { _.snakeCase('Json_parser').should.eql('json_parser'); }); - it('handles trailing _', function () { - _.snakeCase('_thing_one_').should.eql('thing_one'); + it('handles leading _', function () { + _.snakeCase('_thing_one_').should.eql('_thing_one'); }); }); @@ -317,13 +317,22 @@ describe('Utils', function () { _.funcEnum(config, 'config key name', { toString: 'pizza' }, 'toJSON') .should.be.exactly('pizza'); }); - it('throws an error if the selection if invalid', function () { + it('throws an informative error if the selection if invalid', function () { var config = { 'config': 'val' }; + + (function () { + _.funcEnum(config, 'config', {}); + }).should.throw(/expected a function/i); + (function () { _.funcEnum(config, 'config', { main: 'default' }, 'main'); - }).should.throw(/invalid config/i); + }).should.throw(/expected a function or main/i); + + (function () { + _.funcEnum(config, 'config', { main: 'default', other: 'default' }, 'main'); + }).should.throw(/expected a function or one of main, other/i); }); }); });