@@ -1297,12 +1297,16 @@ public void testAuthenticationErrorInHeartbeatThreadTriggersRejoin() throws Exce
12971297 mockClient .prepareResponse (syncGroupResponse (Errors .NONE ));
12981298
12991299 final AuthenticationException authError = new AuthenticationException ("test auth failure" );
1300+ // A matcher exception leaves the response queued, so fail only the first heartbeat.
1301+ final AtomicBoolean authErrorThrown = new AtomicBoolean (false );
13001302
13011303 mockClient .prepareResponse (body -> {
1302- if (body instanceof HeartbeatRequest )
1304+ if (!(body instanceof HeartbeatRequest ))
1305+ return false ;
1306+ if (authErrorThrown .compareAndSet (false , true ))
13031307 throw authError ;
1304- return false ;
1305- }, heartbeatResponse (Errors .UNKNOWN_SERVER_ERROR ));
1308+ return true ;
1309+ }, heartbeatResponse (Errors .NONE ));
13061310
13071311 coordinator .ensureActiveGroup ();
13081312 assertFalse (coordinator .rejoinNeededOrPending (),
@@ -1323,6 +1327,26 @@ public void testAuthenticationErrorInHeartbeatThreadTriggersRejoin() throws Exce
13231327 assertTrue (coordinator .rejoinNeededOrPending (),
13241328 "Expected the heartbeat thread to request a rejoin after an AuthenticationException " +
13251329 "so the next poll() restarts the heartbeat machinery via ensureActiveGroup()" );
1330+
1331+ mockClient .prepareResponse (joinGroupFollowerResponse (2 , memberId , leaderId , Errors .NONE ));
1332+ mockClient .prepareResponse (syncGroupResponse (Errors .NONE ));
1333+
1334+ coordinator .ensureActiveGroup ();
1335+
1336+ assertFalse (coordinator .rejoinNeededOrPending (), "Coordinator should have rejoined the group" );
1337+ assertEquals (2 , coordinator .generation ().generationId );
1338+
1339+ mockClient .prepareResponse (body -> body instanceof HeartbeatRequest , heartbeatResponse (Errors .NONE ));
1340+ mockTime .sleep (HEARTBEAT_INTERVAL_MS );
1341+
1342+ // Ensure the response was consumed instead of passing before a heartbeat was sent.
1343+ TestUtils .waitForCondition (() -> {
1344+ coordinator .pollHeartbeat (mockTime .milliseconds ());
1345+ return !mockClient .hasPendingResponses () && !coordinator .heartbeat ().hasInflight ();
1346+ }, 3000 , "Heartbeat was not sent and completed after recovering from the authentication error" );
1347+
1348+ assertFalse (coordinator .rejoinNeededOrPending (),
1349+ "A successful post-recovery heartbeat should not trigger another rejoin" );
13261350 }
13271351
13281352 @ Test
0 commit comments