winston-middleware 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/test/test.js CHANGED
@@ -1,15 +1,15 @@
1
- var should = require('should');
2
1
  var util = require('util');
3
- var _ = require('underscore');
4
2
 
5
3
  var mocks = require('node-mocks-http');
4
+ var Promise = require('promise/lib/es6-extensions');
5
+ var should = require('should');
6
+ var _ = require('underscore');
6
7
  var winston = require('winston');
7
8
 
8
9
  var expressWinston = require('../index.js');
9
10
 
10
11
  expressWinston.ignoredRoutes.push('/ignored');
11
12
  expressWinston.responseWhitelist.push('body');
12
-
13
13
  expressWinston.bodyBlacklist.push('potato');
14
14
 
15
15
  var MockTransport = function (test, options) {
@@ -28,13 +28,8 @@ var MockTransport = function (test, options) {
28
28
  };
29
29
  util.inherits(MockTransport, winston.Transport);
30
30
 
31
- var req = {};
32
- var res = {};
33
-
34
- var setUp = function (options) {
35
- options = options || {};
36
-
37
- var reqSpec = {
31
+ function mockReq(reqMock) {
32
+ var reqSpec = _.extend({
38
33
  method: 'GET',
39
34
  url: '/hello',
40
35
  headers: {
@@ -46,84 +41,104 @@ var setUp = function (options) {
46
41
  params: {
47
42
  id: 20
48
43
  },
49
- };
50
-
51
- if (options.body) reqSpec.body = options.body;
44
+ }, reqMock);
52
45
 
53
- if (options.ignoreRoute) reqSpec.url = '/ignored';
54
- if (options.url) reqSpec.url = options.url;
46
+ return mocks.createRequest(reqSpec);
47
+ }
55
48
 
56
- req = mocks.createRequest(reqSpec);
57
-
58
- req.route = {
59
- path: reqSpec.url,
60
- methods: {
61
- get: true
62
- }
63
- };
64
-
65
- if (options.routePath) req.route.path = options.routePath;
66
-
67
- res = mocks.createResponse();
49
+ function mockRes() {
50
+ var res = mocks.createResponse();
68
51
  res.status(200);
69
- };
70
-
71
- describe('expressWinston', function () {
72
- it('should contain an array with all the properties whitelisted in the req object', function () {
73
- expressWinston.requestWhitelist.should.be.an.Array;
74
- });
75
-
76
- it('should contain an array with all the properties whitelisted in the res object', function () {
77
- expressWinston.responseWhitelist.should.be.an.Array;
78
- });
52
+ return res;
53
+ }
54
+
55
+ function loggerTestHelper(providedOptions) {
56
+ var options = _.extend({
57
+ loggerOptions: null,
58
+ req: null,
59
+ res: null,
60
+ transportOptions: null,
61
+ next: function (req, res, next) {
62
+ res.end('{ "message": "Hi! I\'m a chunk!" }');
63
+ }
64
+ }, providedOptions);
79
65
 
80
- it('should contain an array for all the ignored routes', function () {
81
- expressWinston.ignoredRoutes.should.be.an.Array;
82
- });
66
+ var req = mockReq(options.req);
67
+ var res = _.extend(mockRes(), options.res);
83
68
 
84
- it('should contain an array with all the properties whitelisted in the body object', function () {
85
- expressWinston.bodyWhitelist.should.be.an.Array;
86
- });
69
+ var result = {
70
+ req: req,
71
+ res: res,
72
+ log: {}
73
+ };
87
74
 
88
- it('should contain a default request filter function', function () {
89
- expressWinston.defaultRequestFilter.should.be.a.Function;
90
- });
75
+ return new Promise(function (resolve, reject) {
76
+ var middleware = expressWinston.logger(_.extend({
77
+ transports: [new MockTransport(result, options.transportOptions)]
78
+ }, options.loggerOptions));
91
79
 
92
- it('should contain a default response filter function', function () {
93
- expressWinston.defaultResponseFilter.should.be.a.Function;
80
+ middleware(req, res, function (_req, _res, next) {
81
+ options.next(req, res, next);
82
+ resolve(result);
83
+ });
94
84
  });
85
+ }
86
+
87
+ function errorLoggerTestHelper(providedOptions) {
88
+ var options = _.extend({
89
+ loggerOptions: null,
90
+ originalError: new Error('This is the Error'),
91
+ req: null,
92
+ res: null,
93
+ transportOptions: null,
94
+ next: function () {}
95
+ }, providedOptions);
96
+
97
+ var req = mockReq(options.req);
98
+ var res = _.extend(mockRes(), options.res);
99
+
100
+ var result = {
101
+ req: req,
102
+ res: res,
103
+ log: {},
104
+ originalError: options.originalError,
105
+ pipelineError: null
106
+ };
95
107
 
96
- it('should contain a default skip function', function () {
97
- expressWinston.defaultSkip.should.be.a.Function;
98
- });
108
+ return new Promise(function (resolve, reject) {
109
+ var middleware = expressWinston.errorLogger(_.extend({
110
+ transports: [new MockTransport(result, options.transportOptions)]
111
+ }, options.loggerOptions));
99
112
 
100
- it('should export a function for the creation of request logger middlewares', function () {
101
- expressWinston.logger.should.be.a.Function;
113
+ middleware(options.originalError, req, res, function (pipelineError) {
114
+ options.next(pipelineError);
115
+ result.pipelineError = pipelineError;
116
+ resolve(result);
117
+ });
102
118
  });
119
+ }
103
120
 
121
+ describe('winston-middleware', function () {
104
122
  describe('.errorLogger()', function () {
105
123
  it('should be a function', function () {
106
- expressWinston.errorLogger.should.be.a.Function;
124
+ expressWinston.errorLogger.should.be.a.Function();
107
125
  });
108
126
 
109
- it('should throw an error without options', function () {
110
- (function () {
111
- expressWinston.errorLogger();
112
- }).should.throwError();
127
+ it('should throw an error when no options are provided', function () {
128
+ var errorLoggerFn = expressWinston.errorLogger.bind(expressWinston);
129
+ errorLoggerFn.should.throw();
113
130
  });
114
131
 
115
- it('should throw an error without any transport specified', function () {
116
- (function () {
117
- expressWinston.errorLogger({});
118
- }).should.throwError();
132
+ it('should throw an error when no transport is specified', function () {
133
+ var errorLoggerFn = expressWinston.errorLogger.bind(expressWinston, {});
134
+ errorLoggerFn.should.throw();
119
135
  });
120
136
 
121
- it('should throw an error with an empty list of transports', function () {
122
- (function () {
123
- expressWinston.errorLogger({
124
- transports: []
125
- });
126
- }).should.throwError();
137
+ it('should throw an error when provided with an empty list of transports', function () {
138
+ var errorLoggerFn = expressWinston.errorLogger.bind(expressWinston, {
139
+ transports: []
140
+ });
141
+ errorLoggerFn.should.throw();
127
142
  });
128
143
 
129
144
  it('should return a middleware function with four arguments that fit (err, req, res, next)', function () {
@@ -134,52 +149,61 @@ describe('expressWinston', function () {
134
149
  middleware.length.should.eql(4);
135
150
  });
136
151
 
137
- describe('errorLogger() middleware()', function () {
138
- var result;
139
-
140
- before(function (done) {
141
- setUp();
152
+ it('should use the exported requestWhitelist', function() {
153
+ var originalWhitelist = expressWinston.requestWhitelist;
154
+ expressWinston.requestWhitelist = ['foo'];
142
155
 
143
- var originalError = new Error('This is the Error');
156
+ var options = {
157
+ req: {foo: "bar"}
158
+ };
159
+ return errorLoggerTestHelper(options).then(function (result) {
160
+ // Return to the original value for later tests
161
+ expressWinston.requestWhitelist = originalWhitelist;
144
162
 
145
- var test = {
146
- req: req,
147
- res: res,
148
- log: {},
149
- originalError: originalError,
150
- pipelineError: null
151
- };
152
-
153
- function next(pipelineError) {
154
- test.pipelineError = pipelineError;
155
-
156
- result = test;
163
+ result.log.meta.req.should.have.property('foo');
164
+ result.log.meta.req.should.not.have.property('url');
165
+ });
166
+ });
157
167
 
158
- return done();
159
- };
168
+ it('should use the exported defaultRequestFilter', function() {
169
+ var originalRequestFilter = expressWinston.defaultRequestFilter;
170
+ expressWinston.defaultRequestFilter = function() {
171
+ return 'foo';
172
+ };
160
173
 
161
- var middleware = expressWinston.errorLogger({
162
- transports: [new MockTransport(test)]
163
- });
174
+ var options = {
175
+ req: {foo: "bar"}
176
+ };
177
+ return errorLoggerTestHelper(options).then(function (result) {
178
+ // Return to the original value for later tests
179
+ expressWinston.defaultRequestFilter = originalRequestFilter;
164
180
 
165
- middleware(originalError, req, res, next);
181
+ result.log.meta.req.url.should.equal('foo');
166
182
  });
183
+ });
167
184
 
168
- describe('encountering an error in the pipeline', function () {
169
- it('should invoke the transport', function () {
185
+ describe('when middleware function encounters an error in the pipeline', function () {
186
+ it('should invoke the transport', function () {
187
+ return errorLoggerTestHelper().then(function (result) {
170
188
  result.transportInvoked.should.eql(true);
171
189
  });
190
+ });
172
191
 
173
- it('should find an error level of "error"', function () {
192
+ it('should find an error level of "error"', function () {
193
+ return errorLoggerTestHelper().then(function (result) {
174
194
  result.log.level.should.eql('error');
175
195
  });
196
+ });
176
197
 
177
- it('should find a message of "middlewareError"', function () {
198
+ it('should find a message of "middlewareError"', function () {
199
+ return errorLoggerTestHelper().then(function (result) {
178
200
  result.log.msg.should.eql('middlewareError');
179
201
  });
202
+ });
180
203
 
181
- it('should contain a filtered request', function () {
182
- result.log.meta.req.should.be.ok;
204
+ it('should contain a filtered request', function () {
205
+ return errorLoggerTestHelper().then(function (result) {
206
+ result.log.meta.req.should.be.ok();
183
207
  result.log.meta.req.method.should.eql('GET');
184
208
  result.log.meta.req.query.should.eql({
185
209
  val: '1'
@@ -187,50 +211,45 @@ describe('expressWinston', function () {
187
211
 
188
212
  result.log.meta.req.should.not.have.property('nonWhitelistedProperty');
189
213
  });
214
+ });
190
215
 
191
- it('should not swallow the pipline error', function () {
192
- result.pipelineError.should.be.ok;
216
+ it('should not swallow the pipeline error', function () {
217
+ return errorLoggerTestHelper().then(function (result) {
218
+ result.pipelineError.should.be.ok();
193
219
  result.pipelineError.should.eql(result.originalError);
194
220
  });
195
221
  });
196
222
  });
197
223
 
198
- describe('log.metaField', function (done) {
199
- var result;
200
-
201
- before(function (done) {
202
- setUp();
203
-
204
- var originalError = new Error('This is the Error');
205
-
206
- var test = {
207
- req: req,
208
- res: res,
209
- log: {},
210
- originalError: originalError,
211
- pipelineError: null
212
- };
213
-
214
- function next(pipelineError) {
215
- test.pipelineError = pipelineError;
216
-
217
- result = test;
224
+ describe('metaField option', function () {
225
+ it('should, when using a custom metaField, log the custom metaField', function () {
226
+ var testHelperOptions = {loggerOptions: {metaField: 'metaField'}};
227
+ return errorLoggerTestHelper(testHelperOptions).then(function (result) {
228
+ result.log.meta.metaField.req.should.be.ok();
229
+ });
230
+ });
231
+ });
218
232
 
219
- return done();
233
+ describe('requestWhitelist option', function () {
234
+ it('should default to global requestWhitelist', function () {
235
+ var options = {
236
+ req: {foo: "bar"}
220
237
  };
221
-
222
- var middleware = expressWinston.errorLogger({
223
- transports: [new MockTransport(test)],
224
- metaField: 'metaField'
238
+ return errorLoggerTestHelper(options).then(function (result) {
239
+ result.log.meta.req.should.not.have.property('foo');
225
240
  });
226
-
227
- middleware(originalError, req, res, next);
228
241
  });
229
242
 
230
- describe('when using custom metaField', function () {
231
-
232
- it('should be logged', function () {
233
- result.log.meta.metaField.req.should.be.ok;
243
+ it('should use specified requestWhitelist', function () {
244
+ var options = {
245
+ req: {foo: "bar"},
246
+ loggerOptions: {
247
+ requestWhitelist: ['foo']
248
+ }
249
+ };
250
+ return errorLoggerTestHelper(options).then(function (result) {
251
+ result.log.meta.req.should.have.property('foo');
252
+ result.log.meta.req.should.not.have.property('method');
234
253
  });
235
254
  });
236
255
  });
@@ -238,36 +257,24 @@ describe('expressWinston', function () {
238
257
 
239
258
  describe('.logger()', function () {
240
259
  it('should be a function', function () {
241
- expressWinston.logger.should.be.a.Function;
260
+ expressWinston.logger.should.be.a.Function();
242
261
  });
243
262
 
244
- it('should throw an error without options', function () {
245
- (function () {
246
- expressWinston.logger();
247
- }).should.throwError();
263
+ it('should throw an error when no options are provided', function () {
264
+ var loggerFn = expressWinston.logger.bind(expressWinston);
265
+ loggerFn.should.throw();
248
266
  });
249
267
 
250
- it('should throw an error without any transport specified', function () {
251
- (function () {
252
- expressWinston.logger({});
253
- }).should.throwError();
268
+ it('should throw an error when no transport is specified', function () {
269
+ var loggerFn = expressWinston.logger.bind(expressWinston, {});
270
+ loggerFn.should.throw();
254
271
  });
255
272
 
256
- it('should throw an error with an empty list of transports', function () {
257
- (function () {
258
- expressWinston.logger({
259
- transports: []
260
- });
261
- }).should.throwError();
262
- });
263
-
264
- it('should throw an error if ignoreRoute option is not a function', function () {
265
- (function () {
266
- expressWinston.logger({
267
- transports: [new MockTransport({})],
268
- ignoreRoute: 'not a function'
269
- });
270
- }).should.throwError();
273
+ it('should throw an error when provided with an empty list of transports', function () {
274
+ var loggerFn = expressWinston.logger.bind(expressWinston, {
275
+ transports: []
276
+ });
277
+ loggerFn.should.throw();
271
278
  });
272
279
 
273
280
  it('should return a middleware function with three arguments that fit (req, res, next)', function () {
@@ -278,481 +285,627 @@ describe('expressWinston', function () {
278
285
  middleware.length.should.eql(3);
279
286
  });
280
287
 
281
- describe('logger() middleware()', function () {
282
- describe('v0.1.x API', function () {
283
- describe('when invoked on a route', function () {
284
- var result;
285
-
286
- before(function (done) {
287
- setUp();
288
-
289
- var test = {
290
- req: req,
291
- res: res,
292
- log: {}
293
- };
294
-
295
- function next(_req, _res, next) {
296
- res.end('{ "message": "Hi! I\'m a chunk!" }');
297
- result = test;
298
- return done();
299
- };
300
-
301
- var middleware = expressWinston.logger({
302
- transports: [new MockTransport(test)]
303
- });
304
-
305
- middleware(req, res, next);
306
- });
307
-
308
- it('should invoke the transport', function () {
309
- result.transportInvoked.should.eql(true);
310
- });
311
-
312
- it('should contain a filtered request', function () {
313
- result.log.meta.req.should.be.ok;
314
- result.log.meta.req.method.should.eql('GET');
315
- result.log.meta.req.query.should.eql({
316
- val: '1'
317
- });
318
-
319
- result.log.meta.req.should.not.have.property('nonWhitelistedProperty');
320
- });
321
- });
322
-
323
- describe('when invoked on a route that should be ignored', function () {
324
- var result;
288
+ it('should not have an empty body in meta.req when invoked on a route with an empty response body', function () {
289
+ function next(req, res, next) {
290
+ res.end();
291
+ }
292
+ var testHelperOptions = {
293
+ next: next,
294
+ req: {
295
+ body: {},
296
+ routeLevelAddedProperty: 'value that should be logged',
297
+ url: '/hello'
298
+ },
299
+ };
300
+ return loggerTestHelper(testHelperOptions).then(function (result) {
301
+ Object.keys(result.log.meta.req).indexOf('body').should.eql(-1);
302
+ });
303
+ });
325
304
 
326
- before(function (done) {
327
- setUp({
328
- ignoreRoute: true
329
- });
305
+ it('should not invoke the transport when invoked on a route with transport level of "error"', function () {
306
+ function next(req, res, next) {
307
+ req._routeWhitelists.req = ['routeLevelAddedProperty'];
308
+ req._routeWhitelists.res = ['routeLevelAddedProperty'];
309
+
310
+ res.end('{ "message": "Hi! I\'m a chunk!" }');
311
+ }
312
+ var testHelperOptions = {
313
+ next: next,
314
+ loggerOptions: {
315
+ statusLevels: true
316
+ },
317
+ req: {
318
+ body: {},
319
+ routeLevelAddedProperty: 'value that should be logged',
320
+ url: '/hello',
321
+ },
322
+ res: {
323
+ nonWhitelistedProperty: 'value that should not be logged',
324
+ routeLevelAddedProperty: 'value that should be logged'
325
+ },
326
+ transportOptions: {
327
+ level: 'error'
328
+ }
329
+ };
330
+ return loggerTestHelper(testHelperOptions).then(function (result) {
331
+ result.transportInvoked.should.eql(false);
332
+ });
333
+ });
330
334
 
331
- var test = {
332
- req: req,
333
- res: res,
334
- log: {}
335
- };
335
+ it('should use the exported requestWhitelist', function() {
336
+ var originalWhitelist = expressWinston.requestWhitelist;
337
+ expressWinston.requestWhitelist = ['foo'];
336
338
 
337
- function next(_req, _res, next) {
338
- res.end('{ "message": "Hi! I\'m a chunk!" }');
339
- result = test;
340
- return done();
341
- };
339
+ var options = {
340
+ req: {foo: "bar"}
341
+ };
342
+ return loggerTestHelper(options).then(function (result) {
343
+ // Return to the original value for later tests
344
+ expressWinston.requestWhitelist = originalWhitelist;
342
345
 
343
- var middleware = expressWinston.logger({
344
- transports: [new MockTransport(test)]
345
- });
346
+ result.log.meta.req.should.have.property('foo');
347
+ result.log.meta.req.should.not.have.property('url');
348
+ });
349
+ });
346
350
 
347
- middleware(req, res, next);
348
- });
351
+ it('should use the exported bodyWhitelist', function() {
352
+ var originalWhitelist = expressWinston.bodyWhitelist;
353
+ expressWinston.bodyWhitelist = ['foo'];
349
354
 
350
- it('should not invoke the transport', function () {
351
- result.transportInvoked.should.eql(false);
352
- });
355
+ var options = {
356
+ req: {body: {foo: 'bar', baz: 'qux'}}
357
+ };
358
+ return loggerTestHelper(options).then(function (result) {
359
+ // Return to the original value for later tests
360
+ expressWinston.bodyWhitelist = originalWhitelist;
353
361
 
354
- it('should contain a filtered request', function () {
355
- result.log.should.be.empty;
356
- });
357
- });
362
+ result.log.meta.req.body.should.have.property('foo');
363
+ result.log.meta.req.body.should.not.have.property('baz');
358
364
  });
365
+ });
359
366
 
360
- describe('v0.2.x API', function () {
361
- describe('when invoked on a route', function () {
362
- var result;
363
-
364
- before(function (done) {
365
- setUp({
366
- body: {
367
- username: "bobby",
368
- password: "top-secret",
369
- age: 42,
370
- potato: 'Russet'
371
- }
372
- });
367
+ it('should use the exported bodyBlacklist', function() {
368
+ var originalWhitelist = expressWinston.bodyBlacklist;
369
+ expressWinston.bodyBlacklist = ['foo'];
373
370
 
374
- req.routeLevelAddedProperty = 'value that should be logged';
371
+ var options = {
372
+ req: {body: {foo: 'bar', baz: 'qux'}}
373
+ };
374
+ return loggerTestHelper(options).then(function (result) {
375
+ // Return to the original value for later tests
376
+ expressWinston.bodyBlacklist = originalWhitelist;
375
377
 
376
- res.nonWhitelistedProperty = 'value that should not be logged';
377
- res.routeLevelAddedProperty = 'value that should be logged';
378
+ result.log.meta.req.body.should.not.have.property('foo');
379
+ result.log.meta.req.body.should.have.property('baz');
380
+ });
381
+ });
378
382
 
379
- var test = {
380
- req: req,
381
- res: res,
382
- log: {}
383
- };
383
+ it('should use the exported responseWhitelist', function() {
384
+ var originalWhitelist = expressWinston.responseWhitelist;
385
+ expressWinston.responseWhitelist = ['foo'];
384
386
 
385
- function next(_req, _res, next) {
386
- req._startTime = (new Date) - 125;
387
+ var options = {
388
+ res: {foo: 'bar', baz: 'qux'}
389
+ };
390
+ return loggerTestHelper(options).then(function (result) {
391
+ // Return to the original value for later tests
392
+ expressWinston.responseWhitelist = originalWhitelist;
387
393
 
388
- req._routeWhitelists.req = ['routeLevelAddedProperty'];
389
- req._routeWhitelists.res = ['routeLevelAddedProperty'];
394
+ result.log.meta.res.should.have.property('foo');
395
+ result.log.meta.res.should.not.have.property('baz');
396
+ });
397
+ });
390
398
 
391
- req._routeWhitelists.body = ['username'];
392
- req._routeBlacklists.body = ['age'];
399
+ it('should use the exported defaultRequestFilter', function() {
400
+ var originalRequestFilter = expressWinston.defaultRequestFilter;
401
+ expressWinston.defaultRequestFilter = function() {
402
+ return 'foo';
403
+ };
393
404
 
394
- res.end('{ "message": "Hi! I\'m a chunk!" }');
405
+ var options = {
406
+ req: {foo: "bar"}
407
+ };
408
+ return loggerTestHelper(options).then(function (result) {
409
+ // Return to the original value for later tests
410
+ expressWinston.defaultRequestFilter = originalRequestFilter;
395
411
 
396
- result = test;
412
+ result.log.meta.req.url.should.equal('foo');
413
+ });
414
+ });
397
415
 
398
- return done();
399
- };
416
+ it('should use the exported defaultResponseFilter', function() {
417
+ var originalResponseFilter = expressWinston.defaultResponseFilter;
418
+ expressWinston.defaultResponseFilter = function() {
419
+ return 'foo';
420
+ };
400
421
 
401
- var middleware = expressWinston.logger({
402
- transports: [new MockTransport(test)]
403
- });
422
+ var options = {
423
+ req: {foo: "bar"}
424
+ };
425
+ return loggerTestHelper(options).then(function (result) {
426
+ // Return to the original value for later tests
427
+ expressWinston.defaultResponseFilter = originalResponseFilter;
404
428
 
405
- middleware(req, res, next);
406
- });
429
+ result.log.meta.res.statusCode.should.equal('foo');
430
+ });
431
+ });
407
432
 
408
- it('should invoke the transport', function () {
409
- result.transportInvoked.should.eql(true);
410
- });
433
+ it('should use the exported defaultSkip', function() {
434
+ var originalSkip = expressWinston.defaultSkip;
435
+ expressWinston.defaultSkip = function() {
436
+ return true;
437
+ };
411
438
 
412
- it('should contain a filtered request', function () {
413
- result.log.meta.req.should.be.ok;
414
- result.log.meta.req.method.should.eql('GET');
415
- result.log.meta.req.query.should.eql({
416
- val: '1'
417
- });
439
+ var options = {
440
+ req: {foo: "bar"}
441
+ };
442
+ return loggerTestHelper(options).then(function (result) {
443
+ // Return to the original value for later tests
444
+ expressWinston.defaultSkip = originalSkip;
418
445
 
419
- result.log.meta.req.body.should.not.have.property('age');
420
- result.log.meta.req.body.should.not.have.property('potato');
421
- });
446
+ result.transportInvoked.should.eql(false);
447
+ });
448
+ });
422
449
 
423
- it('should contain a filtered response', function () {
424
- result.log.meta.res.should.be.ok;
450
+ it('should use the exported ignoredRoutes', function() {
451
+ var originalIgnoredRoutes = expressWinston.ignoredRoutes;
452
+ expressWinston.ignoredRoutes = ['/foo-route'];
425
453
 
426
- result.log.meta.res.statusCode.should.eql(200);
427
- result.log.meta.res.routeLevelAddedProperty.should.be.ok;
454
+ var options = {
455
+ req: {url: '/foo-route'}
456
+ };
457
+ return loggerTestHelper(options).then(function (result) {
458
+ // Return to the original value for later tests
459
+ expressWinston.ignoredRoutes = originalIgnoredRoutes;
428
460
 
429
- result.log.meta.res.should.not.have.property('nonWhitelistedProperty');
430
- });
461
+ result.transportInvoked.should.eql(false);
462
+ });
463
+ });
431
464
 
432
- it('should contain a response time', function () {
433
- result.log.meta.responseTime.should.be.within(120, 130);
434
- });
465
+ describe('when middleware function is invoked on a route', function () {
466
+ function next(req, res, next) {
467
+ req._startTime = (new Date()) - 125;
468
+
469
+ req._routeWhitelists.req = ['routeLevelAddedProperty'];
470
+ req._routeWhitelists.res = ['routeLevelAddedProperty'];
471
+
472
+ req._routeWhitelists.body = ['username'];
473
+ req._routeBlacklists.body = ['age'];
474
+
475
+ res.end('{ "message": "Hi! I\'m a chunk!" }');
476
+ }
477
+ var testHelperOptions = {
478
+ next: next,
479
+ req: {
480
+ body: {
481
+ username: "bobby",
482
+ password: "top-secret",
483
+ age: 42,
484
+ potato: 'Russet'
485
+ },
486
+ routeLevelAddedProperty: 'value that should be logged'
487
+ },
488
+ res: {
489
+ nonWhitelistedProperty: 'value that should not be logged',
490
+ routeLevelAddedProperty: 'value that should be logged'
491
+ },
492
+ };
493
+
494
+ it('should invoke the transport', function () {
495
+ return loggerTestHelper(testHelperOptions).then(function (result) {
496
+ result.transportInvoked.should.eql(true);
435
497
  });
498
+ });
436
499
 
437
- describe('when invoked on a route with an empty response body', function () {
438
- var result;
439
-
440
- before(function (done) {
441
- setUp({
442
- url: '/hello',
443
- body: {}
444
- });
445
-
446
- req.routeLevelAddedProperty = 'value that should be logged';
447
-
448
- var test = {
449
- req: req,
450
- res: res,
451
- log: {}
452
- };
453
-
454
- function next(_req, _res, next) {
455
- res.end();
456
-
457
- result = test;
458
-
459
- return done();
460
- };
461
-
462
- var middleware = expressWinston.logger({
463
- transports: [new MockTransport(test)]
464
- });
465
-
466
- middleware(req, res, next);
500
+ it('should contain a filtered request', function () {
501
+ return loggerTestHelper(testHelperOptions).then(function (result) {
502
+ result.log.meta.req.should.be.ok();
503
+ result.log.meta.req.method.should.eql('GET');
504
+ result.log.meta.req.query.should.eql({
505
+ val: '1'
467
506
  });
468
507
 
469
- it('should not have an empty body in meta.req', function () {
470
- result.log.meta.res.should.not.have.property('body');
471
- });
508
+ result.log.meta.req.body.should.not.have.property('age');
509
+ result.log.meta.req.body.should.not.have.property('potato');
472
510
  });
511
+ });
473
512
 
474
- describe('when invoked on a route with transport level of "error"', function () {
475
- var result;
476
-
477
- before(function (done) {
478
- setUp({
479
- url: "/hello",
480
- body: {}
481
- });
482
-
483
- req.routeLevelAddedProperty = 'value that should be logged';
484
-
485
- res.nonWhitelistedProperty = 'value that should not be logged';
486
- res.routeLevelAddedProperty = 'value that should be logged';
487
-
488
- var test = {
489
- req: req,
490
- res: res,
491
- log: {}
492
- };
493
-
494
- function next(_req, _res, next) {
495
- req._routeWhitelists.req = ['routeLevelAddedProperty'];
496
- req._routeWhitelists.res = ['routeLevelAddedProperty'];
497
-
498
- res.end('{ "message": "Hi! I\'m a chunk!" }');
499
- result = test;
500
- return done();
501
- };
502
-
503
- var middleware = expressWinston.logger({
504
- transports: [new MockTransport(test, {
505
- level: 'error'
506
- })],
507
- statusLevels: true
508
- });
513
+ it('should contain a filtered response', function () {
514
+ return loggerTestHelper(testHelperOptions).then(function (result) {
515
+ result.log.meta.res.should.be.ok();
509
516
 
510
- middleware(req, res, next);
511
- });
517
+ result.log.meta.res.statusCode.should.eql(200);
518
+ result.log.meta.res.routeLevelAddedProperty.should.be.ok();
512
519
 
513
- it('should not invoke the transport', function () {
514
- result.transportInvoked.should.eql(false);
515
- });
520
+ result.log.meta.res.should.not.have.property('nonWhitelistedProperty');
516
521
  });
522
+ });
517
523
 
518
- describe('when invoked on a route that should be ignored (options.ignoreRoute)', function () {
519
- var result;
520
-
521
- before(function (done) {
522
- setUp({
523
- url: '/is-not-logged'
524
- });
525
- req.skip = true;
526
- var test = {
527
- req: req,
528
- res: res,
529
- log: {}
530
- };
531
-
532
- function next(_req, _res, next) {
533
- res.end('{ "message": "Hi! I\'m a chunk!" }');
534
- result = test;
535
- return done();
536
- };
537
-
538
- var middleware = expressWinston.logger({
539
- transports: [new MockTransport(test)],
540
- ignoreRoute: function (req, res) {
541
- return req.skip === true && req.url.match(/^\/is-not-log/);
542
- }
543
- });
544
-
545
- middleware(req, res, next);
546
- });
547
-
548
- it('should not invoke the transport', function () {
549
- result.transportInvoked.should.eql(false);
550
- });
551
-
552
- it('should contain a filtered request', function () {
553
- result.log.should.be.empty;
554
- });
524
+ it('should contain a response time', function () {
525
+ return loggerTestHelper(testHelperOptions).then(function (result) {
526
+ result.log.meta.responseTime.should.be.within(120, 130);
555
527
  });
556
528
  });
529
+ });
557
530
 
558
- describe('log.msg', function () {
559
- var result;
560
-
561
- function logMsgSetup(url, msg, expressFormat, done) {
562
- setUp({
563
- url: url || '/an-url'
564
- });
565
-
566
- var test = {
567
- req: req,
568
- res: res,
569
- log: {}
570
- };
571
-
572
- function next(_req, _res, next) {
573
- res.end('{ "message": "Hi! I\'m a chunk!" }');
574
-
575
- result = test;
531
+ describe('when middleware function is invoked on a route that should be ignored (by .ignoredRoutes)', function () {
532
+ var testHelperOptions = {
533
+ req: {url: '/ignored'}
534
+ };
576
535
 
577
- return done();
578
- };
536
+ it('should not invoke the transport', function () {
537
+ return loggerTestHelper(testHelperOptions).then(function (result) {
538
+ result.transportInvoked.should.eql(false);
539
+ });
540
+ });
579
541
 
580
- var loggerOptions = {
581
- transports: [new MockTransport(test)]
582
- };
542
+ it('should contain a filtered request', function () {
543
+ return loggerTestHelper(testHelperOptions).then(function (result) {
544
+ result.log.should.be.empty();
545
+ });
546
+ });
547
+ });
583
548
 
584
- if (msg) {
585
- loggerOptions.msg = msg;
549
+ describe('expressFormat option', function () {
550
+ it('should match the Express format when logging', function () {
551
+ var testHelperOptions = {
552
+ loggerOptions: {
553
+ expressFormat: true
554
+ },
555
+ req: {
556
+ url: '/all-the-things'
586
557
  }
558
+ };
559
+ return loggerTestHelper(testHelperOptions).then(function (result) {
560
+ var resultMsg = result.log.msg;
561
+ resultMsg.should.startWith('\u001b[90mGET /all-the-things\u001b[39m \u001b[32m200\u001b[39m \u001b[90m');
562
+ resultMsg.should.endWith('ms\u001b[39m');
563
+ });
564
+ });
565
+ });
587
566
 
588
- if (expressFormat) {
589
- delete loggerOptions.msg;
590
- loggerOptions.expressFormat = true;
567
+ describe('msg option', function () {
568
+ it('should have a default log msg', function () {
569
+ var testHelperOptions = {
570
+ req: {
571
+ url: '/url-of-sandwich'
591
572
  }
573
+ };
574
+ return loggerTestHelper(testHelperOptions).then(function (result) {
575
+ result.log.msg.should.eql('HTTP GET /url-of-sandwich');
576
+ });
577
+ });
592
578
 
593
- var middleware = expressWinston.logger(loggerOptions);
579
+ it('should match the custom format when a custom format is provided', function () {
580
+ var testHelperOptions = {
581
+ loggerOptions: {
582
+ msg: 'Foo {{ req.method }} {{ req.url }}'
583
+ },
584
+ req: {
585
+ url: '/all-the-things'
586
+ }
587
+ };
588
+ return loggerTestHelper(testHelperOptions).then(function (result) {
589
+ result.log.msg.should.eql('Foo GET /all-the-things');
590
+ });
591
+ });
592
+ });
594
593
 
595
- middleware(req, res, next);
594
+ describe('ignoreRoute option', function () {
595
+ var testHelperOptions = {
596
+ req: {
597
+ shouldSkip: true,
598
+ url: '/is-not-logged'
599
+ },
600
+ loggerOptions: {
601
+ ignoreRoute: function (req, res) {
602
+ return req.shouldSkip === true && req.url.match(/^\/is-not-log/);
603
+ }
596
604
  }
605
+ };
597
606
 
598
- describe('when default', function () {
599
-
600
- before(function (done) {
601
- logMsgSetup('/url-of-sandwich', null, false, done);
602
- });
603
-
604
- it('should match the custom format', function () {
605
- result.log.msg.should.eql('HTTP GET /url-of-sandwich');
606
- });
607
+ it('should throw an error if ignoreRoute option is provided but not a function', function () {
608
+ var loggerFn = expressWinston.logger.bind(expressWinston, {
609
+ transports: [new MockTransport({})],
610
+ ignoreRoute: 'not a function'
607
611
  });
612
+ loggerFn.should.throw();
613
+ });
608
614
 
609
- describe('using Express format', function () {
610
- before(function (done) {
611
- logMsgSetup('/all-the-things', null, true, done);
612
- });
613
-
614
- it('should match the Express format', function () {
615
- var resultMsg = result.log.msg;
616
- resultMsg.should.startWith('\u001b[90mGET /all-the-things\u001b[39m \u001b[32m200\u001b[39m \u001b[90m');
617
- resultMsg.should.endWith('ms\u001b[39m');
618
- });
615
+ it('should not invoke the transport when invoked on a route that should be ignored', function () {
616
+ return loggerTestHelper(testHelperOptions).then(function (result) {
617
+ result.transportInvoked.should.eql(false);
619
618
  });
619
+ });
620
620
 
621
- describe('when customized', function () {
622
- before(function (done) {
623
- logMsgSetup('/all-the-things', 'Foo {{ req.method }} {{ req.url }}', false, done);
624
- });
625
-
626
- it('should match the custom format', function () {
627
- result.log.msg.should.eql('Foo GET /all-the-things');
628
- });
621
+ it('should contain a filtered request when invoked on a route that should be ignored', function () {
622
+ return loggerTestHelper(testHelperOptions).then(function (result) {
623
+ result.log.should.be.empty();
629
624
  });
630
625
  });
626
+ });
631
627
 
632
- describe('log.skip', function () {
633
- var result;
634
-
635
- function logSkipSetup(url, skip, done) {
636
- setUp({
637
- url: url || '/an-url'
638
- });
639
-
640
- var test = {
641
- req: req,
642
- res: res,
643
- log: {}
644
- };
645
-
646
- function next(_req, _res, next) {
647
- res.end('{ "message": "Hi! I\'m a chunk!" }');
648
-
649
- result = test;
650
-
651
- return done();
652
- };
653
-
654
- var loggerOptions = {
655
- transports: [new MockTransport(test)]
656
- };
628
+ describe('metaField option', function () {
629
+ it('should have a default meta field', function () {
630
+ return loggerTestHelper().then(function (result) {
631
+ result.log.meta.req.should.be.ok();
632
+ });
633
+ });
657
634
 
658
- if (skip) {
659
- loggerOptions.skip = skip;
635
+ it('should use provided custom metaField', function () {
636
+ var testHelperOptions = {
637
+ loggerOptions: {
638
+ metaField: 'foobar'
660
639
  }
640
+ };
641
+ return loggerTestHelper(testHelperOptions).then(function (result) {
642
+ result.log.meta.foobar.req.should.be.ok();
643
+ });
644
+ });
645
+ });
661
646
 
662
- var middleware = expressWinston.logger(loggerOptions);
647
+ describe('skip option', function () {
648
+ it('should not be logged when using custom function returning true', function () {
649
+ var testHelperOptions = {
650
+ loggerOptions: {
651
+ skip: function (req, res) {
652
+ return req.url.indexOf('sandwich') != -1
653
+ }
654
+ },
655
+ req: {
656
+ url: '/url-of-sandwich'
657
+ }
658
+ };
659
+ return loggerTestHelper(testHelperOptions).then(function (result) {
660
+ should.not.exist(result.log.msg);
661
+ });
662
+ });
663
663
 
664
- middleware(req, res, next);
665
- }
664
+ it('should be logged when using custom function returning false', function () {
665
+ var testHelperOptions = {
666
+ loggerOptions: {
667
+ skip: function (req, res) {
668
+ return req.url.indexOf('sandwich') != -1
669
+ }
670
+ },
671
+ req: {
672
+ url: '/hello'
673
+ }
674
+ };
675
+ return loggerTestHelper(testHelperOptions).then(function (result) {
676
+ result.log.msg.should.eql('HTTP GET /hello');
677
+ });
678
+ });
679
+ });
666
680
 
667
- describe('when default', function () {
681
+ describe('statusLevels option', function () {
682
+ it('should have status level of "info" by default', function () {
683
+ var testHelperOptions = {
684
+ next: function (req, res, next) {
685
+ res.status(403).end('{ "message": "Hi! I\'m a chunk!" }');
686
+ }
687
+ };
688
+ return loggerTestHelper(testHelperOptions).then(function (result) {
689
+ result.log.level.should.equal('info');
690
+ });
691
+ });
668
692
 
669
- before(function (done) {
670
- logSkipSetup('/url-of-sandwich', null, done);
693
+ describe('when statusLevels set to true', function () {
694
+ it('should have status level of "info" when 100 <= statusCode < 400', function () {
695
+ var testHelperOptions = {
696
+ next: function (req, res, next) {
697
+ res.status(200).end('{ "message": "Hi! I\'m a chunk!" }');
698
+ },
699
+ loggerOptions: {
700
+ statusLevels: true
701
+ },
702
+ req: {
703
+ url: '/url-of-sandwich'
704
+ }
705
+ };
706
+ return loggerTestHelper(testHelperOptions).then(function (result) {
707
+ result.log.level.should.equal('info');
671
708
  });
709
+ });
672
710
 
673
- it('should be logged', function () {
674
- result.log.msg.should.eql('HTTP GET /url-of-sandwich');
711
+ it('should have status level of "warn" when 400 <= statusCode < 500', function () {
712
+ var testHelperOptions = {
713
+ next: function (req, res, next) {
714
+ res.status(403).end('{ "message": "Hi! I\'m a chunk!" }');
715
+ },
716
+ loggerOptions: {
717
+ statusLevels: true
718
+ }
719
+ };
720
+ return loggerTestHelper(testHelperOptions).then(function (result) {
721
+ result.log.level.should.equal('warn');
675
722
  });
676
723
  });
677
724
 
678
- describe('when using custom function returning true', function () {
679
- before(function (done) {
680
- logSkipSetup('/url-of-sandwich', function(req, res) { return req.url.indexOf('sandwich') != -1 }, done);
725
+ it('should have status level of "error" when statusCode >= 500', function () {
726
+ var testHelperOptions = {
727
+ next: function (req, res, next) {
728
+ res.status(500).end('{ "message": "Hi! I\'m a chunk!" }');
729
+ },
730
+ loggerOptions: {
731
+ statusLevels: true
732
+ }
733
+ };
734
+ return loggerTestHelper(testHelperOptions).then(function (result) {
735
+ result.log.level.should.equal('error');
681
736
  });
737
+ });
738
+ });
682
739
 
683
- it('should not be logged', function () {
684
- should.not.exist(result.log.msg);
740
+ describe('when statusLevels set to an object', function () {
741
+ it('should have custom status level provided by "success" key of object when 100 <= statusCode < 400', function () {
742
+ var testHelperOptions = {
743
+ next: function (req, res, next) {
744
+ res.status(200).end('{ "message": "Hi! I\'m a chunk!" }');
745
+ },
746
+ loggerOptions: {
747
+ statusLevels: {success: 'silly'}
748
+ },
749
+ transportOptions: {
750
+ level: 'silly'
751
+ }
752
+ };
753
+ return loggerTestHelper(testHelperOptions).then(function (result) {
754
+ result.log.level.should.equal('silly');
685
755
  });
686
756
  });
687
757
 
688
- describe('when using custom function returning false', function () {
689
- before(function (done) {
690
- logSkipSetup('/hello', function(req, res) { return req.url.indexOf('sandwich') != -1 }, done);
758
+ it('should have status level provided by "warn" key of object when 400 <= statusCode < 500', function () {
759
+ var testHelperOptions = {
760
+ next: function (req, res, next) {
761
+ res.status(403).end('{ "message": "Hi! I\'m a chunk!" }');
762
+ },
763
+ loggerOptions: {
764
+ statusLevels: {warn: 'debug'}
765
+ },
766
+ transportOptions: {
767
+ level: 'silly'
768
+ }
769
+ };
770
+ return loggerTestHelper(testHelperOptions).then(function (result) {
771
+ result.log.level.should.equal('debug');
691
772
  });
773
+ });
692
774
 
693
- it('should be logged', function () {
694
- result.log.msg.should.eql('HTTP GET /hello');
775
+ it('should have status level provided by "error" key of object when statusCode >= 500', function () {
776
+ var testHelperOptions = {
777
+ next: function (req, res, next) {
778
+ res.status(500).end('{ "message": "Hi! I\'m a chunk!" }');
779
+ },
780
+ loggerOptions: {
781
+ statusLevels: {error: 'verbose'}
782
+ },
783
+ transportOptions: {
784
+ level: 'silly'
785
+ }
786
+ };
787
+ return loggerTestHelper(testHelperOptions).then(function (result) {
788
+ result.log.level.should.equal('verbose');
695
789
  });
696
790
  });
697
791
  });
792
+ });
698
793
 
699
- describe('log.metaField', function () {
700
- var result;
701
-
702
- function logMetaFieldSetup(url, metaField, done) {
703
- setUp({
704
- url: url || '/an-url'
705
- });
706
-
707
- var test = {
708
- req: req,
709
- res: res,
710
- log: {}
711
- };
794
+ describe('requestWhitelist option', function () {
795
+ it('should default to global requestWhitelist', function () {
796
+ var options = {
797
+ req: {foo: "bar"}
798
+ };
799
+ return loggerTestHelper(options).then(function (result) {
800
+ result.log.meta.req.should.not.have.property('foo');
801
+ });
802
+ });
712
803
 
713
- function next(_req, _res, next) {
714
- res.end('{ "message": "Hi! I\'m a chunk!" }');
804
+ it('should use specified requestWhitelist', function () {
805
+ var options = {
806
+ req: {foo: "bar"},
807
+ loggerOptions: {
808
+ requestWhitelist: ['foo']
809
+ }
810
+ };
811
+ return loggerTestHelper(options).then(function (result) {
812
+ result.log.meta.req.should.have.property('foo');
813
+ result.log.meta.req.should.not.have.property('method');
814
+ });
815
+ });
816
+ });
715
817
 
716
- result = test;
818
+ describe('responseWhitelist option', function () {
819
+ it('should default to global responseWhitelist', function () {
820
+ var options = {
821
+ res: {foo: "bar"}
822
+ };
823
+ return loggerTestHelper(options).then(function (result) {
824
+ result.log.meta.res.should.not.have.property('foo');
825
+ });
826
+ });
717
827
 
718
- return done();
719
- };
828
+ it('should use specified responseWhitelist', function () {
829
+ var options = {
830
+ res: {foo: "bar"},
831
+ loggerOptions: {
832
+ responseWhitelist: ['foo']
833
+ }
834
+ };
835
+ return loggerTestHelper(options).then(function (result) {
836
+ result.log.meta.res.should.have.property('foo');
837
+ result.log.meta.res.should.not.have.property('method');
838
+ });
839
+ });
840
+ });
720
841
 
721
- var loggerOptions = {
722
- transports: [new MockTransport(test)]
723
- };
842
+ describe('ignoredRoutes option', function () {
843
+ it('should default to global ignoredRoutes', function () {
844
+ var options = {
845
+ req: {url: "/ignored"}
846
+ };
847
+ return loggerTestHelper(options).then(function (result) {
848
+ result.transportInvoked.should.eql(false);
849
+ });
850
+ });
724
851
 
725
- if (metaField) {
726
- loggerOptions.metaField = metaField;
852
+ it('should use specified ignoredRoutes', function () {
853
+ var options = {
854
+ req: {url: "/ignored-option"},
855
+ loggerOptions: {
856
+ ignoredRoutes: ['/ignored-option']
727
857
  }
858
+ };
859
+ return loggerTestHelper(options).then(function (result) {
860
+ result.transportInvoked.should.eql(false);
861
+ });
862
+ });
863
+ });
864
+ });
728
865
 
729
- var middleware = expressWinston.logger(loggerOptions);
866
+ describe('.requestWhitelist', function () {
867
+ it('should be an array with all the properties whitelisted in the req object', function () {
868
+ expressWinston.requestWhitelist.should.be.an.Array();
869
+ });
870
+ });
730
871
 
731
- middleware(req, res, next);
732
- }
872
+ describe('.bodyWhitelist', function () {
873
+ it('should be an array with all the properties whitelisted in the body object', function () {
874
+ expressWinston.bodyWhitelist.should.be.an.Array();
875
+ });
876
+ });
733
877
 
734
- describe('when default', function () {
878
+ describe('.bodyBlacklist', function () {
735
879
 
736
- before(function (done) {
737
- logMetaFieldSetup('/url-of-sandwich', null, done);
738
- });
880
+ });
739
881
 
740
- it('should be logged', function () {
741
- result.log.meta.req.should.be.ok;
742
- });
743
- });
882
+ describe('.responseWhitelist', function () {
883
+ it('should be an array with all the properties whitelisted in the res object', function () {
884
+ expressWinston.responseWhitelist.should.be.an.Array();
885
+ });
886
+ });
744
887
 
745
- describe('when using custom metaField', function () {
888
+ describe('.defaultRequestFilter', function () {
889
+ it('should be a function', function () {
890
+ expressWinston.defaultRequestFilter.should.be.a.Function();
891
+ });
892
+ });
746
893
 
747
- before(function (done) {
748
- logMetaFieldSetup('/url-of-sandwich', 'metaField', done);
749
- });
894
+ describe('.defaultResponseFilter', function () {
895
+ it('should be a function', function () {
896
+ expressWinston.defaultResponseFilter.should.be.a.Function();
897
+ });
898
+ });
750
899
 
751
- it('should be logged', function () {
752
- result.log.meta.metaField.req.should.be.ok;
753
- });
754
- });
755
- });
900
+ describe('.defaultSkip', function () {
901
+ it('should be a function', function () {
902
+ expressWinston.defaultSkip.should.be.a.Function();
903
+ });
904
+ });
905
+
906
+ describe('.ignoredRoutes', function () {
907
+ it('should be an array for all the ignored routes', function () {
908
+ expressWinston.ignoredRoutes.should.be.an.Array();
756
909
  });
757
910
  });
758
911
  });