twitch-toolkit
Version:
A set of tools to integrate with Twitch API, Twitch Chat and Twitch WebHooks.
443 lines (371 loc) • 13 kB
JavaScript
;
const assume = require('assume');
const { format } = require('logform');
const stream = require('stream');
const TransportStream = require('../');
const Parent = require('./fixtures/parent');
const SimpleTransport = require('./fixtures/simple-transport');
const { testLevels, testOrder } = require('./fixtures');
const {
infosFor,
logFor,
levelAndMessage,
toException,
toWriteReq
} = require('abstract-winston-transport/utils');
const LEVEL = Symbol.for('level');
const MESSAGE = Symbol.for('message');
describe('TransportStream', function () {
it('should have the appropriate methods defined', function () {
const transport = new TransportStream();
assume(transport).instanceof(stream.Writable);
assume(transport._write).is.a('function');
assume(transport.log).equals(undefined);
});
it('should accept a custom log function invoked on _write', function () {
const log = logFor(1);
const transport = new TransportStream({ log });
assume(transport.log).equals(log);
});
it('should invoke a custom log function on _write', function (done) {
const info = {
[LEVEL]: 'test',
level: 'test',
message: 'Testing ... 1 2 3.'
};
const transport = new TransportStream({
log: function (actual) {
assume(actual).equals(info);
done();
}
});
transport.write(info);
});
describe('_write(info, enc, callback)', function () {
it('should log to any level when { level: undefined }', function (done) {
const expected = testOrder.map(levelAndMessage);
const transport = new TransportStream({
log: logFor(testOrder.length, function (err, infos) {
assume(infos.length).equals(expected.length);
assume(infos).deep.equals(expected);
done();
})
});
expected.forEach(transport.write.bind(transport));
});
it('should only log messages BELOW the level priority', function (done) {
const expected = testOrder.map(levelAndMessage);
const transport = new TransportStream({
level: 'info',
log: logFor(5, function (err, infos) {
assume(infos.length).equals(5);
assume(infos).deep.equals(expected.slice(0, 5));
done();
})
});
transport.levels = testLevels;
expected.forEach(transport.write.bind(transport));
});
it('{ level } should be ignored when { handleExceptions: true }', function () {
const expected = testOrder.map(levelAndMessage)
.map(function (info) {
info.exception = true;
return info;
});
const transport = new TransportStream({
level: 'info',
log: logFor(testOrder.length, function (err, infos) {
assume(infos.length).equals(expected.length);
assume(infos).deep.equals(expected);
done();
})
});
transport.levels = testLevels;
expected.forEach(transport.write.bind(transport));
});
describe('when { exception: true } in info', function () {
it('should not invoke log when { handleExceptions: false }', function (done) {
const expected = [
{ exception: true, [LEVEL]: 'error', level: 'error', message: 'Test exception handling' },
{ [LEVEL]: 'test', level: 'test', message: 'Testing ... 1 2 3.' }
];
const transport = new TransportStream({
log: function (info) {
assume(info.exception).equals(undefined);
done();
}
});
expected.forEach(transport.write.bind(transport));
});
it('should invoke log when { handleExceptions: true }', function (done) {
const actual = [];
const expected = [
{ exception: true, [LEVEL]: 'error', level: 'error', message: 'Test exception handling' },
{ [LEVEL]: 'test', level: 'test', message: 'Testing ... 1 2 3.' }
];
const transport = new TransportStream({
handleExceptions: true,
log: function (info, next) {
actual.push(info);
if (actual.length === expected.length) {
assume(actual).deep.equals(expected);
done();
}
next();
}
});
expected.forEach(transport.write.bind(transport));
});
});
});
describe('_writev(chunks, callback)', function () {
it('invokes .log() for each of the valid chunks when necessary in streams plumbing', function (done) {
const expected = infosFor({ count: 50, levels: testOrder });
const transport = new TransportStream({
log: logFor(50 * testOrder.length, function (err, infos) {
assume(infos.length).equals(expected.length);
assume(infos).deep.equals(expected);
done();
})
});
//
// Make the standard _write throw to ensure that _writev is called.
//
transport._write = function () {
throw new Error('TransportStream.prototype._write should never be called.');
};
transport.cork();
expected.forEach(transport.write.bind(transport));
transport.uncork();
});
it('ensures a format is applied to each info when no .logv is defined', function (done) {
const expected = infosFor({ count: 10, levels: testOrder });
const transport = new TransportStream({
format: format.json(),
log: logFor(10 * testOrder.length, function (err, infos) {
assume(infos.length).equals(expected.length);
infos.forEach((info, i) => {
assume(info[MESSAGE]).equals(JSON.stringify(expected[i]));
});
done();
})
});
//
// Make the standard _write throw to ensure that _writev is called.
//
transport._write = function () {
throw new Error('TransportStream.prototype._write should never be called.');
};
transport.cork();
expected.forEach(transport.write.bind(transport));
transport.uncork();
});
it('invokes .logv with all valid chunks when necessary in streams plumbing', function (done) {
const expected = infosFor({ count: 50, levels: testOrder });
const transport = new TransportStream({
level: 'info',
log: function () {
throw new Error('.log() should never be called');
},
logv: function (chunks, callback) {
assume(chunks.length).equals(250);
callback();
done();
}
});
//
// Make the standard _write throw to ensure that _writev is called.
//
transport._write = function () {
throw new Error('TransportStream.prototype._write should never be called.');
};
transport.cork();
transport.levels = testLevels;
expected.forEach(transport.write.bind(transport));
transport.uncork();
});
});
describe('parent (i.e. "logger") ["pipe", "unpipe"]', function () {
it('should define { level, levels } on "pipe"', function (done) {
var parent = new Parent({
level: 'info',
levels: testLevels
});
var transport = new TransportStream({
log: function () {}
});
parent.pipe(transport);
setImmediate(function () {
assume(transport.level).equals('info');
assume(transport.levels).equals(testLevels);
assume(transport.parent).equals(parent);
done();
});
});
it('should not overwrite existing { level } on "pipe"', function (done) {
var parent = new Parent({
level: 'info',
levels: testLevels
});
var transport = new TransportStream({
level: 'error',
log: function () {}
});
parent.pipe(transport);
setImmediate(function () {
assume(transport.level).equals('error');
assume(transport.levels).equals(testLevels);
assume(transport.parent).equals(parent);
done();
});
});
it('should unset parent on "unpipe"', function (done) {
var parent = new Parent({
level: 'info',
levels: testLevels
});
var transport = new TransportStream({
level: 'error',
log: function () {}
});
//
// Trigger "pipe" first so that transport.parent is set
//
parent.pipe(transport);
setImmediate(function () {
assume(transport.parent).equals(parent);
//
// Now verify that after "unpipe" it is set to "null"
//
parent.unpipe(transport);
setImmediate(function () {
assume(transport.parent).equals(null);
done();
});
});
});
it('should invoke a close method on "unpipe"', function (done) {
var parent = new Parent({
level: 'info',
levels: testLevels
});
var transport = new TransportStream({
log: function () {}
});
//
// Test will only successfully complete when `close`
// is invoked.
//
transport.close = function () {
assume(transport.parent).equals(null);
done();
};
//
// Trigger "pipe" first so that transport.parent is set
//
parent.pipe(transport);
setImmediate(function () {
assume(transport.parent).equals(parent);
parent.unpipe(transport);
});
});
});
describe('_accept(info)', function () {
it('should filter only log messages BELOW the level priority', function () {
const expected = testOrder
.map(levelAndMessage)
.map(toWriteReq);
const transport = new TransportStream({ level: 'info' });
transport.levels = testLevels;
const filtered = expected.filter(transport._accept, transport)
.map(function (write) { return write.chunk.level });
assume(filtered).deep.equals([
'error',
'warn',
'dog',
'cat',
'info'
]);
});
it('should filter out { exception: true } when { handleExceptions: false }', function () {
const expected = testOrder
.map(toException)
.map(toWriteReq);
const transport = new TransportStream({
handleExceptions: false,
level: 'info'
});
transport.levels = testLevels;
const filtered = expected.filter(transport._accept, transport)
.map(function (info) { return info.level });
assume(filtered).deep.equals([]);
});
it('should include ALL { exception: true } when { handleExceptions: true }', function () {
const expected = testOrder
.map(toException)
.map(toWriteReq);
const transport = new TransportStream({
handleExceptions: true,
level: 'info'
});
transport.levels = testLevels;
const filtered = expected.filter(transport._accept, transport)
.map(function (write) { return write.chunk.level });
assume(filtered).deep.equals(testOrder);
});
});
describe('{ format }', function () {
it('logs the output of the provided format', function (done) {
const expected = { [LEVEL]: 'info', level: 'info', message: 'there will be json' };
const transport = new TransportStream({
format: format.json(),
log: function (info, next) {
assume(info[MESSAGE]).equals(
JSON.stringify(expected)
);
done();
}
});
transport.write(expected);
});
it('treats the original object immutable', function (done) {
const expected = { [LEVEL]: 'info', level: 'info', message: 'there will be json' };
const transport = new TransportStream({
format: format.json(),
log: function (info, next) {
assume(info).not.equals(expected);
done();
}
});
transport.write(expected);
});
});
describe('{ silent }', function () {
const silentTransport = new TransportStream({
silent: true,
format: format.json(),
log: function (info, next) {
assume(false).true('.log() was called improperly');
done();
}
});
it('{ silent: true } ._write() never calls `.log`', function (done) {
const expected = { [LEVEL]: 'info', level: 'info', message: 'there will be json' };
silentTransport.write(expected);
setImmediate(() => done());
});
it('{ silent: true } ._writev() never calls `.log`', function (done) {
const expected = { [LEVEL]: 'info', level: 'info', message: 'there will be json' };
silentTransport.cork();
for (var i = 0; i < 15; i++) {
silentTransport.write(expected);
}
silentTransport.uncork();
setImmediate(() => done());
});
it('{ silent: true } ensures ._accept(write) always returns false', function () {
const accepted = silentTransport._accept({ chunk: {} });
assume(accepted).false();
});
});
});