Merge "Refactor LeaderTest"
[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.JavaTestKit;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.Map;
10 import org.junit.Assert;
11 import org.junit.Before;
12 import org.junit.Test;
13 import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
14 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
15 import org.opendaylight.controller.cluster.raft.RaftActorContext;
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.RequestVote;
21 import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
22 import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
23
24 public class CandidateTest extends AbstractRaftActorBehaviorTest {
25
26     private final ActorRef candidateActor = getSystem().actorOf(Props.create(
27         DoNothingActor.class));
28
29     private final ActorRef peerActor1 = getSystem().actorOf(Props.create(
30         DoNothingActor.class));
31
32     private final ActorRef peerActor2 = getSystem().actorOf(Props.create(
33         DoNothingActor.class));
34
35     private final ActorRef peerActor3 = getSystem().actorOf(Props.create(
36         DoNothingActor.class));
37
38     private final ActorRef peerActor4 = getSystem().actorOf(Props.create(
39         DoNothingActor.class));
40
41     private final Map<String, String> onePeer = new HashMap<>();
42     private final Map<String, String> twoPeers = new HashMap<>();
43     private final Map<String, String> fourPeers = new HashMap<>();
44
45     @Before
46     public void setUp(){
47         onePeer.put(peerActor1.path().toString(),
48             peerActor1.path().toString());
49
50         twoPeers.put(peerActor1.path().toString(),
51             peerActor1.path().toString());
52         twoPeers.put(peerActor2.path().toString(),
53             peerActor2.path().toString());
54
55         fourPeers.put(peerActor1.path().toString(),
56             peerActor1.path().toString());
57         fourPeers.put(peerActor2.path().toString(),
58             peerActor2.path().toString());
59         fourPeers.put(peerActor3.path().toString(),
60             peerActor3.path().toString());
61         fourPeers.put(peerActor4.path().toString(),
62             peerActor3.path().toString());
63
64
65     }
66
67     @Test
68     public void testWhenACandidateIsCreatedItIncrementsTheCurrentTermAndVotesForItself(){
69         RaftActorContext raftActorContext = createActorContext();
70         long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
71
72         new Candidate(raftActorContext);
73
74         assertEquals(expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
75         assertEquals(raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
76     }
77
78     @Test
79     public void testThatAnElectionTimeoutIsTriggered(){
80         new JavaTestKit(getSystem()) {{
81
82             new Within(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6)) {
83                 @Override
84                 protected void run() {
85
86                     Candidate candidate = new Candidate(createActorContext(getTestActor()));
87
88                     final Boolean out = new ExpectMsg<Boolean>(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6), "ElectionTimeout") {
89                         // do not put code outside this method, will run afterwards
90                         @Override
91                         protected Boolean match(Object in) {
92                             if (in instanceof ElectionTimeout) {
93                                  return true;
94                             } else {
95                                 throw noMatch();
96                             }
97                         }
98                     }.get();
99
100                     assertEquals(true, out);
101                 }
102             };
103         }};
104     }
105
106     @Test
107     public void testHandleElectionTimeoutWhenThereAreZeroPeers(){
108         RaftActorContext raftActorContext = createActorContext();
109         Candidate candidate =
110             new Candidate(raftActorContext);
111
112         RaftActorBehavior raftBehavior =
113             candidate.handleMessage(candidateActor, new ElectionTimeout());
114
115         Assert.assertTrue(raftBehavior instanceof Leader);
116     }
117
118     @Test
119     public void testHandleElectionTimeoutWhenThereAreTwoNodesInCluster(){
120         MockRaftActorContext raftActorContext =
121             createActorContext();
122         raftActorContext.setPeerAddresses(onePeer);
123         Candidate candidate =
124             new Candidate(raftActorContext);
125
126         RaftActorBehavior raftBehavior =
127             candidate.handleMessage(candidateActor, new ElectionTimeout());
128
129         Assert.assertTrue(raftBehavior instanceof Candidate);
130     }
131
132     @Test
133     public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodesInCluster(){
134         MockRaftActorContext raftActorContext =
135             createActorContext();
136         raftActorContext.setPeerAddresses(twoPeers);
137         Candidate candidate =
138             new Candidate(raftActorContext);
139
140         RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
141
142         Assert.assertTrue(behaviorOnFirstVote instanceof Leader);
143
144     }
145
146     @Test
147     public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodesInCluster(){
148         MockRaftActorContext raftActorContext =
149             createActorContext();
150         raftActorContext.setPeerAddresses(fourPeers);
151         Candidate candidate =
152             new Candidate(raftActorContext);
153
154         RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
155
156         RaftActorBehavior behaviorOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
157
158         Assert.assertTrue(behaviorOnFirstVote instanceof Candidate);
159         Assert.assertTrue(behaviorOnSecondVote instanceof Leader);
160
161     }
162
163     @Test
164     public void testResponseToAppendEntriesWithLowerTerm(){
165         new JavaTestKit(getSystem()) {{
166
167             new Within(duration("1 seconds")) {
168                 @Override
169                 protected void run() {
170
171                     Candidate candidate = new Candidate(createActorContext(getTestActor()));
172
173                     candidate.handleMessage(getTestActor(), new AppendEntries(0, "test", 0,0,Collections.<ReplicatedLogEntry>emptyList(), 0, -1));
174
175                     final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
176                         // do not put code outside this method, will run afterwards
177                         @Override
178                         protected Boolean match(Object in) {
179                             if (in instanceof AppendEntriesReply) {
180                                 AppendEntriesReply reply = (AppendEntriesReply) in;
181                                 return reply.isSuccess();
182                             } else {
183                                 throw noMatch();
184                             }
185                         }
186                     }.get();
187
188                     assertEquals(false, out);
189                 }
190             };
191         }};
192     }
193
194     @Test
195     public void testResponseToRequestVoteWithLowerTerm(){
196         new JavaTestKit(getSystem()) {{
197
198             new Within(duration("1 seconds")) {
199                 @Override
200                 protected void run() {
201
202                     Candidate candidate = new Candidate(createActorContext(getTestActor()));
203
204                     candidate.handleMessage(getTestActor(), new RequestVote(0, "test", 0, 0));
205
206                     final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
207                         // do not put code outside this method, will run afterwards
208                         @Override
209                         protected Boolean match(Object in) {
210                             if (in instanceof RequestVoteReply) {
211                                 RequestVoteReply reply = (RequestVoteReply) in;
212                                 return reply.isVoteGranted();
213                             } else {
214                                 throw noMatch();
215                             }
216                         }
217                     }.get();
218
219                     assertEquals(false, out);
220                 }
221             };
222         }};
223     }
224
225     @Test
226     public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
227         new JavaTestKit(getSystem()) {{
228
229             new Within(duration("1 seconds")) {
230                 @Override
231                 protected void run() {
232
233                     RaftActorContext context = createActorContext(getTestActor());
234
235                     context.getTermInformation().update(1000, null);
236
237                     // Once a candidate is created it will immediately increment the current term so after
238                     // construction the currentTerm should be 1001
239                     RaftActorBehavior follower = createBehavior(context);
240
241                     follower.handleMessage(getTestActor(), new RequestVote(1001, "test", 10000, 999));
242
243                     final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
244                         // do not put code outside this method, will run afterwards
245                         @Override
246                         protected Boolean match(Object in) {
247                             if (in instanceof RequestVoteReply) {
248                                 RequestVoteReply reply = (RequestVoteReply) in;
249                                 return reply.isVoteGranted();
250                             } else {
251                                 throw noMatch();
252                             }
253                         }
254                     }.get();
255
256                     assertEquals(true, out);
257                 }
258             };
259         }};
260     }
261
262     @Test
263     public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
264         new JavaTestKit(getSystem()) {{
265
266             new Within(duration("1 seconds")) {
267                 @Override
268                 protected void run() {
269
270                     RaftActorContext context = createActorContext(getTestActor());
271
272                     context.getTermInformation().update(1000, "test");
273
274                     RaftActorBehavior follower = createBehavior(context);
275
276                     follower.handleMessage(getTestActor(), new RequestVote(1001, "candidate", 10000, 999));
277
278                     final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
279                         // do not put code outside this method, will run afterwards
280                         @Override
281                         protected Boolean match(Object in) {
282                             if (in instanceof RequestVoteReply) {
283                                 RequestVoteReply reply = (RequestVoteReply) in;
284                                 return reply.isVoteGranted();
285                             } else {
286                                 throw noMatch();
287                             }
288                         }
289                     }.get();
290
291                     assertEquals(false, out);
292                 }
293             };
294         }};
295     }
296
297
298
299     @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
300         return new Candidate(actorContext);
301     }
302
303     @Override protected MockRaftActorContext createActorContext() {
304         return new MockRaftActorContext("test", getSystem(), candidateActor);
305     }
306
307
308 }