winston-middleware 1.1.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');
144
-
145
- var test = {
146
- req: req,
147
- res: res,
148
- log: {},
149
- originalError: originalError,
150
- pipelineError: null
151
- };
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;
152
162
 
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,47 +211,70 @@ 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
  });
223
+
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
+ });
232
+
233
+ describe('requestWhitelist option', function () {
234
+ it('should default to global requestWhitelist', function () {
235
+ var options = {
236
+ req: {foo: "bar"}
237
+ };
238
+ return errorLoggerTestHelper(options).then(function (result) {
239
+ result.log.meta.req.should.not.have.property('foo');
240
+ });
241
+ });
242
+
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');
253
+ });
254
+ });
255
+ });
197
256
  });
198
257
 
199
258
  describe('.logger()', function () {
200
259
  it('should be a function', function () {
201
- expressWinston.logger.should.be.a.Function;
260
+ expressWinston.logger.should.be.a.Function();
202
261
  });
203
262
 
204
- it('should throw an error without options', function () {
205
- (function () {
206
- expressWinston.logger();
207
- }).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();
208
266
  });
209
267
 
210
- it('should throw an error without any transport specified', function () {
211
- (function () {
212
- expressWinston.logger({});
213
- }).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();
214
271
  });
215
272
 
216
- it('should throw an error with an empty list of transports', function () {
217
- (function () {
218
- expressWinston.logger({
219
- transports: []
220
- });
221
- }).should.throwError();
222
- });
223
-
224
- it('should throw an error if ignoreRoute option is not a function', function () {
225
- (function () {
226
- expressWinston.logger({
227
- transports: [new MockTransport({})],
228
- ignoreRoute: 'not a function'
229
- });
230
- }).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();
231
278
  });
232
279
 
233
280
  it('should return a middleware function with three arguments that fit (req, res, next)', function () {
@@ -238,423 +285,627 @@ describe('expressWinston', function () {
238
285
  middleware.length.should.eql(3);
239
286
  });
240
287
 
241
- describe('logger() middleware()', function () {
242
- describe('v0.1.x API', function () {
243
- describe('when invoked on a route', function () {
244
- var result;
245
-
246
- before(function (done) {
247
- setUp();
248
-
249
- var test = {
250
- req: req,
251
- res: res,
252
- log: {}
253
- };
254
-
255
- function next(_req, _res, next) {
256
- res.end('{ "message": "Hi! I\'m a chunk!" }');
257
- result = test;
258
- return done();
259
- };
260
-
261
- var middleware = expressWinston.logger({
262
- transports: [new MockTransport(test)]
263
- });
264
-
265
- middleware(req, res, next);
266
- });
267
-
268
- it('should invoke the transport', function () {
269
- result.transportInvoked.should.eql(true);
270
- });
271
-
272
- it('should contain a filtered request', function () {
273
- result.log.meta.req.should.be.ok;
274
- result.log.meta.req.method.should.eql('GET');
275
- result.log.meta.req.query.should.eql({
276
- val: '1'
277
- });
278
-
279
- result.log.meta.req.should.not.have.property('nonWhitelistedProperty');
280
- });
281
- });
282
-
283
- describe('when invoked on a route that should be ignored', function () {
284
- 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
+ });
285
304
 
286
- before(function (done) {
287
- setUp({
288
- ignoreRoute: true
289
- });
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
+ });
290
334
 
291
- var test = {
292
- req: req,
293
- res: res,
294
- log: {}
295
- };
335
+ it('should use the exported requestWhitelist', function() {
336
+ var originalWhitelist = expressWinston.requestWhitelist;
337
+ expressWinston.requestWhitelist = ['foo'];
296
338
 
297
- function next(_req, _res, next) {
298
- res.end('{ "message": "Hi! I\'m a chunk!" }');
299
- result = test;
300
- return done();
301
- };
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;
302
345
 
303
- var middleware = expressWinston.logger({
304
- transports: [new MockTransport(test)]
305
- });
346
+ result.log.meta.req.should.have.property('foo');
347
+ result.log.meta.req.should.not.have.property('url');
348
+ });
349
+ });
306
350
 
307
- middleware(req, res, next);
308
- });
351
+ it('should use the exported bodyWhitelist', function() {
352
+ var originalWhitelist = expressWinston.bodyWhitelist;
353
+ expressWinston.bodyWhitelist = ['foo'];
309
354
 
310
- it('should not invoke the transport', function () {
311
- result.transportInvoked.should.eql(false);
312
- });
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;
313
361
 
314
- it('should contain a filtered request', function () {
315
- result.log.should.be.empty;
316
- });
317
- });
362
+ result.log.meta.req.body.should.have.property('foo');
363
+ result.log.meta.req.body.should.not.have.property('baz');
318
364
  });
365
+ });
319
366
 
320
- describe('v0.2.x API', function () {
321
- describe('when invoked on a route', function () {
322
- var result;
323
-
324
- before(function (done) {
325
- setUp({
326
- body: {
327
- username: "bobby",
328
- password: "top-secret",
329
- age: 42,
330
- potato: 'Russet'
331
- }
332
- });
367
+ it('should use the exported bodyBlacklist', function() {
368
+ var originalWhitelist = expressWinston.bodyBlacklist;
369
+ expressWinston.bodyBlacklist = ['foo'];
333
370
 
334
- 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;
335
377
 
336
- res.nonWhitelistedProperty = 'value that should not be logged';
337
- 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
+ });
338
382
 
339
- var test = {
340
- req: req,
341
- res: res,
342
- log: {}
343
- };
383
+ it('should use the exported responseWhitelist', function() {
384
+ var originalWhitelist = expressWinston.responseWhitelist;
385
+ expressWinston.responseWhitelist = ['foo'];
344
386
 
345
- function next(_req, _res, next) {
346
- 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;
347
393
 
348
- req._routeWhitelists.req = ['routeLevelAddedProperty'];
349
- 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
+ });
350
398
 
351
- req._routeWhitelists.body = ['username'];
352
- 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
+ };
353
404
 
354
- 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;
355
411
 
356
- result = test;
412
+ result.log.meta.req.url.should.equal('foo');
413
+ });
414
+ });
357
415
 
358
- return done();
359
- };
416
+ it('should use the exported defaultResponseFilter', function() {
417
+ var originalResponseFilter = expressWinston.defaultResponseFilter;
418
+ expressWinston.defaultResponseFilter = function() {
419
+ return 'foo';
420
+ };
360
421
 
361
- var middleware = expressWinston.logger({
362
- transports: [new MockTransport(test)]
363
- });
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;
364
428
 
365
- middleware(req, res, next);
366
- });
429
+ result.log.meta.res.statusCode.should.equal('foo');
430
+ });
431
+ });
367
432
 
368
- it('should invoke the transport', function () {
369
- result.transportInvoked.should.eql(true);
370
- });
433
+ it('should use the exported defaultSkip', function() {
434
+ var originalSkip = expressWinston.defaultSkip;
435
+ expressWinston.defaultSkip = function() {
436
+ return true;
437
+ };
371
438
 
372
- it('should contain a filtered request', function () {
373
- result.log.meta.req.should.be.ok;
374
- result.log.meta.req.method.should.eql('GET');
375
- result.log.meta.req.query.should.eql({
376
- val: '1'
377
- });
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;
378
445
 
379
- result.log.meta.req.body.should.not.have.property('age');
380
- result.log.meta.req.body.should.not.have.property('potato');
381
- });
446
+ result.transportInvoked.should.eql(false);
447
+ });
448
+ });
382
449
 
383
- it('should contain a filtered response', function () {
384
- 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'];
385
453
 
386
- result.log.meta.res.statusCode.should.eql(200);
387
- 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;
388
460
 
389
- result.log.meta.res.should.not.have.property('nonWhitelistedProperty');
390
- });
461
+ result.transportInvoked.should.eql(false);
462
+ });
463
+ });
391
464
 
392
- it('should contain a response time', function () {
393
- result.log.meta.responseTime.should.be.within(120, 130);
394
- });
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);
395
497
  });
498
+ });
396
499
 
397
- describe('when invoked on a route with an empty response body', function () {
398
- var result;
399
-
400
- before(function (done) {
401
- setUp({
402
- url: '/hello',
403
- body: {}
404
- });
405
-
406
- req.routeLevelAddedProperty = 'value that should be logged';
407
-
408
- var test = {
409
- req: req,
410
- res: res,
411
- log: {}
412
- };
413
-
414
- function next(_req, _res, next) {
415
- res.end();
416
-
417
- result = test;
418
-
419
- return done();
420
- };
421
-
422
- var middleware = expressWinston.logger({
423
- transports: [new MockTransport(test)]
424
- });
425
-
426
- 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'
427
506
  });
428
507
 
429
- it('should not have an empty body in meta.req', function () {
430
- result.log.meta.res.should.not.have.property('body');
431
- });
508
+ result.log.meta.req.body.should.not.have.property('age');
509
+ result.log.meta.req.body.should.not.have.property('potato');
432
510
  });
511
+ });
433
512
 
434
- describe('when invoked on a route with transport level of "error"', function () {
435
- var result;
436
-
437
- before(function (done) {
438
- setUp({
439
- url: "/hello",
440
- body: {}
441
- });
442
-
443
- req.routeLevelAddedProperty = 'value that should be logged';
444
-
445
- res.nonWhitelistedProperty = 'value that should not be logged';
446
- res.routeLevelAddedProperty = 'value that should be logged';
447
-
448
- var test = {
449
- req: req,
450
- res: res,
451
- log: {}
452
- };
513
+ it('should contain a filtered response', function () {
514
+ return loggerTestHelper(testHelperOptions).then(function (result) {
515
+ result.log.meta.res.should.be.ok();
453
516
 
454
- function next(_req, _res, next) {
455
- req._routeWhitelists.req = ['routeLevelAddedProperty'];
456
- req._routeWhitelists.res = ['routeLevelAddedProperty'];
517
+ result.log.meta.res.statusCode.should.eql(200);
518
+ result.log.meta.res.routeLevelAddedProperty.should.be.ok();
457
519
 
458
- res.end('{ "message": "Hi! I\'m a chunk!" }');
459
- result = test;
460
- return done();
461
- };
520
+ result.log.meta.res.should.not.have.property('nonWhitelistedProperty');
521
+ });
522
+ });
462
523
 
463
- var middleware = expressWinston.logger({
464
- transports: [new MockTransport(test, {
465
- level: 'error'
466
- })],
467
- statusLevels: true
468
- });
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);
527
+ });
528
+ });
529
+ });
469
530
 
470
- middleware(req, res, next);
471
- });
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
+ };
472
535
 
473
- it('should not invoke the transport', function () {
474
- result.transportInvoked.should.eql(false);
475
- });
536
+ it('should not invoke the transport', function () {
537
+ return loggerTestHelper(testHelperOptions).then(function (result) {
538
+ result.transportInvoked.should.eql(false);
476
539
  });
540
+ });
477
541
 
478
- describe('when invoked on a route that should be ignored (options.ignoreRoute)', function () {
479
- var result;
480
-
481
- before(function (done) {
482
- setUp({
483
- url: '/is-not-logged'
484
- });
485
- req.skip = true;
486
- var test = {
487
- req: req,
488
- res: res,
489
- log: {}
490
- };
491
-
492
- function next(_req, _res, next) {
493
- res.end('{ "message": "Hi! I\'m a chunk!" }');
494
- result = test;
495
- return done();
496
- };
497
-
498
- var middleware = expressWinston.logger({
499
- transports: [new MockTransport(test)],
500
- ignoreRoute: function (req, res) {
501
- return req.skip === true && req.url.match(/^\/is-not-log/);
502
- }
503
- });
504
-
505
- middleware(req, res, next);
506
- });
507
-
508
- it('should not invoke the transport', function () {
509
- result.transportInvoked.should.eql(false);
510
- });
542
+ it('should contain a filtered request', function () {
543
+ return loggerTestHelper(testHelperOptions).then(function (result) {
544
+ result.log.should.be.empty();
545
+ });
546
+ });
547
+ });
511
548
 
512
- it('should contain a filtered request', function () {
513
- result.log.should.be.empty;
514
- });
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'
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');
515
563
  });
516
564
  });
565
+ });
517
566
 
518
- describe('log.msg', function () {
519
- var result;
567
+ describe('msg option', function () {
568
+ it('should have a default log msg', function () {
569
+ var testHelperOptions = {
570
+ req: {
571
+ url: '/url-of-sandwich'
572
+ }
573
+ };
574
+ return loggerTestHelper(testHelperOptions).then(function (result) {
575
+ result.log.msg.should.eql('HTTP GET /url-of-sandwich');
576
+ });
577
+ });
520
578
 
521
- function logMsgSetup(url, msg, expressFormat, done) {
522
- setUp({
523
- url: url || '/an-url'
524
- });
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
+ });
525
593
 
526
- var test = {
527
- req: req,
528
- res: res,
529
- log: {}
530
- };
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
+ }
604
+ }
605
+ };
531
606
 
532
- function next(_req, _res, next) {
533
- res.end('{ "message": "Hi! I\'m a chunk!" }');
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'
611
+ });
612
+ loggerFn.should.throw();
613
+ });
534
614
 
535
- result = test;
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);
618
+ });
619
+ });
536
620
 
537
- return done();
538
- };
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();
624
+ });
625
+ });
626
+ });
539
627
 
540
- var loggerOptions = {
541
- transports: [new MockTransport(test)]
542
- };
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
+ });
543
634
 
544
- if (msg) {
545
- loggerOptions.msg = msg;
635
+ it('should use provided custom metaField', function () {
636
+ var testHelperOptions = {
637
+ loggerOptions: {
638
+ metaField: 'foobar'
546
639
  }
640
+ };
641
+ return loggerTestHelper(testHelperOptions).then(function (result) {
642
+ result.log.meta.foobar.req.should.be.ok();
643
+ });
644
+ });
645
+ });
547
646
 
548
- if (expressFormat) {
549
- delete loggerOptions.msg;
550
- loggerOptions.expressFormat = true;
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'
551
657
  }
658
+ };
659
+ return loggerTestHelper(testHelperOptions).then(function (result) {
660
+ should.not.exist(result.log.msg);
661
+ });
662
+ });
552
663
 
553
- var middleware = expressWinston.logger(loggerOptions);
554
-
555
- middleware(req, res, next);
556
- }
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
+ });
557
680
 
558
- 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
+ });
559
692
 
560
- before(function (done) {
561
- logMsgSetup('/url-of-sandwich', null, false, 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');
562
708
  });
709
+ });
563
710
 
564
- it('should match the custom format', function () {
565
- 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');
566
722
  });
567
723
  });
568
724
 
569
- describe('using Express format', function () {
570
- before(function (done) {
571
- logMsgSetup('/all-the-things', null, true, 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');
572
736
  });
737
+ });
738
+ });
573
739
 
574
- it('should match the Express format', function () {
575
- var resultMsg = result.log.msg;
576
- resultMsg.should.startWith('\u001b[90mGET /all-the-things\u001b[39m \u001b[32m200\u001b[39m \u001b[90m');
577
- resultMsg.should.endWith('ms\u001b[39m');
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');
578
755
  });
579
756
  });
580
757
 
581
- describe('when customized', function () {
582
- before(function (done) {
583
- logMsgSetup('/all-the-things', 'Foo {{ req.method }} {{ req.url }}', false, 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');
584
772
  });
773
+ });
585
774
 
586
- it('should match the custom format', function () {
587
- result.log.msg.should.eql('Foo GET /all-the-things');
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');
588
789
  });
589
790
  });
590
791
  });
591
-
592
- describe('log.skip', function () {
593
- var result;
594
-
595
- function logSkipSetup(url, skip, done) {
596
- setUp({
597
- url: url || '/an-url'
598
- });
792
+ });
599
793
 
600
- var test = {
601
- req: req,
602
- res: res,
603
- log: {}
604
- };
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
+ });
605
803
 
606
- function next(_req, _res, next) {
607
- 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
+ });
608
817
 
609
- 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
+ });
610
827
 
611
- return done();
612
- };
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
+ });
613
841
 
614
- var loggerOptions = {
615
- transports: [new MockTransport(test)]
616
- };
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
+ });
617
851
 
618
- if (skip) {
619
- loggerOptions.skip = skip;
852
+ it('should use specified ignoredRoutes', function () {
853
+ var options = {
854
+ req: {url: "/ignored-option"},
855
+ loggerOptions: {
856
+ ignoredRoutes: ['/ignored-option']
620
857
  }
858
+ };
859
+ return loggerTestHelper(options).then(function (result) {
860
+ result.transportInvoked.should.eql(false);
861
+ });
862
+ });
863
+ });
864
+ });
621
865
 
622
- 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
+ });
623
871
 
624
- middleware(req, res, next);
625
- }
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
+ });
626
877
 
627
- describe('when default', function () {
878
+ describe('.bodyBlacklist', function () {
628
879
 
629
- before(function (done) {
630
- logSkipSetup('/url-of-sandwich', null, done);
631
- });
880
+ });
632
881
 
633
- it('should be logged', function () {
634
- result.log.msg.should.eql('HTTP GET /url-of-sandwich');
635
- });
636
- });
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
+ });
637
887
 
638
- describe('when using custom function returning true', function () {
639
- before(function (done) {
640
- logSkipSetup('/url-of-sandwich', function(req, res) { return req.url.indexOf('sandwich') != -1 }, done);
641
- });
888
+ describe('.defaultRequestFilter', function () {
889
+ it('should be a function', function () {
890
+ expressWinston.defaultRequestFilter.should.be.a.Function();
891
+ });
892
+ });
642
893
 
643
- it('should not be logged', function () {
644
- should.not.exist(result.log.msg);
645
- });
646
- });
894
+ describe('.defaultResponseFilter', function () {
895
+ it('should be a function', function () {
896
+ expressWinston.defaultResponseFilter.should.be.a.Function();
897
+ });
898
+ });
647
899
 
648
- describe('when using custom function returning false', function () {
649
- before(function (done) {
650
- logSkipSetup('/hello', function(req, res) { return req.url.indexOf('sandwich') != -1 }, done);
651
- });
900
+ describe('.defaultSkip', function () {
901
+ it('should be a function', function () {
902
+ expressWinston.defaultSkip.should.be.a.Function();
903
+ });
904
+ });
652
905
 
653
- it('should be logged', function () {
654
- result.log.msg.should.eql('HTTP GET /hello');
655
- });
656
- });
657
- });
906
+ describe('.ignoredRoutes', function () {
907
+ it('should be an array for all the ignored routes', function () {
908
+ expressWinston.ignoredRoutes.should.be.an.Array();
658
909
  });
659
910
  });
660
911
  });