eslint_d
Version:
Speed up eslint to accelerate your development workflow
275 lines (210 loc) • 8.08 kB
JavaScript
import fs from 'node:fs';
import fs_promises from 'node:fs/promises';
import { resolve } from 'node:path';
import crypto from 'node:crypto';
import net from 'node:net';
import { assert, refute, match, sinon } from '@sinonjs/referee-sinon';
describe('lib/daemon', () => {
const randome_bytes = Buffer.from('token');
const project = 'test/fixture/v9.0.x';
const base = resolve(`${project}/node_modules/eslint`);
let now = Date.now();
let fs_write_promise;
let server;
let watcher;
let argv;
function returnThis() {
// @ts-ignore
return this;
}
beforeEach(() => {
server = new net.Server();
sinon.replace(console, 'error', sinon.fake());
sinon.replace(process, 'on', sinon.fake());
sinon.replace(process, 'exit', sinon.fake());
sinon.replace(crypto, 'randomBytes', sinon.fake.returns(randome_bytes));
sinon.replace(server, 'listen', sinon.fake());
sinon.replace(server, 'address', sinon.fake.returns({ port: 1234 }));
sinon.replace(server, 'close', sinon.fake.yields());
sinon.replace(net, 'createServer', sinon.fake.returns(server));
fs_write_promise = sinon.promise();
sinon.replace(
fs_promises,
'writeFile',
sinon.fake.returns(fs_write_promise)
);
sinon.replace(fs_promises, 'unlink', sinon.fake.resolves());
watcher = { on: sinon.fake(returnThis), close: sinon.fake() };
sinon.replace(fs, 'watch', sinon.fake.returns(watcher));
sinon.replace(fs, 'readFile', sinon.fake());
argv = ['node', 'eslint_d', '0', '0', base, 'hash'];
sinon.replace(process, 'argv', argv);
});
function load() {
return import(`./daemon.js?now=${now++}`);
}
it('creates server, listens on port and writes config when ready', async () => {
await load();
assert.calledOnceWith(net.createServer, { allowHalfOpen: true });
assert.calledOnceWith(server.listen, 0, '127.0.0.1', match.func);
refute.called(fs_promises.writeFile);
server.listen.callback();
assert.calledOnceWith(
fs_promises.writeFile,
`${base}/.eslint_d`,
`${randome_bytes.toString('hex')} 1234 ${process.pid} hash`
);
refute.called(fs.watch);
});
it('watches config file when fs.writeFile succeeded', async () => {
await load();
server.listen.callback();
fs_write_promise.resolve();
await new Promise(setImmediate);
assert.calledOnceWith(fs.watch, `${base}/.eslint_d`, { persistent: false });
});
it('logs error runs shutdown if config file write fails', async () => {
await load();
server.listen.callback();
fs_write_promise.reject(new Error('Oh noes!'));
await new Promise(setImmediate);
refute.called(fs.watch);
assert.calledOnceWith(console.error, 'eslint_d: Error: Oh noes!');
assert.calledOnceWith(server.close, match.func);
assert.calledOnceWith(fs_promises.unlink, `${base}/.eslint_d`);
assert.calledOnceWith(process.exit, 0);
});
it('runs shutdown on SIGTERM', async () => {
await load();
server.listen.callback();
fs_write_promise.resolve();
await new Promise(setImmediate);
const onSigterm = process.on['firstCall'];
assert.equals(onSigterm.args[0], 'SIGTERM');
onSigterm.args[1]();
await new Promise(setImmediate);
assert.calledOnce(watcher.close);
assert.calledOnceWith(server.close, match.func);
assert.calledOnceWith(fs_promises.unlink, `${base}/.eslint_d`);
assert.calledOnceWith(process.exit, 0);
});
context('ppid', () => {
let clock;
beforeEach(() => {
clock = sinon.useFakeTimers({ toFake: ['setInterval', 'clearInterval'] });
});
it('does not invoke process.kill if ppid is "0"', async () => {
sinon.replace(process, 'kill', sinon.fake());
argv[2] = '0';
await load();
clock.tick(1000);
refute.called(process.kill);
});
it('invokes process.kill every second if ppid is "1234"', async () => {
sinon.replace(process, 'kill', sinon.fake());
argv[2] = '1234';
await load();
clock.tick(1000);
assert.calledOnceWith(process.kill, 1234, 0);
process.kill['resetHistory']();
clock.tick(1000);
assert.calledOnceWith(process.kill, 1234, 0);
});
it('runs shutdown if process.kill throws', async () => {
sinon.replace(process, 'kill', sinon.fake.throws(new Error('Bye!')));
argv[2] = '1234';
await load();
clock.tick(1000);
await new Promise(setImmediate);
assert.calledOnceWith(server.close, match.func);
assert.calledOnceWith(fs_promises.unlink, `${base}/.eslint_d`);
assert.calledOnceWith(process.exit, 0);
server.close.resetHistory();
fs_promises.unlink['resetHistory']();
process.exit['resetHistory']();
clock.tick(1000);
refute.called(server.close);
refute.called(fs_promises.unlink);
refute.called(process.exit);
});
});
context('idle', () => {
let clock;
beforeEach(() => {
clock = sinon.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] });
});
it('does not shutdown automatically if idle is "0"', async () => {
argv[3] = '0';
await load();
clock.tick(24 * 60 * 60 * 1000);
await new Promise(setImmediate);
refute.called(server.close);
refute.called(fs_promises.unlink);
refute.called(process.exit);
});
it('runs shutdown after configured minutes', async () => {
argv[3] = '2';
await load();
clock.tick(2 * 60 * 1000);
await new Promise(setImmediate);
assert.calledOnceWith(server.close, match.func);
assert.calledOnceWith(fs_promises.unlink, `${base}/.eslint_d`);
assert.calledOnceWith(process.exit, 0);
});
it('does not run shutdown if service is invoked', async () => {
argv[3] = '2';
await load();
clock.tick(2 * 60 * 1000 - 1);
net.createServer['callback'](new net.Socket());
clock.tick(1);
refute.called(server.close);
refute.called(fs_promises.unlink);
refute.called(process.exit);
clock.tick(2 * 60 * 1000 - 2);
refute.called(server.close);
refute.called(fs_promises.unlink);
refute.called(process.exit);
clock.tick(1);
await new Promise(setImmediate);
assert.calledOnceWith(server.close, match.func);
assert.calledOnceWith(fs_promises.unlink, `${base}/.eslint_d`);
assert.calledOnceWith(process.exit, 0);
});
});
context('process title', () => {
it('sets process title to "eslint_d" and reads project package.json', async () => {
await load();
assert.equals(process.title, 'eslint_d');
assert.calledOnceWith(
fs.readFile,
`${resolve(project)}/package.json`,
'utf8'
);
});
it('sets process title to "eslint_d - <cwd>" if project package.json cannot be read', async () => {
await load();
fs.readFile['callback'](new Error('Nope!'));
assert.equals(process.title, `eslint_d - ${process.cwd()}`);
});
it('sets process title to "eslint_d - <cwd>" if project package.json is not valid JSON', async () => {
await load();
fs.readFile['callback'](null, 'not json');
assert.equals(process.title, `eslint_d - ${process.cwd()}`);
});
it('sets process title to "eslint_d - <cwd>" if project package.json does not have a "name"', async () => {
await load();
fs.readFile['callback'](null, '{}');
assert.equals(process.title, `eslint_d - ${process.cwd()}`);
});
it('sets process title to "eslint_d - <name>" of "name" in project package.json', async () => {
await load();
fs.readFile['callback'](null, '{"name":"my-project"}');
assert.equals(process.title, `eslint_d - my-project`);
});
it('sets process title to "eslint_d - global" of "name" in project package.json is "eslint_d"', async () => {
await load();
fs.readFile['callback'](null, '{"name":"eslint_d"}');
assert.equals(process.title, `eslint_d - global`);
});
});
});