mingyen066 commented on code in PR #22575:
URL: https://github.com/apache/kafka/pull/22575#discussion_r3507380694


##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerFailureHandlingTest.java:
##########
@@ -215,22 +216,110 @@ public void testSendAfterClosed(ClusterInstance 
clusterInstance) throws Interrup
         assertThrows(IllegalStateException.class, () -> 
producer3.send(record));
     }
 
+    /**
+     * Test that sending to an internal topic throws InvalidTopicException
+     * when auto.create.topics.enable=true and the internal topic already 
exists.
+     */
+    @ClusterTest(serverProperties = {
+        @ClusterConfigProperty(key = AUTO_CREATE_TOPICS_ENABLE_CONFIG, value = 
"true")
+    })
+    public void 
testCannotSendToInternalTopicWhenAutoCreateTrueAndTopicExists(ClusterInstance 
clusterInstance) throws Exception {
+        // explicitly create __consumer_offsets to satisfy the "topic exists" 
precondition

Review Comment:
   The four new tests repeat a lot of boilerplate — 
`...TrueAndTopicExists/...FalseAndTopicExists` share nearly identical 
topic-creation code, and all four repeat the same send+assert block with only 
the expected exception differing. Worth extracting two helpers:
   ```java
   private void createInternalTopic(ClusterInstance clusterInstance) throws 
Exception {
       try (Admin admin = clusterInstance.admin()) {
           Map<String, String> topicConfig = clusterInstance.brokers().get(0)
                   .groupCoordinator()
                   .groupMetadataTopicConfigs();
           admin.createTopics(List.of(new 
NewTopic(Topic.GROUP_METADATA_TOPIC_NAME, 1, (short) 1).configs(topicConfig)));
           clusterInstance.waitTopicCreation(Topic.GROUP_METADATA_TOPIC_NAME, 
1);
       }
   }
   
   private void assertSendToInternalTopicFails(ClusterInstance clusterInstance, 
Class<? extends Throwable> expectedCause) {
       try (Producer<byte[], byte[]> producer = 
clusterInstance.producer(producerConfig(1))) {
           Exception thrown = assertThrows(ExecutionException.class,
                   () -> producer.send(new ProducerRecord<>(
                           Topic.GROUP_METADATA_TOPIC_NAME, "test".getBytes(), 
"test".getBytes())).get());
           assertInstanceOf(expectedCause, thrown.getCause(),
                   () -> "Expected " + expectedCause.getSimpleName() + " but 
got " + thrown.getCause());
       }
   }
   ```
   Then each test shrinks to:
   
   ```java
   @ClusterTest(serverProperties = {@ClusterConfigProperty(key = 
AUTO_CREATE_TOPICS_ENABLE_CONFIG, value = "true")})
   public void 
testCannotSendToInternalTopicWhenAutoCreateTrueAndTopicExists(ClusterInstance 
clusterInstance) throws Exception {
       createInternalTopic(clusterInstance);
       assertSendToInternalTopicFails(clusterInstance, 
InvalidTopicException.class);
   }
   ```



##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerFailureHandlingTest.java:
##########
@@ -215,22 +216,110 @@ public void testSendAfterClosed(ClusterInstance 
clusterInstance) throws Interrup
         assertThrows(IllegalStateException.class, () -> 
producer3.send(record));
     }
 
+    /**
+     * Test that sending to an internal topic throws InvalidTopicException
+     * when auto.create.topics.enable=true and the internal topic already 
exists.
+     */
+    @ClusterTest(serverProperties = {
+        @ClusterConfigProperty(key = AUTO_CREATE_TOPICS_ENABLE_CONFIG, value = 
"true")
+    })
+    public void 
testCannotSendToInternalTopicWhenAutoCreateTrueAndTopicExists(ClusterInstance 
clusterInstance) throws Exception {
+        // explicitly create __consumer_offsets to satisfy the "topic exists" 
precondition
+        try (Admin admin = clusterInstance.admin()) {
+            Map<String, String> topicConfig = clusterInstance.brokers().get(0)
+                    .groupCoordinator()
+                    .groupMetadataTopicConfigs();
+            admin.createTopics(List.of(new 
NewTopic(Topic.GROUP_METADATA_TOPIC_NAME, 1, (short) 1).configs(topicConfig)));
+            clusterInstance.waitTopicCreation(Topic.GROUP_METADATA_TOPIC_NAME, 
1);
+        }
+
+        try (Producer<byte[], byte[]> producer = 
clusterInstance.producer(producerConfig(1))) {
+            Exception thrown = assertThrows(ExecutionException.class,
+                    () -> producer.send(new ProducerRecord<>(
+                            Topic.GROUP_METADATA_TOPIC_NAME,
+                            "test".getBytes(),
+                            "test".getBytes())).get());
+            assertInstanceOf(InvalidTopicException.class, thrown.getCause(),
+                    () -> "Expected InvalidTopicException but got " + 
thrown.getCause());
+        }
+    }
+
+    /**
+     * Test that sending to an internal topic throws InvalidTopicException
+     * when auto.create.topics.enable=false and the internal topic already 
exists.
+     */
     @ClusterTest
-    public void testCannotSendToInternalTopic(ClusterInstance clusterInstance) 
throws InterruptedException {
+    public void 
testCannotSendToInternalTopicWhenAutoCreateFalseAndTopicExists(ClusterInstance 
clusterInstance) throws Exception {
+        // explicitly create __consumer_offsets to satisfy the "topic exists" 
precondition
         try (Admin admin = clusterInstance.admin()) {
             Map<String, String> topicConfig = clusterInstance.brokers().get(0)
-                .groupCoordinator()
-                .groupMetadataTopicConfigs();
+                    .groupCoordinator()
+                    .groupMetadataTopicConfigs();
             admin.createTopics(List.of(new 
NewTopic(Topic.GROUP_METADATA_TOPIC_NAME, 1, (short) 1).configs(topicConfig)));
-            clusterInstance.waitTopicDeletion(Topic.GROUP_METADATA_TOPIC_NAME);
+            clusterInstance.waitTopicCreation(Topic.GROUP_METADATA_TOPIC_NAME, 
1);
         }
 
         try (Producer<byte[], byte[]> producer = 
clusterInstance.producer(producerConfig(1))) {
             Exception thrown = assertThrows(ExecutionException.class,
-                    () -> producer.send(new 
ProducerRecord<>(Topic.GROUP_METADATA_TOPIC_NAME, "test".getBytes(),
+                    () -> producer.send(new ProducerRecord<>(
+                            Topic.GROUP_METADATA_TOPIC_NAME,
+                            "test".getBytes(),
                             "test".getBytes())).get());
             assertInstanceOf(InvalidTopicException.class, thrown.getCause(),
-                    () -> "Unexpected exception while sending to an invalid 
topic " + thrown.getCause());
+                    () -> "Expected InvalidTopicException but got " + 
thrown.getCause());
+        }
+    }
+
+    /**
+     * Test that sending to an internal topic throws InvalidTopicException
+     * when auto.create.topics.enable=true and the internal topic does not 
exist.
+     * The broker should auto-create the internal topic even though the send 
is rejected.
+     */
+    @ClusterTest(serverProperties = {
+        @ClusterConfigProperty(key = AUTO_CREATE_TOPICS_ENABLE_CONFIG, value = 
"true"),
+        @ClusterConfigProperty(key = OFFSETS_TOPIC_REPLICATION_FACTOR_CONFIG, 
value = "1")
+    })
+    public void 
testCannotSendToInternalTopicWhenAutoCreateTrueAndTopicNotExists(ClusterInstance
 clusterInstance) throws Exception {
+        clusterInstance.deleteTopic(Topic.GROUP_METADATA_TOPIC_NAME);

Review Comment:
   `clusterInstance.deleteTopic(Topic.GROUP_METADATA_TOPIC_NAME)` is a no-op 
here — `__consumer_offsets` doesn't exist yet at this point, and `deleteTopic` 
discards the result without awaiting it. Looks like leftover from the old test; 
safe to drop.



##########
clients/clients-integration-tests/src/test/java/org/apache/kafka/clients/producer/ProducerFailureHandlingTest.java:
##########
@@ -215,22 +216,110 @@ public void testSendAfterClosed(ClusterInstance 
clusterInstance) throws Interrup
         assertThrows(IllegalStateException.class, () -> 
producer3.send(record));
     }
 
+    /**
+     * Test that sending to an internal topic throws InvalidTopicException
+     * when auto.create.topics.enable=true and the internal topic already 
exists.
+     */
+    @ClusterTest(serverProperties = {
+        @ClusterConfigProperty(key = AUTO_CREATE_TOPICS_ENABLE_CONFIG, value = 
"true")
+    })
+    public void 
testCannotSendToInternalTopicWhenAutoCreateTrueAndTopicExists(ClusterInstance 
clusterInstance) throws Exception {
+        // explicitly create __consumer_offsets to satisfy the "topic exists" 
precondition
+        try (Admin admin = clusterInstance.admin()) {
+            Map<String, String> topicConfig = clusterInstance.brokers().get(0)
+                    .groupCoordinator()
+                    .groupMetadataTopicConfigs();
+            admin.createTopics(List.of(new 
NewTopic(Topic.GROUP_METADATA_TOPIC_NAME, 1, (short) 1).configs(topicConfig)));
+            clusterInstance.waitTopicCreation(Topic.GROUP_METADATA_TOPIC_NAME, 
1);
+        }
+
+        try (Producer<byte[], byte[]> producer = 
clusterInstance.producer(producerConfig(1))) {
+            Exception thrown = assertThrows(ExecutionException.class,
+                    () -> producer.send(new ProducerRecord<>(
+                            Topic.GROUP_METADATA_TOPIC_NAME,
+                            "test".getBytes(),
+                            "test".getBytes())).get());
+            assertInstanceOf(InvalidTopicException.class, thrown.getCause(),
+                    () -> "Expected InvalidTopicException but got " + 
thrown.getCause());
+        }
+    }
+
+    /**
+     * Test that sending to an internal topic throws InvalidTopicException
+     * when auto.create.topics.enable=false and the internal topic already 
exists.
+     */
     @ClusterTest
-    public void testCannotSendToInternalTopic(ClusterInstance clusterInstance) 
throws InterruptedException {
+    public void 
testCannotSendToInternalTopicWhenAutoCreateFalseAndTopicExists(ClusterInstance 
clusterInstance) throws Exception {
+        // explicitly create __consumer_offsets to satisfy the "topic exists" 
precondition
         try (Admin admin = clusterInstance.admin()) {
             Map<String, String> topicConfig = clusterInstance.brokers().get(0)
-                .groupCoordinator()
-                .groupMetadataTopicConfigs();
+                    .groupCoordinator()
+                    .groupMetadataTopicConfigs();
             admin.createTopics(List.of(new 
NewTopic(Topic.GROUP_METADATA_TOPIC_NAME, 1, (short) 1).configs(topicConfig)));
-            clusterInstance.waitTopicDeletion(Topic.GROUP_METADATA_TOPIC_NAME);
+            clusterInstance.waitTopicCreation(Topic.GROUP_METADATA_TOPIC_NAME, 
1);
         }
 
         try (Producer<byte[], byte[]> producer = 
clusterInstance.producer(producerConfig(1))) {
             Exception thrown = assertThrows(ExecutionException.class,
-                    () -> producer.send(new 
ProducerRecord<>(Topic.GROUP_METADATA_TOPIC_NAME, "test".getBytes(),
+                    () -> producer.send(new ProducerRecord<>(
+                            Topic.GROUP_METADATA_TOPIC_NAME,
+                            "test".getBytes(),
                             "test".getBytes())).get());
             assertInstanceOf(InvalidTopicException.class, thrown.getCause(),
-                    () -> "Unexpected exception while sending to an invalid 
topic " + thrown.getCause());
+                    () -> "Expected InvalidTopicException but got " + 
thrown.getCause());
+        }
+    }
+
+    /**
+     * Test that sending to an internal topic throws InvalidTopicException
+     * when auto.create.topics.enable=true and the internal topic does not 
exist.
+     * The broker should auto-create the internal topic even though the send 
is rejected.
+     */
+    @ClusterTest(serverProperties = {
+        @ClusterConfigProperty(key = AUTO_CREATE_TOPICS_ENABLE_CONFIG, value = 
"true"),
+        @ClusterConfigProperty(key = OFFSETS_TOPIC_REPLICATION_FACTOR_CONFIG, 
value = "1")
+    })
+    public void 
testCannotSendToInternalTopicWhenAutoCreateTrueAndTopicNotExists(ClusterInstance
 clusterInstance) throws Exception {
+        clusterInstance.deleteTopic(Topic.GROUP_METADATA_TOPIC_NAME);
+
+        try (Producer<byte[], byte[]> producer = 
clusterInstance.producer(producerConfig(1))) {
+            Exception thrown = assertThrows(ExecutionException.class,
+                    () -> producer.send(new ProducerRecord<>(
+                            Topic.GROUP_METADATA_TOPIC_NAME,
+                            "test".getBytes(),
+                            "test".getBytes())).get());
+            assertInstanceOf(InvalidTopicException.class, thrown.getCause(),
+                    () -> "Expected InvalidTopicException but got " + 
thrown.getCause());
+        }
+
+        // verify the broker auto-created the internal topic
+        clusterInstance.waitTopicCreation(Topic.GROUP_METADATA_TOPIC_NAME, 1);
+    }
+
+    /**
+     * Test that sending to an internal topic throws TimeoutException
+     * when auto.create.topics.enable=false and the internal topic does not 
exist.
+     * The broker returns UNKNOWN_TOPIC_OR_PARTITION as a recoverable error 
causing the producer to time out.
+     */
+    @ClusterTest(serverProperties = {
+        @ClusterConfigProperty(key = OFFSETS_TOPIC_REPLICATION_FACTOR_CONFIG, 
value = "1")
+    })
+    public void 
testCannotSendToInternalTopicWhenAutoCreateFalseAndTopicNotExists(ClusterInstance
 clusterInstance) throws Exception {
+        clusterInstance.deleteTopic(Topic.GROUP_METADATA_TOPIC_NAME);

Review Comment:
   ditto



-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to