142 lines
4.8 KiB
TypeScript
142 lines
4.8 KiB
TypeScript
|
import rewiremock from 'rewiremock';
|
||
|
import '../../../../mocks/electron';
|
||
|
|
||
|
import chai, { expect } from 'chai';
|
||
|
import 'mocha';
|
||
|
import { container } from '../../core/container';
|
||
|
import { setDev } from '../../core/env.spec';
|
||
|
import { ILogger } from './i-logger';
|
||
|
import fs from 'fs-extra';
|
||
|
import { createInterface, Interface } from 'readline';
|
||
|
import fc from 'fast-check';
|
||
|
import { LogLevel } from './log-level';
|
||
|
import chaiFs from 'chai-fs';
|
||
|
|
||
|
chai.use(chaiFs);
|
||
|
|
||
|
describe('Logger Service', () => {
|
||
|
function getLastLine(rl: Interface): Promise<string> {
|
||
|
return new Promise((resolve) => {
|
||
|
let lastLine = '';
|
||
|
rl.on('line', (line) => {
|
||
|
lastLine = line;
|
||
|
});
|
||
|
|
||
|
rl.on('close', () => {
|
||
|
resolve(lastLine);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getDefaultLogReadLineInterface(logger: ILogger) {
|
||
|
return createInterface(fs.createReadStream(logger.getLogFile()));
|
||
|
}
|
||
|
|
||
|
// no debug because it gets tested separately
|
||
|
const logLevels = ['fatal', 'error', 'warning', 'notice', 'info'];
|
||
|
const logLevelsNumber = [0, 1, 2, 3, 4];
|
||
|
|
||
|
// hard-coded because it is hardcoded in the logger as well and therefore can be tested to be this way, in byte
|
||
|
const maxLogSize = 50000;
|
||
|
|
||
|
type LogLevelArbitrary = fc.Arbitrary<'fatal' | 'error' | 'warning' | 'notice' | 'info'>;
|
||
|
const logLevelArbitrary = fc.constantFrom(...logLevels);
|
||
|
|
||
|
const logLevelNumberArbitrary = fc.constantFrom(...logLevelsNumber);
|
||
|
|
||
|
before(() => {
|
||
|
rewiremock.enable();
|
||
|
});
|
||
|
|
||
|
after(() => {
|
||
|
rewiremock.disable();
|
||
|
});
|
||
|
|
||
|
it('creates log files', () => {
|
||
|
const logger: ILogger = container.get(Symbol.for('logger'));
|
||
|
|
||
|
expect(logger.getLogFile()).path('log file is not created');
|
||
|
expect(logger.getExceptionsLogFile()).path('exception log file is not created');
|
||
|
});
|
||
|
|
||
|
it('logs exceptions', async () => {
|
||
|
const logger: ILogger = container.get(Symbol.for('logger'));
|
||
|
|
||
|
await logger.exception(new Error('this is an error'));
|
||
|
});
|
||
|
|
||
|
it("default log file doesn't get bigger than 50KB @slow", async () => {
|
||
|
const logger: ILogger = container.get(Symbol.for('logger'));
|
||
|
|
||
|
for (let i = 0; i < maxLogSize; i++) {
|
||
|
await logger.log(4, 'your waifu is trash');
|
||
|
}
|
||
|
|
||
|
const logFileStats = await fs.stat(logger.getLogFile());
|
||
|
expect(logFileStats.size).lessThan(maxLogSize, 'log is bigger than its max size');
|
||
|
}).timeout(15000);
|
||
|
|
||
|
it("exception log file doesn't get bigger than 50KB @slow", async () => {
|
||
|
const logger: ILogger = container.get(Symbol.for('logger'));
|
||
|
|
||
|
for (let i = 0; i < maxLogSize; ++i) {
|
||
|
await logger.exception(new Error('your waifu is trash'));
|
||
|
}
|
||
|
|
||
|
const logFileStats = await fs.stat(logger.getLogFile());
|
||
|
expect(logFileStats.size).lessThan(maxLogSize, 'log is bigger than its max size');
|
||
|
}).timeout(15000);
|
||
|
|
||
|
it('logs different levels directly', () => {
|
||
|
const logger: ILogger = container.get(Symbol.for('logger'));
|
||
|
|
||
|
return fc.assert(
|
||
|
fc.asyncProperty(logLevelArbitrary as LogLevelArbitrary, fc.string(), async (logLevel, message) => {
|
||
|
await logger[logLevel](message);
|
||
|
const lastLine = await getLastLine(getDefaultLogReadLineInterface(logger));
|
||
|
expect(lastLine).contains(message, 'the log line does not contain the message');
|
||
|
expect(lastLine).contains(logLevel, `the log line does not contain the '${logLevel}' keyword`);
|
||
|
}),
|
||
|
{
|
||
|
numRuns: 50,
|
||
|
}
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('logs different levels indirectly via the generic log function', () => {
|
||
|
const logger: ILogger = container.get(Symbol.for('logger'));
|
||
|
|
||
|
return fc.assert(
|
||
|
fc.asyncProperty(logLevelNumberArbitrary, fc.string(), async (logLevelNumber, message) => {
|
||
|
await logger.log(logLevelNumber, message);
|
||
|
const lastLine = await getLastLine(getDefaultLogReadLineInterface(logger));
|
||
|
expect(lastLine).contains(message, 'the log line does not contain the message');
|
||
|
expect(lastLine).contains(
|
||
|
logLevels[logLevelNumber],
|
||
|
`the log line does not contain the '${logLevels[logLevelNumber]}' keyword`
|
||
|
);
|
||
|
}),
|
||
|
{
|
||
|
numRuns: 50,
|
||
|
}
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('logs debug only in dev mode', async () => {
|
||
|
const logger: ILogger = container.get(Symbol.for('logger'));
|
||
|
|
||
|
setDev();
|
||
|
await logger.debug('this is a development message');
|
||
|
const lastLine = await getLastLine(getDefaultLogReadLineInterface(logger));
|
||
|
expect(lastLine).contains('this is a development message', 'the dev log line does not contain the message');
|
||
|
expect(lastLine).contains('debug', `the dev log line does not contain the 'debug' keyword`);
|
||
|
setDev(false);
|
||
|
await logger.log(LogLevel.warning, 'this is a warning');
|
||
|
await logger.debug('this is a second development message, should not be here');
|
||
|
expect(lastLine).not.contain(
|
||
|
'this is a second development message, should not be here',
|
||
|
'debug is logged even in non-dev mode'
|
||
|
);
|
||
|
});
|
||
|
});
|