/* jshint maxparams:7 */ "use strict"; var vows = require('vows') , assert = require('assert') , util = require('util') , EE = require('events').EventEmitter , levels = require('../lib/levels'); function MockLogger() { var that = this; this.messages = []; this.log = function(level, message, exception) { that.messages.push({ level: level, message: message }); }; this.isLevelEnabled = function(level) { return level.isGreaterThanOrEqualTo(that.level); }; this.level = levels.TRACE; } function MockRequest(remoteAddr, method, originalUrl, headers) { this.socket = { remoteAddress: remoteAddr }; this.originalUrl = originalUrl; this.method = method; this.httpVersionMajor = '5'; this.httpVersionMinor = '0'; this.headers = headers || {}; var self = this; Object.keys(this.headers).forEach(function(key) { self.headers[key.toLowerCase()] = self.headers[key]; }); } function MockResponse() { var r = this; this.end = function(chunk, encoding) { r.emit('finish'); }; this.writeHead = function(code, headers) { this.statusCode = code; this._headers = headers; }; } util.inherits(MockResponse, EE); function request(cl, method, url, code, reqHeaders, resHeaders) { var req = new MockRequest('my.remote.addr', method, url, reqHeaders); var res = new MockResponse(); cl(req, res, function() {}); res.writeHead(code, resHeaders); res.end('chunk','encoding'); } vows.describe('log4js connect logger').addBatch({ 'getConnectLoggerModule': { topic: function() { var clm = require('../lib/connect-logger'); return clm; }, 'should return a "connect logger" factory' : function(clm) { assert.isObject(clm); }, 'take a log4js logger and return a "connect logger"' : { topic: function(clm) { var ml = new MockLogger(); var cl = clm.connectLogger(ml); return cl; }, 'should return a "connect logger"': function(cl) { assert.isFunction(cl); } }, 'log events' : { topic: function(clm) { var ml = new MockLogger(); var cl = clm.connectLogger(ml); var cb = this.callback; request(cl, 'GET', 'http://url', 200); setTimeout(function() { cb(null, ml.messages); },10); }, 'check message': function(messages) { assert.isArray(messages); assert.equal(messages.length, 1); assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.include(messages[0].message, 'GET'); assert.include(messages[0].message, 'http://url'); assert.include(messages[0].message, 'my.remote.addr'); assert.include(messages[0].message, '200'); } }, 'log events with level below logging level' : { topic: function(clm) { var ml = new MockLogger(); ml.level = levels.FATAL; var cl = clm.connectLogger(ml); request(cl, 'GET', 'http://url', 200); return ml.messages; }, 'check message': function(messages) { assert.isArray(messages); assert.isEmpty(messages); } }, 'log events with non-default level and custom format' : { topic: function(clm) { var ml = new MockLogger(); var cb = this.callback; ml.level = levels.INFO; var cl = clm.connectLogger(ml, { level: levels.INFO, format: ':method :url' } ); request(cl, 'GET', 'http://url', 200); setTimeout(function() { cb(null, ml.messages); },10); }, 'check message': function(messages) { assert.isArray(messages); assert.equal(messages.length, 1); assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.equal(messages[0].message, 'GET http://url'); } }, 'logger with options as string': { topic: function(clm) { var ml = new MockLogger(); var cb = this.callback; ml.level = levels.INFO; var cl = clm.connectLogger(ml, ':method :url'); request(cl, 'POST', 'http://meh', 200); setTimeout(function() { cb(null, ml.messages); },10); }, 'should use the passed in format': function(messages) { assert.equal(messages[0].message, 'POST http://meh'); } }, 'auto log levels': { topic: function(clm) { var ml = new MockLogger(); var cb = this.callback; ml.level = levels.INFO; var cl = clm.connectLogger(ml, { level: 'auto', format: ':method :url' }); request(cl, 'GET', 'http://meh', 200); request(cl, 'GET', 'http://meh', 201); request(cl, 'GET', 'http://meh', 302); request(cl, 'GET', 'http://meh', 404); request(cl, 'GET', 'http://meh', 500); setTimeout(function() { cb(null, ml.messages); },10); }, 'should use INFO for 2xx': function(messages) { assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.ok(levels.INFO.isEqualTo(messages[1].level)); }, 'should use WARN for 3xx': function(messages) { assert.ok(levels.WARN.isEqualTo(messages[2].level)); }, 'should use ERROR for 4xx': function(messages) { assert.ok(levels.ERROR.isEqualTo(messages[3].level)); }, 'should use ERROR for 5xx': function(messages) { assert.ok(levels.ERROR.isEqualTo(messages[4].level)); } }, 'format using a function': { topic: function(clm) { var ml = new MockLogger(); var cb = this.callback; ml.level = levels.INFO; var cl = clm.connectLogger(ml, function(req, res, formatFn) { return "I was called"; }); request(cl, 'GET', 'http://blah', 200); setTimeout(function() { cb(null, ml.messages); },10); }, 'should call the format function': function(messages) { assert.equal(messages[0].message, 'I was called'); } }, 'format that includes request headers': { topic: function(clm) { var ml = new MockLogger(); var cb = this.callback; ml.level = levels.INFO; var cl = clm.connectLogger(ml, ':req[Content-Type]'); request( cl, 'GET', 'http://blah', 200, { 'Content-Type': 'application/json' } ); setTimeout(function() { cb(null, ml.messages); },10); }, 'should output the request header': function(messages) { assert.equal(messages[0].message, 'application/json'); } }, 'format that includes response headers': { topic: function(clm) { var ml = new MockLogger(); var cb = this.callback; ml.level = levels.INFO; var cl = clm.connectLogger(ml, ':res[Content-Type]'); request( cl, 'GET', 'http://blah', 200, null, { 'Content-Type': 'application/cheese' } ); setTimeout(function() { cb(null, ml.messages); },10); }, 'should output the response header': function(messages) { assert.equal(messages[0].message, 'application/cheese'); } }, 'log events with custom token' : { topic: function(clm) { var ml = new MockLogger(); var cb = this.callback; ml.level = levels.INFO; var cl = clm.connectLogger(ml, { level: levels.INFO, format: ':method :url :custom_string', tokens: [{ token: ':custom_string', replacement: 'fooBAR' }] } ); request(cl, 'GET', 'http://url', 200); setTimeout(function() { cb(null, ml.messages); },10); }, 'check message': function(messages) { assert.isArray(messages); assert.equal(messages.length, 1); assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.equal(messages[0].message, 'GET http://url fooBAR'); } }, 'log events with custom override token' : { topic: function(clm) { var ml = new MockLogger(); var cb = this.callback; ml.level = levels.INFO; var cl = clm.connectLogger(ml, { level: levels.INFO, format: ':method :url :date', tokens: [{ token: ':date', replacement: "20150310" }] } ); request(cl, 'GET', 'http://url', 200); setTimeout(function() { cb(null, ml.messages); },10); }, 'check message': function(messages) { assert.isArray(messages); assert.equal(messages.length, 1); assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.equal(messages[0].message, 'GET http://url 20150310'); } } } }).export(module);