| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671 | 'use strict';const assume = require('assume');const { format } = require('logform');const Writable = require('readable-stream/writable');const TransportStream = require('../');const Parent = require('./fixtures/parent');const { testLevels, testOrder } = require('./fixtures');const {  infosFor,  logFor,  levelAndMessage,  toException,  toWriteReq} = require('abstract-winston-transport/utils');const { LEVEL, MESSAGE } = require('triple-beam');/* * Returns the provided `info` object with the appropriate LEVEL, * and MESSAGE symbols defined. */function infoify(info) {  info[LEVEL] = info.level;  info[MESSAGE] = info.message;  return info;};describe('TransportStream', () => {  it('should have the appropriate methods defined', () => {    const transport = new TransportStream();    assume(transport).instanceof(Writable);    assume(transport._write).is.a('function');    // eslint-disable-next-line no-undefined    assume(transport.log).equals(undefined);  });  it('should accept a custom log function invoked on _write', () => {    const log = logFor(1);    const transport = new TransportStream({ log });    assume(transport.log).equals(log);  });  it('should invoke a custom log function on _write', done => {    const info = {      [LEVEL]: 'test',      level: 'test',      message: 'Testing ... 1 2 3.'    };    const transport = new TransportStream({      log(actual) {        assume(actual).equals(info);        done();      }    });    transport.write(info);  });  describe('_write(info, enc, callback)', () => {    it('should log to any level when { level: undefined }', done => {      const expected = testOrder.map(levelAndMessage);      const transport = new TransportStream({        log: logFor(testOrder.length, (err, infos) => {          if (err) {            return done(err);          }          assume(infos.length).equals(expected.length);          assume(infos).deep.equals(expected);          done();        })      });      expected.forEach(transport.write.bind(transport));    });    it('should not log when no info object is provided', done => {      const expected = testOrder.map(levelAndMessage).map((info, i) => {        if (testOrder.length > (i + 1)) {          info.private = true;        }        return info;      });      const transport = new TransportStream({        format: format(info => {          if (info.private) return false;          return info;        })(),        log: logFor(1, (err, infos) => {          if (err) {            return done(err);          }          assume(infos.length).equals(1);          assume(infos.pop()).deep.equals(expected.pop());          done();        })      });      expected.forEach(transport.write.bind(transport));    });    it('should only log messages BELOW the level priority', done => {      const expected = testOrder.map(levelAndMessage);      const transport = new TransportStream({        level: 'info',        log: logFor(5, (err, infos) => {          if (err) {            return done(err);          }          assume(infos.length).equals(5);          assume(infos).deep.equals(expected.slice(0, 5));          done();        })      });      transport.levels = testLevels;      expected.forEach(transport.write.bind(transport));    });    it('{ level } should be ignored when { handleExceptions: true }', () => {      const expected = testOrder.map(levelAndMessage).map(info => {        info.exception = true;        return info;      });      const transport = new TransportStream({        level: 'info',        log: logFor(testOrder.length, (err, infos) => {          // eslint-disable-next-line no-undefined          assume(err).equals(undefined);          assume(infos.length).equals(expected.length);          assume(infos).deep.equals(expected);        })      });      transport.levels = testLevels;      expected.forEach(transport.write.bind(transport));    });    describe('when { exception: true } in info', () => {      it('should not invoke log when { handleExceptions: false }', done => {        const expected = [{          exception: true,          [LEVEL]: 'error',          level: 'error',          message: 'Test exception handling'        }, {          [LEVEL]: 'test',          level: 'test',          message: 'Testing ... 1 2 3.'        }];        const transport = new TransportStream({          log(info) {            // eslint-disable-next-line no-undefined            assume(info.exception).equals(undefined);            done();          }        });        expected.forEach(transport.write.bind(transport));      });      it('should invoke log when { handleExceptions: true }', done => {        const actual = [];        const expected = [{          exception: true, [LEVEL]: 'error',          level: 'error',          message: 'Test exception handling'        }, {          [LEVEL]: 'test',          level: 'test',          message: 'Testing ... 1 2 3.'        }];        const transport = new TransportStream({          handleExceptions: true,          log(info, next) {            actual.push(info);            if (actual.length === expected.length) {              assume(actual).deep.equals(expected);              return done();            }            next();          }        });        expected.forEach(transport.write.bind(transport));      });    });  });  describe('_writev(chunks, callback)', () => {    it('invokes .log() for each of the valid chunks when necessary in streams plumbing', done => {      const expected = infosFor({        count: 50,        levels: testOrder      });      const transport = new TransportStream({        log: logFor(50 * testOrder.length, (err, infos) => {          if (err) {            return done(err);          }          assume(infos.length).equals(expected.length);          assume(infos).deep.equals(expected);          done();        })      });      //      // Make the standard _write throw to ensure that _writev is called.      //      transport._write = () => {        throw new Error('TransportStream.prototype._write should never be called.');      };      transport.cork();      expected.forEach(transport.write.bind(transport));      transport.uncork();    });    it('should not log when no info object is provided in streams plumbing', done => {      const expected = testOrder.map(levelAndMessage).map((info, i) => {        if (testOrder.length > (i + 1)) {          info.private = true;        }        return info;      });      const transport = new TransportStream({        format: format(info => {          if (info.private) {            return false;          }          return info;        })(),        log: logFor(1, (err, infos) => {          if (err) {            return done(err);          }          assume(infos.length).equals(1);          assume(infos.pop()).deep.equals(expected.pop());          done();        })      });      //      // Make the standard _write throw to ensure that _writev is called.      //      transport._write = () => {        throw new Error('TransportStream.prototype._write should never be called.');      };      transport.cork();      expected.forEach(transport.write.bind(transport));      transport.uncork();    });    it('ensures a format is applied to each info when no .logv is defined', done => {      const expected = infosFor({ count: 10, levels: testOrder });      const transport = new TransportStream({        format: format.json(),        log: logFor(10 * testOrder.length, (err, infos) => {          if (err) {            return done(err);          }          assume(infos.length).equals(expected.length);          infos.forEach((info, i) => {            assume(info[MESSAGE]).equals(JSON.stringify(expected[i]));          });          done();        })      });      //      // Make the standard _write throw to ensure that _writev is called.      //      transport._write = () => {        throw new Error('TransportStream.prototype._write should never be called.');      };      transport.cork();      expected.forEach(transport.write.bind(transport));      transport.uncork();    });    it('invokes .logv with all valid chunks when necessary in streams plumbing', done => {      const expected = infosFor({        count: 50,        levels: testOrder      });      const transport = new TransportStream({        level: 'info',        log() {          throw new Error('.log() should never be called');        },        logv(chunks, callback) {          assume(chunks.length).equals(250);          callback(); // eslint-disable-line callback-return          done();        }      });      //      // Make the standard _write throw to ensure that _writev is called.      //      transport._write = () => {        throw new Error('TransportStream.prototype._write should never be called.');      };      transport.cork();      transport.levels = testLevels;      expected.forEach(transport.write.bind(transport));      transport.uncork();    });  });  describe('parent (i.e. "logger") ["pipe", "unpipe"]', () => {    it('should define { level, levels } on "pipe"', done => {      const parent = new Parent({        level: 'info',        levels: testLevels      });      const transport = new TransportStream({        log(info, next) {          assume(info.level).equals('info');          assume(info.message).equals('ok sure');          next();          done();        }      });      parent.pipe(transport);      setImmediate(() => {        assume(transport.level).equals(undefined);        assume(transport.levels).equals(testLevels);        assume(transport.parent).equals(parent);        assume(transport.parent.level).equals('info');        transport.write(infoify({ level: 'parrot', message: 'never logged' }));        transport.write(infoify({ level: 'info', message: 'ok sure' }));      });    });    it('should not overwrite existing { level } on "pipe"', done => {      const parent = new Parent({        level: 'info',        levels: testLevels      });      const transport = new TransportStream({        level: 'error',        log(info, next) {          assume(info.level).equals('error');          assume(info.message).equals('ok sure');          next();          done();        }      });      parent.pipe(transport);      setImmediate(() => {        assume(transport.level).equals('error');        assume(transport.levels).equals(testLevels);        assume(transport.parent).equals(parent);        transport.write(infoify({ level: 'info', message: 'never logged' }));        transport.write(infoify({ level: 'error', message: 'ok sure' }));      });    });    it('should respond to changes in parent logging level', done => {      const parent = new Parent({        level: 'error',        levels: testLevels      });      const transport = new TransportStream({        log(info, next) {          assume(info.level).equals('parrot');          assume(info.message).equals('eventually log this');          next();          done();        }      });      parent.pipe(transport);      setImmediate(() => {        assume(transport.levels).equals(testLevels);        assume(transport.parent).equals(parent);        transport.write(infoify({ level: 'info', message: 'never logged' }));        parent.level = 'parrot';        transport.write(infoify({ level: 'parrot', message: 'eventually log this' }));      });    });    it('should unset parent on "unpipe"', done => {      const parent = new Parent({        level: 'info',        levels: testLevels      });      const transport = new TransportStream({        level: 'error',        log() {}      });      //      // Trigger "pipe" first so that transport.parent is set.      //      parent.pipe(transport);      setImmediate(() => {        assume(transport.parent).equals(parent);        //        // Now verify that after "unpipe" it is set to 'null'.        //        parent.unpipe(transport);        setImmediate(() => {          assume(transport.parent).equals(null);          done();        });      });    });    it('should invoke a close method on "unpipe"', done => {      const parent = new Parent({        level: 'info',        levels: testLevels      });      const transport = new TransportStream({        log() {}      });      //      // Test will only successfully complete when `close`      // is invoked      //      transport.close = () => {        assume(transport.parent).equals(null);        done();      };      //      // Trigger "pipe" first so that transport.parent is set.      //      parent.pipe(transport);      setImmediate(() => {        assume(transport.parent).equals(parent);        parent.unpipe(transport);      });    });  });  describe('_accept(info)', function () {    it('should filter only log messages BELOW the level priority', () => {      const expected = testOrder        .map(levelAndMessage)        .map(toWriteReq);      const transport = new TransportStream({        level: 'info'      });      transport.levels = testLevels;      const filtered = expected.filter(transport._accept, transport)        .map(write => write.chunk.level);      assume(filtered).deep.equals([        'error',        'warn',        'dog',        'cat',        'info'      ]);    });    it('should filter out { exception: true } when { handleExceptions: false }', () => {      const expected = testOrder        .map(toException)        .map(toWriteReq);      const transport = new TransportStream({        handleExceptions: false,        level: 'info'      });      transport.levels = testLevels;      const filtered = expected.filter(transport._accept, transport)        .map(info => info.level);      assume(filtered).deep.equals([]);    });    it('should include ALL { exception: true } when { handleExceptions: true }', () => {      const expected = testOrder        .map(toException)        .map(toWriteReq);      const transport = new TransportStream({        handleExceptions: true,        level: 'info'      });      transport.levels = testLevels;      const filtered = expected.filter(transport._accept, transport)        .map(write => write.chunk.level);      assume(filtered).deep.equals(testOrder);    });  });  describe('{ format }', function () {    it('logs the output of the provided format', done => {      const expected = {        [LEVEL]: 'info',        level: 'info',        message: 'there will be json'      };      const transport = new TransportStream({        format: format.json(),        log(info) {          assume(info[MESSAGE]).equals(JSON.stringify(expected));          done();        }      });      transport.write(expected);    });    it('treats the original object immutable', done => {      const expected = {        [LEVEL]: 'info',        level: 'info',        message: 'there will be json'      };      const transport = new TransportStream({        format: format.json(),        log(info) {          assume(info).not.equals(expected);          done();        }      });      transport.write(expected);    });    it('_write continues to write after a format throws', done => {      const transport = new TransportStream({        format: format.printf((info) => {          // Set a trap.          if (info.message === 'ENDOR') {            throw new Error('ITS A TRAP!');          }          return info.message;        }),        log(info, callback) {          callback();          assume(info.level).equals('info');          assume(info.message).equals('safe');          done();        }      });      try {        transport.write({ level: 'info', message: 'ENDOR' });      } catch (ex) {        assume(ex.message).equals('ITS A TRAP!');      }      transport.write({ level: 'info', message: 'safe' });    });    it('_writev continues to write after a format throws', done => {      const transport = new TransportStream({        format: format.printf((info) => {          // Set a trap.          if (info.message === 'ENDOR') {            throw new Error('ITS A TRAP!');          }          return info.message;        }),        log(info, callback) {          assume(info.level).is.a('string');          assume(info.message).is.a('string');          callback();          if (info.message === 'safe') {            done();          }        }      });      const infos = infosFor({        count: 10,        levels: testOrder      });      try {        transport.cork();        infos.forEach(info => transport.write(info));        transport.write({ level: 'info', message: 'ENDOR' });        transport.uncork();      } catch (ex) {        assume(ex.message).equals('ITS A TRAP!');      }      transport.write({ level: 'info', message: 'safe' });    });  });  describe('{ silent }', () => {    const silentTransport = new TransportStream({      silent: true,      format: format.json(),      log() {        assume(false).true('.log() was called improperly');      }    });    it('{ silent: true } ._write() never calls `.log`', done => {      const expected = {        [LEVEL]: 'info',        level: 'info',        message: 'there will be json'      };      silentTransport.write(expected);      setImmediate(() => done());    });    it('{ silent: true } ._writev() never calls `.log`', done => {      const expected = {        [LEVEL]: 'info',        level: 'info',        message: 'there will be json'      };      silentTransport.cork();      for (let i = 0; i < 15; i++) {        silentTransport.write(expected);      }      silentTransport.uncork();      setImmediate(() => done());    });    it('{ silent: true } ensures ._accept(write) always returns false', () => {      const accepted = silentTransport._accept({ chunk: {} });      assume(accepted).false();    });  });});
 |