signal-desktop/test/server/app/logging_test.js
Scott Nonnenberg 64fe9dbfb2
Clean logs on start - and eslint/mocha with code coverage (#1945)
* Clean logs on startup; install server-side testing/linting

* Add eslint config, make all of app/ conform to its demands

* Add Node.js testing and linting to CI

* Lock project to Node.js 7.9.0, used by Electron 1.7.10

* New eslint error: trailing commas in function argumensts

Node 7.9.0 doesn't like trailing commas, but Electron does

* Move electron to devDependency, tell eslint it's built-in
2018-01-08 13:19:25 -08:00

271 lines
8.6 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const tmp = require('tmp');
const { expect } = require('chai');
const {
eliminateOutOfDateFiles,
eliminateOldEntries,
isLineAfterDate,
fetchLog,
fetch,
} = require('../../../app/logging');
describe('app/logging', () => {
let basePath;
let tmpDir;
beforeEach(() => {
tmpDir = tmp.dirSync({
unsafeCleanup: true,
});
basePath = tmpDir.name;
});
afterEach((done) => {
// we need the unsafe option to recursively remove the directory
tmpDir.removeCallback(done);
});
describe('#isLineAfterDate', () => {
it('returns false if falsy', () => {
const actual = isLineAfterDate('', new Date());
expect(actual).to.equal(false);
});
it('returns false if invalid JSON', () => {
const actual = isLineAfterDate('{{}', new Date());
expect(actual).to.equal(false);
});
it('returns false if date is invalid', () => {
const line = JSON.stringify({ time: '2018-01-04T19:17:05.014Z' });
const actual = isLineAfterDate(line, new Date('try6'));
expect(actual).to.equal(false);
});
it('returns false if log time is invalid', () => {
const line = JSON.stringify({ time: 'try7' });
const date = new Date('2018-01-04T19:17:00.000Z');
const actual = isLineAfterDate(line, date);
expect(actual).to.equal(false);
});
it('returns false if date before provided date', () => {
const line = JSON.stringify({ time: '2018-01-04T19:17:00.000Z' });
const date = new Date('2018-01-04T19:17:05.014Z');
const actual = isLineAfterDate(line, date);
expect(actual).to.equal(false);
});
it('returns true if date is after provided date', () => {
const line = JSON.stringify({ time: '2018-01-04T19:17:05.014Z' });
const date = new Date('2018-01-04T19:17:00.000Z');
const actual = isLineAfterDate(line, date);
expect(actual).to.equal(true);
});
});
describe('#eliminateOutOfDateFiles', () => {
it('deletes an empty file', () => {
const date = new Date();
const log = '\n';
const target = path.join(basePath, 'log.log');
fs.writeFileSync(target, log);
return eliminateOutOfDateFiles(basePath, date).then(() => {
expect(fs.existsSync(target)).to.equal(false);
});
});
it('deletes a file with invalid JSON lines', () => {
const date = new Date();
const log = '{{}\n';
const target = path.join(basePath, 'log.log');
fs.writeFileSync(target, log);
return eliminateOutOfDateFiles(basePath, date).then(() => {
expect(fs.existsSync(target)).to.equal(false);
});
});
it('deletes a file with all dates before provided date', () => {
const date = new Date('2018-01-04T19:17:05.014Z');
const contents = [
JSON.stringify({ time: '2018-01-04T19:17:00.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:01.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:02.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:03.014Z' }),
].join('\n');
const target = path.join(basePath, 'log.log');
fs.writeFileSync(target, contents);
return eliminateOutOfDateFiles(basePath, date).then(() => {
expect(fs.existsSync(target)).to.equal(false);
});
});
it('keeps a file with first line date before provided date', () => {
const date = new Date('2018-01-04T19:16:00.000Z');
const contents = [
JSON.stringify({ time: '2018-01-04T19:17:00.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:01.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:02.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:03.014Z' }),
].join('\n');
const target = path.join(basePath, 'log.log');
fs.writeFileSync(target, contents);
return eliminateOutOfDateFiles(basePath, date).then(() => {
expect(fs.existsSync(target)).to.equal(true);
});
});
it('keeps a file with last line date before provided date', () => {
const date = new Date('2018-01-04T19:17:01.000Z');
const contents = [
JSON.stringify({ time: '2018-01-04T19:17:00.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:01.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:02.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:03.014Z' }),
].join('\n');
const target = path.join(basePath, 'log.log');
fs.writeFileSync(target, contents);
return eliminateOutOfDateFiles(basePath, date).then(() => {
expect(fs.existsSync(target)).to.equal(true);
});
});
});
describe('#eliminateOldEntries', () => {
it('eliminates all non-parsing entries', () => {
const date = new Date('2018-01-04T19:17:01.000Z');
const contents = [
'random line',
JSON.stringify({ time: '2018-01-04T19:17:01.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:02.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:03.014Z' }),
].join('\n');
const expected = [
JSON.stringify({ time: '2018-01-04T19:17:01.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:02.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:03.014Z' }),
].join('\n');
const target = path.join(basePath, 'log.log');
const files = [{
path: target,
}];
fs.writeFileSync(target, contents);
return eliminateOldEntries(files, date).then(() => {
expect(fs.readFileSync(target, 'utf8')).to.equal(`${expected}\n`);
});
});
it('preserves all lines if before target date', () => {
const date = new Date('2018-01-04T19:17:03.000Z');
const contents = [
'random line',
JSON.stringify({ time: '2018-01-04T19:17:01.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:02.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:03.014Z' }),
].join('\n');
const expected = [
JSON.stringify({ time: '2018-01-04T19:17:03.014Z' }),
].join('\n');
const target = path.join(basePath, 'log.log');
const files = [{
path: target,
}];
fs.writeFileSync(target, contents);
return eliminateOldEntries(files, date).then(() => {
expect(fs.readFileSync(target, 'utf8')).to.equal(`${expected}\n`);
});
});
});
describe('#fetchLog', () => {
it('returns error if file does not exist', () => {
const target = 'random_file';
return fetchLog(target).then(() => {
throw new Error('Expected an error!');
}, (error) => {
expect(error).to.have.property('message').that.match(/random_file/);
});
});
it('returns empty array if file has no valid JSON lines', () => {
const contents = 'line 1\nline2\n';
const expected = [];
const target = path.join(basePath, 'test.log');
fs.writeFileSync(target, contents);
return fetchLog(target).then((result) => {
expect(result).to.deep.equal(expected);
});
});
it('returns just three fields in each returned line', () => {
const contents = [
JSON.stringify({
one: 1,
two: 2,
level: 1,
time: 2,
msg: 3,
}),
JSON.stringify({
one: 1,
two: 2,
level: 2,
time: 3,
msg: 4,
}),
'',
].join('\n');
const expected = [{
level: 1,
time: 2,
msg: 3,
}, {
level: 2,
time: 3,
msg: 4,
}];
const target = path.join(basePath, 'test.log');
fs.writeFileSync(target, contents);
return fetchLog(target).then((result) => {
expect(result).to.deep.equal(expected);
});
});
});
describe('#fetch', () => {
it('returns single entry if no files', () => {
return fetch(basePath).then((results) => {
expect(results).to.have.length(1);
expect(results[0].msg).to.match(/Loaded this list/);
});
});
it('returns sorted entries from all files', () => {
const first = [
JSON.stringify({ msg: 2, time: '2018-01-04T19:17:05.014Z' }),
'',
].join('\n');
const second = [
JSON.stringify({ msg: 1, time: '2018-01-04T19:17:00.014Z' }),
JSON.stringify({ msg: 3, time: '2018-01-04T19:18:00.014Z' }),
'',
].join('\n');
fs.writeFileSync(path.join(basePath, 'first.log'), first);
fs.writeFileSync(path.join(basePath, 'second.log'), second);
return fetch(basePath).then((results) => {
expect(results).to.have.length(4);
expect(results[0].msg).to.equal(1);
expect(results[1].msg).to.equal(2);
expect(results[2].msg).to.equal(3);
});
});
});
});