Skip to content

Commit

Permalink
Add tests for client.js
Browse files Browse the repository at this point in the history
  • Loading branch information
mantoni committed Aug 21, 2018
1 parent 907847c commit 451978d
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 59 deletions.
2 changes: 1 addition & 1 deletion bin/eslint_d.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
'use strict';

function start() {
require('../lib/launcher')();
require('../lib/launcher').launch();
}

const cmd = process.argv[2];
Expand Down
70 changes: 37 additions & 33 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ function connect(callback) {
const socket = net.connect(config.port, '127.0.0.1', () => {
callback(null, socket, config.token);
});
socket.on('error', () => {
socket.once('error', () => {
process.exitCode = 1;
callback('Could not connect');
});
});
Expand All @@ -39,7 +40,7 @@ exports.status = function () {
return;
}
socket.on('data', (chunk) => {
process.stdout.write(String(chunk));
process.stdout.write(chunk);
});
socket.on('end', () => {
process.stdout.write('\n');
Expand All @@ -48,47 +49,50 @@ exports.status = function () {
});
};

function lint(socket, token, args, text) {
let buf = '';
socket.on('data', (chunk) => {
buf += chunk;
const p = buf.lastIndexOf('\n');
if (p !== -1) {
process.stdout.write(buf.substring(0, p + 1));
buf = buf.substring(p + 1);
}
});
socket.on('end', () => {
if (buf) {
if (buf === '# exit 1') {
process.exitCode = 1;
} else {
process.stdout.write(buf);
}
}
});
const cwd = process.cwd();
socket.end(`${token} ${JSON.stringify({ cwd, args, text })}`);
}

exports.lint = function (args, text) {
if (!args.length && !text) {
process.stdout.write('No files specified\n');
return;
}

function lint(socket, token) {
let buf = '';
socket.on('data', (chunk) => {
buf += chunk;
const p = buf.lastIndexOf('\n');
if (p !== -1) {
process.stdout.write(buf.substring(0, p + 1));
buf = buf.substring(p + 1);
connect((err, socket, token) => {
if (err) {
if (process.exitCode === 1) {
process.stdout.write(`${err}\n`);
return;
}
});
socket.on('end', () => {
if (buf) {
if (buf === '# exit 1') {
require('./launcher').launch((err, socket, token) => {
if (err) {
process.stdout.write(`${err}\n`);
process.exitCode = 1;
} else {
process.stdout.write(buf);
return;
}
}
});
const cwd = process.cwd();
socket.end(`${token} ${JSON.stringify({ cwd, args, text })}`);
}
connect((err, socket, token) => {
if (err) {
require('./launcher')(() => {
connect((err, socket, token) => {
if (err) {
process.stdout.write(`${err}\n`);
return;
}
lint(socket, token);
});
lint(socket, token, args, text);
});
} else {
lint(socket, token);
lint(socket, token, args, text);
}
});
};
38 changes: 20 additions & 18 deletions lib/launcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,32 @@ const net = require('net');
const portfile = require('../lib/portfile');

function check(callback) {
portfile.read((data) => {
if (!data) {
callback(false);
portfile.read((config) => {
if (!config) {
callback('Not running');
return;
}
const socket = net.connect(data.port, '127.0.0.1', () => {
socket.end();
callback(true);
const socket = net.connect(config.port, '127.0.0.1', () => {
callback(null, socket, config.token);
});
socket.once('error', () => {
callback(false);
callback('Could not connect');
});
});
}

function wait(callback) {
check((running) => {
if (running) {
if (typeof callback === 'function') {
callback(null);
}
} else {
check((err, socket, token) => {
if (err) {
setTimeout(() => {
wait(callback);
}, 100);
return;
}
if (typeof callback === 'function') {
callback(null, socket, token);
} else {
socket.end();
}
});
}
Expand All @@ -51,12 +52,13 @@ function launch(callback) {
}, 100);
}

module.exports = function (callback) {
check((running) => {
if (running) {
process.stdout.write('Already running\n');
} else {
exports.launch = function (callback) {
check((err, socket) => {
if (err) {
launch(callback);
} else {
socket.end();
process.stdout.write('Already running\n');
}
});
};
218 changes: 218 additions & 0 deletions test/client-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/*eslint-env mocha*/
'use strict';

const net = require('net');
const crypto = require('crypto');
const EventEmitter = require('events');
const { assert, refute, sinon } = require('@sinonjs/referee-sinon');
const client = require('../lib/client');
const portfile = require('../lib/portfile');
const launcher = require('../lib/launcher');

const token = crypto.randomBytes(8).toString('hex');

describe('client', () => {
let socket;

beforeEach(() => {
socket = new EventEmitter();
socket.end = sinon.fake();
sinon.replace(process.stdout, 'write', sinon.fake());
});

afterEach(() => {
sinon.restore();
delete process.exitCode;
});

function verifyNotRunning(method, ...args) {
sinon.replace(portfile, 'read', sinon.fake.yields(null));

client[method](...args);

assert.calledOnceWith(process.stdout.write, 'Not running\n');
refute.defined(process.exitCode);
}

function verifyCouldNotConnect(method, ...args) {
sinon.replace(portfile, 'read', sinon.fake.yields({ port: 4321, token }));
sinon.replace(net, 'connect', sinon.fake.returns(socket));

client[method](...args);

assert.calledOnceWith(net.connect, 4321, '127.0.0.1', sinon.match.func);

socket.emit('error', new Error());

assert.calledOnceWith(process.stdout.write, 'Could not connect\n');
assert.equals(process.exitCode, 1);
}

describe('status', () => {

it('prints "Not running" if portfile cannot be read', () => {
verifyNotRunning('status');
});

it('prints "Could not connect" if connection fails', () => {
verifyCouldNotConnect('status');
});

it('sends token and "status" command to server', () => {
sinon.replace(portfile, 'read', sinon.fake.yields({ port: 4321, token }));
sinon.replace(net, 'connect', sinon.fake.returns(socket));

client.status();
net.connect.firstCall.callback();

assert.calledOnceWith(socket.end, `${token} status`);

socket.emit('data', 'Some response');
socket.emit('end');
assert.calledWith(process.stdout.write, 'Some response');
assert.calledWith(process.stdout.write, '\n');
refute.defined(process.exitCode);
});

});

describe('stop', () => {

it('prints "Not running" if portfile cannot be read', () => {
verifyNotRunning('stop');
});

it('prints "Could not connect" if connection fails', () => {
verifyCouldNotConnect('stop');
});

it('sends token and "stop" command to server', () => {
sinon.replace(portfile, 'read', sinon.fake.yields({ port: 4321, token }));
sinon.replace(net, 'connect', sinon.fake.returns(socket));
const callback = sinon.fake();

client.stop(callback);
net.connect.firstCall.callback();

assert.calledOnceWith(socket.end, `${token} stop`);
refute.called(process.stdout.write);
refute.called(callback);

socket.end.firstCall.callback();

assert.calledOnce(callback);
refute.defined(process.exitCode);
});

it('does not fail if no callback was given', () => {
sinon.replace(portfile, 'read', sinon.fake.yields({ port: 4321, token }));
sinon.replace(net, 'connect', sinon.fake.returns(socket));

client.stop();
net.connect.firstCall.callback();

refute.exception(() => {
socket.end.firstCall.callback();
});
});

});

describe('lint', () => {
const cwd = process.cwd();
const args = ['--some', '-t'];
const text = '"use strict";\nconsole.log("Lint this!");';

function lint() {
sinon.replace(portfile, 'read', sinon.fake.yields({ port: 4321, token }));
sinon.replace(net, 'connect', sinon.fake.returns(socket));

client.lint(args, text);
net.connect.firstCall.callback();
}

function verifyLinting() {
const json = JSON.stringify({ cwd, args, text });
assert.calledOnceWith(socket.end, `${token} ${json}`);

socket.emit('data', 'Some response');
socket.emit('end');
assert.calledOnceWith(process.stdout.write, 'Some response');
refute.defined(process.exitCode);
}

it('sends token and { cwd, args, text } to server', () => {
lint();

verifyLinting();
});

it('sets exitCode to 1 if response ends with `# exit 1`', () => {
lint();

socket.emit('data', 'Some response\n# exit 1');
socket.emit('end');

assert.calledOnceWith(process.stdout.write, 'Some response\n');
assert.equals(process.exitCode, 1);
});

it('streams lines', () => {
lint();

socket.emit('data', 'Some ');
socket.emit('data', 'response\nfrom ');
socket.emit('data', 'eslint');
socket.emit('end');

assert.calledWith(process.stdout.write, 'Some response\n');
assert.calledWith(process.stdout.write, 'from eslint');
refute.defined(process.exitCode);
});

function launch() {
sinon.replace(portfile, 'read', sinon.fake.yields(null));
sinon.replace(launcher, 'launch', sinon.fake());

client.lint(args, text);
}

it('invokes launcher if not running, then lints', () => {
launch();

assert.calledOnce(launcher.launch);
});

it('send token and json to server once launched successfully', () => {
launch();

launcher.launch.firstCall.callback(null, socket, token);

verifyLinting();
});

it('fails if launcher fails', () => {
launch();

launcher.launch.firstCall.callback('Could not connect');

assert.calledOnceWith(process.stdout.write, 'Could not connect\n');
assert.equals(process.exitCode, 1);
});

it('does not invoke launcher on connection failure', () => {
sinon.replace(portfile, 'read', sinon.fake.yields({ port: 4321, token }));
sinon.replace(net, 'connect', sinon.fake.returns(socket));
sinon.replace(launcher, 'launch', sinon.fake());

client.lint(args, text);
socket.emit('error', new Error());

refute.called(launcher.launch);
assert.calledOnceWith(process.stdout.write, 'Could not connect\n');
assert.equals(process.exitCode, 1);
});

});

});
Loading

0 comments on commit 451978d

Please sign in to comment.