One level down

pull/22/merge
Maciej Winnicki 2013-11-24 13:55:47 +01:00
parent 4588a7a611
commit 317240d02f
8 changed files with 453 additions and 467 deletions

View File

@ -23,7 +23,7 @@
"browser": true,
"globals": {
"io": false,
"io": true,
"describe": false,
"it": false,
"beforeEach": false,

238
index.js
View File

@ -1,3 +1,5 @@
'use strict';
var connect = require('connect');
var cookieParser = require('cookie');
var crypto = require('crypto');
@ -10,132 +12,128 @@ var tail = require('./lib/tail');
var connectBuilder = require('./lib/connect_builder');
var serverBuilder = require('./lib/server_builder');
(function () {
'use strict';
program
.version(require('./package.json').version)
.usage('[options] [file ...]')
.option('-h, --host <host>', 'listening host, default 0.0.0.0', String, '0.0.0.0')
.option('-p, --port <port>', 'listening port, default 9001', Number, 9001)
.option('-n, --number <number>', 'starting lines number, default 10', Number, 10)
.option('-l, --lines <lines>', 'number on lines stored in browser, default 2000', Number, 2000)
.option('-t, --theme <theme>', 'name of the theme (default, dark)', String, 'default')
.option('-d, --daemonize', 'run as daemon')
.option('-U, --user <username>',
'Basic Authentication username, option works only along with -P option', String, false)
.option('-P, --password <password>',
'Basic Authentication password, option works only along with -U option', String, false)
.option('-k, --key <key.pem>',
'Private Key for HTTPS, option works only along with -c option',
String, false)
.option('-c, --certificate <cert.pem>',
'Certificate for HTTPS, option works only along with -k option',
String, false)
.option('--pid-path <path>',
'if run as daemon file that will store the process id, default /var/run/frontail.pid',
String, '/var/run/frontail.pid')
.option('--log-path <path>', 'if run as daemon file that will be used as a log, default /dev/null',
String, '/dev/null')
.parse(process.argv);
program
.version(require('./package.json').version)
.usage('[options] [file ...]')
.option('-h, --host <host>', 'listening host, default 0.0.0.0', String, '0.0.0.0')
.option('-p, --port <port>', 'listening port, default 9001', Number, 9001)
.option('-n, --number <number>', 'starting lines number, default 10', Number, 10)
.option('-l, --lines <lines>', 'number on lines stored in browser, default 2000', Number, 2000)
.option('-t, --theme <theme>', 'name of the theme (default, dark)', String, 'default')
.option('-d, --daemonize', 'run as daemon')
.option('-U, --user <username>',
'Basic Authentication username, option works only along with -P option', String, false)
.option('-P, --password <password>',
'Basic Authentication password, option works only along with -U option', String, false)
.option('-k, --key <key.pem>',
'Private Key for HTTPS, option works only along with -c option',
String, false)
.option('-c, --certificate <cert.pem>',
'Certificate for HTTPS, option works only along with -k option',
String, false)
.option('--pid-path <path>',
'if run as daemon file that will store the process id, default /var/run/frontail.pid',
String, '/var/run/frontail.pid')
.option('--log-path <path>', 'if run as daemon file that will be used as a log, default /dev/null',
String, '/dev/null')
.parse(process.argv);
if (program.args.length === 0) {
console.error('Arguments needed, use --help');
process.exit();
}
if (program.args.length === 0) {
console.error('Arguments needed, use --help');
process.exit();
/**
* Validate params
*/
var doAuthorization = !!(program.user && program.password);
var doSecure = !!(program.key && program.certificate);
var sessionSecret = String(+new Date()) + Math.random();
var sessionKey = 'sid';
var files = program.args.join(' ');
var filesNamespace = crypto.createHash('md5').update(files).digest('hex');
if (program.daemonize) {
/**
* Daemonize process
*/
var logFile = fs.openSync(program.logPath, 'a');
var args = ['-p', program.port, '-n', program.number, '-l', program.lines, '-t', program.theme];
if (doAuthorization) {
args = args.concat(['-U', program.user, '-P', program.password]);
}
args = args.concat(program.args);
var proc = daemon.daemon(
__filename,
args,
{
stdout: logFile,
stderr: logFile
}
);
fs.writeFileSync(program.pidPath, proc.pid);
} else {
/**
* HTTP(s) server setup
*/
var appBuilder = connectBuilder();
if (doAuthorization) {
appBuilder.session(sessionSecret, sessionKey);
appBuilder.authorize(program.user, program.password);
}
appBuilder
.static(__dirname + '/lib/web/assets')
.index(__dirname + '/lib/web/index.html', files, filesNamespace, program.theme);
var builder = serverBuilder();
if (doSecure) {
builder.secure(program.key, program.certificate);
}
var server = builder
.use(appBuilder.build())
.port(program.port)
.host(program.host)
.build();
/**
* socket.io setup
*/
var io = socketio.listen(server, {log: false});
if (doAuthorization) {
io.set('authorization', function (handshakeData, accept) {
if (handshakeData.headers.cookie) {
var cookie = cookieParser.parse(handshakeData.headers.cookie);
var sessionId = connect.utils.parseSignedCookie(cookie[sessionKey], sessionSecret);
if (sessionId) {
return accept(null, true);
}
return accept('Invalid cookie', false);
} else {
return accept('No cookie in header', false);
}
});
}
/**
* Validate params
* When connected send starting data
*/
var doAuthorization = !!(program.user && program.password);
var doSecure = !!(program.key && program.certificate);
var sessionSecret = String(+new Date()) + Math.random();
var sessionKey = 'sid';
var files = program.args.join(' ');
var filesNamespace = crypto.createHash('md5').update(files).digest('hex');
var tailer = tail(program.args, {buffer: program.number});
var filesSocket = io.of('/' + filesNamespace).on('connection', function (socket) {
socket.emit('options:lines', program.lines);
if (program.daemonize) {
/**
* Daemonize process
*/
var logFile = fs.openSync(program.logPath, 'a');
var args = ['-p', program.port, '-n', program.number, '-l', program.lines, '-t', program.theme];
if (doAuthorization) {
args = args.concat(['-U', program.user, '-P', program.password]);
}
args = args.concat(program.args);
var proc = daemon.daemon(
__filename,
args,
{
stdout: logFile,
stderr: logFile
}
);
fs.writeFileSync(program.pidPath, proc.pid);
} else {
/**
* HTTP(s) server setup
*/
var appBuilder = connectBuilder();
if (doAuthorization) {
appBuilder.session(sessionSecret, sessionKey);
appBuilder.authorize(program.user, program.password);
}
appBuilder
.static(__dirname + '/lib/web/assets')
.index(__dirname + '/lib/web/index.html', files, filesNamespace, program.theme);
var builder = serverBuilder();
if (doSecure) {
builder.secure(program.key, program.certificate);
}
var server = builder
.use(appBuilder.build())
.port(program.port)
.host(program.host)
.build();
/**
* socket.io setup
*/
var io = socketio.listen(server, {log: false});
if (doAuthorization) {
io.set('authorization', function (handshakeData, accept) {
if (handshakeData.headers.cookie) {
var cookie = cookieParser.parse(handshakeData.headers.cookie);
var sessionId = connect.utils.parseSignedCookie(cookie[sessionKey], sessionSecret);
if (sessionId) {
return accept(null, true);
}
return accept('Invalid cookie', false);
} else {
return accept('No cookie in header', false);
}
});
}
/**
* When connected send starting data
*/
var tailer = tail(program.args, {buffer: program.number});
var filesSocket = io.of('/' + filesNamespace).on('connection', function (socket) {
socket.emit('options:lines', program.lines);
tailer.getBuffer().forEach(function (line) {
socket.emit('line', line);
});
tailer.getBuffer().forEach(function (line) {
socket.emit('line', line);
});
});
/**
* Send incoming data
*/
tailer.on('line', function (line) {
filesSocket.emit('line', sanitizer(line).xss());
});
}
})();
/**
* Send incoming data
*/
tailer.on('line', function (line) {
filesSocket.emit('line', sanitizer(line).xss());
});
}

View File

@ -1,55 +1,53 @@
'use strict';
var connect = require('connect');
var fs = require('fs');
(function () {
'use strict';
var ConnectBuilder = function () {
this.app = connect();
};
var ConnectBuilder = function () {
this.app = connect();
};
ConnectBuilder.prototype.authorize = function (user, pass) {
this.app.use(connect.basicAuth(function (incomingUser, incomingPass) {
return user === incomingUser && pass === incomingPass;
}));
ConnectBuilder.prototype.authorize = function (user, pass) {
this.app.use(connect.basicAuth(function (incomingUser, incomingPass) {
return user === incomingUser && pass === incomingPass;
}));
return this;
};
return this;
};
ConnectBuilder.prototype.build = function () {
return this.app;
};
ConnectBuilder.prototype.build = function () {
return this.app;
};
ConnectBuilder.prototype.index = function (path, files, filesNamespace, theme) {
theme = theme || 'default';
ConnectBuilder.prototype.index = function (path, files, filesNamespace, theme) {
theme = theme || 'default';
this.app.use(function (req, res) {
fs.readFile(path, function (err, data) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data.toString('utf-8')
.replace(/__TITLE__/g, files)
.replace(/__THEME__/g, theme)
.replace(/__NAMESPACE__/g, filesNamespace),
'utf-8'
);
});
this.app.use(function (req, res) {
fs.readFile(path, function (err, data) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data.toString('utf-8')
.replace(/__TITLE__/g, files)
.replace(/__THEME__/g, theme)
.replace(/__NAMESPACE__/g, filesNamespace),
'utf-8'
);
});
});
return this;
};
return this;
};
ConnectBuilder.prototype.session = function (secret, key) {
this.app.use(connect.cookieParser());
this.app.use(connect.session({secret: secret, key: key}));
return this;
};
ConnectBuilder.prototype.session = function (secret, key) {
this.app.use(connect.cookieParser());
this.app.use(connect.session({secret: secret, key: key}));
return this;
};
ConnectBuilder.prototype.static = function (path) {
this.app.use(connect.static(path));
return this;
};
ConnectBuilder.prototype.static = function (path) {
this.app.use(connect.static(path));
return this;
};
module.exports = function () {
return new ConnectBuilder();
};
})();
module.exports = function () {
return new ConnectBuilder();
};

View File

@ -1,54 +1,52 @@
'use strict';
var fs = require('fs');
var http = require('http');
var https = require('https');
(function () {
'use strict';
var ServerBuilder = function () {
this._host = null;
this._port = 9001;
};
var ServerBuilder = function () {
this._host = null;
this._port = 9001;
};
ServerBuilder.prototype.build = function () {
if (this._key && this._cert) {
var options = {
key: this._key,
cert: this._cert
};
return https.createServer(options, this._callback).listen(this._port, this._host);
} else {
return http.createServer(this._callback).listen(this._port, this._host);
}
};
ServerBuilder.prototype.build = function () {
if (this._key && this._cert) {
var options = {
key: this._key,
cert: this._cert
};
return https.createServer(options, this._callback).listen(this._port, this._host);
} else {
return http.createServer(this._callback).listen(this._port, this._host);
}
};
ServerBuilder.prototype.host = function (host) {
this._host = host;
return this;
};
ServerBuilder.prototype.host = function (host) {
this._host = host;
return this;
};
ServerBuilder.prototype.port = function (port) {
this._port = port;
return this;
};
ServerBuilder.prototype.port = function (port) {
this._port = port;
return this;
};
ServerBuilder.prototype.secure = function (keyPath, certPath) {
try {
this._key = fs.readFileSync(keyPath);
this._cert = fs.readFileSync(certPath);
} catch (e) {
throw new Error('No key or certificate file found');
}
ServerBuilder.prototype.secure = function (keyPath, certPath) {
try {
this._key = fs.readFileSync(keyPath);
this._cert = fs.readFileSync(certPath);
} catch (e) {
throw new Error('No key or certificate file found');
}
return this;
};
return this;
};
ServerBuilder.prototype.use = function (callback) {
this._callback = callback;
return this;
};
ServerBuilder.prototype.use = function (callback) {
this._callback = callback;
return this;
};
module.exports = function () {
return new ServerBuilder();
};
})();
module.exports = function () {
return new ServerBuilder();
};

View File

@ -1,45 +1,43 @@
'use strict';
var EventEmitter = require('events').EventEmitter;
var spawn = require('child_process').spawn;
var util = require('util');
(function () {
'use strict';
var Tail = function (path, options) {
EventEmitter.call(this);
var Tail = function (path, options) {
EventEmitter.call(this);
this.buffer = [];
this.options = options || {buffer: 0};
this.buffer = [];
this.options = options || {buffer: 0};
var self = this;
var tail = spawn('tail', ['-F'].concat(path));
tail.stdout.on('data', function (data) {
var self = this;
var tail = spawn('tail', ['-F'].concat(path));
tail.stdout.on('data', function (data) {
var lines = data.toString('utf-8');
lines = lines.split('\n');
lines.pop();
lines.forEach(function (line) {
if (self.options.buffer) {
if (self.buffer.length === self.options.buffer) {
self.buffer.shift();
}
self.buffer.push(line);
var lines = data.toString('utf-8');
lines = lines.split('\n');
lines.pop();
lines.forEach(function (line) {
if (self.options.buffer) {
if (self.buffer.length === self.options.buffer) {
self.buffer.shift();
}
self.emit('line', line);
});
self.buffer.push(line);
}
self.emit('line', line);
});
});
process.on('exit', function () {
tail.kill();
});
};
util.inherits(Tail, EventEmitter);
process.on('exit', function () {
tail.kill();
});
};
util.inherits(Tail, EventEmitter);
Tail.prototype.getBuffer = function () {
return this.buffer;
};
Tail.prototype.getBuffer = function () {
return this.buffer;
};
module.exports = function (path, options) {
return new Tail(path, options);
};
})();
module.exports = function (path, options) {
return new Tail(path, options);
};

View File

@ -1,109 +1,107 @@
'use strict';
var connectBuilder = require('../lib/connect_builder');
var request = require('supertest');
(function () {
'use strict';
describe('connectBuilder', function () {
it('should build connect app', function () {
connectBuilder().build().should.have.property('use');
connectBuilder().build().should.have.property('listen');
});
it('should build app requiring authorized user', function (done) {
var app = connectBuilder().authorize('user', 'pass').build();
request(app)
.get('/')
.expect('www-authenticate', 'Basic realm="Authorization Required"')
.expect(401, done);
});
it('should build app allowing user to login', function (done) {
var app = connectBuilder().authorize('user', 'pass').build();
app.use(function (req, res) {
res.end('secret!');
});
request(app)
.get('/')
.set('Authorization', 'Basic dXNlcjpwYXNz')
.expect(200, 'secret!', done);
});
it('should build app that setup session', function (done) {
var app = connectBuilder().session('secret', 'sessionkey').build();
app.use(function (req, res) {
res.end();
});
request(app)
.get('/')
.expect('set-cookie', /^sessionkey/, done);
});
it('should build app that serve static files', function (done) {
var app = connectBuilder().static(__dirname + '/fixtures').build();
request(app)
.get('/foo')
.expect('bar', done);
});
it('should build app that serve index file', function (done) {
var app = connectBuilder().index(__dirname + '/fixtures/index', '/testfile').build();
request(app)
.get('/')
.expect(200)
.expect('Content-Type', 'text/html', done);
});
it('should build app that replace index title', function (done) {
var app = connectBuilder()
.index(__dirname + '/fixtures/index_with_title', '/testfile')
.build();
request(app)
.get('/')
.expect('<head><title>/testfile</title></head>', done);
});
it('builds app that sets socket.io namespace based on files', function (done) {
var app = connectBuilder()
.index(__dirname + '/fixtures/index_with_ns', '/testfile', 'ns', 'dark')
.build();
request(app)
.get('/')
.expect('ns', done);
});
it('should build app that sets theme', function (done) {
var app = connectBuilder()
.index(__dirname + '/fixtures/index_with_theme', '/testfile', 'ns', 'dark')
.build();
request(app)
.get('/')
.expect(
'<head><title>/testfile</title><link href="dark.css" rel="stylesheet" type="text/css"/></head>',
done
);
});
it('should build app that sets default theme', function (done) {
var app = connectBuilder()
.index(__dirname + '/fixtures/index_with_theme', '/testfile')
.build();
request(app)
.get('/')
.expect(
'<head><title>/testfile</title><link href="default.css" rel="stylesheet" type="text/css"/></head>',
done
);
});
describe('connectBuilder', function () {
it('should build connect app', function () {
connectBuilder().build().should.have.property('use');
connectBuilder().build().should.have.property('listen');
});
})();
it('should build app requiring authorized user', function (done) {
var app = connectBuilder().authorize('user', 'pass').build();
request(app)
.get('/')
.expect('www-authenticate', 'Basic realm="Authorization Required"')
.expect(401, done);
});
it('should build app allowing user to login', function (done) {
var app = connectBuilder().authorize('user', 'pass').build();
app.use(function (req, res) {
res.end('secret!');
});
request(app)
.get('/')
.set('Authorization', 'Basic dXNlcjpwYXNz')
.expect(200, 'secret!', done);
});
it('should build app that setup session', function (done) {
var app = connectBuilder().session('secret', 'sessionkey').build();
app.use(function (req, res) {
res.end();
});
request(app)
.get('/')
.expect('set-cookie', /^sessionkey/, done);
});
it('should build app that serve static files', function (done) {
var app = connectBuilder().static(__dirname + '/fixtures').build();
request(app)
.get('/foo')
.expect('bar', done);
});
it('should build app that serve index file', function (done) {
var app = connectBuilder().index(__dirname + '/fixtures/index', '/testfile').build();
request(app)
.get('/')
.expect(200)
.expect('Content-Type', 'text/html', done);
});
it('should build app that replace index title', function (done) {
var app = connectBuilder()
.index(__dirname + '/fixtures/index_with_title', '/testfile')
.build();
request(app)
.get('/')
.expect('<head><title>/testfile</title></head>', done);
});
it('builds app that sets socket.io namespace based on files', function (done) {
var app = connectBuilder()
.index(__dirname + '/fixtures/index_with_ns', '/testfile', 'ns', 'dark')
.build();
request(app)
.get('/')
.expect('ns', done);
});
it('should build app that sets theme', function (done) {
var app = connectBuilder()
.index(__dirname + '/fixtures/index_with_theme', '/testfile', 'ns', 'dark')
.build();
request(app)
.get('/')
.expect(
'<head><title>/testfile</title><link href="dark.css" rel="stylesheet" type="text/css"/></head>',
done
);
});
it('should build app that sets default theme', function (done) {
var app = connectBuilder()
.index(__dirname + '/fixtures/index_with_theme', '/testfile')
.build();
request(app)
.get('/')
.expect(
'<head><title>/testfile</title><link href="default.css" rel="stylesheet" type="text/css"/></head>',
done
);
});
});

View File

@ -1,115 +1,113 @@
'use strict';
var fs = require('fs');
var http = require('http');
var https = require('https');
var serverBuilder = require('../lib/server_builder');
var sinon = require('sinon');
(function () {
'use strict';
describe('serverBuilder', function () {
describe('serverBuilder', function () {
describe('http server', function () {
var httpServer;
var createServer;
describe('http server', function () {
var httpServer;
var createServer;
beforeEach(function () {
httpServer = sinon.createStubInstance(http.Server);
httpServer.listen.returns(httpServer);
createServer = sinon.stub(http, 'createServer').returns(httpServer);
});
afterEach(function () {
createServer.restore();
});
it('should build server', function () {
var server = serverBuilder().build();
createServer.calledOnce.should.equal(true);
server.should.be.an.instanceof(http.Server);
});
it('should build server accepting requests', function () {
var callback = function () {};
serverBuilder().use(callback).build();
createServer.calledWith(callback).should.equal(true);
});
it('should build listening server', function () {
serverBuilder().build();
httpServer.listen.calledOnce.should.equal(true);
});
it('should build server listening on specified port', function () {
serverBuilder().port(6666).build();
httpServer.listen.calledWith(6666).should.equal(true);
});
it('should build server listening on default port', function () {
serverBuilder().build();
httpServer.listen.calledWith(9001).should.equal(true);
});
it('should build server listening on specified host', function () {
serverBuilder().host('127.0.0.1').build();
httpServer.listen.calledWith(9001, '127.0.0.1').should.equal(true);
});
it('should build server listening on default host', function () {
serverBuilder().build();
httpServer.listen.calledWith(9001, null).should.equal(true);
});
beforeEach(function () {
httpServer = sinon.createStubInstance(http.Server);
httpServer.listen.returns(httpServer);
createServer = sinon.stub(http, 'createServer').returns(httpServer);
});
describe('https server', function () {
var httpsServer;
var createHttpsServer;
var readFileSyncStub;
afterEach(function () {
createServer.restore();
});
beforeEach(function () {
httpsServer = sinon.createStubInstance(https.Server);
httpsServer.listen.returns(httpsServer);
createHttpsServer = sinon.stub(https, 'createServer').returns(httpsServer);
readFileSyncStub = sinon.stub(fs, 'readFileSync');
readFileSyncStub.withArgs('key.pem').returns('testkey');
readFileSyncStub.withArgs('cert.pem').returns('testcert');
});
it('should build server', function () {
var server = serverBuilder().build();
afterEach(function () {
createHttpsServer.restore();
readFileSyncStub.restore();
});
createServer.calledOnce.should.equal(true);
server.should.be.an.instanceof(http.Server);
});
it('should build server', function () {
var server = serverBuilder().secure('key.pem', 'cert.pem').build();
it('should build server accepting requests', function () {
var callback = function () {};
server.should.be.an.instanceof(https.Server);
createHttpsServer.calledWith({key: 'testkey', cert: 'testcert'}).should.equal(true);
});
serverBuilder().use(callback).build();
it('should build server accepting requests', function () {
var callback = function () {};
createServer.calledWith(callback).should.equal(true);
});
serverBuilder().use(callback).secure('key.pem', 'cert.pem').build();
it('should build listening server', function () {
serverBuilder().build();
createHttpsServer.calledWith({key: 'testkey', cert: 'testcert'}, callback).should.equal(true);
});
httpServer.listen.calledOnce.should.equal(true);
});
it('should throw error if key or cert not provided', function () {
readFileSyncStub.restore();
it('should build server listening on specified port', function () {
serverBuilder().port(6666).build();
(function () {
serverBuilder().secure('nofile', 'nofile');
}).should.throw('No key or certificate file found');
});
httpServer.listen.calledWith(6666).should.equal(true);
});
it('should build server listening on default port', function () {
serverBuilder().build();
httpServer.listen.calledWith(9001).should.equal(true);
});
it('should build server listening on specified host', function () {
serverBuilder().host('127.0.0.1').build();
httpServer.listen.calledWith(9001, '127.0.0.1').should.equal(true);
});
it('should build server listening on default host', function () {
serverBuilder().build();
httpServer.listen.calledWith(9001, null).should.equal(true);
});
});
})();
describe('https server', function () {
var httpsServer;
var createHttpsServer;
var readFileSyncStub;
beforeEach(function () {
httpsServer = sinon.createStubInstance(https.Server);
httpsServer.listen.returns(httpsServer);
createHttpsServer = sinon.stub(https, 'createServer').returns(httpsServer);
readFileSyncStub = sinon.stub(fs, 'readFileSync');
readFileSyncStub.withArgs('key.pem').returns('testkey');
readFileSyncStub.withArgs('cert.pem').returns('testcert');
});
afterEach(function () {
createHttpsServer.restore();
readFileSyncStub.restore();
});
it('should build server', function () {
var server = serverBuilder().secure('key.pem', 'cert.pem').build();
server.should.be.an.instanceof(https.Server);
createHttpsServer.calledWith({key: 'testkey', cert: 'testcert'}).should.equal(true);
});
it('should build server accepting requests', function () {
var callback = function () {};
serverBuilder().use(callback).secure('key.pem', 'cert.pem').build();
createHttpsServer.calledWith({key: 'testkey', cert: 'testcert'}, callback).should.equal(true);
});
it('should throw error if key or cert not provided', function () {
readFileSyncStub.restore();
(function () {
serverBuilder().secure('nofile', 'nofile');
}).should.throw('No key or certificate file found');
});
});
});

View File

@ -1,42 +1,40 @@
'use strict';
var fs = require('fs');
var tail = require('../lib/tail');
var temp = require('temp');
(function () {
'use strict';
describe('tail', function () {
temp.track();
describe('tail', function () {
temp.track();
it('calls event line if new line appear in file', function (done) {
temp.open('', function (err, info) {
tail(info.path).on('line', function (line) {
line.should.equal('testline');
done();
});
fs.writeSync(info.fd, 'testline\n');
fs.closeSync(info.fd);
it('calls event line if new line appear in file', function (done) {
temp.open('', function (err, info) {
tail(info.path).on('line', function (line) {
line.should.equal('testline');
done();
});
fs.writeSync(info.fd, 'testline\n');
fs.closeSync(info.fd);
});
});
it('buffers lines on start', function (done) {
temp.open('', function (err, info) {
fs.writeSync(info.fd, 'testline1\n');
fs.writeSync(info.fd, 'testline2\n');
fs.writeSync(info.fd, 'testline3\n');
fs.closeSync(info.fd);
it('buffers lines on start', function (done) {
temp.open('', function (err, info) {
fs.writeSync(info.fd, 'testline1\n');
fs.writeSync(info.fd, 'testline2\n');
fs.writeSync(info.fd, 'testline3\n');
fs.closeSync(info.fd);
var calls = 0;
var tailer = tail(info.path, {buffer: 2}).on('line', function () {
calls += 1;
var calls = 0;
var tailer = tail(info.path, {buffer: 2}).on('line', function () {
calls += 1;
if (calls === 3) {
tailer.getBuffer().should.eql(['testline2', 'testline3']);
done();
}
});
if (calls === 3) {
tailer.getBuffer().should.eql(['testline2', 'testline3']);
done();
}
});
});
});
})();
});