src/test/morgan.js
46,898 bytes · 1,667 lines · capsule://quake0day/[email protected]
raw on github
process.env.NO_DEPRECATION = 'morgan'
var assert = require('assert')
var fs = require('fs')
var http = require('http')
var https = require('https')
var join = require('path').join
var morgan = require('..')
var request = require('supertest')
var split = require('split')
describe('morgan()', function () {
describe('arguments', function () {
it('should use default format', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert(res.text.length > 0)
assert.strictEqual(line.substr(0, res.text.length), res.text)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(undefined, { stream: stream }))
.get('/')
.expect(200, cb)
})
describe('format', function () {
it('should accept format as format name', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert(/^GET \/ 200 - - \d+\.\d{3} ms$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer('tiny', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should accept format as format string', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'GET /')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':method :url', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should accept format as function', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'GET / 200')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
function format (tokens, req, res) {
return [req.method, req.url, res.statusCode].join(' ')
}
request(createServer(format, { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should reject format as bool', function () {
assert.throws(createServer.bind(null, true), /argument format/)
})
describe('back-compat', function () {
it('should accept options object', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert(res.text.length > 0)
assert.strictEqual(line.substr(0, res.text.length), res.text)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer({ stream: stream }))
.get('/')
.expect(200, cb)
})
it('should accept format in options for back-compat', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'GET /')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer({ format: ':method :url', stream: stream }))
.get('/')
.expect(200, cb)
})
it('should accept format function in options for back-compat', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'apple')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
function format () {
return 'apple'
}
request(createServer({ format: format, stream: stream }))
.get('/')
.expect(200, cb)
})
})
})
describe('stream', function () {
beforeEach(function () {
this.stdout = process.stdout
})
afterEach(function () {
Object.defineProperty(process, 'stdout', {
value: this.stdout
})
})
it('should default to process.stdout', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert(res.text.length > 0)
assert.strictEqual(line.substr(0, res.text.length), res.text)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
Object.defineProperty(process, 'stdout', {
value: stream
})
request(createServer(undefined, { stream: undefined }))
.get('/')
.expect(200, cb)
})
it('should set stream to write logs to', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert(res.text.length > 0)
assert.strictEqual(line.substr(0, res.text.length), res.text)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(undefined, { stream: stream }))
.get('/')
.expect(200, cb)
})
})
})
describe('tokens', function () {
describe(':date', function () {
it('should get current date in "web" format by default', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':date', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should get current date in "clf" format', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^\d{2}\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2} \+0000$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':date[clf]', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should get current date in "iso" format', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':date[iso]', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should get current date in "web" format', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':date[web]', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should be blank for unknown format', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':date[bogus]', { stream: stream }))
.get('/')
.expect(200, cb)
})
})
describe(':http-version', function () {
it('should be 1.0 or 1.1', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^1\.[01]$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':http-version', { stream: stream }))
.get('/')
.expect(200, cb)
})
})
describe(':req', function () {
it('should get request properties', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'me')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':req[x-from-string]', { stream: stream }))
.get('/')
.set('x-from-string', 'me')
.expect(200, cb)
})
it('should display all values of array headers', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'foo=bar, fizz=buzz')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':req[set-cookie]', { stream: stream }))
.get('/')
.set('Set-Cookie', ['foo=bar', 'fizz=buzz'])
.expect(200, cb)
})
})
describe(':res', function () {
it('should get response properties', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'true')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':res[x-sent]', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should display all values of array headers', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'foo, bar')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':res[x-keys]', { stream: stream }, function (req, res, next) {
res.setHeader('X-Keys', ['foo', 'bar'])
next()
})
request(server)
.get('/')
.expect('X-Keys', 'foo, bar')
.expect(200, cb)
})
})
describe(':remote-addr', function () {
it('should get remote address', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(res.text.length > 0)
assert.strictEqual(line, res.text)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':remote-addr', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should use req.ip if there', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '10.0.0.1')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':remote-addr', { stream: stream }, null, function (req) {
req.ip = '10.0.0.1'
})
request(server)
.get('/')
.expect(200, cb)
})
it('should work on https server', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(res.text.length > 0)
assert.strictEqual(line, res.text)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createSecureServer(':remote-addr', { stream: stream })
request(server)
.get('/')
.ca(server.cert)
.expect(200, cb)
})
it('should work when connection: close', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(res.text.length > 0)
assert.strictEqual(line, res.text)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':remote-addr', { stream: stream }))
.get('/')
.set('Connection', 'close')
.expect(200, cb)
})
it('should work when connection: keep-alive', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(res.text.length > 0)
assert.strictEqual(line, res.text)
res.req.connection.destroy()
server.close(done)
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':remote-addr', { stream: stream }, function (req, res, next) {
delete req._remoteAddress
next()
})
request(server.listen())
.get('/')
.set('Connection', 'keep-alive')
.expect(200, cb)
})
it('should work when req.ip is a getter', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '10.0.0.1')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':remote-addr', { stream: stream }, null, function (req) {
Object.defineProperty(req, 'ip', {
get: function () { return req.connection.remoteAddress ? '10.0.0.1' : undefined }
})
})
request(server)
.get('/')
.set('Connection', 'close')
.expect(200, cb)
})
it('should not fail if req.connection missing', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(res.text.length > 0)
assert.strictEqual(line, res.text)
res.req.connection.destroy()
server.close(done)
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':remote-addr', { stream: stream }, null, function (req) {
delete req.connection
})
request(server.listen())
.get('/')
.set('Connection', 'keep-alive')
.expect(200, cb)
})
})
describe(':remote-user', function () {
it('should be empty if none present', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':remote-user', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should support Basic authorization', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'tj')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':remote-user', { stream: stream }))
.get('/')
.set('Authorization', 'Basic dGo6')
.expect(200, cb)
})
it('should be empty for empty Basic authorization user', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':remote-user', { stream: stream }))
.get('/')
.set('Authorization', 'Basic Og==')
.expect(200, cb)
})
})
describe(':pid', function () {
it('should get process id', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, String(process.pid))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':pid', { stream: stream }))
.get('/')
.expect(200, cb)
})
})
describe(':response-time', function () {
it('should be in milliseconds', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
var end = Date.now()
var ms = parseFloat(line)
assert(ms > 0)
assert(ms < end - start + 1)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var start = Date.now()
request(createServer(':response-time', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should have three digits by default', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^[0-9]+\.[0-9]{3}$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':response-time', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should have five digits with argument "5"', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^[0-9]+\.[0-9]{5}$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':response-time[5]', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should have no digits with argument "0"', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^[0-9]+$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':response-time[0]', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should not include response write time', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
var end = Date.now()
var ms = parseFloat(line)
assert(ms > 0)
assert(ms < end - start + 1)
assert(ms < write - start + 1)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':response-time', { stream: stream }, function (req, res) {
res.write('hello, ')
write = Date.now()
setTimeout(function () {
res.end('world!')
}, 50)
})
var start = Date.now()
var write = null
request(server)
.get('/')
.expect(200, cb)
})
it('should be empty without hidden property', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':response-time', { stream: stream }, function (req, res, next) {
delete req._startAt
next()
})
request(server)
.get('/')
.expect(200, cb)
})
it('should be empty before response', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':response-time', {
immediate: true,
stream: stream
})
request(server)
.get('/')
.expect(200, cb)
})
it('should be empty if morgan invoked after response sent', function (done) {
var cb = after(3, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var logger = morgan(':response-time', {
immediate: true,
stream: stream
})
var server = http.createServer(function (req, res) {
setTimeout(function () {
logger(req, res, cb)
}, 10)
res.end()
})
request(server)
.get('/')
.expect(200, cb)
})
})
describe(':status', function () {
it('should get response status', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, String(res.statusCode))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':status', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should not exist before response sent', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':status', {
immediate: true,
stream: stream
})
request(server)
.get('/')
.expect(200, cb)
})
it('should not exist for aborted request', function (done) {
var stream = createLineStream(function (line) {
assert.strictEqual(line, '-')
server.close(done)
})
var server = createServer(':status', { stream: stream }, function () {
test.abort()
})
var test = request(server).post('/')
test.write('0')
})
})
describe(':total-time', function () {
it('should be in milliseconds', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
var end = Date.now()
var ms = parseFloat(line)
assert(ms > 0)
assert(ms < end - start + 1)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var start = Date.now()
request(createServer(':total-time', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should have three digits by default', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^[0-9]+\.[0-9]{3}$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':total-time', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should have five digits with argument "5"', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^[0-9]+\.[0-9]{5}$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':total-time[5]', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should have no digits with argument "0"', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.ok(/^[0-9]+$/.test(line))
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':total-time[0]', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should include response write time', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
var end = Date.now()
var ms = parseFloat(line)
assert(ms > 0)
assert(ms > write - start - 1)
assert(ms < end - start + 1)
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':total-time', { stream: stream }, function (req, res) {
res.write('hello, ')
write = Date.now()
setTimeout(function () {
res.end('world!')
}, 50)
})
var start = Date.now()
var write = null
request(server)
.get('/')
.expect(200, cb)
})
it('should be empty without hidden property', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':total-time', { stream: stream }, function (req, res, next) {
delete req._startAt
next()
})
request(server)
.get('/')
.expect(200, cb)
})
it('should be empty before response', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':total-time', {
immediate: true,
stream: stream
})
request(server)
.get('/')
.expect(200, cb)
})
it('should be empty if morgan invoked after response sent', function (done) {
var cb = after(3, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '-')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var logger = morgan(':total-time', {
immediate: true,
stream: stream
})
var server = http.createServer(function (req, res) {
setTimeout(function () {
logger(req, res, cb)
}, 10)
res.end()
})
request(server)
.get('/')
.expect(200, cb)
})
})
describe(':url', function () {
it('should get request URL', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '/foo')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':url', { stream: stream }))
.get('/foo')
.expect(200, cb)
})
it('should use req.originalUrl if exists', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '/bar')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':url', { stream: stream }, function (req, res, next) {
req.originalUrl = '/bar'
next()
})
request(server)
.get('/')
.expect(200, cb)
})
it('should not exist for aborted request', function (done) {
var stream = createLineStream(function (line) {
assert.strictEqual(line, '-')
server.close(done)
})
var server = createServer(':status', { stream: stream }, function () {
test.abort()
})
var test = request(server).post('/')
test.write('0')
})
})
})
describe('formats', function () {
describe('a function', function () {
it('should log result of function', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'GET / 200')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
function format (tokens, req, res) {
return [req.method, req.url, res.statusCode].join(' ')
}
request(createServer(format, { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should not log for undefined return', function (done) {
var stream = createLineStream(function () {
throw new Error('should not log line')
})
function format (tokens, req, res) {
return undefined
}
request(createServer(format, { stream: stream }))
.get('/')
.expect(200, done)
})
it('should not log for null return', function (done) {
var stream = createLineStream(function () {
throw new Error('should not log line')
})
function format (tokens, req, res) {
return null
}
request(createServer(format, { stream: stream }))
.get('/')
.expect(200, done)
})
})
describe('a string', function () {
it('should accept format as format string of tokens', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'GET /')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer(':method :url', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should accept text mixed with tokens', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'method=GET url=/')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer('method=:method url=:url', { stream: stream }))
.get('/')
.expect(200, cb)
})
it('should accept special characters', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'LOCAL\\tobi "GET /" 200')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer('LOCAL\\:remote-user ":method :url" :status', { stream: stream }))
.get('/')
.set('Authorization', 'Basic dG9iaTpsb2tp')
.expect(200, cb)
})
})
describe('combined', function () {
it('should match expectations', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
var masked = line.replace(/\d{2}\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2} \+0000/, '_timestamp_')
assert.strictEqual(masked, res.text + ' - tj [_timestamp_] "GET / HTTP/1.1" 200 - "http://localhost/" "my-ua"')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer('combined', { stream: stream }))
.get('/')
.set('Authorization', 'Basic dGo6')
.set('Referer', 'http://localhost/')
.set('User-Agent', 'my-ua')
.expect(200, cb)
})
})
describe('common', function () {
it('should match expectations', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
var masked = line.replace(/\d{2}\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2} \+0000/, '_timestamp_')
assert.strictEqual(masked, res.text + ' - tj [_timestamp_] "GET / HTTP/1.1" 200 -')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer('common', { stream: stream }))
.get('/')
.set('Authorization', 'Basic dGo6')
.expect(200, cb)
})
})
describe('default', function () {
it('should match expectations', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
var masked = line.replace(/\w+, \d+ \w+ \d+ \d+:\d+:\d+ \w+/, '_timestamp_')
assert.strictEqual(masked, res.text + ' - tj [_timestamp_] "GET / HTTP/1.1" 200 - "http://localhost/" "my-ua"')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer('default', { stream: stream }))
.get('/')
.set('Authorization', 'Basic dGo6')
.set('Referer', 'http://localhost/')
.set('User-Agent', 'my-ua')
.expect(200, cb)
})
})
describe('dev', function () {
it('should not color 1xx', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line.substr(0, 36), '_color_0_GET / _color_0_102_color_0_')
assert.strictEqual(line.substr(-9), '_color_0_')
done()
})
var stream = createColorLineStream(function onLine (line) {
cb(null, null, line)
})
var server = createServer('dev', { stream: stream }, function (req, res, next) {
res.statusCode = 102
next()
})
request(server)
.get('/')
.expect(102, function (err, res) {
if (err && err.code === 'ECONNRESET') {
// finishing response with 1xx is invalid http
// but node.js server lets the server do this, so
// morgan needs to test in this condition even if
// the http client doesn't like it
err = null
}
cb(err, res)
})
})
it('should color 2xx green', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line.substr(0, 37), '_color_0_GET / _color_32_200_color_0_')
assert.strictEqual(line.substr(-9), '_color_0_')
done()
})
var stream = createColorLineStream(function onLine (line) {
cb(null, null, line)
})
var server = createServer('dev', { stream: stream }, function (req, res, next) {
res.statusCode = 200
next()
})
request(server)
.get('/')
.expect(200, cb)
})
it('should color 3xx cyan', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line.substr(0, 37), '_color_0_GET / _color_36_300_color_0_')
assert.strictEqual(line.substr(-9), '_color_0_')
done()
})
var stream = createColorLineStream(function onLine (line) {
cb(null, null, line)
})
var server = createServer('dev', { stream: stream }, function (req, res, next) {
res.statusCode = 300
next()
})
request(server)
.get('/')
.expect(300, cb)
})
it('should color 4xx yelow', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line.substr(0, 37), '_color_0_GET / _color_33_400_color_0_')
assert.strictEqual(line.substr(-9), '_color_0_')
done()
})
var stream = createColorLineStream(function onLine (line) {
cb(null, null, line)
})
var server = createServer('dev', { stream: stream }, function (req, res, next) {
res.statusCode = 400
next()
})
request(server)
.get('/')
.expect(400, cb)
})
it('should color 5xx red', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line.substr(0, 37), '_color_0_GET / _color_31_500_color_0_')
assert.strictEqual(line.substr(-9), '_color_0_')
done()
})
var stream = createColorLineStream(function onLine (line) {
cb(null, null, line)
})
var server = createServer('dev', { stream: stream }, function (req, res, next) {
res.statusCode = 500
next()
})
request(server)
.get('/')
.expect(500, cb)
})
describe('with "immediate: true" option', function () {
it('should not have color or response values', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, '_color_0_GET / _color_0_-_color_0_ - ms - -_color_0_')
done()
})
var stream = createColorLineStream(function onLine (line) {
cb(null, null, line)
})
var server = createServer('dev', {
immediate: true,
stream: stream
})
request(server)
.get('/')
.expect(200, cb)
})
})
})
describe('short', function () {
it('should match expectations', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
var masked = line.replace(/\d+\.\d{3} ms/, '_timer_')
assert.strictEqual(masked, res.text + ' tj GET / HTTP/1.1 200 - - _timer_')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer('short', { stream: stream }))
.get('/')
.set('Authorization', 'Basic dGo6')
.expect(200, cb)
})
})
describe('tiny', function () {
it('should match expectations', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
var masked = line.replace(/\d+\.\d{3} ms/, '_timer_')
assert.strictEqual(masked, 'GET / 200 - - _timer_')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
request(createServer('tiny', { stream: stream }))
.get('/')
.expect(200, cb)
})
})
})
describe('with buffer option', function () {
it('should flush log periodically', function (done) {
var cb = after(2, function (err, res, log) {
if (err) return done(err)
assert.strictEqual(log, 'GET /first\nGET /second\n')
assert.ok(Date.now() - time >= 1000)
assert.ok(Date.now() - time <= 1100)
done()
})
var server = createServer(':method :url', {
buffer: true,
stream: { write: writeLog }
})
var time = Date.now()
function writeLog (log) {
cb(null, null, log)
}
request(server)
.get('/first')
.expect(200, function (err) {
if (err) return cb(err)
request(server)
.get('/second')
.expect(200, cb)
})
})
it('should not flush before custom interval elapses', function (done) {
var writes = []
var server = createServer(':method :url', {
buffer: 750,
stream: { write: function (log) { writes.push(log) } }
})
request(server)
.get('/first')
.expect(200, function (err) {
if (err) return done(err)
assert.strictEqual(writes.length, 0)
request(server)
.get('/second')
.expect(200, function (err) {
if (err) return done(err)
assert.strictEqual(writes.length, 0)
done()
})
})
})
})
describe('with immediate option', function () {
it('should not have value for :res', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'GET / -')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':method :url :res[x-sent]', {
immediate: true,
stream: stream
})
request(server)
.get('/')
.expect(200, cb)
})
it('should not have value for :response-time', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'GET / -')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':method :url :response-time', {
immediate: true,
stream: stream
})
request(server)
.get('/')
.expect(200, cb)
})
it('should not have value for :status', function (done) {
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'GET / -')
done()
})
var stream = createLineStream(function (line) {
cb(null, null, line)
})
var server = createServer(':method :url :status', {
immediate: true,
stream: stream
})
request(server)
.get('/')
.expect(200, cb)
})
it('should log before response', function (done) {
var lineLogged = false
var cb = after(2, function (err, res, line) {
if (err) return done(err)
assert.strictEqual(line, 'GET / -')
done()
})
var stream = createLineStream(function (line) {
lineLogged = true
cb(null, null, line)
})
var server = createServer(':method :url :res[x-sent]', { immediate: true, stream: stream }, function (req, res, next) {
assert.ok(lineLogged)
next()
})
request(server)
.get('/')
.expect(200, cb)
})
})
describe('with skip option', function () {
it('should be able to skip based on request', function (done) {
var stream = createLineStream(function () {
throw new Error('should not log line')
})
function skip (req) {
return req.url.indexOf('skip=true') !== -1
}
request(createServer({ format: 'default', skip: skip, stream: stream }))
.get('/?skip=true')
.set('Connection', 'close')
.expect(200, done)
})
it('should be able to skip based on response', function (done) {
var stream = createLineStream(function () {
throw new Error('should not log line')
})
function skip (req, res) {
return res.statusCode === 200
}
request(createServer({ format: 'default', skip: skip, stream: stream }))
.get('/')
.expect(200, done)
})
})
})
describe('morgan.compile(format)', function () {
describe('arguments', function () {
describe('format', function () {
it('should be required', function () {
assert.throws(morgan.compile.bind(morgan), /argument format/)
})
it('should reject functions', function () {
assert.throws(morgan.compile.bind(morgan, function () {}), /argument format/)
})
it('should reject numbers', function () {
assert.throws(morgan.compile.bind(morgan, 42), /argument format/)
})
it('should compile a string into a function', function () {
var fn = morgan.compile(':method')
assert.ok(typeof fn === 'function')
assert.ok(fn.length === 3)
})
})
})
})
function after (count, callback) {
var args = new Array(3)
var i = 0
return function (err, arg1, arg2) {
assert.ok(i++ < count, 'callback called ' + count + ' times')
args[0] = args[0] || err
args[1] = args[1] || arg1
args[2] = args[2] || arg2
if (count === i) {
callback.apply(null, args)
}
}
}
function createColorLineStream (callback) {
return createLineStream(function onLine (line) {
callback(expandColorCharacters(line))
})
}
function createLineStream (callback) {
return split().on('data', callback)
}
function createRequestListener (format, opts, fn, fn1) {
var logger = morgan(format, opts)
var middle = fn || noopMiddleware
return function onRequest (req, res) {
// prior alterations
if (fn1) {
fn1(req, res)
}
logger(req, res, function onNext (err) {
// allow req, res alterations
middle(req, res, function onDone () {
if (err) {
res.statusCode = 500
res.end(err.message)
}
res.setHeader('X-Sent', 'true')
res.end((req.connection && req.connection.remoteAddress) || '-')
})
})
}
}
function createSecureServer (format, opts, fn, fn1) {
var cert = fs.readFileSync(join(__dirname, 'fixtures', 'server.crt'), 'ascii')
var key = fs.readFileSync(join(__dirname, 'fixtures', 'server.key'), 'ascii')
return https.createServer({ cert: cert, key: key })
.on('request', createRequestListener(format, opts, fn, fn1))
}
function createServer (format, opts, fn, fn1) {
return http.createServer()
.on('request', createRequestListener(format, opts, fn, fn1))
}
function expandColorCharacters (str) {
// eslint-disable-next-line no-control-regex
return str.replace(/\x1b\[(\d+)m/g, '_color_$1_')
}
function noopMiddleware (req, res, next) {
next()
}