Remove MockReplicatedLogEntry
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / behaviors / CandidateTest.java
index c76368370506af15ab4b7b567672514514e41690..88a261328ade2700077121ecfb765edfb311f2fd 100644 (file)
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
 package org.opendaylight.controller.cluster.raft.behaviors;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import akka.actor.ActorRef;
 import akka.actor.Props;
-import akka.testkit.JavaTestKit;
-import junit.framework.Assert;
+import akka.testkit.TestActorRef;
+import com.google.common.base.Stopwatch;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.cluster.NonPersistentDataProvider;
+import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
+import org.opendaylight.controller.cluster.raft.ElectionTerm;
 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
 import org.opendaylight.controller.cluster.raft.RaftActorContext;
+import org.opendaylight.controller.cluster.raft.RaftActorContextImpl;
 import org.opendaylight.controller.cluster.raft.RaftState;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.VotingState;
 import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
 import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
 import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
+import org.opendaylight.controller.cluster.raft.messages.RaftRPC;
 import org.opendaylight.controller.cluster.raft.messages.RequestVote;
 import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
-import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
+import org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public class CandidateTest extends AbstractRaftActorBehaviorTest {
+public class CandidateTest extends AbstractRaftActorBehaviorTest<Candidate> {
+    static final Logger LOG = LoggerFactory.getLogger(CandidateTest.class);
 
-    private final ActorRef candidateActor = getSystem().actorOf(Props.create(
-        DoNothingActor.class));
+    private final TestActorRef<MessageCollectorActor> candidateActor = actorFactory.createTestActor(
+            Props.create(MessageCollectorActor.class), actorFactory.generateActorId("candidate"));
 
-    private final ActorRef peerActor1 = getSystem().actorOf(Props.create(
-        DoNothingActor.class));
+    private TestActorRef<MessageCollectorActor>[] peerActors;
 
-    private final ActorRef peerActor2 = getSystem().actorOf(Props.create(
-        DoNothingActor.class));
+    private RaftActorBehavior candidate;
 
-    private final ActorRef peerActor3 = getSystem().actorOf(Props.create(
-        DoNothingActor.class));
+    @Before
+    public void setUp(){
+    }
 
-    private final ActorRef peerActor4 = getSystem().actorOf(Props.create(
-        DoNothingActor.class));
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        if (candidate != null) {
+            candidate.close();
+        }
 
-    private final Map<String, String> onePeer = new HashMap<>();
-    private final Map<String, String> twoPeers = new HashMap<>();
-    private final Map<String, String> fourPeers = new HashMap<>();
+        super.tearDown();
+    }
 
-    @Before
-    public void setUp(){
-        onePeer.put(peerActor1.path().toString(),
-            peerActor1.path().toString());
+    @Test
+    public void testWhenACandidateIsCreatedItIncrementsTheCurrentTermAndVotesForItself() {
+        RaftActorContext raftActorContext = createActorContext();
+        long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
 
-        twoPeers.put(peerActor1.path().toString(),
-            peerActor1.path().toString());
-        twoPeers.put(peerActor2.path().toString(),
-            peerActor2.path().toString());
+        candidate = new Candidate(raftActorContext);
 
-        fourPeers.put(peerActor1.path().toString(),
-            peerActor1.path().toString());
-        fourPeers.put(peerActor2.path().toString(),
-            peerActor2.path().toString());
-        fourPeers.put(peerActor3.path().toString(),
-            peerActor3.path().toString());
-        fourPeers.put(peerActor4.path().toString(),
-            peerActor3.path().toString());
+        assertEquals("getCurrentTerm", expectedTerm + 1, raftActorContext.getTermInformation().getCurrentTerm());
+        assertEquals("getVotedFor", raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
+    }
 
+    @Test
+    public void testThatAnElectionTimeoutIsTriggered() {
+        MockRaftActorContext actorContext = createActorContext();
+        candidate = new Candidate(actorContext);
 
+        MessageCollectorActor.expectFirstMatching(candidateActor, ElectionTimeout.class,
+                actorContext.getConfigParams().getElectionTimeOutInterval().$times(6).toMillis());
     }
 
     @Test
-    public void testWhenACandidateIsCreatedItIncrementsTheCurrentTermAndVotesForItself(){
+    public void testHandleElectionTimeoutWhenThereAreZeroPeers() {
         RaftActorContext raftActorContext = createActorContext();
-        long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
+        candidate = new Candidate(raftActorContext);
 
-        new Candidate(raftActorContext);
+        RaftActorBehavior newBehavior =
+            candidate.handleMessage(candidateActor, ElectionTimeout.INSTANCE);
 
-        assertEquals(expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
-        assertEquals(raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
+        assertEquals("Behavior", RaftState.Leader, newBehavior.state());
     }
 
     @Test
-    public void testThatAnElectionTimeoutIsTriggered(){
-        new JavaTestKit(getSystem()) {{
-
-            new Within(duration("1 seconds")) {
-                protected void run() {
-
-                    Candidate candidate = new Candidate(createActorContext(getTestActor()));
-
-                    final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "ElectionTimeout") {
-                        // do not put code outside this method, will run afterwards
-                        protected Boolean match(Object in) {
-                            if (in instanceof ElectionTimeout) {
-                                 return true;
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get();
-
-                    assertEquals(true, out);
-                }
-            };
-        }};
+    public void testHandleElectionTimeoutWhenThereAreTwoNodeCluster() {
+        MockRaftActorContext raftActorContext = createActorContext();
+        raftActorContext.setPeerAddresses(setupPeers(1));
+        candidate = new Candidate(raftActorContext);
+
+        candidate = candidate.handleMessage(candidateActor, ElectionTimeout.INSTANCE);
+
+        assertEquals("Behavior", RaftState.Candidate, candidate.state());
     }
 
     @Test
-    public void testHandleElectionTimeoutWhenThereAreZeroPeers(){
-        RaftActorContext raftActorContext = createActorContext();
-        Candidate candidate =
-            new Candidate(raftActorContext);
+    public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodeCluster() {
+        MockRaftActorContext raftActorContext = createActorContext();
+        raftActorContext.setLastApplied(raftActorContext.getReplicatedLog().lastIndex());
+        raftActorContext.setPeerAddresses(setupPeers(2));
+        candidate = new Candidate(raftActorContext);
 
-        RaftState raftState =
-            candidate.handleMessage(candidateActor, new ElectionTimeout());
+        candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, true));
 
-        Assert.assertEquals(RaftState.Leader, raftState);
+        assertEquals("Behavior", RaftState.Leader, candidate.state());
     }
 
     @Test
-    public void testHandleElectionTimeoutWhenThereAreTwoNodesInCluster(){
-        MockRaftActorContext raftActorContext =
-            (MockRaftActorContext) createActorContext();
-        raftActorContext.setPeerAddresses(onePeer);
-        Candidate candidate =
-            new Candidate(raftActorContext);
+    public void testBecomePreLeaderOnReceivingMajorityVotesInThreeNodeCluster() {
+        MockRaftActorContext raftActorContext = createActorContext();
+        raftActorContext.setLastApplied(-1);
+        raftActorContext.setPeerAddresses(setupPeers(2));
+        candidate = new Candidate(raftActorContext);
 
-        RaftState raftState =
-            candidate.handleMessage(candidateActor, new ElectionTimeout());
+        candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, true));
 
-        Assert.assertEquals(RaftState.Candidate, raftState);
+        // LastApplied is -1 and behind the last index.
+        assertEquals("Behavior", RaftState.PreLeader, candidate.state());
     }
 
     @Test
-    public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodesInCluster(){
-        MockRaftActorContext raftActorContext =
-            (MockRaftActorContext) createActorContext();
-        raftActorContext.setPeerAddresses(twoPeers);
-        Candidate candidate =
-            new Candidate(raftActorContext);
+    public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodeCluster() {
+        MockRaftActorContext raftActorContext = createActorContext();
+        raftActorContext.getTermInformation().update(2L, "other");
+        raftActorContext.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder()
+                .createEntries(0, 5, 1).build());
+        raftActorContext.setCommitIndex(raftActorContext.getReplicatedLog().lastIndex());
+        raftActorContext.setLastApplied(raftActorContext.getReplicatedLog().lastIndex());
+        raftActorContext.setPeerAddresses(setupPeers(4));
+        candidate = new Candidate(raftActorContext);
 
-        RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
+        RequestVote requestVote = MessageCollectorActor.expectFirstMatching(peerActors[0], RequestVote.class);
+        assertEquals("getTerm", 3L, requestVote.getTerm());
+        assertEquals("getCandidateId", raftActorContext.getId(), requestVote.getCandidateId());
+        assertEquals("getLastLogTerm", 1L, requestVote.getLastLogTerm());
+        assertEquals("getLastLogIndex", 4L, requestVote.getLastLogIndex());
 
-        Assert.assertEquals(RaftState.Leader, stateOnFirstVote);
+        MessageCollectorActor.expectFirstMatching(peerActors[1], RequestVote.class);
+        MessageCollectorActor.expectFirstMatching(peerActors[2], RequestVote.class);
+        MessageCollectorActor.expectFirstMatching(peerActors[3], RequestVote.class);
 
+        // First peers denies the vote.
+        candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, false));
+
+        assertEquals("Behavior", RaftState.Candidate, candidate.state());
+
+        candidate = candidate.handleMessage(peerActors[1], new RequestVoteReply(1, true));
+
+        assertEquals("Behavior", RaftState.Candidate, candidate.state());
+
+        candidate = candidate.handleMessage(peerActors[2], new RequestVoteReply(1, true));
+
+        assertEquals("Behavior", RaftState.Leader, candidate.state());
     }
 
     @Test
-    public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodesInCluster(){
-        MockRaftActorContext raftActorContext =
-            (MockRaftActorContext) createActorContext();
-        raftActorContext.setPeerAddresses(fourPeers);
-        Candidate candidate =
-            new Candidate(raftActorContext);
+    public void testBecomeLeaderOnReceivingMajorityVotesWithNonVotingPeers() {
+        ElectionTerm mockElectionTerm = Mockito.mock(ElectionTerm.class);
+        Mockito.doReturn(1L).when(mockElectionTerm).getCurrentTerm();
+        RaftActorContext raftActorContext = new RaftActorContextImpl(candidateActor, candidateActor.actorContext(),
+                "candidate", mockElectionTerm, -1, -1, setupPeers(4), new DefaultConfigParamsImpl(),
+                new NonPersistentDataProvider(), LOG);
+        raftActorContext.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().build());
+        raftActorContext.getPeerInfo("peer1").setVotingState(VotingState.NON_VOTING);
+        raftActorContext.getPeerInfo("peer4").setVotingState(VotingState.NON_VOTING);
+        candidate = new Candidate(raftActorContext);
+
+        MessageCollectorActor.expectFirstMatching(peerActors[1], RequestVote.class);
+        MessageCollectorActor.expectFirstMatching(peerActors[2], RequestVote.class);
+        MessageCollectorActor.assertNoneMatching(peerActors[0], RequestVote.class, 300);
+        MessageCollectorActor.assertNoneMatching(peerActors[3], RequestVote.class, 100);
 
-        RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
+        candidate = candidate.handleMessage(peerActors[1], new RequestVoteReply(1, false));
 
-        RaftState stateOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
+        assertEquals("Behavior", RaftState.Candidate, candidate.state());
 
-        Assert.assertEquals(RaftState.Candidate, stateOnFirstVote);
-        Assert.assertEquals(RaftState.Leader, stateOnSecondVote);
+        candidate = candidate.handleMessage(peerActors[2], new RequestVoteReply(1, true));
 
+        assertEquals("Behavior", RaftState.Leader, candidate.state());
     }
 
     @Test
-    public void testResponseToAppendEntriesWithLowerTerm(){
-        new JavaTestKit(getSystem()) {{
-
-            new Within(duration("1 seconds")) {
-                protected void run() {
-
-                    Candidate candidate = new Candidate(createActorContext(getTestActor()));
-
-                    candidate.handleMessage(getTestActor(), new AppendEntries(0, "test", 0,0,Collections.EMPTY_LIST, 0));
-
-                    final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
-                        // do not put code outside this method, will run afterwards
-                        protected Boolean match(Object in) {
-                            if (in instanceof AppendEntriesReply) {
-                                AppendEntriesReply reply = (AppendEntriesReply) in;
-                                return reply.isSuccess();
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get();
-
-                    assertEquals(false, out);
-                }
-            };
-        }};
+    public void testResponseToHandleAppendEntriesWithLowerTerm() {
+        candidate = new Candidate(createActorContext());
+
+        setupPeers(1);
+        RaftActorBehavior newBehavior = candidate.handleMessage(peerActors[0], new AppendEntries(1, "test", 0, 0,
+                Collections.<ReplicatedLogEntry>emptyList(), 0, -1, (short) 0));
+
+        AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(
+                peerActors[0], AppendEntriesReply.class);
+        assertEquals("isSuccess", false, reply.isSuccess());
+        assertEquals("getTerm", 2, reply.getTerm());
+        assertTrue("New Behavior : " + newBehavior, newBehavior instanceof Candidate);
     }
 
     @Test
-    public void testResponseToRequestVoteWithLowerTerm(){
-        new JavaTestKit(getSystem()) {{
-
-            new Within(duration("1 seconds")) {
-                protected void run() {
-
-                    Candidate candidate = new Candidate(createActorContext(getTestActor()));
-
-                    candidate.handleMessage(getTestActor(), new RequestVote(0, "test", 0, 0));
-
-                    final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
-                        // do not put code outside this method, will run afterwards
-                        protected Boolean match(Object in) {
-                            if (in instanceof RequestVoteReply) {
-                                RequestVoteReply reply = (RequestVoteReply) in;
-                                return reply.isVoteGranted();
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get();
-
-                    assertEquals(false, out);
-                }
-            };
-        }};
+    public void testResponseToHandleAppendEntriesWithHigherTerm() {
+        candidate = new Candidate(createActorContext());
+
+        setupPeers(1);
+        RaftActorBehavior newBehavior = candidate.handleMessage(peerActors[0], new AppendEntries(5, "test", 0, 0,
+                Collections.<ReplicatedLogEntry>emptyList(), 0, -1, (short) 0));
+
+        assertTrue("New Behavior : " + newBehavior, newBehavior instanceof Follower);
     }
 
     @Test
-    public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
-        new JavaTestKit(getSystem()) {{
-
-            new Within(duration("1 seconds")) {
-                protected void run() {
-
-                    RaftActorContext context = createActorContext(getTestActor());
-
-                    context.getTermInformation().update(1000, null);
-
-                    // Once a candidate is created it will immediately increment the current term so after
-                    // construction the currentTerm should be 1001
-                    RaftActorBehavior follower = createBehavior(context);
-
-                    follower.handleMessage(getTestActor(), new RequestVote(1001, "test", 10000, 999));
-
-                    final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
-                        // do not put code outside this method, will run afterwards
-                        protected Boolean match(Object in) {
-                            if (in instanceof RequestVoteReply) {
-                                RequestVoteReply reply = (RequestVoteReply) in;
-                                return reply.isVoteGranted();
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get();
-
-                    assertEquals(true, out);
-                }
-            };
-        }};
+    public void testResponseToHandleAppendEntriesWithEqualTerm() {
+        MockRaftActorContext actorContext = createActorContext();
+
+        candidate = new Candidate(actorContext);
+
+        setupPeers(1);
+        RaftActorBehavior newBehavior = candidate.handleMessage(peerActors[0], new AppendEntries(2, "test", 0, 0,
+                Collections.<ReplicatedLogEntry>emptyList(), 0, -1, (short) 0));
+
+        assertTrue("New Behavior : " + newBehavior + " term = " + actorContext.getTermInformation().getCurrentTerm(),
+                newBehavior instanceof Follower);
+    }
+
+
+    @Test
+    public void testResponseToRequestVoteWithLowerTerm() {
+        candidate = new Candidate(createActorContext());
+
+        setupPeers(1);
+        candidate.handleMessage(peerActors[0], new RequestVote(1, "test", 0, 0));
+
+        RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
+                peerActors[0], RequestVoteReply.class);
+        assertEquals("isVoteGranted", false, reply.isVoteGranted());
+        assertEquals("getTerm", 2, reply.getTerm());
     }
 
     @Test
-    public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
-        new JavaTestKit(getSystem()) {{
+    public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForMatches() {
+        MockRaftActorContext context = createActorContext();
+        context.getTermInformation().update(1000, null);
+
+        // Once a candidate is created it will immediately increment the current term so after
+        // construction the currentTerm should be 1001
+        candidate = new Candidate(context);
+
+        setupPeers(1);
+        candidate.handleMessage(peerActors[0], new RequestVote(1001, context.getId(), 10000, 999));
+
+        RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
+                peerActors[0], RequestVoteReply.class);
+        assertEquals("isVoteGranted", true, reply.isVoteGranted());
+        assertEquals("getTerm", 1001, reply.getTerm());
+    }
+
+    @Test
+    public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForDoesNotMatch() {
+        MockRaftActorContext context = createActorContext();
+        context.getTermInformation().update(1000, null);
+
+        // Once a candidate is created it will immediately increment the current term so after
+        // construction the currentTerm should be 1001
+        candidate = new Candidate(context);
+
+        setupPeers(1);
 
-            new Within(duration("1 seconds")) {
-                protected void run() {
+        // RequestVote candidate ID ("candidate2") does not match this candidate's votedFor
+        // (it votes for itself)
+        candidate.handleMessage(peerActors[0], new RequestVote(1001, "candidate2", 10000, 999));
 
-                    RaftActorContext context = createActorContext(getTestActor());
+        RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
+                peerActors[0], RequestVoteReply.class);
+        assertEquals("isVoteGranted", false, reply.isVoteGranted());
+        assertEquals("getTerm", 1001, reply.getTerm());
+    }
+
+    @Test
+    public void testCandidateSchedulesElectionTimeoutImmediatelyWhenItHasNoPeers() {
+        MockRaftActorContext context = createActorContext();
 
-                    context.getTermInformation().update(1000, "test");
+        Stopwatch stopwatch = Stopwatch.createStarted();
 
-                    RaftActorBehavior follower = createBehavior(context);
+        candidate = createBehavior(context);
 
-                    follower.handleMessage(getTestActor(), new RequestVote(1001, "candidate", 10000, 999));
+        MessageCollectorActor.expectFirstMatching(candidateActor, ElectionTimeout.class);
 
-                    final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
-                        // do not put code outside this method, will run afterwards
-                        protected Boolean match(Object in) {
-                            if (in instanceof RequestVoteReply) {
-                                RequestVoteReply reply = (RequestVoteReply) in;
-                                return reply.isVoteGranted();
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get();
+        long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
 
-                    assertEquals(false, out);
-                }
-            };
-        }};
+        assertTrue(elapsed < context.getConfigParams().getElectionTimeOutInterval().toMillis());
     }
 
+    @Test
+    @Override
+    public void testHandleAppendEntriesAddSameEntryToLog() throws Exception {
+        MockRaftActorContext context = createActorContext();
+
+        context.getTermInformation().update(2, "test");
+
+        // Prepare the receivers log
+        MockRaftActorContext.MockPayload payload = new MockRaftActorContext.MockPayload("zero");
+        setLastLogEntry(context, 2, 0, payload);
+
+        List<ReplicatedLogEntry> entries = new ArrayList<>();
+        entries.add(new SimpleReplicatedLogEntry(0, 2, payload));
+
+        final AppendEntries appendEntries = new AppendEntries(2, "leader-1", -1, -1, entries, 2, -1, (short)0);
+
+        behavior = createBehavior(context);
 
+        // Resetting the Candidates term to make sure it will match
+        // the term sent by AppendEntries. If this was not done then
+        // the test will fail because the Candidate will assume that
+        // the message was sent to it from a lower term peer and will
+        // thus respond with a failure
+        context.getTermInformation().update(2, "test");
 
-    @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
+        // Send an unknown message so that the state of the RaftActor remains unchanged
+        behavior.handleMessage(candidateActor, "unknown");
+
+        RaftActorBehavior raftBehavior = behavior.handleMessage(candidateActor, appendEntries);
+
+        assertEquals("Raft state", RaftState.Follower, raftBehavior.state());
+
+        assertEquals("ReplicatedLog size", 1, context.getReplicatedLog().size());
+
+        handleAppendEntriesAddSameEntryToLogReply(candidateActor);
+    }
+
+    @Override
+    protected Candidate createBehavior(final RaftActorContext actorContext) {
         return new Candidate(actorContext);
     }
 
-    @Override protected RaftActorContext createActorContext() {
-        return new MockRaftActorContext("test", getSystem(), candidateActor);
+    @Override protected MockRaftActorContext createActorContext() {
+        return new MockRaftActorContext("candidate", getSystem(), candidateActor);
     }
 
+    @SuppressWarnings("unchecked")
+    private Map<String, String> setupPeers(final int count) {
+        Map<String, String> peerMap = new HashMap<>();
+        peerActors = new TestActorRef[count];
+        for (int i = 0; i < count; i++) {
+            peerActors[i] = actorFactory.createTestActor(Props.create(MessageCollectorActor.class),
+                    actorFactory.generateActorId("peer"));
+            peerMap.put("peer" + (i + 1), peerActors[i].path().toString());
+        }
+
+        return peerMap;
+    }
 
+    @Override
+    protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(final MockRaftActorContext actorContext,
+            final ActorRef actorRef, final RaftRPC rpc) throws Exception {
+        super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc);
+        if (rpc instanceof RequestVote) {
+            assertEquals("New votedFor", ((RequestVote)rpc).getCandidateId(),
+                    actorContext.getTermInformation().getVotedFor());
+        } else {
+            assertEquals("New votedFor", null, actorContext.getTermInformation().getVotedFor());
+        }
+    }
 }