legacy.test.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. 'use strict';
  2. const assume = require('assume');
  3. const Writable = require('readable-stream/writable');
  4. const LegacyTransportStream = require('../legacy');
  5. const LegacyTransport = require('./fixtures/legacy-transport');
  6. const { testLevels, testOrder } = require('./fixtures');
  7. const { infosFor, logFor, levelAndMessage } = require('abstract-winston-transport/utils');
  8. const { LEVEL } = require('triple-beam');
  9. //
  10. // Silence the deprecation notice for sanity in test output.
  11. // TODO: Test coverage for the deprecation notice because why not?
  12. //
  13. const deprecated = {
  14. original: LegacyTransportStream.prototype._deprecated,
  15. silence() {
  16. LegacyTransportStream.prototype._deprecated = () => {};
  17. },
  18. restore() {
  19. LegacyTransportStream.prototype._deprecated = this.original;
  20. }
  21. };
  22. describe('LegacyTransportStream', () => {
  23. let legacy;
  24. let transport;
  25. before(deprecated.silence);
  26. beforeEach(() => {
  27. legacy = new LegacyTransport();
  28. transport = new LegacyTransportStream({
  29. transport: legacy
  30. });
  31. });
  32. it('should have the appropriate methods defined', () => {
  33. assume(transport).instanceof(Writable);
  34. assume(transport._write).is.a('function');
  35. // eslint-disable-next-line no-undefined
  36. assume(transport.log).equals(undefined);
  37. });
  38. it('should error with no transport', () => {
  39. assume(() => {
  40. transport = new LegacyTransportStream();
  41. assume(transport).instanceof(stream.Writable);
  42. }).throws(/Invalid transport, must be an object with a log method./);
  43. });
  44. it('should error with invalid transport', () => {
  45. assume(() => {
  46. transport = new LegacyTransportStream();
  47. assume(transport).instanceof(stream.Writable);
  48. }).throws(/Invalid transport, must be an object with a log method./);
  49. });
  50. it('should display a deprecation notice', done => {
  51. deprecated.restore();
  52. const error = console.error; // eslint-disable-line no-console
  53. console.error = msg => { // eslint-disable-line no-console
  54. assume(msg).to.include('is a legacy winston transport. Consider upgrading');
  55. setImmediate(done);
  56. };
  57. legacy = new LegacyTransport();
  58. transport = new LegacyTransportStream({
  59. transport: legacy
  60. });
  61. console.error = error; // eslint-disable-line no-console
  62. deprecated.silence();
  63. });
  64. it('sets __winstonError on the LegacyTransport instance', function () {
  65. assume(legacy.__winstonError).is.a('function');
  66. assume(legacy.listeners('error')).deep.equals([
  67. legacy.__winstonError
  68. ]);
  69. });
  70. it('emits an error on LegacyTransport error', done => {
  71. const err = new Error('Pass-through from stream');
  72. transport.on('error', actual => {
  73. assume(err).equals(actual);
  74. done();
  75. });
  76. legacy.emit('error', err);
  77. });
  78. describe('_write(info, enc, callback)', () => {
  79. it('should log to any level when { level: undefined }', done => {
  80. const expected = testOrder.map(levelAndMessage);
  81. legacy.on('logged', logFor(testOrder.length, (err, infos) => {
  82. if (err) {
  83. return done(err);
  84. }
  85. assume(infos.length).equals(expected.length);
  86. assume(infos).deep.equals(expected);
  87. done();
  88. }));
  89. expected.forEach(transport.write.bind(transport));
  90. });
  91. it('should only log messages BELOW the level priority', done => {
  92. const expected = testOrder.map(levelAndMessage);
  93. transport = new LegacyTransportStream({
  94. level: 'info',
  95. transport: legacy
  96. });
  97. legacy.on('logged', logFor(5, (err, infos) => {
  98. if (err) {
  99. return done(err);
  100. }
  101. assume(infos.length).equals(5);
  102. assume(infos).deep.equals(expected.slice(0, 5));
  103. done();
  104. }));
  105. transport.levels = testLevels;
  106. expected.forEach(transport.write.bind(transport));
  107. });
  108. it('{ level } should be ignored when { handleExceptions: true }', () => {
  109. const expected = testOrder.map(levelAndMessage).map(info => {
  110. info.exception = true;
  111. return info;
  112. });
  113. transport = new LegacyTransportStream({
  114. transport: legacy,
  115. level: 'info'
  116. });
  117. legacy.on('logged', logFor(testOrder.length, (err, infos) => {
  118. // eslint-disable-next-line no-undefined
  119. assume(err).equals(undefined);
  120. assume(infos.length).equals(expected.length);
  121. assume(infos).deep.equals(expected);
  122. }));
  123. transport.levels = testLevels;
  124. expected.forEach(transport.write.bind(transport));
  125. });
  126. describe('when { exception: true } in info', () => {
  127. it('should not invoke log when { handleExceptions: false }', done => {
  128. const expected = [{
  129. exception: true,
  130. [LEVEL]: 'error',
  131. level: 'error',
  132. message: 'Test exception handling'
  133. }, {
  134. [LEVEL]: 'test',
  135. level: 'test',
  136. message: 'Testing ... 1 2 3.'
  137. }];
  138. legacy.on('logged', info => {
  139. // eslint-disable-next-line no-undefined
  140. assume(info.exception).equals(undefined);
  141. done();
  142. });
  143. expected.forEach(transport.write.bind(transport));
  144. });
  145. it('should invoke log when { handleExceptions: true }', done => {
  146. const actual = [];
  147. const expected = [{
  148. exception: true,
  149. [LEVEL]: 'error',
  150. level: 'error',
  151. message: 'Test exception handling'
  152. }, {
  153. [LEVEL]: 'test',
  154. level: 'test',
  155. message: 'Testing ... 1 2 3.'
  156. }];
  157. transport = new LegacyTransportStream({
  158. handleExceptions: true,
  159. transport: legacy
  160. });
  161. legacy.on('logged', info => {
  162. actual.push(info);
  163. if (actual.length === expected.length) {
  164. assume(actual).deep.equals(expected);
  165. return done();
  166. }
  167. });
  168. expected.forEach(transport.write.bind(transport));
  169. });
  170. });
  171. });
  172. describe('_writev(chunks, callback)', () => {
  173. it('should be called when necessary in streams plumbing', done => {
  174. const expected = infosFor({
  175. count: 50,
  176. levels: testOrder
  177. });
  178. legacy.on('logged', logFor(50 * testOrder.length, (err, infos) => {
  179. if (err) {
  180. return done(err);
  181. }
  182. assume(infos.length).equals(expected.length);
  183. assume(infos).deep.equals(expected);
  184. done();
  185. }));
  186. //
  187. // Make the standard _write throw to ensure that _writev is called.
  188. //
  189. transport._write = () => {
  190. throw new Error('This should never be called');
  191. };
  192. transport.cork();
  193. expected.forEach(transport.write.bind(transport));
  194. transport.uncork();
  195. });
  196. });
  197. describe('close()', () => {
  198. it('removes __winstonError from the transport', () => {
  199. assume(legacy.__winstonError).is.a('function');
  200. assume(legacy.listenerCount('error')).equal(1);
  201. transport.close();
  202. assume(legacy.__winstonError).falsy();
  203. assume(legacy.listenerCount('error')).equal(0);
  204. });
  205. it('invokes .close() on the transport', done => {
  206. legacy.on('closed:custom', done);
  207. transport.close();
  208. });
  209. });
  210. describe('{ silent }', () => {
  211. it('{ silent: true } ._write() never calls `.log`', done => {
  212. const expected = {
  213. [LEVEL]: 'info',
  214. level: 'info',
  215. message: 'there will be json'
  216. };
  217. legacy.once('logged', () => (
  218. assume(true).false('"logged" event emitted unexpectedly')
  219. ));
  220. transport.silent = true;
  221. transport.write(expected);
  222. setImmediate(() => done());
  223. });
  224. it('{ silent: true } ._writev() never calls `.log`', done => {
  225. const expected = {
  226. [LEVEL]: 'info',
  227. level: 'info',
  228. message: 'there will be json'
  229. };
  230. legacy.once('logged', () => (
  231. assume(true).false('"logged" event emitted unexpectedly')
  232. ));
  233. transport.cork();
  234. for (let i = 0; i < 15; i++) {
  235. transport.write(expected);
  236. }
  237. transport.silent = true;
  238. transport.uncork();
  239. setImmediate(() => done());
  240. });
  241. it('{ silent: true } ensures ._accept(write) always returns false', () => {
  242. transport.silent = true;
  243. const accepted = transport._accept({
  244. chunk: {}
  245. });
  246. assume(accepted).false();
  247. });
  248. });
  249. //
  250. // Restore the deprecation notice after tests complete.
  251. //
  252. after(() => deprecated.restore());
  253. });