60f45523cf204d5d246fb14ac01eddc4dcc760ba
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / behaviors / CandidateTest.java
1 package org.opendaylight.controller.cluster.raft.behaviors;
2
3 import static org.junit.Assert.assertEquals;
4 import akka.actor.ActorRef;
5 import akka.actor.Props;
6 import akka.testkit.TestActorRef;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.Map;
10 import org.junit.After;
11 import org.junit.Before;
12 import org.junit.Test;
13 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
14 import org.opendaylight.controller.cluster.raft.RaftActorContext;
15 import org.opendaylight.controller.cluster.raft.RaftState;
16 import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
17 import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
18 import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
19 import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
20 import org.opendaylight.controller.cluster.raft.messages.RaftRPC;
21 import org.opendaylight.controller.cluster.raft.messages.RequestVote;
22 import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
23 import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
24
25 public class CandidateTest extends AbstractRaftActorBehaviorTest {
26
27     private final TestActorRef<MessageCollectorActor> candidateActor = actorFactory.createTestActor(
28             Props.create(MessageCollectorActor.class), actorFactory.generateActorId("candidate"));
29
30     private TestActorRef<MessageCollectorActor>[] peerActors;
31
32     private RaftActorBehavior candidate;
33
34     @Before
35     public void setUp(){
36     }
37
38     @Override
39     @After
40     public void tearDown() throws Exception {
41         if(candidate != null) {
42             candidate.close();
43         }
44
45         super.tearDown();
46     }
47
48     @Test
49     public void testWhenACandidateIsCreatedItIncrementsTheCurrentTermAndVotesForItself(){
50         RaftActorContext raftActorContext = createActorContext();
51         long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
52
53         candidate = new Candidate(raftActorContext);
54
55         assertEquals("getCurrentTerm", expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
56         assertEquals("getVotedFor", raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
57     }
58
59     @Test
60     public void testThatAnElectionTimeoutIsTriggered(){
61          MockRaftActorContext actorContext = createActorContext();
62          candidate = new Candidate(actorContext);
63
64          MessageCollectorActor.expectFirstMatching(candidateActor, ElectionTimeout.class,
65                  actorContext.getConfigParams().getElectionTimeOutInterval().$times(6).toMillis());
66     }
67
68     @Test
69     public void testHandleElectionTimeoutWhenThereAreZeroPeers(){
70         RaftActorContext raftActorContext = createActorContext();
71         candidate = new Candidate(raftActorContext);
72
73         RaftActorBehavior newBehavior =
74             candidate.handleMessage(candidateActor, new ElectionTimeout());
75
76         assertEquals("Behavior", RaftState.Leader, newBehavior.state());
77     }
78
79     @Test
80     public void testHandleElectionTimeoutWhenThereAreTwoNodeCluster(){
81         MockRaftActorContext raftActorContext = createActorContext();
82         raftActorContext.setPeerAddresses(setupPeers(1));
83         candidate = new Candidate(raftActorContext);
84
85         candidate = candidate.handleMessage(candidateActor, new ElectionTimeout());
86
87         assertEquals("Behavior", RaftState.Candidate, candidate.state());
88     }
89
90     @Test
91     public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodeCluster(){
92         MockRaftActorContext raftActorContext = createActorContext();
93         raftActorContext.setPeerAddresses(setupPeers(2));
94         candidate = new Candidate(raftActorContext);
95
96         candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, true));
97
98         assertEquals("Behavior", RaftState.Leader, candidate.state());
99     }
100
101     @Test
102     public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodeCluster(){
103         MockRaftActorContext raftActorContext = createActorContext();
104         raftActorContext.setPeerAddresses(setupPeers(4));
105         candidate = new Candidate(raftActorContext);
106
107         // First peers denies the vote.
108         candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, false));
109
110         assertEquals("Behavior", RaftState.Candidate, candidate.state());
111
112         candidate = candidate.handleMessage(peerActors[1], new RequestVoteReply(1, true));
113
114         assertEquals("Behavior", RaftState.Candidate, candidate.state());
115
116         candidate = candidate.handleMessage(peerActors[2], new RequestVoteReply(1, true));
117
118         assertEquals("Behavior", RaftState.Leader, candidate.state());
119     }
120
121     @Test
122     public void testResponseToHandleAppendEntriesWithLowerTerm() {
123         candidate = new Candidate(createActorContext());
124
125         setupPeers(1);
126         candidate.handleMessage(peerActors[0], new AppendEntries(1, "test", 0, 0,
127                 Collections.<ReplicatedLogEntry>emptyList(), 0, -1));
128
129         AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(
130                 peerActors[0], AppendEntriesReply.class);
131         assertEquals("isSuccess", false, reply.isSuccess());
132         assertEquals("getTerm", 2, reply.getTerm());
133     }
134
135     @Test
136     public void testResponseToRequestVoteWithLowerTerm() {
137         candidate = new Candidate(createActorContext());
138
139         setupPeers(1);
140         candidate.handleMessage(peerActors[0], new RequestVote(1, "test", 0, 0));
141
142         RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
143                 peerActors[0], RequestVoteReply.class);
144         assertEquals("isVoteGranted", false, reply.isVoteGranted());
145         assertEquals("getTerm", 2, reply.getTerm());
146     }
147
148     @Test
149     public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForMatches() {
150         MockRaftActorContext context = createActorContext();
151         context.getTermInformation().update(1000, null);
152
153         // Once a candidate is created it will immediately increment the current term so after
154         // construction the currentTerm should be 1001
155         candidate = new Candidate(context);
156
157         setupPeers(1);
158         candidate.handleMessage(peerActors[0], new RequestVote(1001, context.getId(), 10000, 999));
159
160         RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
161                 peerActors[0], RequestVoteReply.class);
162         assertEquals("isVoteGranted", true, reply.isVoteGranted());
163         assertEquals("getTerm", 1001, reply.getTerm());
164     }
165
166     @Test
167     public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForDoesNotMatch() {
168         MockRaftActorContext context = createActorContext();
169         context.getTermInformation().update(1000, null);
170
171         // Once a candidate is created it will immediately increment the current term so after
172         // construction the currentTerm should be 1001
173         candidate = new Candidate(context);
174
175         setupPeers(1);
176
177         // RequestVote candidate ID ("candidate2") does not match this candidate's votedFor
178         // (it votes for itself)
179         candidate.handleMessage(peerActors[0], new RequestVote(1001, "candidate2", 10000, 999));
180
181         RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
182                 peerActors[0], RequestVoteReply.class);
183         assertEquals("isVoteGranted", false, reply.isVoteGranted());
184         assertEquals("getTerm", 1001, reply.getTerm());
185     }
186
187
188
189     @Override
190     protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
191         return new Candidate(actorContext);
192     }
193
194     @Override protected MockRaftActorContext createActorContext() {
195         return new MockRaftActorContext("candidate", getSystem(), candidateActor);
196     }
197
198     private Map<String, String> setupPeers(int count) {
199         Map<String, String> peerMap = new HashMap<>();
200         peerActors = new TestActorRef[count];
201         for(int i = 0; i < count; i++) {
202             peerActors[i] = actorFactory.createTestActor(Props.create(MessageCollectorActor.class),
203                     actorFactory.generateActorId("peer"));
204             peerMap.put("peer" + (i+1), peerActors[i].path().toString());
205         }
206
207         return peerMap;
208     }
209
210     @Override
211     protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
212             ActorRef actorRef, RaftRPC rpc) throws Exception {
213         super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc);
214         assertEquals("New votedFor", null, actorContext.getTermInformation().getVotedFor());
215     }
216 }