diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f76579b93..5e844568e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI +name: CI Checks on: pull_request: @@ -11,7 +11,7 @@ on: - master jobs: - unit-tests: + Tests: runs-on: ubuntu-latest strategy: matrix: @@ -28,9 +28,11 @@ jobs: run: npm clean-install - name: Lint run: npm run lint - - name: Test - run: npm test - - name: Coveralls + - name: Integration Tests + run: npm run test:integration + - name: Test Coverage + run: npm run test:coverage + - name: Report test coverage to Coveralls.io if: matrix.node == '16' uses: coverallsapp/github-action@master with: diff --git a/.mocharc.yml b/.mocharc.yml new file mode 100644 index 000000000..2d4ed8a59 --- /dev/null +++ b/.mocharc.yml @@ -0,0 +1,4 @@ +exclude: + - test/helpers/scripts/*.js +exit: true +recursive: true diff --git a/.nycrc.yml b/.nycrc.yml new file mode 100644 index 000000000..ce121842b --- /dev/null +++ b/.nycrc.yml @@ -0,0 +1,8 @@ +reporter: + - text + - lcov +check-coverage: true +branches: 61.51 +lines: 70.85 +functions: 73.21 +statements: 70.54 \ No newline at end of file diff --git a/package.json b/package.json index 4c736a911..99c5aa196 100644 --- a/package.json +++ b/package.json @@ -60,14 +60,15 @@ "types": "./index.d.ts", "scripts": { "lint": "eslint lib/*.js lib/winston/*.js lib/winston/**/*.js --resolve-plugins-relative-to ./node_modules/@dabh/eslint-config-populist", - "pretest": "npm run lint", - "test": "nyc --reporter=text --reporter lcov npm run test:mocha", - "test:mocha": "mocha test/*.test.js test/**/*.test.js --exit", + "test": "mocha", + "test:coverage": "nyc npm run test:unit", + "test:unit": "mocha test/unit", + "test:integration": "mocha test/integration", "build": "rimraf dist && babel lib -d dist", "prepublishOnly": "npm run build" }, "engines": { - "node": ">= 6.4.0" + "node": ">= 12.0.0" }, "license": "MIT" } diff --git a/test/fixtures/file/simple-stream.log b/test/fixtures/file/simple-stream.log deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/helpers/scripts/default-exceptions.js b/test/helpers/scripts/default-exceptions.js index a7350374c..49646f829 100644 --- a/test/helpers/scripts/default-exceptions.js +++ b/test/helpers/scripts/default-exceptions.js @@ -8,10 +8,11 @@ var path = require('path'), winston = require('../../../lib/winston'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); winston.exceptions.handle([ new winston.transports.File({ - filename: path.join(__dirname, '..', '..', 'fixtures', 'logs', 'default-exception.log'), + filename: path.join(testLogFixturesPath, 'default-exception.log'), handleExceptions: true }) ]); diff --git a/test/helpers/scripts/default-rejections.js b/test/helpers/scripts/default-rejections.js index 90ae53281..b095db375 100644 --- a/test/helpers/scripts/default-rejections.js +++ b/test/helpers/scripts/default-rejections.js @@ -8,17 +8,11 @@ var path = require("path"), winston = require("../../../lib/winston"); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); winston.rejections.handle([ new winston.transports.File({ - filename: path.join( - __dirname, - "..", - "..", - "fixtures", - "logs", - "default-rejection.log" - ), + filename: path.join(testLogFixturesPath, "default-rejection.log"), handleRejections: true }) ]); diff --git a/test/helpers/scripts/exit-on-error.js b/test/helpers/scripts/exit-on-error.js index ccbb01140..9231938ba 100644 --- a/test/helpers/scripts/exit-on-error.js +++ b/test/helpers/scripts/exit-on-error.js @@ -8,6 +8,7 @@ var path = require('path'), winston = require('../../../lib/winston'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); winston.exitOnError = function (err) { process.stdout.write(err.message); @@ -16,7 +17,7 @@ winston.exitOnError = function (err) { winston.handleExceptions([ new winston.transports.File({ - filename: path.join(__dirname, '..', 'logs', 'exit-on-error.log'), + filename: path.join(testLogFixturesPath, 'exit-on-error.log'), handleExceptions: true }) ]); diff --git a/test/helpers/scripts/log-exceptions.js b/test/helpers/scripts/log-exceptions.js index 9315b6f4f..f545c01bd 100644 --- a/test/helpers/scripts/log-exceptions.js +++ b/test/helpers/scripts/log-exceptions.js @@ -8,11 +8,12 @@ var path = require('path'), winston = require('../../../lib/winston'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); var logger = winston.createLogger({ transports: [ new winston.transports.File({ - filename: path.join(__dirname, '..', '..', 'fixtures', 'logs', 'exception.log'), + filename: path.join(testLogFixturesPath, 'exception.log'), handleExceptions: true }) ] diff --git a/test/helpers/scripts/log-rejections.js b/test/helpers/scripts/log-rejections.js index b8025a178..ea3592375 100644 --- a/test/helpers/scripts/log-rejections.js +++ b/test/helpers/scripts/log-rejections.js @@ -8,18 +8,12 @@ var path = require("path"), winston = require("../../../lib/winston"); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); var logger = winston.createLogger({ transports: [ new winston.transports.File({ - filename: path.join( - __dirname, - "..", - "..", - "fixtures", - "logs", - "rejections.log" - ), + filename: path.join(testLogFixturesPath, "rejections.log"), handleRejections: true }) ] diff --git a/test/helpers/scripts/log-string-exception.js b/test/helpers/scripts/log-string-exception.js index c186e5c3e..b223cf059 100644 --- a/test/helpers/scripts/log-string-exception.js +++ b/test/helpers/scripts/log-string-exception.js @@ -8,11 +8,12 @@ var path = require('path'), winston = require('../../../lib/winston'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); var logger = winston.createLogger({ transports: [ new winston.transports.File({ - filename: path.join(__dirname, '..', '..', 'fixtures', 'logs', 'string-exception.log'), + filename: path.join(testLogFixturesPath, 'string-exception.log'), handleExceptions: true }) ] diff --git a/test/helpers/scripts/unhandle-exceptions.js b/test/helpers/scripts/unhandle-exceptions.js index b3c0b52e4..3c84a5270 100644 --- a/test/helpers/scripts/unhandle-exceptions.js +++ b/test/helpers/scripts/unhandle-exceptions.js @@ -8,11 +8,12 @@ var path = require('path'), winston = require('../../../lib/winston'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); var logger = winston.createLogger({ transports: [ new winston.transports.File({ - filename: path.join(__dirname, '..', 'logs', 'unhandle-exception.log') + filename: path.join(testLogFixturesPath, 'unhandle-exception.log') }) ] }); diff --git a/test/helpers/scripts/unhandle-rejections.js b/test/helpers/scripts/unhandle-rejections.js index 98c64dabf..4e5574ffc 100644 --- a/test/helpers/scripts/unhandle-rejections.js +++ b/test/helpers/scripts/unhandle-rejections.js @@ -8,11 +8,12 @@ var path = require('path'), winston = require('../../../lib/winston'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); var logger = winston.createLogger({ transports: [ new winston.transports.File({ - filename: path.join(__dirname, '..', 'logs', 'unhandle-rejections.log') + filename: path.join(testLogFixturesPath, 'unhandle-rejections.log') }) ] }); diff --git a/test/winston.test.js b/test/integration/winston.test.js similarity index 94% rename from test/winston.test.js rename to test/integration/winston.test.js index a24c39113..8d084fed0 100644 --- a/test/winston.test.js +++ b/test/integration/winston.test.js @@ -8,7 +8,7 @@ const { format } = require('util'); const assume = require('assume'); -const winston = require('../lib/winston'); +const winston = require('../../lib/winston'); describe('winston', function () { @@ -35,7 +35,7 @@ describe('winston', function () { }); it('exposes version', function () { - assume(winston.version).equals(require('../package').version); + assume(winston.version).equals(require('../../package.json').version); }); it('abstract-winston-logger'); diff --git a/test/logger.test.js b/test/logger.test.js deleted file mode 100755 index 2618d1d60..000000000 --- a/test/logger.test.js +++ /dev/null @@ -1,1081 +0,0 @@ -/* - * logger.test.js: Tests for instances of the winston Logger - * - * (C) 2010 Charlie Robbins - * MIT LICENSE - * - */ - -'use strict'; - -const assume = require('assume'); -const path = require('path'); -const stream = require('readable-stream'); -const util = require('util'); -const { EOL } = require('os'); -const isStream = require('is-stream'); -const stdMocks = require('std-mocks'); -const { MESSAGE, SPLAT } = require('triple-beam'); -const winston = require('../lib/winston'); -const TransportStream = require('winston-transport'); -const format = require('../lib/winston').format; -const helpers = require('./helpers'); -const mockTransport = require('./helpers/mocks/mock-transport'); - -describe('Logger', function () { - it('new Logger()', function () { - var logger = winston.createLogger(); - assume(logger).is.an('object'); - assume(isStream(logger.format)); - assume(logger.level).equals('info'); - assume(logger.exitOnError).equals(true); - }); - - it('new Logger({ parameters })', function () { - var myFormat = format(function (info, opts) { - return info; - })(); - - var logger = winston.createLogger({ - format: myFormat, - level: 'error', - exitOnError: false, - transports: [] - }); - - assume(logger.format).equals(myFormat); - assume(logger.level).equals('error'); - assume(logger.exitOnError).equals(false); - assume(logger._readableState.pipesCount).equals(0); - }); - - it('new Logger({ levels }) defines custom methods', function () { - var myFormat = format(function (info, opts) { - return info; - })(); - - var logger = winston.createLogger({ - levels: winston.config.syslog.levels, - format: myFormat, - level: 'error', - exitOnError: false, - transports: [] - }); - - Object.keys(winston.config.syslog.levels).forEach(level => { - assume(logger[level]).is.a('function'); - }) - }); - - it('new Logger({ levels }) custom methods are not bound to instance', function (done) { - var logger = winston.createLogger({ - level: 'error', - exitOnError: false, - transports: [] - }); - - let logs = []; - let extendedLogger = Object.create(logger, { - write: { - value: function(...args) { - logs.push(args); - if (logs.length === 4) { - assume(logs.length).is.eql(4); - assume(logs[0]).is.eql([{ test: 1, level: 'info' }]); - assume(logs[1]).is.eql([{ test: 2, level: 'warn' }]); - assume(logs[2]).is.eql([{ message: 'test3', level: 'info' }]) - assume(logs[3]).is.eql([{ with: 'meta', - test: 4, - level: 'warn', - message: 'a warning' - }]); - - done(); - } - } - } - }); - - extendedLogger.log('info', { test: 1 }); - extendedLogger.log('warn', { test: 2 }); - extendedLogger.info('test3'); - extendedLogger.warn('a warning', { with: 'meta', test: 4 }); - }); - - it('.add({ invalid Transport })', function () { - var logger = winston.createLogger(); - assume(function () { - logger.add(5); - }).throws(/invalid transport/i); - }); - - it('.add(TransportStream)', function (done) { - var logger = winston.createLogger(); - var expected = { message: 'foo', level: 'info' }; - var transport = new TransportStream({ - log: function (info) { - assume(info.message).equals('foo'); - assume(info.level).equals('info'); - assume(JSON.parse(info[MESSAGE])).deep.equals({ level: 'info', message: 'foo' }); - done(); - } - }); - - logger.add(transport); - logger.log(expected); - }); - - it('.stream()', function () { - var logger = winston.createLogger(); - var outStream = logger.stream(); - - assume(isStream(outStream)).true(); - }); - - it('.configure()', function () { - var logger = winston.createLogger({ - transports: [new winston.transports.Console()] - }); - - assume(logger.transports.length).equals(1); - assume(logger.transports[0].name).equals('console'); - - logger.configure(); - - assume(logger.transports.length).equals(0); - }); - - it('.configure({ transports })', function () { - var logger = winston.createLogger(); - - assume(logger.transports.length).equals(0); - - logger.configure({ - transports: [new winston.transports.Console()] - }); - - assume(logger.transports.length).equals(1); - assume(logger.transports[0].name).equals('console'); - }); - - it('.configure({ transports, format })', function () { - var logger = winston.createLogger(), - format = logger.format; - - assume(logger.transports.length).equals(0); - - logger.configure({ - transports: [new winston.transports.Console()], - format: winston.format.json() - }); - - assume(logger.transports.length).equals(1); - assume(logger.transports[0].name).equals('console'); - assume(logger.format).not.equals(format); - }); - - it('.remove() [transport not added]', function () { - var transports = [ - new winston.transports.Console(), - new winston.transports.File({ filename: path.join(__dirname, 'fixtures', 'logs', 'filelog.log' )}) - ]; - - var logger = winston.createLogger({ transports: transports }) - .remove(new winston.transports.Console()); - - assume(logger.transports.length).equals(2); - assume(logger.transports.map(function (wrap) { - // Unwrap LegacyTransportStream instances - return wrap.transport || wrap; - })).deep.equals(transports); - }); - - it('.remove() [TransportStream]', function () { - var transports = [ - new winston.transports.Console(), - new winston.transports.File({ filename: path.join(__dirname, 'fixtures', 'logs', 'filelog.log' )}) - ]; - - var logger = winston.createLogger({ transports: transports }); - - assume(logger.transports.length).equals(2); - logger.remove(transports[0]); - assume(logger.transports.length).equals(1); - assume(logger.transports[0]).equals(transports[1]); - }); - - it('.clear() [no transports]', function () { - var logger = winston.createLogger(); - assume(logger.transports.length).equals(0); - logger.clear(); - assume(logger.transports.length).equals(0); - }); - - it ('.clear() [transports]', function () { - var logger = winston.createLogger({ - transports: [new winston.transports.Console()] - }); - - assume(logger.transports.length).equals(1); - logger.clear(); - assume(logger.transports.length).equals(0); - }); - - it('{ silent: true }', function (done) { - const neverLogTo = new TransportStream({ - log: function (info) { - assume(false).true('TransportStream was improperly written to'); - } - }); - - var logger = winston.createLogger({ - transports: [neverLogTo], - silent: true - }); - - logger.log({ - level: 'info', - message: 'This should be ignored' - }); - - setImmediate(() => done()); - }); -}); - -describe('Logger (multiple transports of the same type)', function () { - var logger, transports; - - before(function () { - transports = [ - new winston.transports.File({ - name: 'filelog-info.log', - filename: path.join(__dirname, 'fixtures', 'logs', 'filelog-info.log'), - level: 'info' - }), - new winston.transports.File({ - name: 'filelog-error.log', - filename: path.join(__dirname, 'fixtures', 'logs', 'filelog-error.log'), - level: 'error' - }) - ]; - - logger = winston.createLogger({ - transports: transports - }); - }); - - it('should have both transports', function () { - assume(logger.transports.length).equals(2); - assume(logger.transports.map(function (wrap) { - return wrap.transport || wrap; - })).deep.equals(transports); - }); - - it('.remove() of one transport', function () { - logger.remove(transports[0]); - assume(logger.transports.length).equals(1); - assume(logger.transports[0]).equals(transports[1]); - }); -}); - -describe('Logger (levels)', function () { - it('report unknown levels', function (done) { - stdMocks.use(); - var logger = helpers.createLogger(function (info) {}); - var expected = { message: 'foo', level: 'bar' }; - logger.log(expected); - - stdMocks.restore(); - var output = stdMocks.flush(); - - assume(output.stderr).deep.equals(['[winston] Unknown logger level: bar\n']); - done(); - }); - - it('.()', function (done) { - var logger = helpers.createLogger(function (info) { - assume(info).is.an('object'); - assume(info.level).equals('info'); - assume(info.message).is.a('string'); - assume(info[MESSAGE]).is.a('string'); - assume(info.message).equals(''); - assume(JSON.parse(info[MESSAGE])).deep.equals({ - level: 'info', - message: '' - }); - - done(); - }); - - logger.info(); - logger.info(''); - }); - - it('default levels', function (done) { - var logger = winston.createLogger(); - var expected = { message: 'foo', level: 'debug' }; - - function logLevelTransport(level) { - return new TransportStream({ - level: level, - log: function (obj) { - if (level === 'info') { - assume(obj).equals(undefined, 'Transport on level info should never be called'); - } - - assume(obj.message).equals('foo'); - assume(obj.level).equals('debug'); - assume(JSON.parse(obj[MESSAGE])).deep.equals({ level: 'debug', message: 'foo' }); - done(); - } - }); - } - - assume(logger.info).is.a('function'); - assume(logger.debug).is.a('function'); - - logger - .add(logLevelTransport('info')) - .add(logLevelTransport('debug')) - .log(expected); - }); - - it('custom levels', function (done) { - var logger = winston.createLogger({ - levels: { - bad: 0, - test: 1, - ok: 2 - } - }); - - var expected = { message: 'foo', level: 'test' }; - function filterLevelTransport(level) { - return new TransportStream({ - level: level, - log: function (obj) { - if (level === 'bad') { - assume(obj).equals(undefined, 'transport on level "bad" should never be called'); - } - - assume(obj.message).equals('foo'); - assume(obj.level).equals('test'); - assume(JSON.parse(obj[MESSAGE])).deep.equals({ level: 'test', message: 'foo' }); - done(); - } - }); - } - - assume(logger.bad).is.a('function'); - assume(logger.test).is.a('function'); - assume(logger.ok).is.a('function'); - - logger - .add(filterLevelTransport('bad')) - .add(filterLevelTransport('ok')) - .log(expected); - }); - - it('sets transports levels', done => { - let logger; - const transport = new TransportStream({ - log(obj) { - if (obj.level === 'info') { - assume(obj).equals(undefined, 'Transport on level info should never be called'); - } - - assume(obj.message).equals('foo'); - assume(obj.level).equals('error'); - assume(JSON.parse(obj[MESSAGE])).deep.equals({ level: 'error', message: 'foo' }); - done(); - } - }); - - // Begin our test in the next tick after the pipe event is - // emitted from the transport. - transport.once('pipe', () => setImmediate(() => { - const expectedError = { message: 'foo', level: 'error' }; - const expectedInfo = { message: 'bar', level: 'info' }; - - assume(logger.error).is.a('function'); - assume(logger.info).is.a('function'); - - // Set the level - logger.level = 'error'; - - // Log the messages. "info" should never arrive. - logger - .log(expectedInfo) - .log(expectedError); - })); - - logger = winston.createLogger({ - transports: [transport] - }); - }); -}); - -describe('Logger (level enabled/disabled)', function () { - it('default levels', function () { - var logger = winston.createLogger({ - level: 'verbose', - levels: winston.config.npm.levels, - transports: [new winston.transports.Console()] - }); - - assume(logger.isLevelEnabled).is.a('function'); - - assume(logger.isErrorEnabled).is.a('function'); - assume(logger.isWarnEnabled).is.a('function'); - assume(logger.isInfoEnabled).is.a('function'); - assume(logger.isVerboseEnabled).is.a('function'); - assume(logger.isDebugEnabled).is.a('function'); - assume(logger.isSillyEnabled).is.a('function'); - - assume(logger.isLevelEnabled('error')).true(); - assume(logger.isLevelEnabled('warn')).true(); - assume(logger.isLevelEnabled('info')).true(); - assume(logger.isLevelEnabled('verbose')).true(); - assume(logger.isLevelEnabled('debug')).false(); - assume(logger.isLevelEnabled('silly')).false(); - - assume(logger.isErrorEnabled()).true(); - assume(logger.isWarnEnabled()).true(); - assume(logger.isInfoEnabled()).true(); - assume(logger.isVerboseEnabled()).true(); - assume(logger.isDebugEnabled()).false(); - assume(logger.isSillyEnabled()).false(); - }); - - it('default levels, transport override', function () { - var transport = new winston.transports.Console(); - transport.level = 'debug'; - - var logger = winston.createLogger({ - level: 'info', - levels: winston.config.npm.levels, - transports: [transport] - }); - - assume(logger.isLevelEnabled).is.a('function'); - - assume(logger.isErrorEnabled).is.a('function'); - assume(logger.isWarnEnabled).is.a('function'); - assume(logger.isInfoEnabled).is.a('function'); - assume(logger.isVerboseEnabled).is.a('function'); - assume(logger.isDebugEnabled).is.a('function'); - assume(logger.isSillyEnabled).is.a('function'); - - assume(logger.isLevelEnabled('error')).true(); - assume(logger.isLevelEnabled('warn')).true(); - assume(logger.isLevelEnabled('info')).true(); - assume(logger.isLevelEnabled('verbose')).true(); - assume(logger.isLevelEnabled('debug')).true(); - assume(logger.isLevelEnabled('silly')).false(); - - assume(logger.isErrorEnabled()).true(); - assume(logger.isWarnEnabled()).true(); - assume(logger.isInfoEnabled()).true(); - assume(logger.isVerboseEnabled()).true(); - assume(logger.isDebugEnabled()).true(); - assume(logger.isSillyEnabled()).false(); - }); - - it('default levels, no transports', function () { - var logger = winston.createLogger({ - level: 'verbose', - levels: winston.config.npm.levels, - transports: [] - }); - - assume(logger.isLevelEnabled).is.a('function'); - - assume(logger.isErrorEnabled).is.a('function'); - assume(logger.isWarnEnabled).is.a('function'); - assume(logger.isInfoEnabled).is.a('function'); - assume(logger.isVerboseEnabled).is.a('function'); - assume(logger.isDebugEnabled).is.a('function'); - assume(logger.isSillyEnabled).is.a('function'); - - assume(logger.isLevelEnabled('error')).true(); - assume(logger.isLevelEnabled('warn')).true(); - assume(logger.isLevelEnabled('info')).true(); - assume(logger.isLevelEnabled('verbose')).true(); - assume(logger.isLevelEnabled('debug')).false(); - assume(logger.isLevelEnabled('silly')).false(); - - assume(logger.isErrorEnabled()).true(); - assume(logger.isWarnEnabled()).true(); - assume(logger.isInfoEnabled()).true(); - assume(logger.isVerboseEnabled()).true(); - assume(logger.isDebugEnabled()).false(); - assume(logger.isSillyEnabled()).false(); - }); - - it('custom levels', function () { - var logger = winston.createLogger({ - level: 'test', - levels: { - bad: 0, - test: 1, - ok: 2 - }, - transports: [new winston.transports.Console()] - }); - - assume(logger.isLevelEnabled).is.a('function'); - - assume(logger.isBadEnabled).is.a('function'); - assume(logger.isTestEnabled).is.a('function'); - assume(logger.isOkEnabled).is.a('function'); - - assume(logger.isLevelEnabled('bad')).true(); - assume(logger.isLevelEnabled('test')).true(); - assume(logger.isLevelEnabled('ok')).false(); - - assume(logger.isBadEnabled()).true(); - assume(logger.isTestEnabled()).true(); - assume(logger.isOkEnabled()).false(); - }); - - it('custom levels, no transports', function () { - var logger = winston.createLogger({ - level: 'test', - levels: { - bad: 0, - test: 1, - ok: 2 - }, - transports: [] - }); - - assume(logger.isLevelEnabled).is.a('function'); - - assume(logger.isBadEnabled).is.a('function'); - assume(logger.isTestEnabled).is.a('function'); - assume(logger.isOkEnabled).is.a('function'); - - assume(logger.isLevelEnabled('bad')).true(); - assume(logger.isLevelEnabled('test')).true(); - assume(logger.isLevelEnabled('ok')).false(); - - assume(logger.isBadEnabled()).true(); - assume(logger.isTestEnabled()).true(); - assume(logger.isOkEnabled()).false(); - }); - - it('custom levels, transport override', function () { - var transport = new winston.transports.Console(); - transport.level = 'ok'; - - var logger = winston.createLogger({ - level: 'bad', - levels: { - bad: 0, - test: 1, - ok: 2 - }, - transports: [transport] - }); - - assume(logger.isLevelEnabled).is.a('function'); - - assume(logger.isBadEnabled).is.a('function'); - assume(logger.isTestEnabled).is.a('function'); - assume(logger.isOkEnabled).is.a('function'); - - assume(logger.isLevelEnabled('bad')).true(); - assume(logger.isLevelEnabled('test')).true(); - assume(logger.isLevelEnabled('ok')).true(); - - assume(logger.isBadEnabled()).true(); - assume(logger.isTestEnabled()).true(); - assume(logger.isOkEnabled()).true(); - }); -}); - -describe('Logger (stream semantics)', function () { - it(`'finish' event awaits transports to emit 'finish'`, function (done) { - const transports = [ - new TransportStream({ log: function () {} }), - new TransportStream({ log: function () {} }), - new TransportStream({ log: function () {} }) - ]; - - const finished = []; - const logger = winston.createLogger({ transports }); - - // Assert each transport emits finish - transports.forEach((transport, i) => { - transport.on('finish', () => finished[i] = true); - }); - - // Manually end the last transport to simulate mixed - // finished state - transports[2].end(); - - // Assert that all transport 'finish' events have been - // emitted when the logger emits 'finish'. - logger.on('finish', function () { - assume(finished[0]).true(); - assume(finished[1]).true(); - assume(finished[2]).true(); - done(); - }); - - setImmediate(() => logger.end()); - }); - - it(`rethrows errors from user-defined formats`, function () { - stdMocks.use(); - const logger = winston.createLogger( { - transports: [new winston.transports.Console()], - format: winston.format.printf((info) => { - // Set a trap. - if (info.message === 'ENDOR') { - throw new Error('ITS A TRAP!'); - } - - return info.message; - }) - }); - - // Trigger the trap. Swallow the error so processing continues. - try { - logger.info('ENDOR'); - } catch (err) { - assume(err.message).equals('ITS A TRAP!'); - } - - const expected = [ - 'Now witness the power of the fully armed and operational logger', - 'Consider the philosophical and metaphysical – BANANA BANANA BANANA', - 'I was god once. I saw – you were doing well until everyone died.' - ]; - - expected.forEach(msg => logger.info(msg)); - - stdMocks.restore(); - const actual = stdMocks.flush(); - assume(actual.stdout).deep.equals(expected.map(msg => `${msg}${EOL}`)); - assume(actual.stderr).deep.equals([]); - }); -}); - -describe('Logger (winston@2 logging API)', function () { - it('.log(level, message)', function (done) { - var logger = helpers.createLogger(function (info) { - assume(info).is.an('object'); - assume(info.level).equals('info'); - assume(info.message).equals('Some super awesome log message'); - assume(info[MESSAGE]).is.a('string'); - done(); - }); - - logger.log('info', 'Some super awesome log message') - }); - - it(`.log(level, undefined) creates info with { message: undefined }`, function (done) { - const logger = helpers.createLogger(function (info) { - assume(info.message).equals(undefined); - done(); - }); - - logger.log('info', undefined); - }); - - it(`.log(level, null) creates info with { message: null }`, function (done) { - const logger = helpers.createLogger(function (info) { - assume(info.message).equals(null); - done(); - }); - - logger.log('info', null); - }); - - it(`.log(level, new Error()) uses Error instance as info`, function (done) { - const err = new Error('test'); - const logger = helpers.createLogger(function (info) { - assume(info).instanceOf(Error); - assume(info).equals(err); - done(); - }); - - logger.log('info', err); - }); - - it('.log(level, message, meta)', function (done) { - var meta = { one: 2 }; - var logger = helpers.createLogger(function (info) { - assume(info).is.an('object'); - assume(info.level).equals('info'); - assume(info.message).equals('Some super awesome log message'); - assume(info.one).equals(2); - assume(info[MESSAGE]).is.a('string'); - done(); - }); - - logger.log('info', 'Some super awesome log message', meta); - }); - - it('.log(level, formatStr, ...splat)', function (done) { - const format = winston.format.combine( - winston.format.splat(), - winston.format.printf(info => `${info.level}: ${info.message}`) - ); - - var logger = helpers.createLogger(function (info) { - assume(info).is.an('object'); - assume(info.level).equals('info'); - assume(info.message).equals('100% such wow {"much":"javascript"}'); - assume(info[SPLAT]).deep.equals([100, 'wow', { much: 'javascript' }]); - assume(info[MESSAGE]).equals('info: 100% such wow {"much":"javascript"}'); - done(); - }, format); - - logger.log('info', '%d%% such %s %j', 100, 'wow', { much: 'javascript' }); - }); - - it('.log(level, formatStr, ...splat, meta)', function (done) { - const format = winston.format.combine( - winston.format.splat(), - winston.format.printf(info => `${info.level}: ${info.message} ${JSON.stringify({ thisIsMeta: info.thisIsMeta })}`) - ); - - var logger = helpers.createLogger(function (info) { - assume(info).is.an('object'); - assume(info.level).equals('info'); - assume(info.message).equals('100% such wow {"much":"javascript"}'); - assume(info[SPLAT]).deep.equals([100, 'wow', { much: 'javascript' }]); - assume(info.thisIsMeta).true(); - assume(info[MESSAGE]).equals('info: 100% such wow {"much":"javascript"} {"thisIsMeta":true}'); - done(); - }, format); - - logger.log('info', '%d%% such %s %j', 100, 'wow', { much: 'javascript' }, { thisIsMeta: true }); - }); -}); - -describe('Logger (logging exotic data types)', function () { - describe('.log', function () { - it(`.log(new Error()) uses Error instance as info`, function (done) { - const err = new Error('test'); - err.level = 'info'; - - const logger = helpers.createLogger(function (info) { - assume(info).instanceOf(Error); - assume(info).equals(err); - done(); - }); - - logger.log(err); - }); - - it(`.info('Hello') preserve meta without splat format`, function (done) { - const logged = []; - const logger = helpers.createLogger(function (info, enc, next) { - logged.push(info); - assume(info.label).equals('world'); - next(); - - if (logged.length === 1) done(); - }); - - logger.info('Hello', { label: 'world' }); - }); - - it(`.info('Hello %d') does not mutate unnecessarily with string interpolation tokens`, function (done) { - const logged = []; - const logger = helpers.createLogger(function (info, enc, next) { - logged.push(info); - assume(info.label).equals(undefined); - next(); - - if (logged.length === 1) done(); - }); - - logger.info('Hello %j', { label: 'world' }, { extra: true }); - }); - - it(`.info('Hello') and .info('Hello %d') preserve meta with splat format`, function (done) { - const logged = []; - const logger = helpers.createLogger(function (info, enc, next) { - logged.push(info); - assume(info.label).equals('world'); - next(); - - if (logged.length === 2) done(); - }, format.splat()); - - logger.info('Hello', { label: 'world' }); - logger.info('Hello %d', 100, { label: 'world' }); - }); - }); - - describe('.info', function () { - it('.info(undefined) creates info with { message: undefined }', function (done) { - const logger = helpers.createLogger(function (info) { - assume(info.message).equals(undefined); - done(); - }); - - logger.info(undefined); - }); - - it('.info(null) creates info with { message: null }', function (done) { - const logger = helpers.createLogger(function (info) { - assume(info.message).equals(null); - done(); - }); - - logger.info(null); - }); - - it('.info(new Error()) uses Error instance as info', function (done) { - const err = new Error('test'); - const logger = helpers.createLogger(function (info) { - assume(info).instanceOf(Error); - assume(info).equals(err); - done(); - }); - - logger.info(err); - }); - - it.skip(`.info('any string', new Error())`, function (done) { - const err = new Error('test'); - const logger = helpers.createLogger(function (info) { - // TODO (indexzero): assert this works. - done(); - }); - - logger.info(err); - }); - }); -}); - -describe('Logger (profile, startTimer)', function (done) { - it('profile(id, info)', function (done) { - var logger = helpers.createLogger(function (info) { - assume(info).is.an('object'), - assume(info.something).equals('ok'); - assume(info.level).equals('info'); - assume(info.durationMs).is.a('number'); - assume(info.message).equals('testing1'); - assume(info[MESSAGE]).is.a('string'); - done(); - }); - - logger.profile('testing1'); - setTimeout(function () { - logger.profile('testing1', { - something: 'ok', - level: 'info' - }) - }, 100); - }); - - it('profile(id, callback) ignores callback', function (done) { - var logger = helpers.createLogger(function (info) { - assume(info).is.an('object'), - assume(info.something).equals('ok'); - assume(info.level).equals('info'); - assume(info.durationMs).is.a('number'); - assume(info.message).equals('testing2'); - assume(info[MESSAGE]).is.a('string'); - done(); - }); - - logger.profile('testing2', function () { - done(new Error('Unexpected callback invoked')); - }); - - setTimeout(function () { - logger.profile('testing2', { - something: 'ok', - level: 'info' - }) - }, 100); - }); - - it('startTimer()', function (done) { - var logger = helpers.createLogger(function (info) { - assume(info).is.an('object'), - assume(info.something).equals('ok'); - assume(info.level).equals('info'); - assume(info.durationMs).is.a('number'); - assume(info.message).equals('testing1'); - assume(info[MESSAGE]).is.a('string'); - done(); - }); - - var timer = logger.startTimer(); - setTimeout(function () { - timer.done({ - message: 'testing1', - something: 'ok', - level: 'info' - }); - }, 100); - }); -}); - -describe('Should bubble transport events', () => { - it('error', (done) => { - const consoleTransport = new winston.transports.Console(); - const logger = winston.createLogger({ - transports: [consoleTransport] - }); - - logger.on('error', (err, transport) => { - assume(err).instanceOf(Error); - assume(transport).is.an('object'); - done(); - }); - consoleTransport.emit('error', new Error()); - }); - - it('warn', (done) => { - const consoleTransport = new winston.transports.Console(); - const logger = winston.createLogger({ - transports: [consoleTransport] - }); - - logger.on('warn', (err, transport) => { - assume(err).instanceOf(Error); - assume(transport).is.an('object'); - done(); - }); - consoleTransport.emit('warn', new Error()); - }); -}); - -describe('Should support child loggers & defaultMeta', () => { - it('sets child meta for text messages correctly', (done) => { - const assertFn = ((msg) => { - assume(msg.level).equals('info'); - assume(msg.message).equals('dummy message'); - assume(msg.requestId).equals('451'); - done(); - }); - - const logger = winston.createLogger({ - transports: [ - mockTransport.createMockTransport(assertFn) - ] - }); - - const childLogger = logger.child({ requestId: '451' }); - childLogger.info('dummy message'); - }); - - it('sets child meta for json messages correctly', (done) => { - const assertFn = ((msg) => { - assume(msg.level).equals('info'); - assume(msg.message.text).equals('dummy'); - assume(msg.requestId).equals('451'); - done(); - }); - - const logger = winston.createLogger({ - transports: [ - mockTransport.createMockTransport(assertFn) - ] - }); - - const childLogger = logger.child({ requestId: '451' }); - childLogger.info({ text: 'dummy' }); - }); - - it('merges child and provided meta correctly', (done) => { - const assertFn = ((msg) => { - assume(msg.level).equals('info'); - assume(msg.message).equals('dummy message'); - assume(msg.service).equals('user-service'); - assume(msg.requestId).equals('451'); - done(); - }); - - const logger = winston.createLogger({ - transports: [ - mockTransport.createMockTransport(assertFn) - ] - }); - - const childLogger = logger.child({ service: 'user-service' }); - childLogger.info('dummy message', { requestId: '451' }); - }); - - it('provided meta take precedence over defaultMeta', (done) => { - const assertFn = ((msg) => { - assume(msg.level).equals('info'); - assume(msg.message).equals('dummy message'); - assume(msg.service).equals('audit-service'); - assume(msg.requestId).equals('451'); - done(); - }); - - const logger = winston.createLogger({ - defaultMeta: { service: 'user-service' }, - transports: [ - mockTransport.createMockTransport(assertFn) - ] - }); - - logger.info('dummy message', { - requestId: '451', - service: 'audit-service' - }); - }); - - it('provided meta take precedence over child meta', (done) => { - const assertFn = ((msg) => { - assume(msg.level).equals('info'); - assume(msg.message).equals('dummy message'); - assume(msg.service).equals('audit-service'); - assume(msg.requestId).equals('451'); - done(); - }); - - const logger = winston.createLogger({ - transports: [ - mockTransport.createMockTransport(assertFn) - ] - }); - - const childLogger = logger.child({ service: 'user-service' }); - childLogger.info('dummy message', { - requestId: '451', - service: 'audit-service' - }); - }); - - it('handles error stack traces in child loggers correctly', (done) => { - const assertFn = ((msg) => { - assume(msg.level).equals('error'); - assume(msg.message).equals('dummy error'); - assume(msg.stack).includes('logger.test.js'); - assume(msg.service).equals('user-service'); - done(); - }); - - const logger = winston.createLogger({ - transports: [ - mockTransport.createMockTransport(assertFn) - ] - }); - - const childLogger = logger.child({ service: 'user-service' }); - childLogger.error(Error('dummy error')); - }); - - it('defaultMeta() autobinds correctly', (done) => { - const logger = helpers.createLogger(info => { - assume(info.message).equals('test'); - done(); - }); - - const log = logger.info; - log('test'); - }); -}); diff --git a/test/formats/errors.test.js b/test/unit/formats/errors.test.js similarity index 98% rename from test/formats/errors.test.js rename to test/unit/formats/errors.test.js index c3ff7bdcf..400de910a 100644 --- a/test/formats/errors.test.js +++ b/test/unit/formats/errors.test.js @@ -10,9 +10,9 @@ const assume = require('assume'); const { LEVEL, MESSAGE, SPLAT } = require('triple-beam'); -const winston = require('../../lib/winston'); +const winston = require('../../../lib/winston'); const { format } = winston; -const helpers = require('../helpers'); +const helpers = require('../../helpers'); function assumeExpectedInfo(info, target = {}) { const expected = Object.assign({}, { diff --git a/test/config.test.js b/test/unit/winston/config/config.test.js similarity index 83% rename from test/config.test.js rename to test/unit/winston/config/config.test.js index a25c63a8c..1925921d9 100644 --- a/test/config.test.js +++ b/test/unit/winston/config/config.test.js @@ -7,8 +7,8 @@ */ const assume = require('assume'); -const winston = require('../lib/winston'); -const helpers = require('./helpers'); +const winston = require('../../../../lib/winston'); +const helpers = require('../../../helpers'); describe('winston.config', function () { it('should have expected methods', function () { diff --git a/test/container.test.js b/test/unit/winston/container.test.js similarity index 97% rename from test/container.test.js rename to test/unit/winston/container.test.js index f122c7650..ab587d7e2 100644 --- a/test/container.test.js +++ b/test/unit/winston/container.test.js @@ -7,7 +7,7 @@ */ const assume = require('assume'); -const winston = require('../lib/winston'); +const winston = require('../../../lib/winston'); describe('Container', function () { describe('no transports', function () { diff --git a/test/unit/winston/create-logger.test.js b/test/unit/winston/create-logger.test.js new file mode 100644 index 000000000..080e131ff --- /dev/null +++ b/test/unit/winston/create-logger.test.js @@ -0,0 +1,107 @@ +const winston = require("../../../lib/winston"); +const assume = require("assume"); +const isStream = require("is-stream"); +const {format} = require("../../../lib/winston"); +const TransportStream = require("winston-transport"); + +describe('Create Logger', function () { + it('should build a logger with default values', function () { + let logger = winston.createLogger(); + assume(logger).is.an('object'); + assume(isStream(logger.format)); + assume(logger.level).equals('info'); + assume(logger.exitOnError).equals(true); + }); + + it('new Logger({ silent: true })', function (done) { + const neverLogTo = new TransportStream({ + log: function (info) { + assume(false).true('TransportStream was improperly written to'); + } + }); + + var logger = winston.createLogger({ + transports: [neverLogTo], + silent: true + }); + + logger.log({ + level: 'info', + message: 'This should be ignored' + }); + + setImmediate(() => done()); + }); + + it('new Logger({ parameters })', function () { + let myFormat = format(function (info, opts) { + return info; + })(); + + let logger = winston.createLogger({ + format: myFormat, + level: 'error', + exitOnError: false, + transports: [] + }); + + assume(logger.format).equals(myFormat); + assume(logger.level).equals('error'); + assume(logger.exitOnError).equals(false); + assume(logger._readableState.pipesCount).equals(0); + }); + + it('new Logger({ levels }) defines custom methods', function () { + let myFormat = format(function (info, opts) { + return info; + })(); + + let logger = winston.createLogger({ + levels: winston.config.syslog.levels, + format: myFormat, + level: 'error', + exitOnError: false, + transports: [] + }); + + Object.keys(winston.config.syslog.levels).forEach(level => { + assume(logger[level]).is.a('function'); + }) + }); + + it('new Logger({ levels }) custom methods are not bound to instance', function (done) { + let logger = winston.createLogger({ + level: 'error', + exitOnError: false, + transports: [] + }); + + let logs = []; + let extendedLogger = Object.create(logger, { + write: { + value: function (...args) { + logs.push(args); + if (logs.length === 4) { + assume(logs.length).is.eql(4); + assume(logs[0]).is.eql([{test: 1, level: 'info'}]); + assume(logs[1]).is.eql([{test: 2, level: 'warn'}]); + assume(logs[2]).is.eql([{message: 'test3', level: 'info'}]) + assume(logs[3]).is.eql([{ + with: 'meta', + test: 4, + level: 'warn', + message: 'a warning' + }]); + + done(); + } + } + } + }); + + extendedLogger.log('info', {test: 1}); + extendedLogger.log('warn', {test: 2}); + extendedLogger.info('test3'); + extendedLogger.warn('a warning', {with: 'meta', test: 4}); + }); +}); diff --git a/test/exception-handler.test.js b/test/unit/winston/exception-handler.test.js similarity index 97% rename from test/exception-handler.test.js rename to test/unit/winston/exception-handler.test.js index 9a2286c7b..8ddb12dcc 100644 --- a/test/exception-handler.test.js +++ b/test/unit/winston/exception-handler.test.js @@ -9,8 +9,8 @@ const stream = require('stream'); const assume = require('assume'); const mocha = require('mocha'); -const winston = require('../lib/winston'); -const helpers = require('./helpers'); +const winston = require('../../../lib/winston'); +const helpers = require('../../helpers'); // // This is an awful and fragile hack that diff --git a/test/exception-stream.test.js b/test/unit/winston/exception-stream.test.js similarity index 76% rename from test/exception-stream.test.js rename to test/unit/winston/exception-stream.test.js index 328680209..201c733fa 100644 --- a/test/exception-stream.test.js +++ b/test/unit/winston/exception-stream.test.js @@ -9,12 +9,13 @@ const assume = require('assume'); const { Writable } = require('readable-stream'); const path = require('path'); -const winston = require('../lib/winston'); -const ExceptionStream = require('../lib/winston/exception-stream'); +const winston = require('../../../lib/winston'); +const ExceptionStream = require('../../../lib/winston/exception-stream'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); describe('ExceptionStream', function () { it('has expected methods', function () { - var filename = path.join(__dirname, 'fixtures', 'logs', 'exception-stream.log'); + var filename = path.join(testLogFixturesPath, 'exception-stream.log'); var transport = new winston.transports.File({ filename }); var instance = new ExceptionStream(transport); diff --git a/test/log-exception.test.js b/test/unit/winston/log-exception.test.js similarity index 68% rename from test/log-exception.test.js rename to test/unit/winston/log-exception.test.js index 3abd2d8f2..65e1d0cfb 100644 --- a/test/log-exception.test.js +++ b/test/unit/winston/log-exception.test.js @@ -10,19 +10,21 @@ const assume = require('assume'); const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); -const winston = require('../lib/winston'); -const helpers = require('./helpers'); +const winston = require('../../../lib/winston'); +const helpers = require('../../helpers'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); +const testHelperScriptsPath = path.join(__dirname, '..', '..', 'helpers', 'scripts'); describe('Logger, ExceptionHandler', function () { this.timeout(5000); describe('.exceptions.unhandle()', function () { it('does not log to any transports', function (done) { - var logFile = path.join(__dirname, 'fixtures', 'logs', 'unhandle-exception.log'); + var logFile = path.join(testLogFixturesPath, 'unhandle-exception.log'); helpers.tryUnlink(logFile); - spawn('node', [path.join(__dirname, 'helpers', 'scripts', 'unhandle-exceptions.js')]) + spawn('node', [path.join(testHelperScriptsPath, 'unhandle-exceptions.js')]) .on('exit', function () { fs.exists(logFile, function (exists) { assume(exists).false(); @@ -40,7 +42,7 @@ describe('Logger, ExceptionHandler', function () { var logger = winston.createLogger({ exceptionHandlers: [ new winston.transports.Console(), - new winston.transports.File({ filename: path.join(__dirname, 'fixtures', 'logs', 'filelog.log') }) + new winston.transports.File({ filename: path.join(testLogFixturesPath, 'filelog.log') }) ] }); @@ -53,8 +55,7 @@ describe('Logger, ExceptionHandler', function () { }); it('Custom exitOnError function does not exit', function (done) { - const scriptDir = path.join(__dirname, 'helpers', 'scripts'); - const child = spawn('node', [path.join(scriptDir, 'exit-on-error.js')]); + const child = spawn('node', [path.join(testHelperScriptsPath, 'exit-on-error.js')]); const stdout = []; child.stdout.setEncoding('utf8'); @@ -73,19 +74,19 @@ describe('Logger, ExceptionHandler', function () { describe('.exceptions.handle()', function () { describe('should save the error information to the specified file', function () { it('when strings are thrown as errors', helpers.assertHandleExceptions({ - script: path.join(__dirname, 'helpers', 'scripts', 'log-string-exception.js'), - logfile: path.join(__dirname, 'fixtures', 'logs', 'string-exception.log'), + script: path.join(testHelperScriptsPath, 'log-string-exception.js'), + logfile: path.join(testLogFixturesPath, 'string-exception.log'), message: 'OMG NEVER DO THIS STRING EXCEPTIONS ARE AWFUL' })); it('with a custom winston.Logger instance', helpers.assertHandleExceptions({ - script: path.join(__dirname, 'helpers', 'scripts', 'log-exceptions.js'), - logfile: path.join(__dirname, 'fixtures', 'logs', 'exception.log') + script: path.join(testHelperScriptsPath, 'log-exceptions.js'), + logfile: path.join(testLogFixturesPath, 'exception.log') })); it('with the default winston logger', helpers.assertHandleExceptions({ - script: path.join(__dirname, 'helpers', 'scripts', 'default-exceptions.js'), - logfile: path.join(__dirname, 'fixtures', 'logs', 'default-exception.log') + script: path.join(testHelperScriptsPath, 'default-exceptions.js'), + logfile: path.join(testLogFixturesPath, 'default-exception.log') })); }); }); diff --git a/test/logger-legacy.test.js b/test/unit/winston/logger-legacy.test.js similarity index 92% rename from test/logger-legacy.test.js rename to test/unit/winston/logger-legacy.test.js index 2dbb5b4d2..c30d99257 100644 --- a/test/logger-legacy.test.js +++ b/test/unit/winston/logger-legacy.test.js @@ -15,11 +15,11 @@ const util = require('util'); const isStream = require('is-stream'); const stdMocks = require('std-mocks'); const { MESSAGE } = require('triple-beam'); -const winston = require('../lib/winston'); -const LegacyTransport = require('./helpers/mocks/legacy-transport'); -const LegacyMixedTransport = require('./helpers/mocks/legacy-mixed-transport'); +const winston = require('../../../lib/winston'); +const LegacyTransport = require('../../helpers/mocks/legacy-transport'); +const LegacyMixedTransport = require('../../helpers/mocks/legacy-mixed-transport'); const TransportStream = require('winston-transport'); -const helpers = require('./helpers'); +const helpers = require('../../helpers'); /* * Assumes that the `TransportClass` with the given { name, displayName } diff --git a/test/unit/winston/logger.test.js b/test/unit/winston/logger.test.js new file mode 100755 index 000000000..83504f594 --- /dev/null +++ b/test/unit/winston/logger.test.js @@ -0,0 +1,1021 @@ +/* + * logger.test.js: Tests for instances of the winston Logger + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ + +'use strict'; + +const assume = require('assume'); +const path = require('path'); +const stream = require('readable-stream'); +const util = require('util'); +const { EOL } = require('os'); +const isStream = require('is-stream'); +const stdMocks = require('std-mocks'); +const { MESSAGE, SPLAT } = require('triple-beam'); +const winston = require('../../../lib/winston'); +const TransportStream = require('winston-transport'); +const format = require('../../../lib/winston').format; +const helpers = require('../../helpers'); +const mockTransport = require('../../helpers/mocks/mock-transport'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); + +describe('Logger Instance', function () { + describe('Configuration', function () { + it('.configure()', function () { + let logger = winston.createLogger({ + transports: [new winston.transports.Console()] + }); + + assume(logger.transports.length).equals(1); + assume(logger.transports[0].name).equals('console'); + + logger.configure(); + + assume(logger.transports.length).equals(0); + }); + + it('.configure({ transports })', function () { + let logger = winston.createLogger(); + + assume(logger.transports.length).equals(0); + + logger.configure({ + transports: [new winston.transports.Console()] + }); + + assume(logger.transports.length).equals(1); + assume(logger.transports[0].name).equals('console'); + }); + + it('.configure({ transports, format })', function () { + let logger = winston.createLogger(), + format = logger.format; + + assume(logger.transports.length).equals(0); + + logger.configure({ + transports: [new winston.transports.Console()], + format: winston.format.json() + }); + + assume(logger.transports.length).equals(1); + assume(logger.transports[0].name).equals('console'); + assume(logger.format).not.equals(format); + }); + }); + + describe('Transports', function() { + describe('add', function () { + it('should throw error when adding an invalid transport', function () { + let logger = winston.createLogger(); + assume(function () { + logger.add(5); + }).throws(/invalid transport/i); + }); + + it('should add the expected transport', function (done) { + let logger = winston.createLogger(); + let expected = {message: 'foo', level: 'info'}; + let transport = new TransportStream({ + log: function (info) { + assume(info.message).equals('foo'); + assume(info.level).equals('info'); + assume(JSON.parse(info[MESSAGE])).deep.equals({level: 'info', message: 'foo'}); + done(); + } + }); + + logger.add(transport); + logger.log(expected); + }); + + it('should allow adding multiple transports', function () { + let transports = [ + new winston.transports.File({ + name: 'filelog-info.log', + filename: path.join(testLogFixturesPath, 'filelog-info.log'), + level: 'info' + }), + new winston.transports.File({ + name: 'filelog-error.log', + filename: path.join(testLogFixturesPath, 'filelog-error.log'), + level: 'error' + }) + ]; + let logger = winston.createLogger({ + transports: transports + }); + + assume(logger.transports.length).equals(2); + assume(logger.transports.map(function (wrap) { + return wrap.transport || wrap; + })).deep.equals(transports); + }); + }); + + describe('remove', function () { + it('should do nothing if transport was not added', function () { + let transports = [ + new winston.transports.Console(), + new winston.transports.File({filename: path.join(testLogFixturesPath, 'filelog.log')}) + ]; + + let logger = winston.createLogger({transports: transports}) + .remove(new winston.transports.Console()); + + assume(logger.transports.length).equals(2); + assume(logger.transports.map(function (wrap) { + // Unwrap LegacyTransportStream instances + return wrap.transport || wrap; + })).deep.equals(transports); + }); + + it('should remove transport when matching one is found', function () { + let transports = [ + new winston.transports.Console(), + new winston.transports.File({filename: path.join(testLogFixturesPath, 'filelog.log')}) + ]; + + let logger = winston.createLogger({transports: transports}); + + assume(logger.transports.length).equals(2); + logger.remove(transports[0]); + assume(logger.transports.length).equals(1); + assume(logger.transports[0]).equals(transports[1]); + }); + + it('should remove specified logger even when duplicate exists', function () { + let transports = [ + new winston.transports.File({ + name: 'filelog-info.log', + filename: path.join(testLogFixturesPath, 'filelog-info.log'), + level: 'info' + }), + new winston.transports.File({ + name: 'filelog-error.log', + filename: path.join(testLogFixturesPath, 'filelog-error.log'), + level: 'error' + }) + ]; + let logger = winston.createLogger({ + transports: transports + }); + + logger.remove(transports[0]); + assume(logger.transports.length).equals(1); + assume(logger.transports[0]).equals(transports[1]); + }); + }); + + describe('clear', function () { + it('should do nothing when no transports exist', function () { + let logger = winston.createLogger(); + assume(logger.transports.length).equals(0); + logger.clear(); + assume(logger.transports.length).equals(0); + }); + + it('should remove all transports', function () { + let logger = winston.createLogger({ + transports: [new winston.transports.Console()] + }); + + assume(logger.transports.length).equals(1); + logger.clear(); + assume(logger.transports.length).equals(0); + }); + }); + + describe('stream', function () { + it('should return a log stream for all transports', function () { + let logger = winston.createLogger(); + let outStream = logger.stream(); + + assume(isStream(outStream)).true(); + }); + }); + }); + + describe('Log Levels', function () { + it('report unknown levels', function (done) { + stdMocks.use(); + let logger = helpers.createLogger(function (info) { + }); + let expected = {message: 'foo', level: 'bar'}; + logger.log(expected); + + stdMocks.restore(); + let output = stdMocks.flush(); + + assume(output.stderr).deep.equals(['[winston] Unknown logger level: bar\n']); + done(); + }); + + it('.()', function (done) { + let logger = helpers.createLogger(function (info) { + assume(info).is.an('object'); + assume(info.level).equals('info'); + assume(info.message).is.a('string'); + assume(info[MESSAGE]).is.a('string'); + assume(info.message).equals(''); + assume(JSON.parse(info[MESSAGE])).deep.equals({ + level: 'info', + message: '' + }); + + done(); + }); + + logger.info(); + logger.info(''); + }); + + it('default levels', function (done) { + let logger = winston.createLogger(); + let expected = {message: 'foo', level: 'debug'}; + + function logLevelTransport(level) { + return new TransportStream({ + level: level, + log: function (obj) { + if (level === 'info') { + assume(obj).equals(undefined, 'Transport on level info should never be called'); + } + + assume(obj.message).equals('foo'); + assume(obj.level).equals('debug'); + assume(JSON.parse(obj[MESSAGE])).deep.equals({level: 'debug', message: 'foo'}); + done(); + } + }); + } + + assume(logger.info).is.a('function'); + assume(logger.debug).is.a('function'); + + logger + .add(logLevelTransport('info')) + .add(logLevelTransport('debug')) + .log(expected); + }); + + it('custom levels', function (done) { + let logger = winston.createLogger({ + levels: { + bad: 0, + test: 1, + ok: 2 + } + }); + + let expected = {message: 'foo', level: 'test'}; + + function filterLevelTransport(level) { + return new TransportStream({ + level: level, + log: function (obj) { + if (level === 'bad') { + assume(obj).equals(undefined, 'transport on level "bad" should never be called'); + } + + assume(obj.message).equals('foo'); + assume(obj.level).equals('test'); + assume(JSON.parse(obj[MESSAGE])).deep.equals({level: 'test', message: 'foo'}); + done(); + } + }); + } + + assume(logger.bad).is.a('function'); + assume(logger.test).is.a('function'); + assume(logger.ok).is.a('function'); + + logger + .add(filterLevelTransport('bad')) + .add(filterLevelTransport('ok')) + .log(expected); + }); + + it('sets transports levels', done => { + let logger; + const transport = new TransportStream({ + log(obj) { + if (obj.level === 'info') { + assume(obj).equals(undefined, 'Transport on level info should never be called'); + } + + assume(obj.message).equals('foo'); + assume(obj.level).equals('error'); + assume(JSON.parse(obj[MESSAGE])).deep.equals({level: 'error', message: 'foo'}); + done(); + } + }); + + // Begin our test in the next tick after the pipe event is + // emitted from the transport. + transport.once('pipe', () => setImmediate(() => { + const expectedError = {message: 'foo', level: 'error'}; + const expectedInfo = {message: 'bar', level: 'info'}; + + assume(logger.error).is.a('function'); + assume(logger.info).is.a('function'); + + // Set the level + logger.level = 'error'; + + // Log the messages. "info" should never arrive. + logger + .log(expectedInfo) + .log(expectedError); + })); + + logger = winston.createLogger({ + transports: [transport] + }); + }); + + describe('Log Levels Enabled', function () { + it('default levels', function () { + let logger = winston.createLogger({ + level: 'verbose', + levels: winston.config.npm.levels, + transports: [new winston.transports.Console()] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isErrorEnabled).is.a('function'); + assume(logger.isWarnEnabled).is.a('function'); + assume(logger.isInfoEnabled).is.a('function'); + assume(logger.isVerboseEnabled).is.a('function'); + assume(logger.isDebugEnabled).is.a('function'); + assume(logger.isSillyEnabled).is.a('function'); + + assume(logger.isLevelEnabled('error')).true(); + assume(logger.isLevelEnabled('warn')).true(); + assume(logger.isLevelEnabled('info')).true(); + assume(logger.isLevelEnabled('verbose')).true(); + assume(logger.isLevelEnabled('debug')).false(); + assume(logger.isLevelEnabled('silly')).false(); + + assume(logger.isErrorEnabled()).true(); + assume(logger.isWarnEnabled()).true(); + assume(logger.isInfoEnabled()).true(); + assume(logger.isVerboseEnabled()).true(); + assume(logger.isDebugEnabled()).false(); + assume(logger.isSillyEnabled()).false(); + }); + + it('default levels, transport override', function () { + let transport = new winston.transports.Console(); + transport.level = 'debug'; + + let logger = winston.createLogger({ + level: 'info', + levels: winston.config.npm.levels, + transports: [transport] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isErrorEnabled).is.a('function'); + assume(logger.isWarnEnabled).is.a('function'); + assume(logger.isInfoEnabled).is.a('function'); + assume(logger.isVerboseEnabled).is.a('function'); + assume(logger.isDebugEnabled).is.a('function'); + assume(logger.isSillyEnabled).is.a('function'); + + assume(logger.isLevelEnabled('error')).true(); + assume(logger.isLevelEnabled('warn')).true(); + assume(logger.isLevelEnabled('info')).true(); + assume(logger.isLevelEnabled('verbose')).true(); + assume(logger.isLevelEnabled('debug')).true(); + assume(logger.isLevelEnabled('silly')).false(); + + assume(logger.isErrorEnabled()).true(); + assume(logger.isWarnEnabled()).true(); + assume(logger.isInfoEnabled()).true(); + assume(logger.isVerboseEnabled()).true(); + assume(logger.isDebugEnabled()).true(); + assume(logger.isSillyEnabled()).false(); + }); + + it('default levels, no transports', function () { + let logger = winston.createLogger({ + level: 'verbose', + levels: winston.config.npm.levels, + transports: [] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isErrorEnabled).is.a('function'); + assume(logger.isWarnEnabled).is.a('function'); + assume(logger.isInfoEnabled).is.a('function'); + assume(logger.isVerboseEnabled).is.a('function'); + assume(logger.isDebugEnabled).is.a('function'); + assume(logger.isSillyEnabled).is.a('function'); + + assume(logger.isLevelEnabled('error')).true(); + assume(logger.isLevelEnabled('warn')).true(); + assume(logger.isLevelEnabled('info')).true(); + assume(logger.isLevelEnabled('verbose')).true(); + assume(logger.isLevelEnabled('debug')).false(); + assume(logger.isLevelEnabled('silly')).false(); + + assume(logger.isErrorEnabled()).true(); + assume(logger.isWarnEnabled()).true(); + assume(logger.isInfoEnabled()).true(); + assume(logger.isVerboseEnabled()).true(); + assume(logger.isDebugEnabled()).false(); + assume(logger.isSillyEnabled()).false(); + }); + + it('custom levels', function () { + let logger = winston.createLogger({ + level: 'test', + levels: { + bad: 0, + test: 1, + ok: 2 + }, + transports: [new winston.transports.Console()] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isBadEnabled).is.a('function'); + assume(logger.isTestEnabled).is.a('function'); + assume(logger.isOkEnabled).is.a('function'); + + assume(logger.isLevelEnabled('bad')).true(); + assume(logger.isLevelEnabled('test')).true(); + assume(logger.isLevelEnabled('ok')).false(); + + assume(logger.isBadEnabled()).true(); + assume(logger.isTestEnabled()).true(); + assume(logger.isOkEnabled()).false(); + }); + + it('custom levels, no transports', function () { + let logger = winston.createLogger({ + level: 'test', + levels: { + bad: 0, + test: 1, + ok: 2 + }, + transports: [] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isBadEnabled).is.a('function'); + assume(logger.isTestEnabled).is.a('function'); + assume(logger.isOkEnabled).is.a('function'); + + assume(logger.isLevelEnabled('bad')).true(); + assume(logger.isLevelEnabled('test')).true(); + assume(logger.isLevelEnabled('ok')).false(); + + assume(logger.isBadEnabled()).true(); + assume(logger.isTestEnabled()).true(); + assume(logger.isOkEnabled()).false(); + }); + + it('custom levels, transport override', function () { + let transport = new winston.transports.Console(); + transport.level = 'ok'; + + let logger = winston.createLogger({ + level: 'bad', + levels: { + bad: 0, + test: 1, + ok: 2 + }, + transports: [transport] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isBadEnabled).is.a('function'); + assume(logger.isTestEnabled).is.a('function'); + assume(logger.isOkEnabled).is.a('function'); + + assume(logger.isLevelEnabled('bad')).true(); + assume(logger.isLevelEnabled('test')).true(); + assume(logger.isLevelEnabled('ok')).true(); + + assume(logger.isBadEnabled()).true(); + assume(logger.isTestEnabled()).true(); + assume(logger.isOkEnabled()).true(); + }); + }) + }); + + describe('Transport Events', function () { + it(`'finish' event awaits transports to emit 'finish'`, function (done) { + const transports = [ + new TransportStream({ + log: function () { + } + }), + new TransportStream({ + log: function () { + } + }), + new TransportStream({ + log: function () { + } + }) + ]; + + const finished = []; + const logger = winston.createLogger({transports}); + + // Assert each transport emits finish + transports.forEach((transport, i) => { + transport.on('finish', () => finished[i] = true); + }); + + // Manually end the last transport to simulate mixed + // finished state + transports[2].end(); + + // Assert that all transport 'finish' events have been + // emitted when the logger emits 'finish'. + logger.on('finish', function () { + assume(finished[0]).true(); + assume(finished[1]).true(); + assume(finished[2]).true(); + done(); + }); + + setImmediate(() => logger.end()); + }); + + it('error', (done) => { + const consoleTransport = new winston.transports.Console(); + const logger = winston.createLogger({ + transports: [consoleTransport] + }); + + logger.on('error', (err, transport) => { + assume(err).instanceOf(Error); + assume(transport).is.an('object'); + done(); + }); + consoleTransport.emit('error', new Error()); + }); + + it('warn', (done) => { + const consoleTransport = new winston.transports.Console(); + const logger = winston.createLogger({ + transports: [consoleTransport] + }); + + logger.on('warn', (err, transport) => { + assume(err).instanceOf(Error); + assume(transport).is.an('object'); + done(); + }); + consoleTransport.emit('warn', new Error()); + }); + }) + + describe('Formats', function () { + it(`rethrows errors from user-defined formats`, function () { + stdMocks.use(); + const logger = winston.createLogger({ + transports: [new winston.transports.Console()], + format: winston.format.printf((info) => { + // Set a trap. + if (info.message === 'ENDOR') { + throw new Error('ITS A TRAP!'); + } + + return info.message; + }) + }); + + // Trigger the trap. Swallow the error so processing continues. + try { + logger.info('ENDOR'); + } catch (err) { + assume(err.message).equals('ITS A TRAP!'); + } + + const expected = [ + 'Now witness the power of the fully armed and operational logger', + 'Consider the philosophical and metaphysical – BANANA BANANA BANANA', + 'I was god once. I saw – you were doing well until everyone died.' + ]; + + expected.forEach(msg => logger.info(msg)); + + stdMocks.restore(); + const actual = stdMocks.flush(); + assume(actual.stdout).deep.equals(expected.map(msg => `${msg}${EOL}`)); + assume(actual.stderr).deep.equals([]); + }); + }) + + describe('Profiling', function () { + it('ending profiler with object argument should be included in output', function (done) { + let logger = helpers.createLogger(function (info) { + assume(info).is.an('object'); + assume(info.something).equals('ok'); + assume(info.level).equals('info'); + assume(info.durationMs).is.a('number'); + assume(info.message).equals('testing1'); + assume(info[MESSAGE]).is.a('string'); + done(); + }); + + logger.profile('testing1'); + setTimeout(function () { + logger.profile('testing1', { + something: 'ok', + level: 'info' + }) + }, 100); + }); + + // TODO: Revisit if this is a valid test + it('calling profile with a callback function should not make a difference', function (done) { + let logger = helpers.createLogger(function (info) { + assume(info).is.an('object'); + assume(info.something).equals('ok'); + assume(info.level).equals('info'); + assume(info.durationMs).is.a('number'); + assume(info.message).equals('testing2'); + assume(info[MESSAGE]).is.a('string'); + done(); + }); + + logger.profile('testing2', function () { + done(new Error('Unexpected callback invoked')); + }); + + setTimeout(function () { + logger.profile('testing2', { + something: 'ok', + level: 'info' + }) + }, 100); + }); + + it('should stop a timer when `done` is called on it', function (done) { + let logger = helpers.createLogger(function (info) { + assume(info).is.an('object'); + assume(info.something).equals('ok'); + assume(info.level).equals('info'); + assume(info.durationMs).is.a('number'); + assume(info.message).equals('testing1'); + assume(info[MESSAGE]).is.a('string'); + done(); + }); + + let timer = logger.startTimer(); + setTimeout(function () { + timer.done({ + message: 'testing1', + something: 'ok', + level: 'info' + }); + }, 100); + }); + }); + + // TODO: Revisit to improve these + describe('Logging non-primitive data types', function () { + describe('.log', function () { + it(`.log(new Error()) uses Error instance as info`, function (done) { + const err = new Error('test'); + err.level = 'info'; + + const logger = helpers.createLogger(function (info) { + assume(info).instanceOf(Error); + assume(info).equals(err); + done(); + }); + + logger.log(err); + }); + + it(`.info('Hello') preserve meta without splat format`, function (done) { + const logged = []; + const logger = helpers.createLogger(function (info, enc, next) { + logged.push(info); + assume(info.label).equals('world'); + next(); + + if (logged.length === 1) done(); + }); + + logger.info('Hello', {label: 'world'}); + }); + + it(`.info('Hello %d') does not mutate unnecessarily with string interpolation tokens`, function (done) { + const logged = []; + const logger = helpers.createLogger(function (info, enc, next) { + logged.push(info); + assume(info.label).equals(undefined); + next(); + + if (logged.length === 1) done(); + }); + + logger.info('Hello %j', {label: 'world'}, {extra: true}); + }); + + it(`.info('Hello') and .info('Hello %d') preserve meta with splat format`, function (done) { + const logged = []; + const logger = helpers.createLogger(function (info, enc, next) { + logged.push(info); + assume(info.label).equals('world'); + next(); + + if (logged.length === 2) done(); + }, format.splat()); + + logger.info('Hello', {label: 'world'}); + logger.info('Hello %d', 100, {label: 'world'}); + }); + }); + + describe('.info', function () { + it('.info(undefined) creates info with { message: undefined }', function (done) { + const logger = helpers.createLogger(function (info) { + assume(info.message).equals(undefined); + done(); + }); + + logger.info(undefined); + }); + + it('.info(null) creates info with { message: null }', function (done) { + const logger = helpers.createLogger(function (info) { + assume(info.message).equals(null); + done(); + }); + + logger.info(null); + }); + + it('.info(new Error()) uses Error instance as info', function (done) { + const err = new Error('test'); + const logger = helpers.createLogger(function (info) { + assume(info).instanceOf(Error); + assume(info).equals(err); + done(); + }); + + logger.info(err); + }); + + // TODO: This test needs finished or removed + it.skip(`.info('any string', new Error())`, function (done) { + const err = new Error('test'); + const logger = helpers.createLogger(function (info) { + done(); + }); + + logger.info(err); + }); + }); + }); + + describe('Metadata Precedence', function () { + describe('Should support child loggers & defaultMeta', () => { + it('sets child meta for text messages correctly', (done) => { + const assertFn = ((msg) => { + assume(msg.level).equals('info'); + assume(msg.message).equals('dummy message'); + assume(msg.requestId).equals('451'); + done(); + }); + + const logger = winston.createLogger({ + transports: [ + mockTransport.createMockTransport(assertFn) + ] + }); + + const childLogger = logger.child({requestId: '451'}); + childLogger.info('dummy message'); + }); + + it('sets child meta for json messages correctly', (done) => { + const assertFn = ((msg) => { + assume(msg.level).equals('info'); + assume(msg.message.text).equals('dummy'); + assume(msg.requestId).equals('451'); + done(); + }); + + const logger = winston.createLogger({ + transports: [ + mockTransport.createMockTransport(assertFn) + ] + }); + + const childLogger = logger.child({requestId: '451'}); + childLogger.info({text: 'dummy'}); + }); + + it('merges child and provided meta correctly', (done) => { + const assertFn = ((msg) => { + assume(msg.level).equals('info'); + assume(msg.message).equals('dummy message'); + assume(msg.service).equals('user-service'); + assume(msg.requestId).equals('451'); + done(); + }); + + const logger = winston.createLogger({ + transports: [ + mockTransport.createMockTransport(assertFn) + ] + }); + + const childLogger = logger.child({service: 'user-service'}); + childLogger.info('dummy message', {requestId: '451'}); + }); + + it('provided meta take precedence over defaultMeta', (done) => { + const assertFn = ((msg) => { + assume(msg.level).equals('info'); + assume(msg.message).equals('dummy message'); + assume(msg.service).equals('audit-service'); + assume(msg.requestId).equals('451'); + done(); + }); + + const logger = winston.createLogger({ + defaultMeta: {service: 'user-service'}, + transports: [ + mockTransport.createMockTransport(assertFn) + ] + }); + + logger.info('dummy message', { + requestId: '451', + service: 'audit-service' + }); + }); + + it('provided meta take precedence over child meta', (done) => { + const assertFn = ((msg) => { + assume(msg.level).equals('info'); + assume(msg.message).equals('dummy message'); + assume(msg.service).equals('audit-service'); + assume(msg.requestId).equals('451'); + done(); + }); + + const logger = winston.createLogger({ + transports: [ + mockTransport.createMockTransport(assertFn) + ] + }); + + const childLogger = logger.child({service: 'user-service'}); + childLogger.info('dummy message', { + requestId: '451', + service: 'audit-service' + }); + }); + + it('handles error stack traces in child loggers correctly', (done) => { + const assertFn = ((msg) => { + assume(msg.level).equals('error'); + assume(msg.message).equals('dummy error'); + assume(msg.stack).includes('logger.test.js'); + assume(msg.service).equals('user-service'); + done(); + }); + + const logger = winston.createLogger({ + transports: [ + mockTransport.createMockTransport(assertFn) + ] + }); + + const childLogger = logger.child({service: 'user-service'}); + childLogger.error(Error('dummy error')); + }); + + it('defaultMeta() autobinds correctly', (done) => { + const logger = helpers.createLogger(info => { + assume(info.message).equals('test'); + done(); + }); + + const log = logger.info; + log('test'); + }); + }); + }); + + describe('Backwards Compatability', function () { + describe('Winston V2 Log', function () { + it('.log(level, message)', function (done) { + let logger = helpers.createLogger(function (info) { + assume(info).is.an('object'); + assume(info.level).equals('info'); + assume(info.message).equals('Some super awesome log message'); + assume(info[MESSAGE]).is.a('string'); + done(); + }); + + logger.log('info', 'Some super awesome log message') + }); + + it(`.log(level, undefined) creates info with { message: undefined }`, function (done) { + const logger = helpers.createLogger(function (info) { + assume(info.message).equals(undefined); + done(); + }); + + logger.log('info', undefined); + }); + + it(`.log(level, null) creates info with { message: null }`, function (done) { + const logger = helpers.createLogger(function (info) { + assume(info.message).equals(null); + done(); + }); + + logger.log('info', null); + }); + + it(`.log(level, new Error()) uses Error instance as info`, function (done) { + const err = new Error('test'); + const logger = helpers.createLogger(function (info) { + assume(info).instanceOf(Error); + assume(info).equals(err); + done(); + }); + + logger.log('info', err); + }); + + it('.log(level, message, meta)', function (done) { + let meta = {one: 2}; + let logger = helpers.createLogger(function (info) { + assume(info).is.an('object'); + assume(info.level).equals('info'); + assume(info.message).equals('Some super awesome log message'); + assume(info.one).equals(2); + assume(info[MESSAGE]).is.a('string'); + done(); + }); + + logger.log('info', 'Some super awesome log message', meta); + }); + + it('.log(level, formatStr, ...splat)', function (done) { + const format = winston.format.combine( + winston.format.splat(), + winston.format.printf(info => `${info.level}: ${info.message}`) + ); + + let logger = helpers.createLogger(function (info) { + assume(info).is.an('object'); + assume(info.level).equals('info'); + assume(info.message).equals('100% such wow {"much":"javascript"}'); + assume(info[SPLAT]).deep.equals([100, 'wow', {much: 'javascript'}]); + assume(info[MESSAGE]).equals('info: 100% such wow {"much":"javascript"}'); + done(); + }, format); + + logger.log('info', '%d%% such %s %j', 100, 'wow', {much: 'javascript'}); + }); + + it('.log(level, formatStr, ...splat, meta)', function (done) { + const format = winston.format.combine( + winston.format.splat(), + winston.format.printf(info => `${info.level}: ${info.message} ${JSON.stringify({thisIsMeta: info.thisIsMeta})}`) + ); + + let logger = helpers.createLogger(function (info) { + assume(info).is.an('object'); + assume(info.level).equals('info'); + assume(info.message).equals('100% such wow {"much":"javascript"}'); + assume(info[SPLAT]).deep.equals([100, 'wow', {much: 'javascript'}]); + assume(info.thisIsMeta).true(); + assume(info[MESSAGE]).equals('info: 100% such wow {"much":"javascript"} {"thisIsMeta":true}'); + done(); + }, format); + + logger.log('info', '%d%% such %s %j', 100, 'wow', {much: 'javascript'}, {thisIsMeta: true}); + }); + }); + }); +}); diff --git a/test/profiler.test.js b/test/unit/winston/profiler.test.js similarity index 89% rename from test/profiler.test.js rename to test/unit/winston/profiler.test.js index 6983e0af4..b0f86e95b 100644 --- a/test/profiler.test.js +++ b/test/unit/winston/profiler.test.js @@ -7,7 +7,7 @@ */ const assume = require('assume'); -const Profiler = require('../lib/winston/profiler'); +const Profiler = require('../../../lib/winston/profiler'); describe('Profiler', function () { it('new Profiler()', function () { @@ -19,7 +19,7 @@ describe('Profiler', function () { it('.done({ info })', function (done) { var profiler = new Profiler({ write: function (info) { - assume(info).is.an('object'), + assume(info).is.an('object'); assume(info.something).equals('ok'); assume(info.level).equals('info'); assume(info.durationMs).is.a('number'); diff --git a/test/rejection-handler.test.js b/test/unit/winston/rejection-handler.test.js similarity index 97% rename from test/rejection-handler.test.js rename to test/unit/winston/rejection-handler.test.js index aef41b1aa..dd2147d8a 100644 --- a/test/rejection-handler.test.js +++ b/test/unit/winston/rejection-handler.test.js @@ -9,8 +9,8 @@ const stream = require('stream'); const assume = require('assume'); const mocha = require('mocha'); -const winston = require('../lib/winston'); -const helpers = require('./helpers'); +const winston = require('../../../lib/winston'); +const helpers = require('../../helpers'); // // This is an awful and fragile hack that diff --git a/test/tail-file.test.js b/test/unit/winston/tail-file.test.js similarity index 88% rename from test/tail-file.test.js rename to test/unit/winston/tail-file.test.js index a007d35f8..030b0f85d 100644 --- a/test/tail-file.test.js +++ b/test/unit/winston/tail-file.test.js @@ -9,9 +9,10 @@ const assume = require('assume'); const fs = require('fs'); const path = require('path'); -const winston = require('../lib/winston'); -const tailFile = require('../lib/winston/tail-file'); +const winston = require('../../../lib/winston'); +const tailFile = require('../../../lib/winston/tail-file'); const { Stream } = require('readable-stream'); +const testLogFixturesPath = path.join(__dirname, '..', '..', 'fixtures', 'logs'); // // Test helper that performs writes to a specific log file @@ -60,7 +61,7 @@ describe('tailFile', function () { }); it('returns a stream that emits "line" for every line', function (done) { - var tailable = path.join(__dirname, 'fixtures', 'logs', 'common-tail-file.log'); + var tailable = path.join(testLogFixturesPath, 'common-tail-file.log'); var expected = 0; // // Performs the actual tail and asserts it. diff --git a/test/transports/00-file-stress.test.js b/test/unit/winston/transports/00-file-stress.test.js similarity index 88% rename from test/transports/00-file-stress.test.js rename to test/unit/winston/transports/00-file-stress.test.js index e8740d571..55cae623d 100644 --- a/test/transports/00-file-stress.test.js +++ b/test/unit/winston/transports/00-file-stress.test.js @@ -12,17 +12,17 @@ const fs = require('fs'); const os = require('os'); const path = require('path'); const assume = require('assume'); -const helpers = require('../helpers'); +const helpers = require('../../../helpers'); const split = require('split2'); -const winston = require('../../lib/winston'); +const winston = require('../../../../lib/winston'); describe('File (stress)', function () { this.timeout(30 * 1000); - const logPath = path.resolve(__dirname, '../fixtures/logs/file-stress-test.log'); + const fileStressLogFile = path.resolve(__dirname, '../../../fixtures/logs/file-stress-test.log'); beforeEach(function () { try { - fs.unlinkSync(logPath); + fs.unlinkSync(fileStressLogFile); } catch (ex) { if (ex && ex.code !== 'ENOENT') { return done(ex); } } @@ -31,7 +31,7 @@ describe('File (stress)', function () { it('should handle a high volume of writes', function (done) { const logger = winston.createLogger({ transports: [new winston.transports.File({ - filename: logPath + filename: fileStressLogFile })] }); @@ -47,7 +47,7 @@ describe('File (stress)', function () { setTimeout(function () { clearInterval(interval); - helpers.tryRead(logPath) + helpers.tryRead(fileStressLogFile) .on('error', function (err) { assume(err).false(); logger.close(); @@ -70,7 +70,7 @@ describe('File (stress)', function () { it('should handle a high volume of large writes', function (done) { const logger = winston.createLogger({ transports: [new winston.transports.File({ - filename: logPath + filename: fileStressLogFile })] }); @@ -90,7 +90,7 @@ describe('File (stress)', function () { setTimeout(function () { clearInterval(interval); - helpers.tryRead(logPath) + helpers.tryRead(fileStressLogFile) .on('error', function (err) { assume(err).false(); logger.close(); @@ -114,7 +114,7 @@ describe('File (stress)', function () { it('should handle a high volume of large writes synchronous', function (done) { const logger = winston.createLogger({ transports: [new winston.transports.File({ - filename: logPath + filename: fileStressLogFile })] }); @@ -130,7 +130,7 @@ describe('File (stress)', function () { msgs.forEach(msg => logger.info(msg)); setTimeout(function () { - helpers.tryRead(logPath) + helpers.tryRead(fileStressLogFile) .on('error', function (err) { assume(err).false(); logger.close(); diff --git a/test/transports/01-file-maxsize.test.js b/test/unit/winston/transports/01-file-maxsize.test.js similarity index 91% rename from test/transports/01-file-maxsize.test.js rename to test/unit/winston/transports/01-file-maxsize.test.js index f098e9c96..01ca88076 100644 --- a/test/transports/01-file-maxsize.test.js +++ b/test/unit/winston/transports/01-file-maxsize.test.js @@ -9,7 +9,8 @@ const rimraf = require('rimraf'); const fs = require('fs'); const path = require('path'); const assume = require('assume'); -const winston = require('../../'); +const winston = require('../../../../lib/winston'); +const testLogFixturesPath = path.join(__dirname, '..', '..', '..', 'fixtures', 'logs'); const MESSAGE = Symbol.for('message'); @@ -17,7 +18,7 @@ const MESSAGE = Symbol.for('message'); // Remove all log fixtures // function removeFixtures(done) { - rimraf(path.join(__dirname, '..', 'fixtures', 'logs', 'testmaxsize*'), done); + rimraf(path.join(testLogFixturesPath, 'testmaxsize*'), done); } describe('File (maxsize)', function () { @@ -35,7 +36,7 @@ describe('File (maxsize)', function () { const maxsizeTransport = new winston.transports.File({ level: 'info', format: winston.format.printf(info => info.message), - filename: path.join(__dirname, '..', 'fixtures', 'logs', 'testmaxsize.log'), + filename: path.join(testLogFixturesPath, 'testmaxsize.log'), maxsize: 4096 }) diff --git a/test/transports/console.test.js b/test/unit/winston/transports/console.test.js similarity index 97% rename from test/transports/console.test.js rename to test/unit/winston/transports/console.test.js index 61b5bccd3..b6f4aeed5 100644 --- a/test/transports/console.test.js +++ b/test/unit/winston/transports/console.test.js @@ -11,8 +11,8 @@ const path = require('path'); const assume = require('assume'); const { LEVEL, MESSAGE } = require('triple-beam'); -const winston = require('../../lib/winston'); -const helpers = require('../helpers'); +const winston = require('../../../../lib/winston'); +const helpers = require('../../../helpers'); const stdMocks = require('std-mocks'); const defaultLevels = winston.config.npm.levels; @@ -139,6 +139,7 @@ require('abstract-winston-transport')({ Transport: winston.transports.Console }); +// TODO(invalid-test): test is no longer valid as we don't have the vows dependency anymore // vows.describe('winston/transports/console').addBatch({ // "An instance of the Console Transport": { // "with syslog levels": { diff --git a/test/transports/error.test.js b/test/unit/winston/transports/error.test.js similarity index 98% rename from test/transports/error.test.js rename to test/unit/winston/transports/error.test.js index b1c3f54c9..05468a3bf 100644 --- a/test/transports/error.test.js +++ b/test/unit/winston/transports/error.test.js @@ -1,4 +1,4 @@ -const winston = require('../../lib/winston'); +const winston = require('../../../../lib/winston'); const assume = require('assume'); // https://github.com/winstonjs/winston/issues/1364 diff --git a/test/transports/file-archive.test.js b/test/unit/winston/transports/file-archive.test.js similarity index 88% rename from test/transports/file-archive.test.js rename to test/unit/winston/transports/file-archive.test.js index 801432775..fc7cafcfd 100644 --- a/test/transports/file-archive.test.js +++ b/test/unit/winston/transports/file-archive.test.js @@ -11,7 +11,8 @@ const assert = require('assert'); const rimraf = require('rimraf'); const fs = require('fs'); const path = require('path'); -const winston = require('../../lib/winston'); +const winston = require('../../../../lib/winston'); +const testLogFixturesPath = path.join(__dirname, '..', '..', '..', 'fixtures', 'logs'); const { MESSAGE } = require('triple-beam'); @@ -19,7 +20,7 @@ const { MESSAGE } = require('triple-beam'); // Remove all log fixtures // function removeFixtures(done) { - rimraf(path.join(__dirname, '..', 'fixtures', 'logs', 'testarchive*'), done); + rimraf(path.join(testLogFixturesPath, 'testarchive*'), done); } @@ -37,7 +38,7 @@ describe('winston/transports/file/zippedArchive', function () { zippedArchive: true, tailable: true, filename: 'testarchive.log', - dirname: path.join(__dirname, '..', 'fixtures', 'logs'), + dirname: testLogFixturesPath, maxsize: 4096, maxFiles: 3 }); @@ -76,7 +77,7 @@ describe('winston/transports/file/zippedArchive', function () { it('should be only 3 files called testarchive.log, testarchive1.log.gz and testarchive2.log.gz', function () { for (var num = 0; num < 4; num++) { const file = !num ? 'testarchive.log' : 'testarchive' + num + '.log.gz'; - const fullpath = path.join(__dirname, '..', 'fixtures', 'logs', file); + const fullpath = path.join(testLogFixturesPath, file); if (num === 3) { return assert.throws(function () { diff --git a/test/transports/file-create-dir-test.js b/test/unit/winston/transports/file-create-dir.test.js similarity index 73% rename from test/transports/file-create-dir-test.js rename to test/unit/winston/transports/file-create-dir.test.js index 82407ce33..b13efc1ec 100644 --- a/test/transports/file-create-dir-test.js +++ b/test/unit/winston/transports/file-create-dir.test.js @@ -3,15 +3,21 @@ const fs = require('fs'); const assert = require('assert'); const path = require('path'); -const winston = require('../../lib/winston'); +const winston = require('../../../../lib/winston'); +const rimraf = require("rimraf"); /* eslint-disable no-sync */ describe('winston/transports/file/createLogDir', function () { - const logDir = path.resolve(__dirname, '../fixtures/temp_logs'); + const logDir = path.resolve(__dirname, '../../../fixtures/temp_logs'); beforeEach(function () { - fs.rmdirSync(logDir); + rimraf(logDir, (err) => { + if (err){ + console.log('Error encountered when removing `temp_logs` dir') + console.log(err); + } + }) }); it('should create directory if it does not exist', function () { diff --git a/test/transports/file-maxfiles-test.js b/test/unit/winston/transports/file-maxfiles.test.js similarity index 79% rename from test/transports/file-maxfiles-test.js rename to test/unit/winston/transports/file-maxfiles.test.js index b7448b5c3..4422aad91 100644 --- a/test/transports/file-maxfiles-test.js +++ b/test/unit/winston/transports/file-maxfiles.test.js @@ -1,5 +1,5 @@ /* - * file-maxfiles-test.js: Tests for instances of the File transport setting the max file size, + * file-maxfiles.test.js: Tests for instances of the File transport setting the max file size, * and setting a number for max files created. * maxSize * maxFiles = total storage used by winston. * @@ -8,18 +8,21 @@ * */ +/* + TODO(invalid-test): test is no longer valid as we don't have the vows dependency anymore var assert = require('assert'), exec = require('child_process').exec, fs = require('fs'), path = require('path'), vows = require('vows'), - winston = require('../../lib/winston'), - helpers = require('../helpers'); + winston = require('../../../../lib/winston'), + helpers = require('../../../helpers'); +const testLogFixturesPath = path.join(__dirname, '..', '..', '..', 'fixtures', 'logs'); var maxfilesTransport = new winston.transports.File({ timestamp: false, json: false, - filename: path.join(__dirname, '..', 'fixtures', 'logs', 'testmaxfiles.log'), + filename: path.join(testLogFixturesPath, 'testmaxfiles.log'), maxsize: 4096, maxFiles: 3 }); @@ -34,7 +37,7 @@ vows.describe('winston/transports/file/maxfiles').addBatch({ }, "when delete old test files": { topic: function () { - exec('rm -rf ' + path.join(__dirname, '..', 'fixtures', 'logs', 'testmaxfiles*'), this.callback); + exec('rm -rf ' + path.join(testLogFixturesPath, 'testmaxfiles*'), this.callback); }, "and when passed more files than the maxFiles": { topic: function () { @@ -69,7 +72,7 @@ vows.describe('winston/transports/file/maxfiles').addBatch({ "should be only 3 files called 5.log, 4.log and 3.log": function () { for (var num = 0; num < 6; num++) { var file = !num ? 'testmaxfiles.log' : 'testmaxfiles' + num + '.log', - fullpath = path.join(__dirname, '..', 'fixtures', 'logs', file); + fullpath = path.join(testLogFixturesPath, file); // There should be no files with that name if (num >= 0 && num < 3) { @@ -87,8 +90,7 @@ vows.describe('winston/transports/file/maxfiles').addBatch({ "should have the correct content": function () { ['D', 'E', 'F'].forEach(function (name, inx) { var counter = inx + 3, - logsDir = path.join(__dirname, '..', 'fixtures', 'logs'), - content = fs.readFileSync(path.join(logsDir, 'testmaxfiles' + counter + '.log'), 'utf-8'); + content = fs.readFileSync(path.join(testLogFixturesPath, 'testmaxfiles' + counter + '.log'), 'utf-8'); // The content minus the 7 characters added by winston assert.lengthOf(content.match(new RegExp(name, 'g')), 4068); }); @@ -97,3 +99,4 @@ vows.describe('winston/transports/file/maxfiles').addBatch({ } } }).export(module); +*/ diff --git a/test/transports/file-tailrolling.test.js b/test/unit/winston/transports/file-tailrolling.test.js similarity index 84% rename from test/transports/file-tailrolling.test.js rename to test/unit/winston/transports/file-tailrolling.test.js index f07e33a67..503a8efb6 100644 --- a/test/transports/file-tailrolling.test.js +++ b/test/unit/winston/transports/file-tailrolling.test.js @@ -3,7 +3,8 @@ const assert = require('assert'); const rimraf = require('rimraf'); const fs = require('fs'); const path = require('path'); -const winston = require('../../lib/winston'); +const winston = require('../../../../lib/winston'); +const testLogFixturesPath = path.join(__dirname, '..', '..', '..', 'fixtures', 'logs'); const { MESSAGE } = require('triple-beam'); @@ -11,7 +12,7 @@ const { MESSAGE } = require('triple-beam'); // Remove all log fixtures // function removeFixtures(done) { - rimraf(path.join(__dirname, '..', 'fixtures', 'logs', 'testtailrollingfiles*'), done); + rimraf(path.join(testLogFixturesPath, 'testtailrollingfiles*'), done); } @@ -27,7 +28,7 @@ describe('winston/transports/file/tailrolling', function () { tailrollTransport = new winston.transports.File({ timestamp: false, json: false, - filename: path.join(__dirname, '..', 'fixtures', 'logs', 'testtailrollingfiles.log'), + filename: path.join(testLogFixturesPath, 'testtailrollingfiles.log'), maxsize: 4096, maxFiles: 3, tailable: true @@ -68,7 +69,7 @@ describe('winston/transports/file/tailrolling', function () { it('should be 3 log files, base to maxFiles - 1', function () { for (var num = 0; num < 4; num++) { const file = !num ? 'testtailrollingfiles.log' : 'testtailrollingfiles' + num + '.log'; - const fullpath = path.join(__dirname, '..', 'fixtures', 'logs', file); + const fullpath = path.join(testLogFixturesPath, file); if (num === 3) { return assert.ok(!fs.existsSync(fullpath)); @@ -83,7 +84,7 @@ describe('winston/transports/file/tailrolling', function () { it('should have files in correct order', function () { ['D', 'C', 'B'].forEach(function (letter, i) { const file = !i ? 'testtailrollingfiles.log' : 'testtailrollingfiles' + i + '.log'; - let content = fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'logs', file), 'ascii'); + let content = fs.readFileSync(path.join(testLogFixturesPath, file), 'ascii'); content = content.replace(/\s+/g, ''); assert(content.match(new RegExp(letter, 'g'))[0].length, content.length); diff --git a/test/transports/file.test.js b/test/unit/winston/transports/file.test.js similarity index 89% rename from test/transports/file.test.js rename to test/unit/winston/transports/file.test.js index 3847cd0ad..7d892f33e 100644 --- a/test/transports/file.test.js +++ b/test/unit/winston/transports/file.test.js @@ -1,12 +1,13 @@ 'use strict'; const path = require('path'); -const winston = require('../../'); -const helpers = require('../helpers'); +const winston = require('../../../../lib/winston'); +const helpers = require('../../../helpers'); const fs = require('fs'); const { MESSAGE } = require('triple-beam'); const split = require('split2'); const assume = require('assume'); +const testFileFixturesPath = path.join(__dirname, '..', '..', '..', 'fixtures', 'file'); function noop() {}; @@ -14,7 +15,7 @@ describe('File({ filename })', function () { this.timeout(10 * 1000); it('should write to the file when logged to with expected object', function (done) { - var filename = path.join(__dirname, '..', 'fixtures', 'file', 'simple.log'); + var filename = path.join(testFileFixturesPath, 'simple.log'); var transport = new winston.transports.File({ filename: filename }); @@ -96,7 +97,7 @@ describe('File({ filename })', function () { describe('File({ stream })', function () { it('should display the deprecation notice'); it('should write to the stream when logged to with expected object', function (done) { - var streamfile = path.join(__dirname, '..', 'fixtures', 'file', 'simple-stream.log'); + var streamfile = path.join(testFileFixturesPath, 'simple-stream.log'); var stream = fs.createWriteStream(streamfile); var streamTransport = new winston.transports.File({ stream: stream @@ -113,7 +114,7 @@ require('abstract-winston-transport')({ name: 'File', Transport: winston.transports.File, construct: { - filename: path.join(__dirname, '..', 'fixtures', 'file', 'abstract.log') + filename: path.join(testFileFixturesPath, 'abstract.log') }, after(opts, done) { const abstractFile = opts.construct.filename; diff --git a/test/transports/http.test.js b/test/unit/winston/transports/http.test.js similarity index 98% rename from test/transports/http.test.js rename to test/unit/winston/transports/http.test.js index a2b641b8e..3074811cc 100644 --- a/test/transports/http.test.js +++ b/test/unit/winston/transports/http.test.js @@ -7,7 +7,7 @@ const http = require('http'); const hock = require('hock'); const assume = require('assume'); -const Http = require('../../lib/winston/transports/http'); +const Http = require('../../../../lib/winston/transports/http'); const stringifyJson = require('safe-stable-stringify'); const host = '127.0.0.1'; diff --git a/test/transports/stream.test.js b/test/unit/winston/transports/stream.test.js similarity index 91% rename from test/transports/stream.test.js rename to test/unit/winston/transports/stream.test.js index e0efbd2fd..6f75d5cdd 100644 --- a/test/transports/stream.test.js +++ b/test/unit/winston/transports/stream.test.js @@ -1,10 +1,10 @@ 'use strict'; const path = require('path'); -const writeable = require('../helpers').writeable; +const writeable = require('../../../helpers').writeable; const { MESSAGE } = require('triple-beam'); const os = require('os'); -const winston = require('../../'); +const winston = require('../../../../lib/winston'); const split = require('split2'); const assume = require('assume');