@platformatic/rdkafka 4.0.0 → 4.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/README.md +5 -5
  2. package/bench/seed.sh +0 -0
  3. package/binding.gyp +3 -3
  4. package/ci/prepublish.js +4 -3
  5. package/ci/update-version.js +123 -0
  6. package/config.d.ts +126 -10
  7. package/configure +0 -0
  8. package/deps/librdkafka/.semaphore/run-all-tests.yml +77 -0
  9. package/deps/librdkafka/.semaphore/semaphore-integration.yml +250 -0
  10. package/deps/librdkafka/.semaphore/semaphore.yml +67 -44
  11. package/deps/librdkafka/.semaphore/verify-linux-packages.yml +41 -0
  12. package/deps/librdkafka/CHANGELOG.md +471 -1
  13. package/deps/librdkafka/CONFIGURATION.md +29 -9
  14. package/deps/librdkafka/INTRODUCTION.md +909 -601
  15. package/deps/librdkafka/Makefile +2 -1
  16. package/deps/librdkafka/configure +0 -0
  17. package/deps/librdkafka/debian/rules +0 -0
  18. package/deps/librdkafka/dev-conf.sh +0 -0
  19. package/deps/librdkafka/examples/consumer.c +18 -8
  20. package/deps/librdkafka/examples/describe_consumer_groups.c +25 -6
  21. package/deps/librdkafka/examples/incremental_alter_configs.c +4 -5
  22. package/deps/librdkafka/examples/kafkatest_verifiable_client.cpp +26 -42
  23. package/deps/librdkafka/examples/openssl_engine_example.cpp +3 -4
  24. package/deps/librdkafka/examples/producer.cpp +2 -2
  25. package/deps/librdkafka/examples/user_scram.c +4 -5
  26. package/deps/librdkafka/examples/win_ssl_cert_store.cpp +4 -3
  27. package/deps/librdkafka/lds-gen.py +0 -0
  28. package/deps/librdkafka/mklove/Makefile.base +0 -0
  29. package/deps/librdkafka/packaging/RELEASE.md +8 -0
  30. package/deps/librdkafka/packaging/alpine/build-alpine.sh +0 -0
  31. package/deps/librdkafka/packaging/cp/README.md +4 -1
  32. package/deps/librdkafka/packaging/cp/check_features.c +12 -4
  33. package/deps/librdkafka/packaging/cp/verify-deb.sh +6 -6
  34. package/deps/librdkafka/packaging/cp/verify-packages.sh +52 -31
  35. package/deps/librdkafka/packaging/cp/verify-rpm.sh +6 -13
  36. package/deps/librdkafka/packaging/debian/docs +5 -0
  37. package/deps/librdkafka/packaging/debian/rules +0 -0
  38. package/deps/librdkafka/packaging/get_version.py +0 -0
  39. package/deps/librdkafka/packaging/homebrew/brew-update-pr.sh +0 -0
  40. package/deps/librdkafka/packaging/mingw-w64/{travis-before-install.sh → export-variables.sh} +0 -7
  41. package/deps/librdkafka/packaging/mingw-w64/semaphoreci-build.sh +1 -1
  42. package/deps/librdkafka/packaging/nuget/artifact.py +0 -0
  43. package/deps/librdkafka/packaging/nuget/cleanup-s3.py +0 -0
  44. package/deps/librdkafka/packaging/nuget/nuget.sh +0 -0
  45. package/deps/librdkafka/packaging/nuget/packaging.py +0 -0
  46. package/deps/librdkafka/packaging/nuget/push-to-nuget.sh +0 -0
  47. package/deps/librdkafka/packaging/nuget/release.py +0 -0
  48. package/deps/librdkafka/packaging/nuget/requirements.txt +3 -3
  49. package/deps/librdkafka/packaging/rpm/mock-on-docker.sh +0 -0
  50. package/deps/librdkafka/packaging/rpm/tests/run-test.sh +0 -0
  51. package/deps/librdkafka/packaging/rpm/tests/test-on-docker.sh +0 -0
  52. package/deps/librdkafka/packaging/tools/build-configurations-checks.sh +3 -1
  53. package/deps/librdkafka/packaging/tools/build-deb-package.sh +0 -0
  54. package/deps/librdkafka/packaging/tools/build-debian.sh +0 -0
  55. package/deps/librdkafka/packaging/tools/build-manylinux.sh +0 -0
  56. package/deps/librdkafka/packaging/tools/build-release-artifacts.sh +0 -0
  57. package/deps/librdkafka/packaging/tools/distro-build.sh +0 -0
  58. package/deps/librdkafka/packaging/tools/gh-release-checksums.py +0 -0
  59. package/deps/librdkafka/packaging/tools/rdutcoverage.sh +0 -0
  60. package/deps/librdkafka/packaging/tools/run-in-docker.sh +28 -0
  61. package/deps/librdkafka/packaging/tools/run-integration-tests.sh +31 -0
  62. package/deps/librdkafka/packaging/tools/run-style-check.sh +4 -0
  63. package/deps/librdkafka/packaging/tools/style-format.sh +6 -5
  64. package/deps/librdkafka/packaging/tools/update_rpcs_max_versions.py +100 -0
  65. package/deps/librdkafka/service.yml +154 -0
  66. package/deps/librdkafka/src/generate_proto.sh +0 -0
  67. package/deps/librdkafka/src/rd.h +145 -2
  68. package/deps/librdkafka/src/rdaddr.h +6 -7
  69. package/deps/librdkafka/src/rdatomic.h +15 -11
  70. package/deps/librdkafka/src/rdavg.h +3 -2
  71. package/deps/librdkafka/src/rdbase64.c +33 -2
  72. package/deps/librdkafka/src/rdbase64.h +2 -0
  73. package/deps/librdkafka/src/rdendian.h +10 -9
  74. package/deps/librdkafka/src/rdhttp.c +418 -100
  75. package/deps/librdkafka/src/rdhttp.h +23 -5
  76. package/deps/librdkafka/src/rdkafka.c +299 -145
  77. package/deps/librdkafka/src/rdkafka.h +89 -6
  78. package/deps/librdkafka/src/rdkafka_admin.c +482 -105
  79. package/deps/librdkafka/src/rdkafka_admin.h +42 -0
  80. package/deps/librdkafka/src/rdkafka_broker.c +463 -312
  81. package/deps/librdkafka/src/rdkafka_broker.h +68 -10
  82. package/deps/librdkafka/src/rdkafka_buf.c +8 -5
  83. package/deps/librdkafka/src/rdkafka_buf.h +4 -3
  84. package/deps/librdkafka/src/rdkafka_cert.c +25 -1
  85. package/deps/librdkafka/src/rdkafka_cert.h +4 -3
  86. package/deps/librdkafka/src/rdkafka_cgrp.c +468 -143
  87. package/deps/librdkafka/src/rdkafka_cgrp.h +36 -2
  88. package/deps/librdkafka/src/rdkafka_conf.c +526 -102
  89. package/deps/librdkafka/src/rdkafka_conf.h +68 -4
  90. package/deps/librdkafka/src/rdkafka_feature.c +472 -35
  91. package/deps/librdkafka/src/rdkafka_feature.h +2 -0
  92. package/deps/librdkafka/src/rdkafka_fetcher.c +22 -2
  93. package/deps/librdkafka/src/rdkafka_fetcher.h +2 -2
  94. package/deps/librdkafka/src/rdkafka_int.h +84 -46
  95. package/deps/librdkafka/src/rdkafka_metadata.c +221 -136
  96. package/deps/librdkafka/src/rdkafka_metadata.h +15 -11
  97. package/deps/librdkafka/src/rdkafka_metadata_cache.c +279 -73
  98. package/deps/librdkafka/src/rdkafka_mock.c +795 -19
  99. package/deps/librdkafka/src/rdkafka_mock.h +119 -0
  100. package/deps/librdkafka/src/rdkafka_mock_cgrp.c +1298 -132
  101. package/deps/librdkafka/src/rdkafka_mock_handlers.c +345 -49
  102. package/deps/librdkafka/src/rdkafka_mock_int.h +180 -61
  103. package/deps/librdkafka/src/rdkafka_msg.c +35 -19
  104. package/deps/librdkafka/src/rdkafka_msg.h +3 -3
  105. package/deps/librdkafka/src/rdkafka_msgset_writer.c +14 -10
  106. package/deps/librdkafka/src/rdkafka_offset.c +33 -5
  107. package/deps/librdkafka/src/rdkafka_op.c +1 -1
  108. package/deps/librdkafka/src/rdkafka_op.h +45 -21
  109. package/deps/librdkafka/src/rdkafka_partition.c +118 -23
  110. package/deps/librdkafka/src/rdkafka_partition.h +16 -7
  111. package/deps/librdkafka/src/rdkafka_proto.h +4 -1
  112. package/deps/librdkafka/src/rdkafka_protocol.h +1 -0
  113. package/deps/librdkafka/src/rdkafka_queue.c +125 -48
  114. package/deps/librdkafka/src/rdkafka_queue.h +19 -0
  115. package/deps/librdkafka/src/rdkafka_range_assignor.c +1 -1
  116. package/deps/librdkafka/src/rdkafka_request.c +336 -64
  117. package/deps/librdkafka/src/rdkafka_request.h +24 -1
  118. package/deps/librdkafka/src/rdkafka_sasl.c +5 -3
  119. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.c +14 -10
  120. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.c +1166 -89
  121. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.h +13 -3
  122. package/deps/librdkafka/src/rdkafka_sasl_plain.c +3 -3
  123. package/deps/librdkafka/src/rdkafka_sasl_scram.c +4 -3
  124. package/deps/librdkafka/src/rdkafka_ssl.c +306 -81
  125. package/deps/librdkafka/src/rdkafka_ssl.h +22 -0
  126. package/deps/librdkafka/src/rdkafka_sticky_assignor.c +4 -0
  127. package/deps/librdkafka/src/rdkafka_telemetry.c +80 -23
  128. package/deps/librdkafka/src/rdkafka_telemetry_encode.c +5 -1
  129. package/deps/librdkafka/src/rdkafka_telemetry_encode.h +20 -20
  130. package/deps/librdkafka/src/rdkafka_topic.c +220 -137
  131. package/deps/librdkafka/src/rdkafka_topic.h +14 -8
  132. package/deps/librdkafka/src/rdkafka_transport.c +59 -47
  133. package/deps/librdkafka/src/rdkafka_transport.h +5 -0
  134. package/deps/librdkafka/src/rdkafka_txnmgr.c +6 -1
  135. package/deps/librdkafka/src/rdmap.c +6 -1
  136. package/deps/librdkafka/src/rdrand.c +11 -1
  137. package/deps/librdkafka/src/rdtime.h +48 -17
  138. package/deps/librdkafka/src/rdunittest.c +42 -25
  139. package/deps/librdkafka/src/rdunittest.h +2 -0
  140. package/deps/librdkafka/src/rdwin32.h +1 -1
  141. package/deps/librdkafka/src/regexp.c +47 -42
  142. package/deps/librdkafka/src/tinycthread_extra.c +45 -21
  143. package/deps/librdkafka/src/tinycthread_extra.h +7 -3
  144. package/deps/librdkafka/src-cpp/rdkafkacpp.h +28 -2
  145. package/deps/librdkafka/tests/0001-multiobj.c +4 -0
  146. package/deps/librdkafka/tests/0004-conf.c +103 -36
  147. package/deps/librdkafka/tests/0009-mock_cluster.c +1 -0
  148. package/deps/librdkafka/tests/0011-produce_batch.c +2 -1
  149. package/deps/librdkafka/tests/0016-client_swname.c +18 -3
  150. package/deps/librdkafka/tests/0017-compression.c +6 -8
  151. package/deps/librdkafka/tests/0018-cgrp_term.c +8 -3
  152. package/deps/librdkafka/tests/0021-rkt_destroy.c +1 -0
  153. package/deps/librdkafka/tests/0022-consume_batch.c +4 -1
  154. package/deps/librdkafka/tests/0026-consume_pause.c +12 -14
  155. package/deps/librdkafka/tests/0028-long_topicnames.c +1 -1
  156. package/deps/librdkafka/tests/0029-assign_offset.c +6 -8
  157. package/deps/librdkafka/tests/0030-offset_commit.c +2 -2
  158. package/deps/librdkafka/tests/0033-regex_subscribe.c +33 -17
  159. package/deps/librdkafka/tests/0034-offset_reset.c +25 -4
  160. package/deps/librdkafka/tests/0036-partial_fetch.c +1 -0
  161. package/deps/librdkafka/tests/0038-performance.c +1 -0
  162. package/deps/librdkafka/tests/0040-io_event.c +11 -5
  163. package/deps/librdkafka/tests/0041-fetch_max_bytes.c +1 -0
  164. package/deps/librdkafka/tests/0044-partition_cnt.c +3 -2
  165. package/deps/librdkafka/tests/0045-subscribe_update.c +284 -20
  166. package/deps/librdkafka/tests/0047-partial_buf_tmout.c +1 -0
  167. package/deps/librdkafka/tests/0048-partitioner.c +1 -1
  168. package/deps/librdkafka/tests/0050-subscribe_adds.c +1 -0
  169. package/deps/librdkafka/tests/0051-assign_adds.c +1 -0
  170. package/deps/librdkafka/tests/0052-msg_timestamps.c +22 -4
  171. package/deps/librdkafka/tests/0053-stats_cb.cpp +12 -20
  172. package/deps/librdkafka/tests/0055-producer_latency.c +176 -3
  173. package/deps/librdkafka/tests/0056-balanced_group_mt.c +6 -2
  174. package/deps/librdkafka/tests/0058-log.cpp +3 -3
  175. package/deps/librdkafka/tests/0059-bsearch.cpp +8 -4
  176. package/deps/librdkafka/tests/0061-consumer_lag.cpp +25 -5
  177. package/deps/librdkafka/tests/0067-empty_topic.cpp +3 -0
  178. package/deps/librdkafka/tests/0068-produce_timeout.c +21 -23
  179. package/deps/librdkafka/tests/0069-consumer_add_parts.c +3 -7
  180. package/deps/librdkafka/tests/0075-retry.c +38 -1
  181. package/deps/librdkafka/tests/0076-produce_retry.c +2 -0
  182. package/deps/librdkafka/tests/0077-compaction.c +8 -2
  183. package/deps/librdkafka/tests/0080-admin_ut.c +34 -4
  184. package/deps/librdkafka/tests/0081-admin.c +455 -155
  185. package/deps/librdkafka/tests/0082-fetch_max_bytes.cpp +4 -0
  186. package/deps/librdkafka/tests/0083-cb_event.c +8 -3
  187. package/deps/librdkafka/tests/0084-destroy_flags.c +14 -18
  188. package/deps/librdkafka/tests/0085-headers.cpp +4 -0
  189. package/deps/librdkafka/tests/0086-purge.c +40 -7
  190. package/deps/librdkafka/tests/0088-produce_metadata_timeout.c +1 -1
  191. package/deps/librdkafka/tests/0089-max_poll_interval.c +12 -7
  192. package/deps/librdkafka/tests/0090-idempotence.c +1 -2
  193. package/deps/librdkafka/tests/0091-max_poll_interval_timeout.c +5 -7
  194. package/deps/librdkafka/tests/0092-mixed_msgver.c +6 -0
  195. package/deps/librdkafka/tests/0093-holb.c +8 -5
  196. package/deps/librdkafka/tests/0094-idempotence_msg_timeout.c +1 -0
  197. package/deps/librdkafka/tests/0097-ssl_verify.cpp +262 -70
  198. package/deps/librdkafka/tests/0098-consumer-txn.cpp +16 -16
  199. package/deps/librdkafka/tests/0099-commit_metadata.c +1 -1
  200. package/deps/librdkafka/tests/0101-fetch-from-follower.cpp +1 -1
  201. package/deps/librdkafka/tests/0102-static_group_rebalance.c +317 -16
  202. package/deps/librdkafka/tests/0103-transactions.c +7 -7
  203. package/deps/librdkafka/tests/0104-fetch_from_follower_mock.c +1 -0
  204. package/deps/librdkafka/tests/0105-transactions_mock.c +18 -11
  205. package/deps/librdkafka/tests/0106-cgrp_sess_timeout.c +35 -14
  206. package/deps/librdkafka/tests/0107-topic_recreate.c +2 -2
  207. package/deps/librdkafka/tests/0109-auto_create_topics.cpp +86 -26
  208. package/deps/librdkafka/tests/0110-batch_size.cpp +1 -2
  209. package/deps/librdkafka/tests/0112-assign_unknown_part.c +3 -14
  210. package/deps/librdkafka/tests/0113-cooperative_rebalance.cpp +248 -104
  211. package/deps/librdkafka/tests/0114-sticky_partitioning.cpp +3 -3
  212. package/deps/librdkafka/tests/0115-producer_auth.cpp +4 -1
  213. package/deps/librdkafka/tests/0116-kafkaconsumer_close.cpp +7 -5
  214. package/deps/librdkafka/tests/0117-mock_errors.c +15 -5
  215. package/deps/librdkafka/tests/0118-commit_rebalance.c +41 -8
  216. package/deps/librdkafka/tests/0119-consumer_auth.cpp +20 -1
  217. package/deps/librdkafka/tests/0120-asymmetric_subscription.c +8 -3
  218. package/deps/librdkafka/tests/0125-immediate_flush.c +1 -1
  219. package/deps/librdkafka/tests/0126-oauthbearer_oidc.c +323 -8
  220. package/deps/librdkafka/tests/0129-fetch_aborted_msgs.c +1 -0
  221. package/deps/librdkafka/tests/0132-strategy_ordering.c +12 -4
  222. package/deps/librdkafka/tests/0133-ssl_keys.c +28 -6
  223. package/deps/librdkafka/tests/0137-barrier_batch_consume.c +24 -14
  224. package/deps/librdkafka/tests/0139-offset_validation_mock.c +510 -2
  225. package/deps/librdkafka/tests/0140-commit_metadata.cpp +1 -1
  226. package/deps/librdkafka/tests/0142-reauthentication.c +37 -17
  227. package/deps/librdkafka/tests/0143-exponential_backoff_mock.c +39 -40
  228. package/deps/librdkafka/tests/0146-metadata_mock.c +76 -15
  229. package/deps/librdkafka/tests/0147-consumer_group_consumer_mock.c +952 -0
  230. package/deps/librdkafka/tests/0148-offset_fetch_commit_error_mock.c +563 -0
  231. package/deps/librdkafka/tests/0149-broker-same-host-port.c +140 -0
  232. package/deps/librdkafka/tests/0150-telemetry_mock.c +9 -6
  233. package/deps/librdkafka/tests/0151-purge-brokers.c +566 -0
  234. package/deps/librdkafka/tests/0152-rebootstrap.c +59 -0
  235. package/deps/librdkafka/tests/0153-memberid.c +128 -0
  236. package/deps/librdkafka/tests/CMakeLists.txt +6 -0
  237. package/deps/librdkafka/tests/LibrdkafkaTestApp.py +34 -9
  238. package/deps/librdkafka/tests/autotest.sh +0 -0
  239. package/deps/librdkafka/tests/broker_version_tests.py +0 -0
  240. package/deps/librdkafka/tests/buildbox.sh +0 -0
  241. package/deps/librdkafka/tests/cleanup-checker-tests.sh +0 -0
  242. package/deps/librdkafka/tests/cluster_testing.py +0 -0
  243. package/deps/librdkafka/tests/delete-test-topics.sh +0 -0
  244. package/deps/librdkafka/tests/fixtures/oauthbearer/jwt_assertion_template.json +10 -0
  245. package/deps/librdkafka/tests/fixtures/ssl/client.keystore.intermediate.p12 +0 -0
  246. package/deps/librdkafka/tests/fixtures/ssl/client.keystore.p12 +0 -0
  247. package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.intermediate.pem +72 -0
  248. package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.pem +42 -101
  249. package/deps/librdkafka/tests/fixtures/ssl/client2.intermediate.key +46 -0
  250. package/deps/librdkafka/tests/fixtures/ssl/client2.key +41 -29
  251. package/deps/librdkafka/tests/fixtures/ssl/create_keys.sh +112 -37
  252. package/deps/librdkafka/tests/gen-ssl-certs.sh +0 -0
  253. package/deps/librdkafka/tests/interactive_broker_version.py +0 -0
  254. package/deps/librdkafka/tests/java/Makefile +2 -1
  255. package/deps/librdkafka/tests/java/run-class.sh +0 -0
  256. package/deps/librdkafka/tests/lz4_manual_test.sh +0 -0
  257. package/deps/librdkafka/tests/multi-broker-version-test.sh +0 -0
  258. package/deps/librdkafka/tests/parse-refcnt.sh +0 -0
  259. package/deps/librdkafka/tests/performance_plot.py +0 -0
  260. package/deps/librdkafka/tests/requirements.txt +1 -1
  261. package/deps/librdkafka/tests/run-all-tests.sh +79 -0
  262. package/deps/librdkafka/tests/run-consumer-tests.sh +0 -0
  263. package/deps/librdkafka/tests/run-producer-tests.sh +0 -0
  264. package/deps/librdkafka/tests/run-test-batches.py +157 -0
  265. package/deps/librdkafka/tests/run-test.sh +0 -0
  266. package/deps/librdkafka/tests/sasl_test.py +0 -0
  267. package/deps/librdkafka/tests/test.c +478 -18
  268. package/deps/librdkafka/tests/test.h +62 -13
  269. package/deps/librdkafka/tests/testcpp.cpp +6 -1
  270. package/deps/librdkafka/tests/testcpp.h +30 -2
  271. package/deps/librdkafka/tests/testshared.h +16 -2
  272. package/deps/librdkafka/tests/tools/stats/graph.py +0 -0
  273. package/deps/librdkafka/tests/tools/stats/to_csv.py +1 -1
  274. package/deps/librdkafka/tests/trivup/trivup-0.14.0.tar.gz +0 -0
  275. package/deps/librdkafka/tests/until-fail.sh +0 -0
  276. package/deps/librdkafka/tests/xxxx-metadata.cpp +1 -1
  277. package/deps/librdkafka/vcpkg.json +1 -1
  278. package/deps/librdkafka/win32/setup-msys2.ps1 +24 -8
  279. package/deps/librdkafka/win32/setup-vcpkg.ps1 +28 -6
  280. package/deps/librdkafka/win32/tests/tests.vcxproj +6 -0
  281. package/deps/librdkafka/win32/wingetopt.h +5 -4
  282. package/errors.d.ts +27 -1
  283. package/lib/error.js +29 -3
  284. package/make_docs.sh +0 -0
  285. package/package.json +12 -13
  286. package/run_docker.sh +0 -0
  287. package/src/callbacks.cc +6 -9
  288. package/src/callbacks.h +1 -5
  289. package/test/kafka-consumer-worker.js +10 -7
  290. package/test/kafka-consumer.spec.js +32 -21
  291. package/deps/librdkafka/.semaphore/project.yml +0 -43
  292. package/deps/librdkafka/.semaphore/project_public.yml +0 -20
  293. package/deps/librdkafka/win32/install-openssl.ps1 +0 -33
@@ -39,6 +39,8 @@
39
39
  #include "rdkafka_mock_int.h"
40
40
  #include "rdkafka_transport_int.h"
41
41
  #include "rdkafka_mock.h"
42
+ #include "rdunittest.h"
43
+
42
44
  #include <stdarg.h>
43
45
 
44
46
  typedef struct rd_kafka_mock_request_s rd_kafka_mock_request_t;
@@ -47,6 +49,8 @@ static void rd_kafka_mock_cluster_destroy0(rd_kafka_mock_cluster_t *mcluster);
47
49
  static rd_kafka_mock_request_t *
48
50
  rd_kafka_mock_request_new(int32_t id, int16_t api_key, int64_t timestamp_us);
49
51
  static void rd_kafka_mock_request_free(void *element);
52
+ static void rd_kafka_mock_coord_remove(rd_kafka_mock_cluster_t *mcluster,
53
+ int32_t broker_id);
50
54
 
51
55
  static rd_kafka_mock_broker_t *
52
56
  rd_kafka_mock_broker_find(const rd_kafka_mock_cluster_t *mcluster,
@@ -435,16 +439,22 @@ rd_kafka_mock_partition_assign_replicas(rd_kafka_mock_partition_t *mpart,
435
439
  int replica_cnt = RD_MIN(replication_factor, mcluster->broker_cnt);
436
440
  rd_kafka_mock_broker_t *mrkb;
437
441
  int i = 0;
438
- int first_replica =
439
- (mpart->id * replication_factor) % mcluster->broker_cnt;
442
+ int first_replica;
440
443
  int skipped = 0;
441
444
 
442
445
  if (mpart->replicas)
443
446
  rd_free(mpart->replicas);
444
447
 
445
- mpart->replicas = rd_calloc(replica_cnt, sizeof(*mpart->replicas));
448
+ mpart->replicas = replica_cnt
449
+ ? rd_calloc(replica_cnt, sizeof(*mpart->replicas))
450
+ : NULL;
446
451
  mpart->replica_cnt = replica_cnt;
452
+ if (replica_cnt == 0) {
453
+ rd_kafka_mock_partition_set_leader0(mpart, NULL);
454
+ return;
455
+ }
447
456
 
457
+ first_replica = (mpart->id * replication_factor) % mcluster->broker_cnt;
448
458
 
449
459
  /* Use a predictable, determininistic order on a per-topic basis.
450
460
  *
@@ -918,6 +928,23 @@ static void rd_kafka_mock_cluster_io_add(rd_kafka_mock_cluster_t *mcluster,
918
928
  mcluster->fd_cnt++;
919
929
  }
920
930
 
931
+ /**
932
+ * @brief Reassign partition replicas to broker, after deleting or
933
+ * adding a new one.
934
+ */
935
+ static void
936
+ rd_kafka_mock_cluster_reassign_partitions(rd_kafka_mock_cluster_t *mcluster) {
937
+ rd_kafka_mock_topic_t *mtopic;
938
+ TAILQ_FOREACH(mtopic, &mcluster->topics, link) {
939
+ int i;
940
+ for (i = 0; i < mtopic->partition_cnt; i++) {
941
+ rd_kafka_mock_partition_t *mpart =
942
+ &mtopic->partitions[i];
943
+ rd_kafka_mock_partition_assign_replicas(
944
+ mpart, mpart->replica_cnt);
945
+ }
946
+ }
947
+ }
921
948
 
922
949
  static void rd_kafka_mock_connection_close(rd_kafka_mock_connection_t *mconn,
923
950
  const char *reason) {
@@ -1335,8 +1362,14 @@ static void rd_kafka_mock_connection_io(rd_kafka_mock_cluster_t *mcluster,
1335
1362
  }
1336
1363
  }
1337
1364
 
1338
- if (events & (POLLERR | POLLHUP)) {
1339
- rd_kafka_mock_connection_close(mconn, "Disconnected");
1365
+ if (events & POLLERR) {
1366
+ rd_kafka_mock_connection_close(mconn,
1367
+ "Disconnected: "
1368
+ "Error condition");
1369
+ return;
1370
+ }
1371
+ if (events & POLLHUP) {
1372
+ rd_kafka_mock_connection_close(mconn, "Disconnected: Hang up");
1340
1373
  return;
1341
1374
  }
1342
1375
 
@@ -1564,6 +1597,8 @@ static void rd_kafka_mock_broker_destroy(rd_kafka_mock_broker_t *mrkb) {
1564
1597
  if (mrkb->rack)
1565
1598
  rd_free(mrkb->rack);
1566
1599
 
1600
+ rd_kafka_mock_coord_remove(mrkb->cluster, mrkb->id);
1601
+
1567
1602
  TAILQ_REMOVE(&mrkb->cluster->brokers, mrkb, link);
1568
1603
  mrkb->cluster->broker_cnt--;
1569
1604
 
@@ -1571,6 +1606,30 @@ static void rd_kafka_mock_broker_destroy(rd_kafka_mock_broker_t *mrkb) {
1571
1606
  }
1572
1607
 
1573
1608
 
1609
+ rd_kafka_resp_err_t
1610
+ rd_kafka_mock_broker_decommission(rd_kafka_mock_cluster_t *mcluster,
1611
+ int32_t broker_id) {
1612
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
1613
+
1614
+ rko->rko_u.mock.broker_id = broker_id;
1615
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_BROKER_DECOMMISSION;
1616
+
1617
+ return rd_kafka_op_err_destroy(
1618
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
1619
+ }
1620
+
1621
+ rd_kafka_resp_err_t rd_kafka_mock_broker_add(rd_kafka_mock_cluster_t *mcluster,
1622
+ int32_t broker_id) {
1623
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
1624
+
1625
+ rko->rko_u.mock.broker_id = broker_id;
1626
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_BROKER_ADD;
1627
+
1628
+ return rd_kafka_op_err_destroy(
1629
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
1630
+ }
1631
+
1632
+
1574
1633
  /**
1575
1634
  * @brief Starts listening on the mock broker socket.
1576
1635
  *
@@ -1664,16 +1723,28 @@ static int rd_kafka_mock_broker_new_listener(rd_kafka_mock_cluster_t *mcluster,
1664
1723
 
1665
1724
 
1666
1725
  static rd_kafka_mock_broker_t *
1667
- rd_kafka_mock_broker_new(rd_kafka_mock_cluster_t *mcluster, int32_t broker_id) {
1726
+ rd_kafka_mock_broker_new(rd_kafka_mock_cluster_t *mcluster,
1727
+ int32_t broker_id,
1728
+ rd_kafka_resp_err_t *err) {
1668
1729
  rd_kafka_mock_broker_t *mrkb;
1669
1730
  rd_socket_t listen_s;
1670
1731
  struct sockaddr_in sin = {
1671
1732
  .sin_family = AF_INET,
1672
1733
  .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)}};
1673
1734
 
1735
+ if (rd_kafka_mock_broker_find(mcluster, broker_id)) {
1736
+ if (err)
1737
+ *err = RD_KAFKA_RESP_ERR__INVALID_ARG;
1738
+ /* A broker with this id already exists. */
1739
+ return NULL;
1740
+ }
1741
+
1674
1742
  listen_s = rd_kafka_mock_broker_new_listener(mcluster, &sin);
1675
- if (listen_s == -1)
1743
+ if (listen_s == -1) {
1744
+ if (err)
1745
+ *err = RD_KAFKA_RESP_ERR__TRANSPORT;
1676
1746
  return NULL;
1747
+ }
1677
1748
 
1678
1749
  /*
1679
1750
  * Create mock broker object
@@ -1698,6 +1769,8 @@ rd_kafka_mock_broker_new(rd_kafka_mock_cluster_t *mcluster, int32_t broker_id) {
1698
1769
 
1699
1770
  if (rd_kafka_mock_broker_start_listener(mrkb) == -1) {
1700
1771
  rd_kafka_mock_broker_destroy(mrkb);
1772
+ if (err)
1773
+ *err = RD_KAFKA_RESP_ERR__TRANSPORT;
1701
1774
  return NULL;
1702
1775
  }
1703
1776
 
@@ -1806,6 +1879,20 @@ rd_kafka_mock_coord_set(rd_kafka_mock_cluster_t *mcluster,
1806
1879
  return mcoord;
1807
1880
  }
1808
1881
 
1882
+ /**
1883
+ * @brief Remove coordinator by broker id.
1884
+ */
1885
+ void rd_kafka_mock_coord_remove(rd_kafka_mock_cluster_t *mcluster,
1886
+ int32_t broker_id) {
1887
+ rd_kafka_mock_coord_t *mcoord, *tmp;
1888
+
1889
+ TAILQ_FOREACH_SAFE(mcoord, &mcluster->coords, link, tmp) {
1890
+ if (mcoord->broker_id == broker_id) {
1891
+ rd_kafka_mock_coord_destroy(mcluster, mcoord);
1892
+ }
1893
+ }
1894
+ }
1895
+
1809
1896
 
1810
1897
  /**
1811
1898
  * @brief Remove and return the next error, or RD_KAFKA_RESP_ERR_NO_ERROR
@@ -2205,6 +2292,31 @@ rd_kafka_mock_broker_set_rack(rd_kafka_mock_cluster_t *mcluster,
2205
2292
  rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2206
2293
  }
2207
2294
 
2295
+ void rd_kafka_mock_broker_set_host_port(rd_kafka_mock_cluster_t *cluster,
2296
+ int32_t broker_id,
2297
+ const char *host,
2298
+ int port) {
2299
+ rd_kafka_mock_broker_t *mrkb;
2300
+
2301
+ mtx_lock(&cluster->lock);
2302
+ TAILQ_FOREACH(mrkb, &cluster->brokers, link) {
2303
+ if (mrkb->id == broker_id) {
2304
+ rd_kafka_dbg(
2305
+ cluster->rk, MOCK, "MOCK",
2306
+ "Broker %" PRId32
2307
+ ": Setting advertised listener from %s:%d to %s:%d",
2308
+ broker_id, mrkb->advertised_listener, mrkb->port,
2309
+ host, port);
2310
+ rd_snprintf(mrkb->advertised_listener,
2311
+ sizeof(mrkb->advertised_listener), "%s",
2312
+ host);
2313
+ mrkb->port = port;
2314
+ break;
2315
+ }
2316
+ }
2317
+ mtx_unlock(&cluster->lock);
2318
+ }
2319
+
2208
2320
  rd_kafka_resp_err_t
2209
2321
  rd_kafka_mock_coordinator_set(rd_kafka_mock_cluster_t *mcluster,
2210
2322
  const char *key_type,
@@ -2326,6 +2438,11 @@ rd_kafka_mock_broker_cmd(rd_kafka_mock_cluster_t *mcluster,
2326
2438
  mrkb->rack = NULL;
2327
2439
  break;
2328
2440
 
2441
+ case RD_KAFKA_MOCK_CMD_BROKER_DECOMMISSION:
2442
+ rd_kafka_mock_broker_destroy(mrkb);
2443
+ rd_kafka_mock_cluster_reassign_partitions(mcluster);
2444
+ break;
2445
+
2329
2446
  default:
2330
2447
  RD_BUG("Unhandled mock cmd %d", rko->rko_u.mock.cmd);
2331
2448
  break;
@@ -2380,6 +2497,7 @@ rd_kafka_mock_cluster_cmd(rd_kafka_mock_cluster_t *mcluster,
2380
2497
  rd_kafka_mock_partition_t *mpart;
2381
2498
  rd_kafka_mock_broker_t *mrkb;
2382
2499
  size_t i;
2500
+ rd_kafka_resp_err_t err;
2383
2501
 
2384
2502
  switch (rko->rko_u.mock.cmd) {
2385
2503
  case RD_KAFKA_MOCK_CMD_TOPIC_CREATE:
@@ -2490,8 +2608,16 @@ rd_kafka_mock_cluster_cmd(rd_kafka_mock_cluster_t *mcluster,
2490
2608
  case RD_KAFKA_MOCK_CMD_BROKER_SET_UPDOWN:
2491
2609
  case RD_KAFKA_MOCK_CMD_BROKER_SET_RTT:
2492
2610
  case RD_KAFKA_MOCK_CMD_BROKER_SET_RACK:
2611
+ case RD_KAFKA_MOCK_CMD_BROKER_DECOMMISSION:
2493
2612
  return rd_kafka_mock_brokers_cmd(mcluster, rko);
2494
2613
 
2614
+ case RD_KAFKA_MOCK_CMD_BROKER_ADD:
2615
+ if (!rd_kafka_mock_broker_new(mcluster,
2616
+ rko->rko_u.mock.broker_id, &err))
2617
+ return err;
2618
+
2619
+ rd_kafka_mock_cluster_reassign_partitions(mcluster);
2620
+ break;
2495
2621
  case RD_KAFKA_MOCK_CMD_COORD_SET:
2496
2622
  if (!rd_kafka_mock_coord_set(mcluster, rko->rko_u.mock.name,
2497
2623
  rko->rko_u.mock.str,
@@ -2578,7 +2704,8 @@ rd_kafka_mock_cluster_op_serve(rd_kafka_t *rk,
2578
2704
  static void rd_kafka_mock_cluster_destroy0(rd_kafka_mock_cluster_t *mcluster) {
2579
2705
  rd_kafka_mock_topic_t *mtopic;
2580
2706
  rd_kafka_mock_broker_t *mrkb;
2581
- rd_kafka_mock_cgrp_t *mcgrp;
2707
+ rd_kafka_mock_cgrp_classic_t *mcgrp_classic;
2708
+ rd_kafka_mock_cgrp_consumer_t *mcgrp_consumer;
2582
2709
  rd_kafka_mock_coord_t *mcoord;
2583
2710
  rd_kafka_mock_error_stack_t *errstack;
2584
2711
  thrd_t dummy_rkb_thread;
@@ -2591,8 +2718,11 @@ static void rd_kafka_mock_cluster_destroy0(rd_kafka_mock_cluster_t *mcluster) {
2591
2718
  while ((mrkb = TAILQ_FIRST(&mcluster->brokers)))
2592
2719
  rd_kafka_mock_broker_destroy(mrkb);
2593
2720
 
2594
- while ((mcgrp = TAILQ_FIRST(&mcluster->cgrps)))
2595
- rd_kafka_mock_cgrp_destroy(mcgrp);
2721
+ while ((mcgrp_classic = TAILQ_FIRST(&mcluster->cgrps_classic)))
2722
+ rd_kafka_mock_cgrp_classic_destroy(mcgrp_classic);
2723
+
2724
+ while ((mcgrp_consumer = TAILQ_FIRST(&mcluster->cgrps_consumer)))
2725
+ rd_kafka_mock_cgrp_consumer_destroy(mcgrp_consumer);
2596
2726
 
2597
2727
  while ((mcoord = TAILQ_FIRST(&mcluster->coords)))
2598
2728
  rd_kafka_mock_coord_destroy(mcluster, mcoord);
@@ -2606,16 +2736,16 @@ static void rd_kafka_mock_cluster_destroy0(rd_kafka_mock_cluster_t *mcluster) {
2606
2736
 
2607
2737
  rd_list_destroy(&mcluster->request_list);
2608
2738
 
2739
+ dummy_rkb_thread = mcluster->dummy_rkb->rkb_thread;
2740
+
2609
2741
  /*
2610
- * Destroy dummy broker
2742
+ * Destroy dummy broker.
2743
+ * WARNING: This is last time we can read
2744
+ * from dummy_rkb in this thread!
2611
2745
  */
2612
2746
  rd_kafka_q_enq(mcluster->dummy_rkb->rkb_ops,
2613
2747
  rd_kafka_op_new(RD_KAFKA_OP_TERMINATE));
2614
2748
 
2615
- dummy_rkb_thread = mcluster->dummy_rkb->rkb_thread;
2616
-
2617
- rd_kafka_broker_destroy(mcluster->dummy_rkb);
2618
-
2619
2749
  if (thrd_join(dummy_rkb_thread, &ret) != thrd_success)
2620
2750
  rd_assert(!*"failed to join mock dummy broker thread");
2621
2751
 
@@ -2688,7 +2818,7 @@ rd_kafka_mock_cluster_t *rd_kafka_mock_cluster_new(rd_kafka_t *rk,
2688
2818
  TAILQ_INIT(&mcluster->brokers);
2689
2819
 
2690
2820
  for (i = 1; i <= broker_cnt; i++) {
2691
- if (!(mrkb = rd_kafka_mock_broker_new(mcluster, i))) {
2821
+ if (!(mrkb = rd_kafka_mock_broker_new(mcluster, i, NULL))) {
2692
2822
  rd_kafka_mock_cluster_destroy(mcluster);
2693
2823
  return NULL;
2694
2824
  }
@@ -2702,10 +2832,14 @@ rd_kafka_mock_cluster_t *rd_kafka_mock_cluster_new(rd_kafka_t *rk,
2702
2832
  TAILQ_INIT(&mcluster->topics);
2703
2833
  mcluster->defaults.partition_cnt = 4;
2704
2834
  mcluster->defaults.replication_factor = RD_MIN(3, broker_cnt);
2705
- mcluster->defaults.group_initial_rebalance_delay_ms = 3000;
2706
- mcluster->track_requests = rd_false;
2835
+ mcluster->defaults.group_initial_rebalance_delay_ms = 3000;
2836
+ mcluster->track_requests = rd_false;
2837
+ mcluster->defaults.group_consumer_session_timeout_ms = 30000;
2838
+ mcluster->defaults.group_consumer_heartbeat_interval_ms = 3000;
2839
+
2840
+ TAILQ_INIT(&mcluster->cgrps_classic);
2707
2841
 
2708
- TAILQ_INIT(&mcluster->cgrps);
2842
+ TAILQ_INIT(&mcluster->cgrps_consumer);
2709
2843
 
2710
2844
  TAILQ_INIT(&mcluster->coords);
2711
2845
 
@@ -2883,3 +3017,645 @@ int16_t rd_kafka_mock_request_api_key(rd_kafka_mock_request_t *mreq) {
2883
3017
  rd_ts_t rd_kafka_mock_request_timestamp(rd_kafka_mock_request_t *mreq) {
2884
3018
  return mreq->timestamp;
2885
3019
  }
3020
+
3021
+ /* Unit tests */
3022
+
3023
+ /**
3024
+ * @brief Create a topic-partition list with vararg arguments.
3025
+ *
3026
+ * @param cnt Number of topic-partitions.
3027
+ * @param ...vararg is a tuple of:
3028
+ * const char *topic_name
3029
+ * int32_t partition
3030
+ *
3031
+ * @remark The returned pointer ownership is transferred to the caller.
3032
+ */
3033
+ static rd_kafka_topic_partition_list_t *ut_topic_partitions(int cnt, ...) {
3034
+ va_list ap;
3035
+ const char *topic_name;
3036
+ int i = 0;
3037
+
3038
+ rd_kafka_topic_partition_list_t *rktparlist =
3039
+ rd_kafka_topic_partition_list_new(cnt);
3040
+ va_start(ap, cnt);
3041
+ while (i < cnt) {
3042
+ topic_name = va_arg(ap, const char *);
3043
+ int32_t partition = va_arg(ap, int32_t);
3044
+
3045
+ rd_kafka_topic_partition_list_add(rktparlist, topic_name,
3046
+ partition);
3047
+ i++;
3048
+ }
3049
+ va_end(ap);
3050
+
3051
+ return rktparlist;
3052
+ }
3053
+
3054
+ /**
3055
+ * @brief Assert \p expected partition list is equal to \p actual.
3056
+ *
3057
+ * @param expected Expected partition list.
3058
+ * @param actual Actual partition list.
3059
+ * @return Comparation result.
3060
+ */
3061
+ static int ut_assert_topic_partitions(rd_kafka_topic_partition_list_t *expected,
3062
+ rd_kafka_topic_partition_list_t *actual) {
3063
+ rd_bool_t equal;
3064
+ char expected_str[256] = "";
3065
+ char actual_str[256] = "";
3066
+
3067
+ if (expected)
3068
+ RD_UT_ASSERT(actual, "list should be not-NULL, but it's NULL");
3069
+ else
3070
+ RD_UT_ASSERT(!actual, "list should be NULL, but it's not-NULL");
3071
+
3072
+
3073
+ if (!expected)
3074
+ return 0;
3075
+
3076
+ equal = !rd_kafka_topic_partition_list_cmp(
3077
+ actual, expected, rd_kafka_topic_partition_cmp);
3078
+
3079
+ if (!equal) {
3080
+ rd_kafka_topic_partition_list_str(expected, expected_str,
3081
+ sizeof(expected_str),
3082
+ RD_KAFKA_FMT_F_NO_ERR);
3083
+ rd_kafka_topic_partition_list_str(actual, actual_str,
3084
+ sizeof(actual_str),
3085
+ RD_KAFKA_FMT_F_NO_ERR);
3086
+ }
3087
+
3088
+ RD_UT_ASSERT(equal, "list should be equal. Expected: %s, got: %s",
3089
+ expected_str, actual_str);
3090
+ return 0;
3091
+ }
3092
+
3093
+ /**
3094
+ * @struct Fixture used for testing next assignment calculation.
3095
+ */
3096
+ struct cgrp_consumer_member_next_assignment_fixture {
3097
+ /** Current member epoch (after calling next assignment). */
3098
+ int32_t current_member_epoch;
3099
+ /** Current consumer assignment, if changed. */
3100
+ rd_kafka_topic_partition_list_t *current_assignment;
3101
+ /** Returned assignment, if expected. */
3102
+ rd_kafka_topic_partition_list_t *returned_assignment;
3103
+ /** Target assignment, if changed. */
3104
+ rd_kafka_topic_partition_list_t *target_assignment;
3105
+ /** Should simulate a disconnection and reconnection. */
3106
+ rd_bool_t reconnected;
3107
+ /** Should simulate a session time out. */
3108
+ rd_bool_t session_timed_out;
3109
+ /** Comment to log. */
3110
+ const char *comment;
3111
+ };
3112
+
3113
+ /**
3114
+ * @brief Test next assignment calculation using passed \p fixtures.
3115
+ * using a new cluster with a topic with name \p topic and
3116
+ * \p partitions partitions.
3117
+ *
3118
+ * @param topic Topic name to create.
3119
+ * @param partitions Topic partition.
3120
+ * @param fixtures Array of fixtures for this test.
3121
+ * @param fixtures_cnt Number of elements in \p fixtures.
3122
+ * @return Number of occurred errors.
3123
+ */
3124
+ static int ut_cgrp_consumer_member_next_assignment0(
3125
+ const char *topic,
3126
+ int partitions,
3127
+ struct cgrp_consumer_member_next_assignment_fixture *fixtures,
3128
+ size_t fixtures_cnt) {
3129
+ int failures = 0;
3130
+ int32_t current_member_epoch = 0;
3131
+ size_t i;
3132
+ rd_kafka_t *rk;
3133
+ rd_kafka_mock_cluster_t *mcluster;
3134
+ static rd_kafka_mock_topic_t *mtopic;
3135
+ rd_kafka_mock_cgrp_consumer_t *mcgrp;
3136
+ rd_kafka_mock_cgrp_consumer_member_t *member;
3137
+ char errstr[512];
3138
+ rd_kafkap_str_t GroupId = {.str = "group", .len = 5};
3139
+ rd_kafkap_str_t MemberId = {.str = "A", .len = 1};
3140
+ rd_kafkap_str_t InstanceId = {.len = -1};
3141
+ rd_kafkap_str_t SubscribedTopic = {.str = topic, .len = strlen(topic)};
3142
+ rd_kafkap_str_t SubscribedTopicRegex = RD_KAFKAP_STR_INITIALIZER_EMPTY;
3143
+ struct rd_kafka_mock_connection_s *conn =
3144
+ (struct rd_kafka_mock_connection_s
3145
+ *)1; /* fake connection instance */
3146
+
3147
+ rk = rd_kafka_new(RD_KAFKA_CONSUMER, NULL, errstr, sizeof(errstr));
3148
+ mcluster = rd_kafka_mock_cluster_new(rk, 1);
3149
+ mcgrp = rd_kafka_mock_cgrp_consumer_get(mcluster, &GroupId);
3150
+ member = rd_kafka_mock_cgrp_consumer_member_add(
3151
+ mcgrp, conn, &MemberId, &InstanceId, &SubscribedTopic, 1,
3152
+ &SubscribedTopicRegex);
3153
+ mtopic = rd_kafka_mock_topic_new(mcluster, topic, partitions, 1);
3154
+
3155
+ for (i = 0; i < fixtures_cnt; i++) {
3156
+ int j;
3157
+ rd_kafka_topic_partition_list_t *current_assignment,
3158
+ *member_target_assignment, *next_assignment,
3159
+ *returned_assignment;
3160
+
3161
+ RD_UT_SAY("test fixture %" PRIusz ": %s", i,
3162
+ fixtures[i].comment);
3163
+
3164
+ if (fixtures[i].session_timed_out) {
3165
+ rd_kafka_mock_cgrp_consumer_member_leave(mcgrp, member,
3166
+ rd_false);
3167
+ member = rd_kafka_mock_cgrp_consumer_member_add(
3168
+ mcgrp, conn, &MemberId, &InstanceId,
3169
+ &SubscribedTopic, 1, &SubscribedTopicRegex);
3170
+ }
3171
+
3172
+ if (fixtures[i].reconnected) {
3173
+ rd_kafka_mock_cgrps_connection_closed(mcluster, conn);
3174
+ conn++;
3175
+ member = rd_kafka_mock_cgrp_consumer_member_add(
3176
+ mcgrp, conn, &MemberId, &InstanceId,
3177
+ &SubscribedTopic, 1, &SubscribedTopicRegex);
3178
+ }
3179
+
3180
+ member_target_assignment = fixtures[i].target_assignment;
3181
+ if (member_target_assignment) {
3182
+ rd_kafka_mock_cgrp_consumer_target_assignment_t
3183
+ *target_assignment;
3184
+
3185
+ target_assignment =
3186
+ rd_kafka_mock_cgrp_consumer_target_assignment_new(
3187
+ (char **)&MemberId.str, 1,
3188
+ &member_target_assignment);
3189
+
3190
+ rd_kafka_mock_cgrp_consumer_target_assignment(
3191
+ mcluster, GroupId.str, target_assignment);
3192
+ rd_kafka_mock_cgrp_consumer_target_assignment_destroy(
3193
+ target_assignment);
3194
+ rd_kafka_topic_partition_list_destroy(
3195
+ member_target_assignment);
3196
+ }
3197
+
3198
+ current_assignment = fixtures[i].current_assignment;
3199
+ if (current_assignment) {
3200
+ /* Set topic id */
3201
+ for (j = 0; j < current_assignment->cnt; j++) {
3202
+ rd_kafka_topic_partition_set_topic_id(
3203
+ &current_assignment->elems[j], mtopic->id);
3204
+ }
3205
+ }
3206
+
3207
+ next_assignment =
3208
+ rd_kafka_mock_cgrp_consumer_member_next_assignment(
3209
+ member, current_assignment, &current_member_epoch);
3210
+ RD_IF_FREE(current_assignment,
3211
+ rd_kafka_topic_partition_list_destroy);
3212
+ RD_UT_ASSERT(
3213
+ current_member_epoch == fixtures[i].current_member_epoch,
3214
+ "current member epoch after call. Expected: %" PRId32
3215
+ ", got: %" PRId32,
3216
+ fixtures[i].current_member_epoch, current_member_epoch);
3217
+
3218
+ returned_assignment = fixtures[i].returned_assignment;
3219
+ failures += ut_assert_topic_partitions(returned_assignment,
3220
+ next_assignment);
3221
+
3222
+ RD_IF_FREE(next_assignment,
3223
+ rd_kafka_topic_partition_list_destroy);
3224
+ RD_IF_FREE(returned_assignment,
3225
+ rd_kafka_topic_partition_list_destroy);
3226
+ }
3227
+
3228
+ rd_kafka_mock_cluster_destroy(mcluster);
3229
+ rd_kafka_destroy(rk);
3230
+ return failures;
3231
+ }
3232
+
3233
+ /**
3234
+ * @brief Test case where multiple revocations are acked.
3235
+ * Only when they're acked member epoch is bumped
3236
+ * and a new partition is returned to the member.
3237
+ *
3238
+ * @return Number of occurred errors.
3239
+ */
3240
+ static int ut_cgrp_consumer_member_next_assignment1(void) {
3241
+ RD_UT_SAY("Case 1: multiple revocations acked");
3242
+
3243
+ const char *topic = "topic";
3244
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3245
+ {
3246
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3247
+ .current_member_epoch = 3,
3248
+ .current_assignment = NULL,
3249
+ .target_assignment =
3250
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3251
+ .returned_assignment =
3252
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3253
+ },
3254
+ {
3255
+ .comment = "Current assignment empty",
3256
+ .current_member_epoch = 3,
3257
+ .current_assignment = ut_topic_partitions(0),
3258
+ .returned_assignment = NULL,
3259
+ },
3260
+ {
3261
+ .comment = "Empty heartbeat",
3262
+ .current_member_epoch = 3,
3263
+ .current_assignment = NULL,
3264
+ .returned_assignment = NULL,
3265
+ },
3266
+ {
3267
+ .comment = "Current assignment 0",
3268
+ .current_member_epoch = 3,
3269
+ .current_assignment = ut_topic_partitions(1, topic, 0),
3270
+ .returned_assignment = NULL,
3271
+ },
3272
+ {
3273
+ .comment = "Empty heartbeat",
3274
+ .current_member_epoch = 3,
3275
+ .current_assignment = NULL,
3276
+ .returned_assignment = NULL,
3277
+ },
3278
+ {
3279
+ .comment = "Current assignment 0,1",
3280
+ .current_member_epoch = 3,
3281
+ .current_assignment =
3282
+ ut_topic_partitions(2, topic, 0, topic, 1),
3283
+ .returned_assignment = NULL,
3284
+ },
3285
+ {
3286
+ .comment = "Empty heartbeat",
3287
+ .current_member_epoch = 3,
3288
+ .current_assignment = NULL,
3289
+ .returned_assignment = NULL,
3290
+ },
3291
+ {
3292
+ .comment = "Current assignment 0,1,2",
3293
+ .current_member_epoch = 3,
3294
+ .current_assignment =
3295
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3296
+ .returned_assignment = NULL,
3297
+ },
3298
+ {
3299
+ .comment = "Target assignment 0,1,3. Returned assignment 0,1",
3300
+ .current_member_epoch = 3,
3301
+ .target_assignment =
3302
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3303
+ .current_assignment = NULL,
3304
+ .returned_assignment =
3305
+ ut_topic_partitions(2, topic, 0, topic, 1),
3306
+ },
3307
+ {
3308
+ .comment = "Target assignment 0,3. Returned assignment 0",
3309
+ .current_member_epoch = 3,
3310
+ .target_assignment = ut_topic_partitions(2, topic, 0, topic, 3),
3311
+ .current_assignment = NULL,
3312
+ .returned_assignment = ut_topic_partitions(1, topic, 0),
3313
+ },
3314
+ {
3315
+ .comment = "Empty heartbeat",
3316
+ .current_member_epoch = 3,
3317
+ .current_assignment = NULL,
3318
+ .returned_assignment = NULL,
3319
+ },
3320
+ {
3321
+ .comment = "Current assignment 0,1",
3322
+ .current_member_epoch = 3,
3323
+ .current_assignment =
3324
+ ut_topic_partitions(2, topic, 0, topic, 1),
3325
+ .returned_assignment = NULL,
3326
+ },
3327
+ {
3328
+ .comment = "Empty heartbeat",
3329
+ .current_member_epoch = 3,
3330
+ .current_assignment = NULL,
3331
+ .returned_assignment = NULL,
3332
+ },
3333
+ {
3334
+ .comment = "Current assignment 0. Returned assignment 0,3. "
3335
+ "Epoch 3 -> 5",
3336
+ .current_member_epoch = 5,
3337
+ .current_assignment = ut_topic_partitions(1, topic, 0),
3338
+ .returned_assignment =
3339
+ ut_topic_partitions(2, topic, 0, topic, 3),
3340
+ },
3341
+ {
3342
+ .comment = "Empty heartbeat",
3343
+ .current_member_epoch = 5,
3344
+ .current_assignment = NULL,
3345
+ .returned_assignment = NULL,
3346
+ },
3347
+ {
3348
+ .comment = "Current assignment 0,3",
3349
+ .current_member_epoch = 5,
3350
+ .current_assignment =
3351
+ ut_topic_partitions(2, topic, 0, topic, 3),
3352
+ .returned_assignment = NULL,
3353
+ },
3354
+ };
3355
+ return ut_cgrp_consumer_member_next_assignment0(
3356
+ topic, 4, fixtures, RD_ARRAY_SIZE(fixtures));
3357
+ }
3358
+
3359
+ /**
3360
+ * @brief Test case where multiple revocations happen.
3361
+ * Only the first revocation is acked and after that
3362
+ * there's a reassignment and epoch bump.
3363
+ *
3364
+ * @return Number of occurred errors.
3365
+ */
3366
+ static int ut_cgrp_consumer_member_next_assignment2(void) {
3367
+ RD_UT_SAY(
3368
+ "Case 2: reassignment of revoked partition, partial revocation "
3369
+ "acknowledge");
3370
+
3371
+ const char *topic = "topic";
3372
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3373
+ {
3374
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3375
+ .current_member_epoch = 3,
3376
+ .current_assignment = NULL,
3377
+ .target_assignment =
3378
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3379
+ .returned_assignment =
3380
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3381
+ },
3382
+ {
3383
+ .comment = "Current assignment 0,1,2",
3384
+ .current_member_epoch = 3,
3385
+ .current_assignment =
3386
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3387
+ .returned_assignment = NULL,
3388
+ },
3389
+ {
3390
+ .comment = "Target assignment 0,1,3. Returned assignment 0,1",
3391
+ .current_member_epoch = 3,
3392
+ .target_assignment =
3393
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3394
+ .current_assignment = NULL,
3395
+ .returned_assignment =
3396
+ ut_topic_partitions(2, topic, 0, topic, 1),
3397
+ },
3398
+ {
3399
+ .comment = "Target assignment 0,3. Returned assignment 0",
3400
+ .current_member_epoch = 3,
3401
+ .target_assignment = ut_topic_partitions(2, topic, 0, topic, 3),
3402
+ .current_assignment = NULL,
3403
+ .returned_assignment = ut_topic_partitions(1, topic, 0),
3404
+ },
3405
+ {
3406
+ .comment = "Empty heartbeat",
3407
+ .current_member_epoch = 3,
3408
+ .current_assignment = NULL,
3409
+ .returned_assignment = NULL,
3410
+ },
3411
+ {
3412
+ .comment = "Current assignment 0,1",
3413
+ .current_member_epoch = 3,
3414
+ .current_assignment =
3415
+ ut_topic_partitions(2, topic, 0, topic, 1),
3416
+ .returned_assignment = NULL,
3417
+ },
3418
+ {
3419
+ .comment = "Empty heartbeat",
3420
+ .current_member_epoch = 3,
3421
+ .current_assignment = NULL,
3422
+ .returned_assignment = NULL,
3423
+ },
3424
+ {
3425
+ .comment = "Target+Returned assignment 0,1,3. Epoch 3 -> 6",
3426
+ .current_member_epoch = 6,
3427
+ .target_assignment =
3428
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3429
+ .current_assignment = NULL,
3430
+ .returned_assignment =
3431
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3432
+ },
3433
+ {
3434
+ .comment = "Empty heartbeat",
3435
+ .current_member_epoch = 6,
3436
+ .current_assignment = NULL,
3437
+ .returned_assignment = NULL,
3438
+ },
3439
+ {
3440
+ .comment = "Current assignment 0,1,3",
3441
+ .current_member_epoch = 6,
3442
+ .current_assignment =
3443
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3444
+ .returned_assignment = NULL,
3445
+ },
3446
+ };
3447
+ return ut_cgrp_consumer_member_next_assignment0(
3448
+ topic, 4, fixtures, RD_ARRAY_SIZE(fixtures));
3449
+ }
3450
+
3451
+ /**
3452
+ * @brief Test case where multiple revocations happen.
3453
+ * They aren't acked but then a
3454
+ * reassignment of all the revoked partition happens, bumping the epoch.
3455
+ *
3456
+ * @return Number of occurred errors.
3457
+ */
3458
+ static int ut_cgrp_consumer_member_next_assignment3(void) {
3459
+ RD_UT_SAY(
3460
+ "Case 3: reassignment of revoked partition and new partition, no "
3461
+ "revocation acknowledge");
3462
+
3463
+ const char *topic = "topic";
3464
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3465
+ {
3466
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3467
+ .current_member_epoch = 3,
3468
+ .current_assignment = NULL,
3469
+ .target_assignment =
3470
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3471
+ .returned_assignment =
3472
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3473
+ },
3474
+ {
3475
+ .comment = "Current assignment 0,1,2",
3476
+ .current_member_epoch = 3,
3477
+ .current_assignment =
3478
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3479
+ .returned_assignment = NULL,
3480
+ },
3481
+ {
3482
+ .comment = "Target assignment 0,1,3. Returned assignment 0,1",
3483
+ .current_member_epoch = 3,
3484
+ .target_assignment =
3485
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3486
+ .current_assignment = NULL,
3487
+ .returned_assignment =
3488
+ ut_topic_partitions(2, topic, 0, topic, 1),
3489
+ },
3490
+ {
3491
+ .comment = "Target assignment 0,3. Returned assignment 0",
3492
+ .current_member_epoch = 3,
3493
+ .target_assignment = ut_topic_partitions(2, topic, 0, topic, 3),
3494
+ .current_assignment = NULL,
3495
+ .returned_assignment = ut_topic_partitions(1, topic, 0),
3496
+ },
3497
+ {
3498
+ .comment = "Empty heartbeat",
3499
+ .current_member_epoch = 3,
3500
+ .current_assignment = NULL,
3501
+ .returned_assignment = NULL,
3502
+ },
3503
+ {
3504
+ .comment = "Target+Returned assignment 0,1,2,3. Epoch 3 -> 6",
3505
+ .current_member_epoch = 6,
3506
+ .target_assignment = ut_topic_partitions(
3507
+ 3, topic, 0, topic, 1, topic, 2, topic, 3, NULL),
3508
+ .returned_assignment = ut_topic_partitions(
3509
+ 3, topic, 0, topic, 1, topic, 2, topic, 3, NULL),
3510
+ },
3511
+ {
3512
+ .comment = "Empty heartbeat",
3513
+ .current_member_epoch = 6,
3514
+ .current_assignment = NULL,
3515
+ .returned_assignment = NULL,
3516
+ },
3517
+ {
3518
+ .comment = "Current assignment 0,1,2,3",
3519
+ .current_member_epoch = 6,
3520
+ .current_assignment = ut_topic_partitions(
3521
+ 3, topic, 0, topic, 1, topic, 2, topic, 3, NULL),
3522
+ .returned_assignment = NULL,
3523
+ },
3524
+ };
3525
+ return ut_cgrp_consumer_member_next_assignment0(
3526
+ topic, 4, fixtures, RD_ARRAY_SIZE(fixtures));
3527
+ }
3528
+
3529
+ /**
3530
+ * @brief Test case where a disconnection happens and after that
3531
+ * the client send its assignment again, with same member epoch,
3532
+ * and receives back the returned assignment, even if the same.
3533
+ *
3534
+ * @return Number of occurred errors.
3535
+ */
3536
+ static int ut_cgrp_consumer_member_next_assignment4(void) {
3537
+ RD_UT_SAY("Case 4: reconciliation after disconnection");
3538
+
3539
+ const char *topic = "topic";
3540
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3541
+ {
3542
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3543
+ .current_member_epoch = 3,
3544
+ .current_assignment = NULL,
3545
+ .target_assignment =
3546
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3547
+ .returned_assignment =
3548
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3549
+ },
3550
+ {
3551
+ .comment = "Current assignment empty",
3552
+ .current_member_epoch = 3,
3553
+ .current_assignment = ut_topic_partitions(0),
3554
+ .returned_assignment = NULL,
3555
+ },
3556
+ {
3557
+ .comment = "Disconnected, resends current assignment. Returns "
3558
+ "assignment again",
3559
+ .reconnected = rd_true,
3560
+ .current_member_epoch = 3,
3561
+ .current_assignment = ut_topic_partitions(0),
3562
+ .returned_assignment =
3563
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3564
+ },
3565
+ {
3566
+ .comment = "Empty heartbeat",
3567
+ .current_member_epoch = 3,
3568
+ .current_assignment = NULL,
3569
+ .returned_assignment = NULL,
3570
+ },
3571
+ {
3572
+ .comment = "Current assignment 0,1,2",
3573
+ .current_member_epoch = 3,
3574
+ .current_assignment =
3575
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3576
+ .returned_assignment = NULL,
3577
+ },
3578
+ };
3579
+ return ut_cgrp_consumer_member_next_assignment0(
3580
+ topic, 3, fixtures, RD_ARRAY_SIZE(fixtures));
3581
+ }
3582
+
3583
+ /**
3584
+ * @brief Test case where a session timeout happens and then
3585
+ * the client receives a FENCED_MEMBER_EPOCH error,
3586
+ * revokes all of its partitions and rejoins with epoch 0.
3587
+ *
3588
+ * @return Number of occurred errors.
3589
+ */
3590
+ static int ut_cgrp_consumer_member_next_assignment5(void) {
3591
+ RD_UT_SAY("Case 5: fenced consumer");
3592
+
3593
+ const char *topic = "topic";
3594
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3595
+ {
3596
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3597
+ .current_member_epoch = 3,
3598
+ .current_assignment = NULL,
3599
+ .target_assignment =
3600
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3601
+ .returned_assignment =
3602
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3603
+ },
3604
+ {
3605
+ .comment = "Session times out, receives FENCED_MEMBER_EPOCH. "
3606
+ "Epoch 3 -> 0",
3607
+ .session_timed_out = rd_true,
3608
+ .current_member_epoch = -1,
3609
+ .current_assignment = NULL,
3610
+ .returned_assignment = NULL,
3611
+ },
3612
+ {
3613
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 6",
3614
+ .target_assignment =
3615
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3616
+ .current_member_epoch = 4,
3617
+ .current_assignment = NULL,
3618
+ .returned_assignment =
3619
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3620
+ },
3621
+ {
3622
+ .comment = "Current assignment 0,1,2",
3623
+ .current_member_epoch = 4,
3624
+ .current_assignment =
3625
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3626
+ .returned_assignment = NULL,
3627
+ },
3628
+ };
3629
+ return ut_cgrp_consumer_member_next_assignment0(
3630
+ topic, 3, fixtures, RD_ARRAY_SIZE(fixtures));
3631
+ }
3632
+
3633
+ /**
3634
+ * @brief Test all next assignment calculation cases,
3635
+ * for KIP-848 consumer group type and collect
3636
+ * number of errors.
3637
+ *
3638
+ * @return Number of occurred errors.
3639
+ */
3640
+ static int ut_cgrp_consumer_member_next_assignment(void) {
3641
+ RD_UT_BEGIN();
3642
+ int failures = 0;
3643
+
3644
+ failures += ut_cgrp_consumer_member_next_assignment1();
3645
+ failures += ut_cgrp_consumer_member_next_assignment2();
3646
+ failures += ut_cgrp_consumer_member_next_assignment3();
3647
+ failures += ut_cgrp_consumer_member_next_assignment4();
3648
+ failures += ut_cgrp_consumer_member_next_assignment5();
3649
+
3650
+ RD_UT_ASSERT(!failures, "some tests failed");
3651
+ RD_UT_PASS();
3652
+ }
3653
+
3654
+ /**
3655
+ * @brief Mock cluster unit tests
3656
+ */
3657
+ int unittest_mock_cluster(void) {
3658
+ int fails = 0;
3659
+ fails += ut_cgrp_consumer_member_next_assignment();
3660
+ return fails;
3661
+ }