2727 */
2828
2929#include "test.h"
30+ #include "rdkafka.h"
3031
3132#include "../src/rdkafka_proto.h"
3233
3334#include <stdarg.h>
35+ #include <stdio.h>
36+ #include <string.h>
3437
3538
3639/**
@@ -928,6 +931,122 @@ static void do_test_quick_unsubscribe_tests(void) {
928931 }
929932}
930933
934+ /**
935+ * Integration test for KIP-848 partition metadata refresh:
936+ * - Create topic with 2 partitions
937+ * - Start consumer group and verify initial assignment
938+ * - Increase partition count to 4
939+ * - Reset log tracking variables after partition creation
940+ * - Wait for HeartbeatRequest, HeartbeatResponse, and metadata refresh logs
941+ * - Assert that metadata refresh is triggered for new partitions
942+ */
943+
944+ // Globals to track log sequence
945+ static volatile int seen_heartbeat_req = 0 ;
946+ static volatile int seen_heartbeat_resp = 0 ;
947+ static volatile int seen_metadata_log = 0 ;
948+
949+ static void reset_log_tracking (void ) {
950+ seen_heartbeat_req = 0 ;
951+ seen_heartbeat_resp = 0 ;
952+ seen_metadata_log = 0 ;
953+ }
954+
955+ static void wait_for_metadata_refresh_log (int timeout_ms ) {
956+ int elapsed = 0 ;
957+ while (elapsed < timeout_ms && !seen_metadata_log ) {
958+ rd_usleep (500 * 1000 , NULL ); // 500 ms
959+ elapsed += 500 ;
960+ }
961+ TEST_ASSERT (
962+ seen_heartbeat_req ,
963+ "Expected HeartbeatRequest log not seen after partition creation" );
964+ TEST_ASSERT (
965+ seen_heartbeat_resp ,
966+ "Expected HeartbeatResponse log not seen after partition creation" );
967+ TEST_ASSERT (seen_metadata_log ,
968+ "Expected metadata refresh log not seen after partition "
969+ "creation and heartbeat" );
970+ }
971+
972+ // Custom log callback to capture and process librdkafka logs
973+ static void test_metadata_log_cb (const rd_kafka_t * rk ,
974+ int level ,
975+ const char * fac ,
976+ const char * buf ) {
977+ if (strstr (buf , "Sent ConsumerGroupHeartbeatRequest" )) {
978+ seen_heartbeat_req = 1 ;
979+ }
980+ if (seen_heartbeat_req &&
981+ strstr (buf , "Received ConsumerGroupHeartbeatResponse" )) {
982+ seen_heartbeat_resp = 1 ;
983+ }
984+ if (seen_heartbeat_resp &&
985+ strstr (buf ,
986+ "Partition assigned to this consumer is not present in "
987+ "cached metadata" )) {
988+ seen_metadata_log = 1 ;
989+ }
990+ }
991+
992+ static rd_kafka_t * create_consumers (const char * group ) {
993+ rd_kafka_conf_t * conf ;
994+ test_conf_init (& conf , NULL , 60 );
995+ test_conf_set (conf , "group.id" , group );
996+ test_conf_set (conf , "auto.offset.reset" , "earliest" );
997+ test_conf_set (conf , "debug" , "cgrp, protocol" );
998+ rd_kafka_conf_set_log_cb (conf , test_metadata_log_cb );
999+ rd_kafka_t * consumer = test_create_consumer (group , NULL , conf , NULL );
1000+ return consumer ;
1001+ }
1002+
1003+ static void do_test_setup_and_run_metadata_refresh_test (void ) {
1004+ const char * topic = test_mk_topic_name ("cgrp_metadata" , 1 );
1005+ int initial_partitions = 2 ;
1006+ int new_partitions = 4 ;
1007+ rd_kafka_t * c1 , * c2 , * rk ;
1008+ const char * group = "grp_metadata" ;
1009+
1010+ SUB_TEST_QUICK ();
1011+
1012+ TEST_SAY ("Creating topic %s with %d partitions\n" , topic ,
1013+ initial_partitions );
1014+ test_create_topic (NULL , topic , initial_partitions , 1 );
1015+
1016+ TEST_SAY ("Creating consumers\n" );
1017+ c1 = create_consumers (group );
1018+ c2 = create_consumers (group );
1019+
1020+ rk = test_create_handle (RD_KAFKA_PRODUCER , NULL );
1021+
1022+ TEST_SAY ("Subscribing to topic %s\n" , topic );
1023+ test_consumer_subscribe (c1 , topic );
1024+ test_consumer_subscribe (c2 , topic );
1025+
1026+ // Wait for initial assignment
1027+ test_consumer_wait_assignment (c1 , rd_false );
1028+ test_consumer_wait_assignment (c2 , rd_false );
1029+
1030+ // Create new partitions
1031+ TEST_SAY ("Increasing partition count to %d\n" , new_partitions );
1032+ test_create_partitions (rk , topic , new_partitions );
1033+
1034+ // Reset log tracking variables to only consider logs after partition
1035+ // creation
1036+ reset_log_tracking ();
1037+
1038+ // Wait for expected logs for up to 10 seconds
1039+ wait_for_metadata_refresh_log (10000 );
1040+
1041+ TEST_SAY ("Closing consumers\n" );
1042+ test_consumer_close (c1 );
1043+ test_consumer_close (c2 );
1044+ rd_kafka_destroy (c1 );
1045+ rd_kafka_destroy (c2 );
1046+
1047+ SUB_TEST_PASS ();
1048+ }
1049+
9311050int main_0147_consumer_group_consumer_mock (int argc , char * * argv ) {
9321051 TEST_SKIP_MOCK_CLUSTER (0 );
9331052
@@ -948,5 +1067,7 @@ int main_0147_consumer_group_consumer_mock(int argc, char **argv) {
9481067
9491068 do_test_quick_unsubscribe_tests ();
9501069
1070+ do_test_setup_and_run_metadata_refresh_test ();
1071+
9511072 return 0 ;
9521073}
0 commit comments