/* * Copyright (C) 2018, 2020 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ import chai from 'chai'; import {expect} from 'chai'; import chaiAsPromised from 'chai-as-promised'; import chaiSpies from 'chai-spies'; import {suite, test} from '@testdeck/mocha'; import {Logger} from '../src/logger'; import {AddLogLevel} from '../src/transformations/add-log-level'; import {Colorize} from '../src/transformations/colorize'; import {DummyTransport} from './dummyTransport'; chai.should(); chai.use(chaiSpies); chai.use(chaiAsPromised); @suite() export class LoggerSpec { static sandbox: ChaiSpies.Sandbox; static before() { LoggerSpec.sandbox = chai.spy.sandbox(); } before() { Logger.setTransformations([ new AddLogLevel(), ]); } after() { LoggerSpec.sandbox.restore(); } @test async 'default log level'() { expect((Logger as any).getLevel('LOG')).to.be.equal(31); expect((Logger as any).getLevel('EXIT')).to.be.equal(0); } @test async error() { const spy = LoggerSpec.sandbox.on(console, 'error', () => { // noop }); await Logger.error('Foobar'); expect(spy).to.have.been.called(); expect(spy.__spy.calls[0][0]).to.contain('[ERROR]'); expect(spy.__spy.calls[0][0]).to.contain('Foobar'); } @test async 'error in productive environment'() { const spy = LoggerSpec.sandbox.on(console, 'error', () => { // noop }); const nodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = 'production'; await Logger.error('Foobar').should.be.rejectedWith(Error); expect(spy).to.have.been.called.exactly(1); process.env.ALLOW_NO_TRANSPORT = 'true'; await Logger.error('Foobar').should.not.be.rejectedWith(Error); expect(spy).to.have.been.called.exactly(2); delete process.env.ALLOW_NO_TRANSPORT; Logger.setTransport(new DummyTransport()); await Logger.error('Foobar').should.not.be.rejectedWith(Error); expect(spy).to.have.been.called.exactly(3); Logger.setTransport(); process.env.NODE_ENV = nodeEnv; } @test async 'error without output'() { const spy = LoggerSpec.sandbox.on(console, 'error', () => { // noop }); process.env.STAPPS_LOG_LEVEL = '0'; await Logger.error('Foobar'); delete process.env.STAPPS_LOG_LEVEL; expect(spy).not.to.have.been.called(); } @test async 'error with Error'() { const spy = LoggerSpec.sandbox.on(console, 'error', () => { // noop }); await Logger.error(new Error()); expect(spy).to.have.been.called(); expect(spy.__spy.calls[0][0]).to.contain('Error'); expect(spy.__spy.calls[0][0]).to.contain(process.cwd()); } @test info() { const spy = LoggerSpec.sandbox.on(console, 'info', () => { // noop }); Logger.info('Foobar'); expect(spy).to.have.been.called(); expect(spy.__spy.calls[0][0]).to.contain('[INFO]'); expect(spy.__spy.calls[0][0]).to.contain('Foobar'); } @test async exits() { const infoSpy = LoggerSpec.sandbox.on(console, 'info', () => { // noop }); const logSpy = LoggerSpec.sandbox.on(console, 'log', () => { // noop }); const warnSpy = LoggerSpec.sandbox.on(console, 'warn', () => { // noop }); const errorSpy = LoggerSpec.sandbox.on(console, 'error', () => { // noop }); const processSpy = LoggerSpec.sandbox.on(process, 'exit', () => { // noop }); const exitLevel = process.env.STAPPS_EXIT_LEVEL; process.env.STAPPS_EXIT_LEVEL = '31'; Logger.info('Foobar'); Logger.log('Foobar'); Logger.warn('Foobar'); Logger.ok('Foobar'); await Logger.error('Foobar'); expect(infoSpy).to.have.been.called.once; expect(infoSpy.__spy.calls[0][0]).to.contain('[INFO]'); expect(infoSpy.__spy.calls[0][0]).to.contain('Foobar'); expect(logSpy).to.have.been.called.twice; expect(warnSpy).to.have.been.called.once; expect(warnSpy.__spy.calls[0][0]).to.contain('[WARN]'); expect(warnSpy.__spy.calls[0][0]).to.contain('Foobar'); expect(errorSpy).to.have.been.called.exactly(6); expect(processSpy).to.have.been.called.exactly(5); process.env.STAPPS_EXIT_LEVEL = exitLevel; } @test 'info without output'() { const spy = LoggerSpec.sandbox.on(console, 'info', () => { // noop }); process.env.STAPPS_LOG_LEVEL = '0'; Logger.info('Foobar'); delete process.env.STAPPS_LOG_LEVEL; expect(spy).not.to.have.been.called; } @test initialized() { Logger.setTransport(new DummyTransport()); expect(() => { Logger.initialized(); }).not.to.throw(); Logger.setTransport(); } @test 'initialized in productive environment'() { const nodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = 'production'; Logger.setTransport(new DummyTransport()); expect(() => { Logger.initialized(); }).not.to.throw(); Logger.setTransport(); expect(() => { Logger.initialized(); }).to.throw(); const spy = LoggerSpec.sandbox.on(console, 'warn', () => { // noop }); process.env.ALLOW_NO_TRANSPORT = 'true'; expect(() => { Logger.initialized(); }).not.to.throw(); delete process.env.ALLOW_NO_TRANSPORT; expect(spy).to.have.been.called(); process.env.NODE_ENV = nodeEnv; } @test 'is compatible with log aggregation in productive environment'() { Logger.setTransformations([new AddLogLevel(), new Colorize()]); let spy = LoggerSpec.sandbox.on(console, 'log', () => { // noop }); Logger.log('Foo\nbar'); expect(spy).to.have.been.called.once; expect(spy.__spy.calls[0][0]).to.equal('\u001B[37m[LOG] Foo\u001B[39m\n\u001B[37mbar\u001B[39m'); const nodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = 'production'; process.env.ALLOW_NO_TRANSPORT = 'true'; Logger.setTransformations([new AddLogLevel(), new Colorize()]); Logger.log('Foo\nbar'); expect(spy).to.have.been.called.twice; expect(spy.__spy.calls[1][0]).to.equal('[LOG] Foo bar'); process.env.NODE_ENV = nodeEnv; delete process.env.ALLOW_NO_TRANSPORT; } @test log() { const spy = LoggerSpec.sandbox.on(console, 'log', () => { // noop }); Logger.log('Foobar'); expect(spy).to.have.been.called(); expect(spy.__spy.calls[0][0]).to.contain('[LOG]'); expect(spy.__spy.calls[0][0]).to.contain('Foobar'); } @test 'log without output'() { const spy = LoggerSpec.sandbox.on(console, 'log', () => { // noop }); process.env.STAPPS_LOG_LEVEL = '0'; Logger.log('Foobar'); delete process.env.STAPPS_LOG_LEVEL; expect(spy).to.not.have.been.called(); } @test ok() { const spy = LoggerSpec.sandbox.on(console, 'log', () => { // noop }); Logger.ok('Foobar'); expect(spy).to.have.been.called(); expect(spy.__spy.calls[0][0]).to.contain('[OK]'); expect(spy.__spy.calls[0][0]).to.contain('Foobar'); } @test 'ok without output'() { const spy = LoggerSpec.sandbox.on(console, 'log', () => { // noop }); process.env.STAPPS_LOG_LEVEL = '0'; Logger.ok('Foobar'); delete process.env.STAPPS_LOG_LEVEL; expect(spy).not.to.have.been.called(); } @test setTransport() { expect(() => { Logger.setTransport(new DummyTransport()); Logger.setTransport(); }).not.to.throw(); } @test 'stringify'() { const spy = LoggerSpec.sandbox.on(console, 'log', () => { // noop }); Logger.log('foo', 'bar'); expect(spy).to.have.been.called(); expect(spy.__spy.calls[0][0]).to.contain('foo'); expect(spy.__spy.calls[0][0]).to.contain('bar'); } @test 'stringify object'() { const spy = LoggerSpec.sandbox.on(console, 'log', () => { // noop }); Logger.log({ foo: 'bar', }); expect(spy).to.have.been.called(); expect(spy.__spy.calls[0][0]).to.contain('foo'); expect(spy.__spy.calls[0][0]).to.contain('bar'); } @test warn() { const spy = LoggerSpec.sandbox.on(console, 'warn', () => { // noop }); Logger.warn('Foobar'); expect(spy).to.have.been.called(); expect(spy.__spy.calls[0][0]).to.contain('[WARN]'); expect(spy.__spy.calls[0][0]).to.contain('Foobar'); } @test 'warn without output'() { const spy = LoggerSpec.sandbox.on(console, 'warn', () => { // noop }); process.env.STAPPS_LOG_LEVEL = '0'; Logger.warn('Foobar'); delete process.env.STAPPS_LOG_LEVEL; expect(spy).not.to.have.been.called(); } @test 'log level exclusiveness'() { const warnSpy = LoggerSpec.sandbox.on(console, 'warn', () => { // noop WARN }); const infoSpy = LoggerSpec.sandbox.on(console, 'info', () => { // noop INFO }); const okSpy = LoggerSpec.sandbox.on(console, 'log', () => { // noop OK }); // only warn and info = warn + info = 4 + 1 = 5 process.env.STAPPS_LOG_LEVEL = '5'; Logger.warn('Foo'); Logger.info('Bar'); Logger.ok('Baz'); expect(warnSpy).to.have.been.called(); expect(warnSpy.__spy.calls[0][0]).to.contain('[WARN]'); expect(warnSpy.__spy.calls[0][0]).to.contain('Foo'); expect(infoSpy).to.have.been.called(); expect(infoSpy.__spy.calls[0][0]).to.contain('[INFO]'); expect(infoSpy.__spy.calls[0][0]).to.contain('Bar'); expect(okSpy).to.not.have.been.called(); delete process.env.STAPPS_LOG_LEVEL; } @test getExitLevel() { const savedProcess = process; // @ts-ignore process = undefined; (global as any).window = { STAPPS_EXIT_LEVEL: 0, }; const stub = LoggerSpec.sandbox.on(console, 'info', () => { // noop }); Logger.info('Foobar'); process = savedProcess; delete (global as any).window; expect(stub).not.to.have.been.called(); } @test getLogLevel() { const savedProcess = process; // @ts-ignore process = undefined; (global as any).window = { STAPPS_LOG_LEVEL: 0, }; const stub = LoggerSpec.sandbox.on(console, 'info', () => { // noop }); Logger.info('Foobar'); process = savedProcess; delete (global as any).window; expect(stub).not.to.have.been.called(); } @test 'output without transformations'() { Logger.setTransformations([]); const stub = LoggerSpec.sandbox.on(console, 'log', () => { // noop }); const applyTransformationsSpy = LoggerSpec.sandbox.on(new Logger(), 'applyTransformations'); Logger.log('Foobar'); expect(stub).to.have.been.called.with('Foobar'); expect(applyTransformationsSpy).not.to.have.been.called; } }