/* * node-rdkafka - Node.js wrapper for RdKafka C/C++ library * * Copyright (c) 2016 Blizzard Entertainment * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE.txt file for details. */ var KafkaConsumerStream = require('../lib/kafka-consumer-stream'); var t = require('assert'); var Writable = require('stream').Writable; var Emitter = require('events'); var fakeClient; module.exports = { 'KafkaConsumerStream stream': { 'beforeEach': function() { fakeClient = new Emitter(); fakeClient._isConnecting = false; fakeClient._isConnected = true; fakeClient.isConnected = function() { return true; }; fakeClient.unsubscribe = function() { this.emit('unsubscribed'); return true; }; fakeClient.disconnect = function(cb) { this.emit('disconnected'); if (cb) { t.equal(typeof cb, 'function'); setImmediate(cb); } }; fakeClient.consume = function(size, cb) { if (!size) { cb = size; } t.equal(typeof cb, 'function', 'Provided callback should always be a function'); setImmediate(function() { cb(null, [{ value: Buffer.from('test'), key: 'testkey', offset: 1 }]); }); }; fakeClient.subscribe = function(topics) { t.equal(Array.isArray(topics), true); return this; }; }, 'exports a stream class': function() { t.equal(typeof(KafkaConsumerStream), 'function'); }, 'can be instantiated': function() { t.equal(typeof new KafkaConsumerStream(fakeClient, { topics: 'topic' }), 'object'); }, 'properly reads off the fake client': function(cb) { var stream = new KafkaConsumerStream(fakeClient, { topics: 'topic' }); stream.on('error', function(err) { t.fail(err); }); stream.once('readable', function() { var message = stream.read(); t.notEqual(message, null); t.ok(Buffer.isBuffer(message.value)); t.equal('test', message.value.toString()); t.equal('testkey', message.key); t.equal(typeof message.offset, 'number'); stream.pause(); cb(); }); }, 'properly reads off the fake with a topic function': function(cb) { fakeClient._metadata = { orig_broker_id: 1, orig_broker_name: "broker_name", brokers: [ { id: 1, host: 'localhost', port: 40 } ], topics: [ { name: 'awesome-topic', partitions: [ { id: 1, leader: 20, replicas: [1, 2], isrs: [1, 2] } ] } ] }; var stream = new KafkaConsumerStream(fakeClient, { topics: function(metadata) { var topics = metadata.topics.map(function(v) { return v.name; }); return topics; } }); fakeClient.subscribe = function(topics) { t.equal(Array.isArray(topics), true); t.equal(topics[0], 'awesome-topic'); t.equal(topics.length, 1); return this; }; stream.on('error', function(err) { t.fail(err); }); stream.once('readable', function() { var message = stream.read(); t.notEqual(message, null); t.ok(Buffer.isBuffer(message.value)); t.equal('test', message.value.toString()); t.equal('testkey', message.key); t.equal(typeof message.offset, 'number'); stream.pause(); cb(); }); }, 'properly reads correct number of messages but does not stop': function(next) { var numMessages = 10; var numReceived = 0; var numSent = 0; fakeClient.consume = function(size, cb) { if (numSent < numMessages) { numSent++; setImmediate(function() { cb(null, [{ value: Buffer.from('test'), offset: 1 }]); }); } else { } }; var stream = new KafkaConsumerStream(fakeClient, { topics: 'topic' }); stream.on('error', function(err) { // Ignore }); stream.on('readable', function() { var message = stream.read(); numReceived++; t.notEqual(message, null); t.ok(Buffer.isBuffer(message.value)); t.equal(typeof message.offset, 'number'); if (numReceived === numMessages) { // give it a second to get an error next(); } }); }, 'can be piped around': function(cb) { var stream = new KafkaConsumerStream(fakeClient, { topics: 'topic' }); var writable = new Writable({ write: function(message, encoding, next) { t.notEqual(message, null); t.ok(Buffer.isBuffer(message.value)); t.equal(typeof message.offset, 'number'); this.cork(); cb(); }, objectMode: true }); stream.pipe(writable); stream.on('error', function(err) { t.fail(err); }); }, 'streams as batch when specified': function(next) { var numMessages = 10; var numReceived = 0; var numSent = 0; fakeClient.consume = function(size, cb) { if (numSent < numMessages) { numSent++; setImmediate(function() { cb(null, [{ value: Buffer.from('test'), offset: 1 }]); }); } else { } }; var stream = new KafkaConsumerStream(fakeClient, { topics: 'topic', streamAsBatch: true }); stream.on('error', function(err) { // Ignore }); stream.on('readable', function() { var messages = stream.read(); numReceived++; t.equal(Array.isArray(messages), true); t.equal(messages.length, 1); var message = messages[0]; t.notEqual(message, null); t.ok(Buffer.isBuffer(message.value)); t.equal(typeof message.offset, 'number'); if (numReceived === numMessages) { // give it a second to get an error next(); } }); }, 'stops reading on unsubscribe': function(next) { var numMessages = 10; var numReceived = 0; var numSent = 0; fakeClient.consume = function(size, cb) { if (numSent < numMessages) { numSent++; setImmediate(function() { cb(null, [{ value: Buffer.from('test'), offset: 1 }]); }); } else { } }; var stream = new KafkaConsumerStream(fakeClient, { topics: 'topic' }); stream.on('error', function(err) { // Ignore }); stream.on('readable', function() { var message = stream.read(); numReceived++; if (message) { t.ok(Buffer.isBuffer(message.value)); t.equal(typeof message.offset, 'number'); if (numReceived === numMessages) { // give it a second to get an error fakeClient.emit('unsubscribed'); } } }); stream.on('end', function() { next(); }); }, 'calls the callback on destroy': function (next) { fakeClient.unsubscribe = function () {}; var stream = new KafkaConsumerStream(fakeClient, { topics: 'topic' }); stream.once('readable', function () { stream.destroy(); stream.once('close', next); }); }, } };