func testCM_SOLO_Follower_ElectsSelfOnElectionTimeout( t *testing.T, mcm *managedConsensusModule, mrs *testhelpers.MockRpcSender, ) { if mcm.pcm.GetServerState() != FOLLOWER { t.Fatal() } if mcm.pcm.RaftPersistentState.GetVotedFor() != "" { t.Fatal() } // Test that a tick before election timeout causes no state change. err := mcm.Tick() if err != nil { t.Fatal(err) } if mcm.pcm.RaftPersistentState.GetCurrentTerm() != testdata.CurrentTerm { t.Fatal() } if mcm.pcm.GetServerState() != FOLLOWER { t.Fatal() } var expectedNewTerm TermNo = testdata.CurrentTerm + 1 timeout1 := mcm.pcm.ElectionTimeoutTracker.GetCurrentElectionTimeout() // Test that election timeout causes a new election mcm.tickTilElectionTimeout(t) if mcm.pcm.RaftPersistentState.GetCurrentTerm() != expectedNewTerm { t.Fatal(expectedNewTerm, mcm.pcm.RaftPersistentState.GetCurrentTerm()) } // Single node should immediately elect itself as leader if mcm.pcm.GetServerState() != LEADER { t.Fatal() } // candidate has voted for itself if mcm.pcm.RaftPersistentState.GetVotedFor() != testdata.ThisServerId { t.Fatal() } // a new election timeout was chosen if mcm.pcm.ElectionTimeoutTracker.GetCurrentElectionTimeout() == timeout1 { t.Fatal() } // candidate state is fresh expectedCvs, err := consensus_state.NewCandidateVolatileState(mcm.pcm.ClusterInfo) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(mcm.pcm.CandidateVolatileState, expectedCvs) { t.Fatal() } // no RPCs issued mrs.CheckSentRpcs(t, make(map[ServerId]interface{})) mrs.ClearSentRpcs() }
func testCM_FollowerOrCandidate_StartsElectionOnElectionTimeout_Part2( t *testing.T, mcm *managedConsensusModule, mrs *testhelpers.MockRpcSender, expectedNewTerm TermNo, ) { timeout1 := mcm.pcm.ElectionTimeoutTracker.GetCurrentElectionTimeout() // Test that election timeout causes a new election mcm.tickTilElectionTimeout(t) if mcm.pcm.RaftPersistentState.GetCurrentTerm() != expectedNewTerm { t.Fatal(expectedNewTerm, mcm.pcm.RaftPersistentState.GetCurrentTerm()) } if mcm.pcm.GetServerState() != CANDIDATE { t.Fatal() } // candidate has voted for itself if mcm.pcm.RaftPersistentState.GetVotedFor() != testdata.ThisServerId { t.Fatal() } // a new election timeout was chosen // Playing the odds here :P if mcm.pcm.ElectionTimeoutTracker.GetCurrentElectionTimeout() == timeout1 { t.Fatal() } // candidate state is fresh expectedCvs, err := consensus_state.NewCandidateVolatileState(mcm.pcm.ClusterInfo) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(mcm.pcm.CandidateVolatileState, expectedCvs) { t.Fatal() } // candidate has issued RequestVote RPCs to all other servers. lastLogIndex, lastLogTerm, err := GetIndexAndTermOfLastEntry(mcm.pcm.LogRO) if err != nil { t.Fatal(err) } expectedRpc := &RpcRequestVote{expectedNewTerm, lastLogIndex, lastLogTerm} expectedRpcs := map[ServerId]interface{}{} err = mcm.pcm.ClusterInfo.ForEachPeer(func(serverId ServerId) error { expectedRpcs[serverId] = expectedRpc return nil }) if err != nil { t.Fatal(err) } mrs.CheckSentRpcs(t, expectedRpcs) mrs.ClearSentRpcs() }