From 508c2c50325e8436e6700445bf8e811e4228c4b7 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 27 Mar 2026 06:31:57 +0200 Subject: Implement Raft heartbeat dispatch c7885f70-033c-42e2-b831-50c6d3cb315a --- .../implementations/VSRaftProtocolTest.java | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'src/test/java') diff --git a/src/test/java/protocols/implementations/VSRaftProtocolTest.java b/src/test/java/protocols/implementations/VSRaftProtocolTest.java index b249761..3e8f3fa 100644 --- a/src/test/java/protocols/implementations/VSRaftProtocolTest.java +++ b/src/test/java/protocols/implementations/VSRaftProtocolTest.java @@ -378,6 +378,44 @@ class VSRaftProtocolTest { assertEquals(4750L, taskCaptor.getValue().getTaskTime()); } + @Test + void testClientReceiveHeartbeatBecomesFollowerResetsTimeoutAndSendsAck() + throws Exception { + protocol.currentContextIsServer(false); + protocol.onClientInit(); + clearInvocations(mockProcess, mockTaskManager); + setIntField("currentTerm", 1); + setBooleanField("isCandidate", true); + when(mockProcess.getTime()).thenReturn(350L, 350L); + + VSMessage heartbeat = new VSMessage(); + heartbeat.setString("type", "heartbeat"); + heartbeat.setInteger("term", 2); + heartbeat.setInteger("leaderId", 11); + + ArgumentCaptor messageCaptor = + ArgumentCaptor.forClass(VSMessage.class); + ArgumentCaptor taskCaptor = ArgumentCaptor.forClass(VSTask.class); + + protocol.onClientRecv(heartbeat); + + verify(mockProcess).sendMessage(messageCaptor.capture()); + verify(mockTaskManager, times(2)).removeAllTasks(any()); + verify(mockTaskManager).addTask(taskCaptor.capture()); + + VSMessage heartbeatAck = messageCaptor.getValue(); + assertEquals("heartbeatAck", heartbeatAck.getString("type")); + assertEquals(2, heartbeatAck.getInteger("term")); + assertEquals(7, heartbeatAck.getInteger("pid")); + assertEquals(11, heartbeatAck.getInteger("targetPid")); + assertEquals(2, getIntField("currentTerm")); + assertEquals(11, getIntField("leaderId")); + assertFalse(getBooleanField("isLeader")); + assertFalse(getBooleanField("isCandidate")); + assertEquals(350L, getLongField("lastHeartbeatTime")); + assertEquals(4850L, taskCaptor.getValue().getTaskTime()); + } + @Test void testVoteResponseMajorityPromotesCandidateToLeader() throws Exception { protocol.currentContextIsServer(false); @@ -476,6 +514,42 @@ class VSRaftProtocolTest { assertEquals(5000L, taskCaptor.getValue().getTaskTime()); } + @Test + void testServerReceiveHeartbeatAckForLeaderLogsAck() throws Exception { + protocol.onStart(); + clearInvocations(mockProcess, mockTaskManager); + + VSMessage heartbeatAck = new VSMessage(); + heartbeatAck.setString("type", "heartbeatAck"); + heartbeatAck.setInteger("term", 0); + heartbeatAck.setInteger("pid", 2); + heartbeatAck.setInteger("targetPid", 7); + + protocol.onServerRecv(heartbeatAck); + + verify(mockProcess).log("Heartbeat ACK from process 2 received"); + verify(mockTaskManager, never()).removeAllTasks(any()); + verify(mockTaskManager, never()).addTask(any()); + } + + @Test + void testHeartbeatAckForDifferentTargetDoesNotLog() throws Exception { + protocol.onStart(); + clearInvocations(mockProcess, mockTaskManager); + + VSMessage heartbeatAck = new VSMessage(); + heartbeatAck.setString("type", "heartbeatAck"); + heartbeatAck.setInteger("term", 0); + heartbeatAck.setInteger("pid", 2); + heartbeatAck.setInteger("targetPid", 99); + + protocol.onServerRecv(heartbeatAck); + + verify(mockProcess, never()).log(anyString()); + verify(mockTaskManager, never()).removeAllTasks(any()); + verify(mockTaskManager, never()).addTask(any()); + } + @Test void testDuplicateVoteResponsesFromSamePeerDoNotCreateMajority() throws Exception { -- cgit v1.2.3