ahuang98 commented on code in PR #19982: URL: https://github.com/apache/kafka/pull/19982#discussion_r2153340326
########## raft/src/test/java/org/apache/kafka/raft/KafkaRaftClientReconfigTest.java: ########## @@ -374,26 +428,104 @@ public void testAddVoter() throws Exception { apiVersionRequest.destination(), apiVersionsResponse(Errors.NONE) ); + } - // Handle the API_VERSIONS response - context.client.poll(); - // Append new VotersRecord to log - context.client.poll(); + private void commitAddVoter( + RaftClientTestContext context, + ReplicaKey leader, + ReplicaKey follower, + ReplicaKey newVoter, + int epoch + ) throws Exception { // The new voter is now a voter after writing the VotersRecord to the log assertTrue(context.client.quorum().isVoter(newVoter)); checkLeaderMetricValues(3, 0, 1, context); // Send a FETCH to increase the HWM and commit the new voter set context.deliverRequest( - context.fetchRequest(epoch, follower, context.log.endOffset().offset(), epoch, 0) + context.fetchRequest( + epoch, + follower, + context.log.endOffset().offset(), + epoch, + 0 + ) ); context.pollUntilResponse(); - context.assertSentFetchPartitionResponse(Errors.NONE, epoch, OptionalInt.of(local.id())); + context.assertSentFetchPartitionResponse(Errors.NONE, epoch, OptionalInt.of(leader.id())); checkLeaderMetricValues(3, 0, 0, context); + } - // Expect reply for AddVoter request + @ParameterizedTest + @EnumSource(value = RaftProtocol.class, names = { + "KIP_853_PROTOCOL", + "KIP_996_PROTOCOL" + }) + void testAddVoterCompatibility(RaftProtocol protocol) throws Exception { + ReplicaKey local = replicaKey(randomReplicaId(), true); + ReplicaKey follower = replicaKey(local.id() + 1, true); + + VoterSet voters = VoterSetTest.voterSet(Stream.of(local, follower)); + + RaftClientTestContext context = new RaftClientTestContext.Builder(local.id(), local.directoryId().get()) + .withRaftProtocol(protocol) + .withBootstrapSnapshot(Optional.of(voters)) + .withUnknownLeader(3) + .build(); + + context.unattachedToLeader(); + int epoch = context.currentEpoch(); + + ReplicaKey newVoter = replicaKey(local.id() + 2, true); + InetSocketAddress newAddress = InetSocketAddress.createUnresolved( + "localhost", + 9990 + newVoter.id() + ); + Endpoints newListeners = Endpoints.fromInetSocketAddresses( + Map.of(context.channel.listenerName(), newAddress) + ); + + prepareToSendAddVoter(context, epoch, local, follower, newVoter); + + // Attempt to add new voter to the quorum + assertThrows( + UnsupportedVersionException.class, + () -> context.deliverRequest( + context.addVoterRequest( + Integer.MAX_VALUE, + newVoter, + newListeners + ).setAckWhenCommitted(false) + ) + ); + } + + // This method sets up the context so a test can send an AddVoter request after + // exiting this method + private void prepareToSendAddVoter( Review Comment: this name is a bit deceiving, this is all prep we do on the local (leader) node in order for it to be able to respond favorably to an addvoter request, could we perhaps rename to "prepareLeaderToReceiveAddVoter"? I'm also not sure how I feel about having a helper method for this, I do see how this is quite a bit of code duplication but I wonder if it might be more clear to have this written out explicitly in the tests (you probably don't need to keep all the same assertions for all the tests) -- 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