/** * @fileOverview Test the logging libraries */ 'use strict'; const Transport = require('winston-transport'); const chai = require('chai'); const sinon = require('sinon'); const sinonChai = require('sinon-chai'); const _ = require('lodash'); const logging = require('../logging'); const expect = chai.expect; const sandbox = sinon.createSandbox(); chai.use(sinonChai); const FILENAME = '/some/file/name.js'; const ID = 'test:logging'; const INFO_LOG_STRING = 'String sent to log.info'; const ERROR_LOG_STRING = 'String sent to log.error'; const EXTRA_INFO = { extra1: 1, extra2: '2' }; const FAKE_IP = '127.0.0.2'; const REQUEST_ID = 1234; const USER_ID = '05afcaac4b73658acc79a26d981246978135edadf1'; const FAKE_REQ = { ip: FAKE_IP, bridgeUniqueId: REQUEST_ID, session: { data: { user: USER_ID } } }; /** * Expected results */ const EXPECTED_BASIC_LOG = { meta: { ip: FAKE_IP, file: FILENAME, logId: ID, reqId: REQUEST_ID, userId: USER_ID } }; const EXPECTED_EXTENDED_LOG = _.defaults({}, EXPECTED_BASIC_LOG, { meta: { _extra1: 1, _extra2: '2' } } ); const EXPECTED_INFO_LEVEL = { level: 'info', message: INFO_LOG_STRING }; const EXPECTED_ERROR_LEVEL = { level: 'error', message: ERROR_LOG_STRING }; /** * Make a fake log transport with a spy */ class SpyTransport extends Transport { // eslint-disable-next-line class-methods-use-this log(info, callback) { // Null transport doesn't do anything callback(); } } const spyTransport = new SpyTransport(); sandbox.spy(spyTransport, 'log'); /** * Function to expect that the correct values were called based on the values * collected by the spy. * * @param {Object} expected - the values we expect to be logged * * @returns {Promise} - promise for the result of the expectation */ function expectLoggedValues(expected) { return expect(spyTransport.log).to.be .calledOnce .calledWith(sinon.match(expected)); } /** * The tests */ describe('Initialised log', () => { let log; let fakeTimer; before(() => { /** * Use fake timers so we can control the timing of the logged timestamp. */ fakeTimer = sinon.useFakeTimers(); const logger = logging._test.getLogger(); logger.add(spyTransport); log = logging(FILENAME, ID); }); after(() => { /** * Put real timers back after all the tests are complete. */ fakeTimer.restore(); }); beforeEach(() => { /** * Create the log anew, and reset any sandbox history for each test. */ log = logging(FILENAME, ID); sandbox.resetHistory(); }); it('has an info() function', () => { return expect(log) .to.have.property('info') .to.be.instanceOf(Function); }); it('has an error() function', () => { return expect(log) .to.have.property('error') .to.be.instanceOf(Function); }); describe('calling info function', () => { it('with a `req` and a message logs all basic details at info level', () => { log.info(FAKE_REQ, INFO_LOG_STRING); const expected = _.defaults({}, EXPECTED_BASIC_LOG, EXPECTED_INFO_LEVEL); return expectLoggedValues(expected); }); it('with a `req`, a message, & more data, merges the extra props prefixed with `_` at info level', () => { log.info(FAKE_REQ, INFO_LOG_STRING, EXTRA_INFO); const expected = _.defaults({}, EXPECTED_EXTENDED_LOG, EXPECTED_INFO_LEVEL); return expectLoggedValues(expected); }); }); describe('calling error function', () => { it('with a `req` and a message logs all basic details at error level', () => { log.error(FAKE_REQ, ERROR_LOG_STRING); const expected = _.defaults({}, EXPECTED_BASIC_LOG, EXPECTED_ERROR_LEVEL); return expectLoggedValues(expected); }); it('with a `req`, a message, & more data, merges the extra props prefixed with `_` at error level', () => { log.error(FAKE_REQ, ERROR_LOG_STRING, EXTRA_INFO); const expected = _.defaults({}, EXPECTED_EXTENDED_LOG, EXPECTED_ERROR_LEVEL); return expectLoggedValues(expected); }); }); });