aliehsaeedii commented on code in PR #20266: URL: https://github.com/apache/kafka/pull/20266#discussion_r2252206336
########## core/src/test/scala/integration/kafka/api/PlaintextAdminIntegrationTest.scala: ########## @@ -4363,6 +4364,396 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest { } } } + + @ParameterizedTest(name = TestInfoUtils.TestWithParameterizedGroupProtocolNames) + @MethodSource(Array("getTestGroupProtocolParametersStreamsGroupProtocolOnly")) + def testDescribeStreamsGroups(groupProtocol: String): Unit = { + val streamsGroupId = "stream_group_id" + val testTopicName = "test_topic" + val testOutputTopicName = "test_output_topic" + val testNumPartitions = 1 + + val config = createConfig + client = Admin.create(config) + + prepareTopics(List(testTopicName, testOutputTopicName), testNumPartitions) + prepareRecords(testTopicName) + + val streamsConfig = new Properties() + streamsConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest") + streamsConfig.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 10 * 1000) + val streams = createStreamsGroup( + configOverrides = streamsConfig, + inputTopic = testTopicName, + outputTopic = testOutputTopicName, + streamsGroupId = streamsGroupId, + groupProtocol = groupProtocol + ) + + try { + streams.cleanUp() + streams.start() + + TestUtils.waitUntilTrue(() => streams.state() == KafkaStreams.State.RUNNING, "Streams not in RUNNING state") + + TestUtils.waitUntilTrue(() => { + client.listGroups().all().get().stream() + .anyMatch(g => g.groupId() == streamsGroupId) + }, "Streams group not ready to describe yet") + + Thread.sleep(10000) + + TestUtils.waitUntilTrue(() => { + try { + val describedGroups = client.describeStreamsGroups(util.List.of(streamsGroupId)).all().get() + val group = describedGroups.get(streamsGroupId) + if (group != null) { + group.groupState() == GroupState.STABLE && !group.subtopologies().isEmpty + } else { + false + } + } catch { + case _: Exception => false + } + }, "Stream group not fully initialized with topology") + + // Verify the describe call works correctly + val describedGroups = client.describeStreamsGroups(util.List.of(streamsGroupId)).all().get() + val group = describedGroups.get(streamsGroupId) + + assertNotNull(group) + assertEquals(streamsGroupId, group.groupId()) + assertFalse(group.members().isEmpty) + assertNotNull(group.subtopologies()) + assertFalse(group.subtopologies().isEmpty) + + // Verify the topology contains the expected source and sink topics + val subtopologies = group.subtopologies().asScala + assertTrue(subtopologies.exists(subtopology => + subtopology.sourceTopics().contains(testTopicName))) + + } finally { + Utils.closeQuietly(streams, "streams") + Utils.closeQuietly(client, "adminClient") + } + } + + @ParameterizedTest(name = TestInfoUtils.TestWithParameterizedGroupProtocolNames) + @MethodSource(Array("getTestGroupProtocolParametersStreamsGroupProtocolOnly")) + def testDeleteStreamsGroups(groupProtocol: String): Unit = { Review Comment: The same here! ########## core/src/test/scala/integration/kafka/api/PlaintextAdminIntegrationTest.scala: ########## @@ -4363,6 +4364,396 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest { } } } + + @ParameterizedTest(name = TestInfoUtils.TestWithParameterizedGroupProtocolNames) + @MethodSource(Array("getTestGroupProtocolParametersStreamsGroupProtocolOnly")) + def testDescribeStreamsGroups(groupProtocol: String): Unit = { Review Comment: It was in the previous PR! Duplicate! ########## core/src/test/scala/integration/kafka/api/PlaintextAdminIntegrationTest.scala: ########## @@ -4363,6 +4364,396 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest { } } } + + @ParameterizedTest(name = TestInfoUtils.TestWithParameterizedGroupProtocolNames) + @MethodSource(Array("getTestGroupProtocolParametersStreamsGroupProtocolOnly")) + def testDescribeStreamsGroups(groupProtocol: String): Unit = { + val streamsGroupId = "stream_group_id" + val testTopicName = "test_topic" + val testOutputTopicName = "test_output_topic" + val testNumPartitions = 1 + + val config = createConfig + client = Admin.create(config) + + prepareTopics(List(testTopicName, testOutputTopicName), testNumPartitions) + prepareRecords(testTopicName) + + val streamsConfig = new Properties() + streamsConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest") + streamsConfig.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 10 * 1000) + val streams = createStreamsGroup( + configOverrides = streamsConfig, + inputTopic = testTopicName, + outputTopic = testOutputTopicName, + streamsGroupId = streamsGroupId, + groupProtocol = groupProtocol + ) + + try { + streams.cleanUp() + streams.start() + + TestUtils.waitUntilTrue(() => streams.state() == KafkaStreams.State.RUNNING, "Streams not in RUNNING state") + + TestUtils.waitUntilTrue(() => { + client.listGroups().all().get().stream() + .anyMatch(g => g.groupId() == streamsGroupId) + }, "Streams group not ready to describe yet") + + Thread.sleep(10000) + + TestUtils.waitUntilTrue(() => { + try { + val describedGroups = client.describeStreamsGroups(util.List.of(streamsGroupId)).all().get() + val group = describedGroups.get(streamsGroupId) + if (group != null) { + group.groupState() == GroupState.STABLE && !group.subtopologies().isEmpty + } else { + false + } + } catch { + case _: Exception => false + } + }, "Stream group not fully initialized with topology") + + // Verify the describe call works correctly + val describedGroups = client.describeStreamsGroups(util.List.of(streamsGroupId)).all().get() + val group = describedGroups.get(streamsGroupId) + + assertNotNull(group) + assertEquals(streamsGroupId, group.groupId()) + assertFalse(group.members().isEmpty) + assertNotNull(group.subtopologies()) + assertFalse(group.subtopologies().isEmpty) + + // Verify the topology contains the expected source and sink topics + val subtopologies = group.subtopologies().asScala + assertTrue(subtopologies.exists(subtopology => + subtopology.sourceTopics().contains(testTopicName))) + + } finally { + Utils.closeQuietly(streams, "streams") + Utils.closeQuietly(client, "adminClient") + } + } + + @ParameterizedTest(name = TestInfoUtils.TestWithParameterizedGroupProtocolNames) + @MethodSource(Array("getTestGroupProtocolParametersStreamsGroupProtocolOnly")) + def testDeleteStreamsGroups(groupProtocol: String): Unit = { + val testTopicName = "test_topic" + val testOutputTopicName = "test_output_topic" + val testNumPartitions = 3 + val testNumStreamsGroup = 3 + + val targetDeletedGroups = util.List.of("stream_group_id_2", "stream_group_id_3") + val targetRemainingGroups = util.List.of("stream_group_id_1") + + val config = createConfig + client = Admin.create(config) + + prepareTopics(List(testTopicName, testOutputTopicName), testNumPartitions) + prepareRecords(testTopicName) + + val streamsList = scala.collection.mutable.ListBuffer[(String, KafkaStreams)]() + + try { + for (i <- 1 to testNumStreamsGroup) { + val streamsGroupId = s"stream_group_id_$i" + val streamsConfig = new Properties() + streamsConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest") + streamsConfig.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 10 * 1000) + val streams = createStreamsGroup( + configOverrides = streamsConfig, + inputTopic = testTopicName, + outputTopic = testOutputTopicName, + streamsGroupId = streamsGroupId, + groupProtocol = groupProtocol + ) + streams.cleanUp() + streams.start() + streamsList += ((streamsGroupId, streams)) + } + + TestUtils.waitUntilTrue(() => { + client.listGroups().all().get().stream() + .anyMatch(g => g.groupId().startsWith("stream_group_id_")) + }, "Streams groups not ready to delete yet") + + // Verify that there are 3 groups created + val groups = client.listGroups().all().get() + assertEquals(testNumStreamsGroup, groups.size()) + + // Test deletion of non-empty existing groups + var deleteStreamsGroupResult = client.deleteStreamsGroups(targetDeletedGroups) + assertFutureThrows(classOf[GroupNotEmptyException], deleteStreamsGroupResult.all()) + assertEquals(2, deleteStreamsGroupResult.deletedGroups().size()) + + // Stop and clean up the streams for the groups that are going to be deleted + streamsList + .filter { case (groupId, _) => targetDeletedGroups.contains(groupId) } + .foreach { case (_, streams) => + streams.close(java.time.Duration.ofSeconds(10)) + streams.cleanUp() + } + + // Test deletion of emptied existing streams groups + deleteStreamsGroupResult = client.deleteStreamsGroups(targetDeletedGroups) + assertEquals(2, deleteStreamsGroupResult.deletedGroups().size()) + + // Wait for the deleted groups to be removed + TestUtils.waitUntilTrue(() => { + val groupIds = client.listGroups().all().get().asScala.map(_.groupId()).toSet + targetDeletedGroups.asScala.forall(id => !groupIds.contains(id)) + }, "Deleted groups not yet deleted") + + // Verify that the deleted groups are no longer present + val remainingGroups = client.listGroups().all().get() + assertEquals(targetRemainingGroups.size(), remainingGroups.size()) + remainingGroups.stream().forEach(g => { + assertTrue(targetRemainingGroups.contains(g.groupId())) + }) + + // Test deletion of a non-existing group + val nonExistingGroup = "non_existing_stream_group" + val deleteNonExistingGroupResult = client.deleteStreamsGroups(util.List.of(nonExistingGroup)) + assertFutureThrows(classOf[GroupIdNotFoundException], deleteNonExistingGroupResult.all()) + assertEquals(1, deleteNonExistingGroupResult.deletedGroups().size()) + + } finally{ + streamsList.foreach { case (_, streams) => + streams.close(java.time.Duration.ofSeconds(10)) + streams.cleanUp() + } + Utils.closeQuietly(client, "adminClient") + } + } + + @ParameterizedTest(name = TestInfoUtils.TestWithParameterizedGroupProtocolNames) + @MethodSource(Array("getTestGroupProtocolParametersStreamsGroupProtocolOnly")) + def testListStreamsGroupOffsets(groupProtocol: String): Unit = { + val streamsGroupId = "stream_group_id" + val testTopicName = "test_topic" + val testOutputTopicName = "test_output_topic" + val testNumPartitions = 3 + + val config = createConfig + client = Admin.create(config) + val producer = createProducer(configOverrides = new Properties()) + + prepareTopics(List(testTopicName, testOutputTopicName), testNumPartitions) + prepareRecords(testTopicName) + + val streamsConfig = new Properties() + streamsConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest") + streamsConfig.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 1000) + val streams = createStreamsGroup( + configOverrides = streamsConfig, + inputTopic = testTopicName, + outputTopic = testOutputTopicName, + streamsGroupId = streamsGroupId, + groupProtocol = groupProtocol + ) + + try { + streams.cleanUp() + streams.start() + + TestUtils.waitUntilTrue(() => streams.state() == KafkaStreams.State.RUNNING, "Streams not in RUNNING state") + + // Producer sends messages + for (i <- 1 to 20) { + TestUtils.waitUntilTrue(() => { + val producerRecord = producer.send( + new ProducerRecord[Array[Byte], Array[Byte]](testTopicName, s"key-$i".getBytes(), s"value-$i".getBytes())) + .get() + producerRecord != null && producerRecord.topic() == testTopicName + }, "Fail to produce record to topic") + } + + Thread.sleep(3000) + + // List streams group offsets + TestUtils.waitUntilTrue(() => { + val allTopicPartitions = client.listStreamsGroupOffsets( + util.Map.of(streamsGroupId, new ListStreamsGroupOffsetsSpec()) + ).partitionsToOffsetAndMetadata(streamsGroupId).get() + allTopicPartitions!=null && allTopicPartitions.size() == testNumPartitions + },"Streams group offsets not ready to list yet") + + val allTopicPartitions = client.listStreamsGroupOffsets( + util.Map.of(streamsGroupId, new ListStreamsGroupOffsetsSpec()) Review Comment: Please don't call the `client.listStreamsGroupOffsets` two times in the same test -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: jira-unsubscr...@kafka.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org