UNPKG

eslint_d

Version:

Speed up eslint to accelerate your development workflow

275 lines (210 loc) 8.1 kB
import fs from 'node:fs'; import fs_promises from 'node:fs/promises'; import { normalize, 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, normalize(`${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`); }); }); });