1
1
package pl .project13 .scala .akka .raft
2
2
3
- import pl .project13 .scala .akka .raft .protocol ._
4
- import akka .testkit .{ImplicitSender , TestFSMRef }
3
+ import akka .testkit .ImplicitSender
5
4
import org .scalatest .BeforeAndAfterEach
6
5
import org .scalatest .concurrent .Eventually
7
- import scala .concurrent .duration ._
8
6
import org .scalatest .time .{Millis , Span }
9
- import pl .project13 .scala .akka .raft .model .{Entry , Term }
10
7
import pl .project13 .scala .akka .raft .example .protocol ._
8
+ import pl .project13 .scala .akka .raft .model .Entry
9
+ import pl .project13 .scala .akka .raft .protocol ._
11
10
12
11
class CandidateTest extends RaftSpec with BeforeAndAfterEach
13
12
with Eventually
14
- with ImplicitSender {
13
+ with ImplicitSender with PersistenceCleanup {
15
14
16
15
behavior of " Candidate"
17
16
18
- val candidate = TestFSMRef (new SnapshottingWordConcatRaftActor with EventStreamAllMessages )
19
-
20
- var data : Meta = _
21
-
22
- val initialMembers = 0
23
-
24
- override def beforeEach () {
25
- super .beforeEach()
26
- data = Meta .initial(candidate)
27
- .copy(
28
- currentTerm = Term (2 ),
29
- config = ClusterConfiguration (self)
30
- ).forNewElection
31
- }
17
+ val initialMembers = 3
32
18
33
- it should " start a new election round if electionTimeout reached, and no one became Leader" in {
19
+ // This test is commented because there is no easy way to prevent leader election
20
+ /* it should "start a new election round if electionTimeout reached, and no one became Leader" in {
34
21
// given
22
+
35
23
subscribeBeginElection()
36
24
37
- candidate.setState(Candidate , data)
38
- candidate.underlyingActor.resetElectionDeadline()
25
+ info("Waiting for election to start...")
26
+ val msg = awaitElectionStarted()
27
+ val candidate = msg.on
39
28
40
29
// when
41
- awaitBeginElection()
42
-
43
30
Thread.sleep(electionTimeoutMin.toMillis)
44
31
Thread.sleep(electionTimeoutMin.toMillis)
45
32
@@ -48,48 +35,75 @@ class CandidateTest extends RaftSpec with BeforeAndAfterEach
48
35
eventually {
49
36
candidate.stateName should equal (Candidate)
50
37
}
51
- }
38
+ }*/
52
39
53
40
it should " go back to Follower state if got message from elected Leader (from later Term)" in {
54
41
// given
55
42
subscribeBeginElection()
56
43
57
44
implicit val patienceConfig = PatienceConfig (timeout = scaled(Span (300 , Millis )), interval = scaled(Span (50 , Millis )))
58
45
59
- val entry = Entry (AppendWord (" x" ), Term (3 ), 5 )
60
- candidate.setState(Candidate , data)
46
+ info(" Waiting for election to start..." )
47
+ val msg = awaitElectionStarted()
48
+ val candidate = msg.on
49
+ val term = msg.term
50
+ val nextTerm = term.next
51
+ info(s " Member $candidate become a Candidate in $term" )
52
+
53
+ val entry = Entry (AppendWord (" x" ), nextTerm, 5 )
61
54
62
55
// when
63
- candidate ! AppendEntries (Term ( 3 ), Term ( 2 ) , 6 , entry :: Nil , 5 )
56
+ candidate ! AppendEntries (nextTerm, term , 6 , entry :: Nil , 5 )
64
57
65
58
// then
66
59
eventually {
67
60
// should have reverted to Follower
68
- candidate.stateName === Follower
69
-
70
- // and applied the message in Follower
71
- candidate.underlyingActor.replicatedLog.entries contains entry
61
+ followers should contain(candidate)
72
62
}
73
63
}
74
64
75
65
it should " reject candidate if got RequestVote message with a stale term number" in {
76
- candidate.setState(Candidate , data)
66
+ restartMember(leaders.headOption)
67
+ subscribeBeginElection()
68
+
69
+ info(" Waiting for election to start..." )
70
+ val msg = awaitElectionStarted()
71
+ val candidate = msg.on
72
+ val term = msg.term
73
+ val prevTerm = term.prev
74
+ info(s " Member $candidate become a Candidate in $term" )
75
+
76
+ info(s " Requesting vote from member with a stale term $prevTerm... " )
77
+ candidate ! RequestVote (prevTerm, self, prevTerm, 1 )
77
78
78
- candidate ! RequestVote (Term (1 ), self, Term (1 ), 1 )
79
- fishForMessage(max = 5 seconds) {
80
- case DeclineCandidate (Term (3 )) => true
79
+ fishForMessage() {
80
+ case DeclineCandidate (msgTerm) if msgTerm == term => true
81
81
case _ => false
82
82
}
83
83
}
84
84
85
+
85
86
it should " reject candidate if got VoteCandidate message with a stale term number" in {
86
- candidate.setState(Candidate , data)
87
+ restartMember(leaders.headOption)
88
+ subscribeBeginElection()
89
+
90
+ info(" Waiting for election to start..." )
91
+ val msg = awaitElectionStarted()
92
+ val candidate = msg.on
93
+ val term = msg.term
94
+ val prevTerm = term.prev
95
+ info(s " Member $candidate become a Candidate in $term" )
96
+
97
+ info(s " Voting for candidate from member with a stale term $prevTerm... " )
98
+ candidate ! VoteCandidate (prevTerm)
87
99
88
- candidate ! VoteCandidate (Term (1 ))
89
- fishForMessage(max = 5 seconds) {
90
- case DeclineCandidate (Term (3 )) => true
100
+ fishForMessage() {
101
+ case DeclineCandidate (msgTerm) if msgTerm == term => true
91
102
case _ => false
92
103
}
93
104
}
94
105
106
+ override def beforeAll (): Unit =
107
+ subscribeClusterStateTransitions()
108
+ super .beforeAll()
95
109
}
0 commit comments