diff --git a/test/tap/connect-logger-test.js b/test/tap/connect-logger-test.js new file mode 100644 index 0000000..5c61b99 --- /dev/null +++ b/test/tap/connect-logger-test.js @@ -0,0 +1,243 @@ +/* jshint maxparams:7 */ + +'use strict'; + +const test = require('tap').test; +const EE = require('events').EventEmitter; +const levels = require('../../lib/levels'); + +class MockLogger { + constructor() { + this.level = levels.TRACE; + this.messages = []; + this.log = function (level, message) { + this.messages.push({ level: level, message: message }); + }; + this.isLevelEnabled = function (level) { + return level.isGreaterThanOrEqualTo(this.level); + }; + } +} + +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 || {}; + + const self = this; + Object.keys(this.headers).forEach((key) => { + self.headers[key.toLowerCase()] = self.headers[key]; + }); +} + +class MockResponse extends EE { + constructor() { + super(); + const r = this; + this.end = function () { + r.emit('finish'); + }; + + this.writeHead = function (code, headers) { + this.statusCode = code; + this._headers = headers; + }; + } +} + +function request(cl, method, url, code, reqHeaders, resHeaders) { + const req = new MockRequest('my.remote.addr', method, url, reqHeaders); + const res = new MockResponse(); + cl(req, res, () => { + }); + res.writeHead(code, resHeaders); + res.end('chunk', 'encoding'); +} + +test('log4js connect logger', (batch) => { + const clm = require('../../lib/connect-logger'); + batch.test('getConnectLoggerModule', (t) => { + t.type(clm, 'object', 'should return a connect logger factory'); + + t.test('should take a log4js logger and return a "connect logger"', (assert) => { + const ml = new MockLogger(); + const cl = clm.connectLogger(ml); + + assert.type(cl, 'function'); + assert.end(); + }); + + t.test('log events', (assert) => { + const ml = new MockLogger(); + const cl = clm.connectLogger(ml); + request(cl, 'GET', 'http://url', 200); + + const messages = ml.messages; + assert.type(messages, 'Array'); + 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'); + assert.end(); + }); + + t.test('log events with level below logging level', (assert) => { + const ml = new MockLogger(); + ml.level = levels.FATAL; + const cl = clm.connectLogger(ml); + request(cl, 'GET', 'http://url', 200); + + assert.type(ml.messages, 'Array'); + assert.equal(ml.messages.length, 0); + assert.end(); + }); + + t.test('log events with non-default level and custom format', (assert) => { + const ml = new MockLogger(); + ml.level = levels.INFO; + const cl = clm.connectLogger(ml, { level: levels.INFO, format: ':method :url' }); + request(cl, 'GET', 'http://url', 200); + + const messages = ml.messages; + assert.type(messages, Array); + assert.equal(messages.length, 1); + assert.ok(levels.INFO.isEqualTo(messages[0].level)); + assert.equal(messages[0].message, 'GET http://url'); + assert.end(); + }); + t.end(); + }); + + batch.test('logger with options as string', (t) => { + const ml = new MockLogger(); + ml.level = levels.INFO; + const cl = clm.connectLogger(ml, ':method :url'); + request(cl, 'POST', 'http://meh', 200); + + const messages = ml.messages; + t.equal(messages[0].message, 'POST http://meh'); + t.end(); + }); + + batch.test('auto log levels', (t) => { + const ml = new MockLogger(); + ml.level = levels.INFO; + const 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); + + const messages = ml.messages; + t.test('should use INFO for 2xx', (assert) => { + assert.ok(levels.INFO.isEqualTo(messages[0].level)); + assert.ok(levels.INFO.isEqualTo(messages[1].level)); + assert.end(); + }); + + t.test('should use WARN for 3xx', (assert) => { + assert.ok(levels.WARN.isEqualTo(messages[2].level)); + assert.end(); + }); + + t.test('should use ERROR for 4xx', (assert) => { + assert.ok(levels.ERROR.isEqualTo(messages[3].level)); + assert.end(); + }); + + t.test('should use ERROR for 5xx', (assert) => { + assert.ok(levels.ERROR.isEqualTo(messages[4].level)); + assert.end(); + }); + t.end(); + }); + + batch.test('format using a function', (t) => { + const ml = new MockLogger(); + ml.level = levels.INFO; + const cl = clm.connectLogger(ml, () => 'I was called'); + request(cl, 'GET', 'http://blah', 200); + + t.equal(ml.messages[0].message, 'I was called'); + t.end(); + }); + + batch.test('format that includes request headers', (t) => { + const ml = new MockLogger(); + ml.level = levels.INFO; + const cl = clm.connectLogger(ml, ':req[Content-Type]'); + request( + cl, + 'GET', 'http://blah', 200, + { 'Content-Type': 'application/json' } + ); + + t.equal(ml.messages[0].message, 'application/json'); + t.end(); + }); + + batch.test('format that includes response headers', (t) => { + const ml = new MockLogger(); + ml.level = levels.INFO; + const cl = clm.connectLogger(ml, ':res[Content-Type]'); + request( + cl, + 'GET', 'http://blah', 200, + null, + { 'Content-Type': 'application/cheese' } + ); + + t.equal(ml.messages[0].message, 'application/cheese'); + t.end(); + }); + + batch.test('log events with custom token', (t) => { + const ml = new MockLogger(); + ml.level = levels.INFO; + const cl = clm.connectLogger(ml, { + level: levels.INFO, + format: ':method :url :custom_string', + tokens: [ + { + token: ':custom_string', replacement: 'fooBAR' + } + ] + }); + request(cl, 'GET', 'http://url', 200); + + t.type(ml.messages, 'Array'); + t.equal(ml.messages.length, 1); + t.ok(levels.INFO.isEqualTo(ml.messages[0].level)); + t.equal(ml.messages[0].message, 'GET http://url fooBAR'); + t.end(); + }); + + batch.test('log events with custom override token', (t) => { + const ml = new MockLogger(); + ml.level = levels.INFO; + const cl = clm.connectLogger(ml, { + level: levels.INFO, + format: ':method :url :date', + tokens: [ + { + token: ':date', replacement: '20150310' + } + ] + }); + request(cl, 'GET', 'http://url', 200); + + t.type(ml.messages, 'Array'); + t.equal(ml.messages.length, 1); + t.ok(levels.INFO.isEqualTo(ml.messages[0].level)); + t.equal(ml.messages[0].message, 'GET http://url 20150310'); + t.end(); + }); + + batch.end(); +});