You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

350 lines
9.1 KiB

  1. /*
  2. * node-rdkafka - Node.js wrapper for RdKafka C/C++ library
  3. * Copyright (c) 2016 Blizzard Entertainment
  4. *
  5. * This software may be modified and distributed under the terms
  6. * of the MIT license. See the LICENSE.txt file for details.
  7. */
  8. var t = require('assert');
  9. var crypto = require('crypto');
  10. var eventListener = require('./listener');
  11. var KafkaConsumer = require('../').KafkaConsumer;
  12. var kafkaBrokerList = process.env.KAFKA_HOST || 'localhost:9092';
  13. var topic = 'test';
  14. describe('Consumer', function() {
  15. var gcfg;
  16. beforeEach(function() {
  17. var grp = 'kafka-mocha-grp-' + crypto.randomBytes(20).toString('hex');
  18. gcfg = {
  19. 'bootstrap.servers': kafkaBrokerList,
  20. 'group.id': grp,
  21. 'debug': 'all',
  22. 'rebalance_cb': true,
  23. 'enable.auto.commit': false
  24. };
  25. });
  26. describe('commit', function() {
  27. var consumer;
  28. beforeEach(function(done) {
  29. consumer = new KafkaConsumer(gcfg, {});
  30. consumer.connect({ timeout: 2000 }, function(err, info) {
  31. t.ifError(err);
  32. done();
  33. });
  34. eventListener(consumer);
  35. });
  36. it('should allow commit with an array', function(done) {
  37. consumer.commit([{ topic: topic, partition: 0, offset: -1 }]);
  38. done();
  39. });
  40. it('should allow commit without an array', function(done) {
  41. consumer.commit({ topic: topic, partition: 0, offset: -1 });
  42. done();
  43. });
  44. afterEach(function(done) {
  45. consumer.disconnect(function() {
  46. done();
  47. });
  48. });
  49. });
  50. describe('committed and position', function() {
  51. var consumer;
  52. beforeEach(function(done) {
  53. consumer = new KafkaConsumer(gcfg, {});
  54. consumer.connect({ timeout: 2000 }, function(err, info) {
  55. t.ifError(err);
  56. done();
  57. });
  58. eventListener(consumer);
  59. });
  60. afterEach(function(done) {
  61. consumer.disconnect(function() {
  62. done();
  63. });
  64. });
  65. it('before assign, committed offsets are empty', function(done) {
  66. consumer.committed(null, 1000, function(err, committed) {
  67. t.ifError(err);
  68. t.equal(Array.isArray(committed), true, 'Committed offsets should be an array');
  69. t.equal(committed.length, 0);
  70. done();
  71. });
  72. });
  73. it('before assign, position returns an empty array', function() {
  74. var position = consumer.position();
  75. t.equal(Array.isArray(position), true, 'Position should be an array');
  76. t.equal(position.length, 0);
  77. });
  78. it('after assign, should get committed array without offsets ', function(done) {
  79. consumer.assign([{topic:topic, partition:0}]);
  80. // Defer this for a second
  81. setTimeout(function() {
  82. consumer.committed(null, 1000, function(err, committed) {
  83. t.ifError(err);
  84. t.equal(committed.length, 1);
  85. t.equal(typeof committed[0], 'object', 'TopicPartition should be an object');
  86. t.deepStrictEqual(committed[0].partition, 0);
  87. t.equal(committed[0].offset, undefined);
  88. done();
  89. });
  90. }, 1000);
  91. });
  92. it('after assign and commit, should get committed offsets', function(done) {
  93. this.timeout(6000);
  94. consumer.assign([{topic:topic, partition:0}]);
  95. consumer.commitSync({topic:topic, partition:0, offset:1000});
  96. consumer.committed(null, 1000, function(err, committed) {
  97. t.ifError(err);
  98. t.equal(committed.length, 1);
  99. t.equal(typeof committed[0], 'object', 'TopicPartition should be an object');
  100. t.deepStrictEqual(committed[0].partition, 0);
  101. t.deepStrictEqual(committed[0].offset, 1000);
  102. done();
  103. });
  104. });
  105. it('after assign, before consume, position should return an array without offsets', function(done) {
  106. consumer.assign([{topic:topic, partition:0}]);
  107. var position = consumer.position();
  108. t.equal(Array.isArray(position), true, 'Position should be an array');
  109. t.equal(position.length, 1);
  110. t.equal(typeof position[0], 'object', 'TopicPartition should be an object');
  111. t.deepStrictEqual(position[0].partition, 0);
  112. t.equal(position[0].offset, undefined, 'before consuming, offset is undefined');
  113. // see both.spec.js 'should be able to produce, consume messages, read position...'
  114. // for checking of offset numeric value
  115. done();
  116. });
  117. it('should obey the timeout', function(done) {
  118. consumer.committed(null, 0, function(err, committed) {
  119. if (!err) {
  120. t.fail(err, 'not null', 'Error should be set for a timeout');
  121. }
  122. done();
  123. });
  124. });
  125. });
  126. describe('seek and positioning', function() {
  127. var consumer;
  128. beforeEach(function(done) {
  129. consumer = new KafkaConsumer(gcfg, {});
  130. consumer.connect({ timeout: 2000 }, function(err, info) {
  131. t.ifError(err);
  132. consumer.assign([{
  133. topic: 'test',
  134. partition: 0,
  135. offset: 0
  136. }]);
  137. done();
  138. });
  139. eventListener(consumer);
  140. });
  141. afterEach(function(done) {
  142. consumer.disconnect(function() {
  143. done();
  144. });
  145. });
  146. it('should be able to seek', function(cb) {
  147. consumer.seek({
  148. topic: 'test',
  149. partition: 0,
  150. offset: 0
  151. }, 1, function(err) {
  152. t.ifError(err);
  153. cb();
  154. });
  155. });
  156. it('should be able to seek with a timeout of 0', function(cb) {
  157. consumer.seek({
  158. topic: 'test',
  159. partition: 0,
  160. offset: 0
  161. }, 0, function(err) {
  162. t.ifError(err);
  163. cb();
  164. });
  165. });
  166. });
  167. describe('subscribe', function() {
  168. var consumer;
  169. beforeEach(function(done) {
  170. consumer = new KafkaConsumer(gcfg, {});
  171. consumer.connect({ timeout: 2000 }, function(err, info) {
  172. t.ifError(err);
  173. done();
  174. });
  175. eventListener(consumer);
  176. });
  177. afterEach(function(done) {
  178. consumer.disconnect(function() {
  179. done();
  180. });
  181. });
  182. it('should be able to subscribe', function() {
  183. t.equal(0, consumer.subscription().length);
  184. consumer.subscribe([topic]);
  185. t.equal(1, consumer.subscription().length);
  186. t.equal('test', consumer.subscription()[0]);
  187. t.equal(0, consumer.assignments().length);
  188. });
  189. it('should be able to unsubscribe', function() {
  190. consumer.subscribe([topic]);
  191. t.equal(1, consumer.subscription().length);
  192. consumer.unsubscribe();
  193. t.equal(0, consumer.subscription().length);
  194. t.equal(0, consumer.assignments().length);
  195. });
  196. });
  197. describe('assign', function() {
  198. var consumer;
  199. beforeEach(function(done) {
  200. consumer = new KafkaConsumer(gcfg, {});
  201. consumer.connect({ timeout: 2000 }, function(err, info) {
  202. t.ifError(err);
  203. done();
  204. });
  205. eventListener(consumer);
  206. });
  207. afterEach(function(done) {
  208. consumer.disconnect(function() {
  209. done();
  210. });
  211. });
  212. it('should be able to take an assignment', function() {
  213. t.equal(0, consumer.assignments().length);
  214. consumer.assign([{ topic:topic, partition:0 }]);
  215. t.equal(1, consumer.assignments().length);
  216. t.equal(topic, consumer.assignments()[0].topic);
  217. t.equal(0, consumer.subscription().length);
  218. });
  219. it('should be able to take an empty assignment', function() {
  220. consumer.assign([{ topic:topic, partition:0 }]);
  221. t.equal(1, consumer.assignments().length);
  222. consumer.assign([]);
  223. t.equal(0, consumer.assignments().length);
  224. });
  225. });
  226. describe('disconnect', function() {
  227. var tcfg = { 'auto.offset.reset': 'earliest' };
  228. it('should happen gracefully', function(cb) {
  229. var consumer = new KafkaConsumer(gcfg, tcfg);
  230. consumer.connect({ timeout: 2000 }, function(err, info) {
  231. t.ifError(err);
  232. consumer.disconnect(function() {
  233. cb();
  234. });
  235. });
  236. });
  237. it('should happen without issue after subscribing', function(cb) {
  238. var consumer = new KafkaConsumer(gcfg, tcfg);
  239. consumer.connect({ timeout: 2000 }, function(err, info) {
  240. t.ifError(err);
  241. consumer.subscribe([topic]);
  242. consumer.disconnect(function() {
  243. cb();
  244. });
  245. });
  246. });
  247. it('should happen without issue after consuming', function(cb) {
  248. this.timeout(11000);
  249. var consumer = new KafkaConsumer(gcfg, tcfg);
  250. consumer.setDefaultConsumeTimeout(10000);
  251. consumer.connect({ timeout: 2000 }, function(err, info) {
  252. t.ifError(err);
  253. consumer.subscribe([topic]);
  254. consumer.consume(1, function(err, messages) {
  255. t.ifError(err);
  256. consumer.disconnect(function() {
  257. cb();
  258. });
  259. });
  260. });
  261. });
  262. it('should happen without issue after consuming an error', function(cb) {
  263. var consumer = new KafkaConsumer(gcfg, tcfg);
  264. consumer.setDefaultConsumeTimeout(1);
  265. consumer.connect({ timeout: 2000 }, function(err, info) {
  266. t.ifError(err);
  267. consumer.subscribe([topic]);
  268. consumer.consume(1, function(err, messages) {
  269. // Timeouts do not classify as errors anymore
  270. t.equal(messages[0], undefined, 'Message should not be set');
  271. consumer.disconnect(function() {
  272. cb();
  273. });
  274. });
  275. });
  276. });
  277. });
  278. });