more tests, now with 91% coverage!
This commit is contained in:
@ -32,13 +32,8 @@ function File(log, config) {
|
||||
_.inherits(File, StreamLogger);
|
||||
|
||||
File.prototype.onProcessExit = _.handler(function () {
|
||||
// flush the write buffer to disk
|
||||
var writeBuffer = this.stream._writableState.buffer;
|
||||
var out = '';
|
||||
if (writeBuffer) {
|
||||
writeBuffer.forEach(function (buffered) {
|
||||
out += buffered.chunk.toString();
|
||||
});
|
||||
fs.appendFileSync(this.path, out);
|
||||
var toWrite = _.getUnwrittenFromStream(this.stream);
|
||||
if (toWrite) {
|
||||
fs.appendFileSync(this.path, toWrite);
|
||||
}
|
||||
});
|
||||
|
||||
@ -21,7 +21,7 @@ function Stream(log, config) {
|
||||
if (config.stream && config.stream.write && config.stream.end) {
|
||||
this.stream = config.stream;
|
||||
} else {
|
||||
throw new TypeError('Invalid stream, use an instance of stream.Writeable');
|
||||
throw new TypeError('Invalid stream, use an instance of stream.Writable');
|
||||
}
|
||||
|
||||
process.once('exit', this.bound.onProcessExit);
|
||||
@ -36,14 +36,10 @@ Stream.prototype.cleanUpListeners = _.handler(function () {
|
||||
// flush the write buffer to stderr synchronously
|
||||
Stream.prototype.onProcessExit = _.handler(function () {
|
||||
// process is dying, lets manually flush the buffer synchronously to stderr.
|
||||
if (this.stream._writableState && this.stream._writableState.buffer) {
|
||||
var writeBuffer = this.stream._writableState.buffer;
|
||||
if (writeBuffer.length) {
|
||||
console.error('Log stream did not get to finish writing. Flushing to stderr');
|
||||
writeBuffer.forEach(function (buffered) {
|
||||
console.error(buffered.chunk.toString());
|
||||
});
|
||||
}
|
||||
var unwritten = _.getUnwrittenFromStream(this.stream);
|
||||
if (unwritten) {
|
||||
console.error('Log stream did not get to finish writing. Flushing to stderr');
|
||||
console.error(unwritten);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -226,32 +226,6 @@ utils.repeat = function (what, times) {
|
||||
return (new Array(times + 1)).join(what);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Remove leading/trailing spaces from a string
|
||||
*
|
||||
* @param str {String} - Any string
|
||||
* @returns {String}
|
||||
*/
|
||||
utils.trim = function (str) {
|
||||
return typeof str === 'string' ? str.replace(/^\s+|\s+$/g, '') : '';
|
||||
};
|
||||
|
||||
utils.collectMatches = function (text, regExp) {
|
||||
var matches = [], match;
|
||||
while (match = regExp.exec(text)) {
|
||||
matches.push(match);
|
||||
if (regExp.global !== true) {
|
||||
// would loop forever if not true
|
||||
break;
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call a function, applying the arguments object to it in an optimized way, rather than always turning it into an array
|
||||
*
|
||||
@ -328,22 +302,14 @@ _.scheduled = _.handler;
|
||||
* ```
|
||||
*
|
||||
* @param {Object} obj - The object to bind the methods to
|
||||
* @param {Array} [methods] - The methods to bind, false values === bind all flagged with _provideBound
|
||||
*/
|
||||
_.makeBoundMethods = function (obj, methods) {
|
||||
_.makeBoundMethods = function (obj) {
|
||||
obj.bound = {};
|
||||
if (!methods) {
|
||||
methods = [];
|
||||
for (var prop in obj) {
|
||||
// dearest maintainer, we want to look through the prototype
|
||||
if (typeof obj[prop] === 'function' && obj[prop]._provideBound === true) {
|
||||
obj.bound[prop] = _.bind(obj[prop], obj);
|
||||
}
|
||||
for (var prop in obj) {
|
||||
// dearest maintainer, we want to look through the prototype
|
||||
if (typeof obj[prop] === 'function' && obj[prop]._provideBound === true) {
|
||||
obj.bound[prop] = _.bind(obj[prop], obj);
|
||||
}
|
||||
} else {
|
||||
_.each(methods, function (method) {
|
||||
obj.bound[method] = _.bindKey(obj, method);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -414,4 +380,26 @@ _.createArray = function (input, transform) {
|
||||
return output;
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a WritableStream, and returns the chunks that have not successfully written, returning them as a string.
|
||||
*
|
||||
* ONLY WORKS FOR TEXT STREAMS
|
||||
*
|
||||
* @param {WritableStream} stream - an instance of stream.Writable
|
||||
* @return {string} - the remaining test to be written to the stream
|
||||
*/
|
||||
_.getUnwrittenFromStream = function (stream) {
|
||||
if (stream && stream._writableState && stream._writableState.buffer) {
|
||||
// flush the write buffer to disk
|
||||
var writeBuffer = stream._writableState.buffer;
|
||||
var out = '';
|
||||
if (writeBuffer.length) {
|
||||
writeBuffer.forEach(function (buffered) {
|
||||
out += buffered.chunk.toString();
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = utils;
|
||||
|
||||
@ -33,7 +33,7 @@ exports.start = function (cb) {
|
||||
);
|
||||
|
||||
server.stdout.on('data', function onProcessData(line) {
|
||||
line = _.trim(line.toString());
|
||||
line = line.toString().trim();
|
||||
var match;
|
||||
if (match = line.match(/\{inet\[\/?([^:]+):(\d+)\]\}/)) {
|
||||
server.__hostname = match[1];
|
||||
@ -49,7 +49,7 @@ exports.start = function (cb) {
|
||||
});
|
||||
|
||||
server.stderr.on('data', function (line) {
|
||||
console.error(_.trim(line.toString()));
|
||||
console.error(line.toString().trim());
|
||||
});
|
||||
|
||||
server.on('close', function (code) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Just a buffer really, but one that implements the Writeable class
|
||||
* Just a buffer really, but one that implements the Writable class
|
||||
* @type {Constuctor}
|
||||
*/
|
||||
module.exports = MockWritableStream;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
var Log = require('../../src/lib/log');
|
||||
var LoggerAbstract = require('../../src/lib/logger');
|
||||
var TracerLogger = require('../../src/lib/loggers/tracer');
|
||||
var now = new Date('2013-03-01T00:00:00Z');
|
||||
var sinon = require('sinon');
|
||||
|
||||
@ -234,12 +235,16 @@ module.exports = function (makeLogger) {
|
||||
|
||||
it('joins the message and curl call with a newline', function () {
|
||||
var logger = makeLogger();
|
||||
stub(logger, 'write', function (label, msg) {
|
||||
msg.should.eql('curlcall\nmessage');
|
||||
});
|
||||
|
||||
logger.onTrace('message', 'curlcall');
|
||||
logger.write.callCount.should.eql(1);
|
||||
// tracer logger has custom trace logic...
|
||||
if (!(logger instanceof TracerLogger)) {
|
||||
stub(logger, 'write', function (label, msg) {
|
||||
msg.should.eql('curlcall\nmessage');
|
||||
});
|
||||
|
||||
logger.onTrace('message', 'curlcall');
|
||||
logger.write.callCount.should.eql(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -1,7 +1,12 @@
|
||||
var Log = require('../../src/lib/log');
|
||||
var FileLogger = require('../../src/lib/loggers/file');
|
||||
var sinon = require('sinon');
|
||||
var once = require('events').EventEmitter.prototype.once;
|
||||
var write = require('stream').Writable.prototype.write;
|
||||
var MockWritableStream = require('../mocks/writable_stream');
|
||||
var _ = require('lodash');
|
||||
var parentLog;
|
||||
var fs = require('fs');
|
||||
|
||||
beforeEach(function () {
|
||||
parentLog = new Log();
|
||||
@ -17,7 +22,11 @@ function makeLogger(parent, levels, path) {
|
||||
levels: Log.parseLevels(levels || 'trace'),
|
||||
path: path === void 0 ? 'elasticsearch.log' : path
|
||||
};
|
||||
return new FileLogger(parent, config);
|
||||
var logger = new FileLogger(parent, config);
|
||||
logger.stream.end();
|
||||
|
||||
logger.stream = new MockWritableStream();
|
||||
return logger;
|
||||
}
|
||||
|
||||
var stub = require('./auto_release_stub').make();
|
||||
@ -26,4 +35,37 @@ describe('File Logger', function () {
|
||||
|
||||
require('./generic_logger_tests')(makeLogger);
|
||||
|
||||
describe('buffer flush', function () {
|
||||
it('writes everything in the buffer to console.error', function () {
|
||||
var line = 'This string is writte 10 times to create buffered output.\n';
|
||||
|
||||
var exitHandler;
|
||||
stub(process, 'once', function (event, handler) {
|
||||
if (event === 'exit') {
|
||||
exitHandler = handler;
|
||||
}
|
||||
once.call(process, event, handler);
|
||||
});
|
||||
|
||||
var logger = makeLogger();
|
||||
|
||||
// write the line 10 times
|
||||
_.times(10, function () {
|
||||
logger.onDebug(line);
|
||||
});
|
||||
|
||||
// collect everything that is written to fs.appendFileSync
|
||||
var flushedOutput = '';
|
||||
stub(fs, 'appendFileSync', function (path, str) {
|
||||
flushedOutput += str;
|
||||
});
|
||||
|
||||
// call the event handler
|
||||
exitHandler.call(process);
|
||||
|
||||
// the first line is sent immediately to _write and there is nothing we can do about that
|
||||
flushedOutput.match(new RegExp(line, 'g')).length.should.eql(9);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -3,7 +3,6 @@ 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 stream = new MockWritableStream();
|
||||
var _ = require('lodash');
|
||||
@ -14,6 +13,10 @@ var stub = require('./auto_release_stub').make();
|
||||
|
||||
beforeEach(function () {
|
||||
parentLog = new Log();
|
||||
if (stream._writableState.buffer.length) {
|
||||
// empty the buffer manually
|
||||
stream._writableState.buffer.splice(0);
|
||||
}
|
||||
stub(stream, 'write');
|
||||
stub(stream, 'end');
|
||||
});
|
||||
@ -34,37 +37,36 @@ function makeLogger(parent, levels) {
|
||||
describe('Stream Logger', function () {
|
||||
describe('buffer flushing', function () {
|
||||
it('writes everything in the buffer to console.error', function () {
|
||||
var onExitCallback;
|
||||
sinon.stub(process, 'once', function (evName, cb) {
|
||||
if (evName === 'exit') {
|
||||
onExitCallback = cb;
|
||||
process.once.restore();
|
||||
} else {
|
||||
once.call(process, evName, cb);
|
||||
}
|
||||
});
|
||||
|
||||
var logger = makeLogger();
|
||||
var line = 'This string is repeated 10 times to create buffered output.\n';
|
||||
var line = 'This string is writte 10 times to create buffered output.\n';
|
||||
|
||||
// get the last handler for process's "exit" event
|
||||
var exitHandlers = process._events.exit;
|
||||
var exitHandler = _.isArray(exitHandlers) ? _.last(exitHandlers) : exitHandlers;
|
||||
|
||||
// allow the logger to acctually write to the stream
|
||||
stream.write.restore();
|
||||
|
||||
// write the line 10 times
|
||||
_.times(10, function () {
|
||||
write.call(stream, line);
|
||||
logger.onDebug(line);
|
||||
});
|
||||
|
||||
// collect everything that is written to console.error
|
||||
var flushedOutput = '';
|
||||
sinon.stub(console, 'error', function (str) {
|
||||
stub(console, 'error', function (str) {
|
||||
flushedOutput += str;
|
||||
});
|
||||
onExitCallback();
|
||||
console.error.restore();
|
||||
// empty the buffer manually
|
||||
stream._writableState.buffer.splice(0);
|
||||
|
||||
// the first line is stuck "in writing" and there is nothing we can do about that
|
||||
// call the event handler
|
||||
exitHandler.call(process);
|
||||
|
||||
// restore console.error asap.
|
||||
console.error.restore();
|
||||
|
||||
// the first line is sent immediately to _write and there is nothing we are not going to worry about it
|
||||
flushedOutput.match(new RegExp(line, 'g')).length.should.eql(9);
|
||||
});
|
||||
|
||||
it('works with older streams');
|
||||
});
|
||||
|
||||
|
||||
|
||||
41
test/unit/test_tracer_logger.js
Normal file
41
test/unit/test_tracer_logger.js
Normal file
@ -0,0 +1,41 @@
|
||||
var Log = require('../../src/lib/log');
|
||||
var TracerLogger = require('../../src/lib/loggers/tracer');
|
||||
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 TracerLogger(parent, config);
|
||||
}
|
||||
|
||||
var stub = require('./auto_release_stub').make();
|
||||
|
||||
describe('Tracer Logger', function () {
|
||||
|
||||
require('./generic_logger_tests')(makeLogger);
|
||||
|
||||
describe('#write', function () {
|
||||
it('comments out everything accept the curlCall', function () {
|
||||
var logger = makeLogger();
|
||||
|
||||
stub(logger.stream, 'write', function (string) {
|
||||
string.replace(/(^#.*$|^curlcall$)/mg, '').trim().should.be.exactly('');
|
||||
});
|
||||
|
||||
logger.onTrace('message', 'curlcall');
|
||||
|
||||
logger.stream.write.callCount.should.eql(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,8 @@
|
||||
var _ = require('../../src/lib/utils');
|
||||
var should = require('should');
|
||||
|
||||
var stub = require('./auto_release_stub').make();
|
||||
|
||||
describe('Utils', function () {
|
||||
|
||||
describe('Additional Type Checkers', function () {
|
||||
@ -30,7 +32,6 @@ describe('Utils', function () {
|
||||
}
|
||||
},
|
||||
function (thing, name) {
|
||||
|
||||
describe('#isArrayOf' + name, function (test) {
|
||||
it('likes arrays of ' + name, function () {
|
||||
should(_['isArrayOf' + name + 's'](thing.is)).be.ok;
|
||||
@ -335,4 +336,68 @@ describe('Utils', function () {
|
||||
}).should.throw(/expected a function or one of main, other/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#applyArgs', function () {
|
||||
_.times(10, function (i) {
|
||||
var method = i > 5 ? 'apply' : 'call';
|
||||
var argCount = i + 1;
|
||||
var slice = 1;
|
||||
|
||||
it('uses ' + method + ' with ' + i + ' args', function () {
|
||||
var func = function () {};
|
||||
stub(func, method);
|
||||
|
||||
var args = _.map(new Array(i), function (val, i) { return i; });
|
||||
_.applyArgs(func, null, args);
|
||||
|
||||
func[method].callCount.should.eql(1);
|
||||
if (method === 'apply') {
|
||||
func.apply.lastCall.args[1].should.eql(args);
|
||||
} else {
|
||||
func.call.lastCall.args.splice(1).should.eql(args);
|
||||
}
|
||||
});
|
||||
|
||||
it('slices the arguments properly before calling ' + method + ' with ' + argCount + ' args sliced at ' + slice,
|
||||
function () {
|
||||
var func = function () {};
|
||||
stub(func, method);
|
||||
|
||||
var args = _.map(new Array(argCount), function (val, i) { return i; });
|
||||
var expected = args.slice(slice);
|
||||
_.applyArgs(func, null, args, slice);
|
||||
|
||||
func[method].callCount.should.eql(1);
|
||||
if (method === 'apply') {
|
||||
func.apply.lastCall.args[1].should.eql(expected);
|
||||
} else {
|
||||
func.call.lastCall.args.splice(1).should.eql(expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getUnwrittenFromStream', function () {
|
||||
it('ignores things that do not have writableState', function () {
|
||||
should.not.exist(_.getUnwrittenFromStream());
|
||||
should.not.exist(_.getUnwrittenFromStream(false));
|
||||
should.not.exist(_.getUnwrittenFromStream([]));
|
||||
should.not.exist(_.getUnwrittenFromStream({}));
|
||||
});
|
||||
|
||||
var MockWritableStream = require('../mocks/writable_stream');
|
||||
|
||||
it('ignores empty stream', function () {
|
||||
var stream = new MockWritableStream();
|
||||
_.getUnwrittenFromStream(stream).should.be.exactly('');
|
||||
});
|
||||
|
||||
it('returns only what is in the buffer', function () {
|
||||
var stream = new MockWritableStream();
|
||||
stream.write('hot');
|
||||
stream.write('dog');
|
||||
_.getUnwrittenFromStream(stream).should.be.exactly('dog');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user