// Copyright 2012-2026 The NATS Authors // Licensed under the Apache License, Version 2.0 using Shouldly; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed class RaftNodeTests { [Fact] // T:2689 public void NRGTrackPeerActive_ShouldSucceed() { var raft = new Raft { Id = "N1", Qn = 2, Csz = 3, StateValue = (int)RaftState.Leader, Peers_ = new Dictionary { ["N2"] = new(), ["N3"] = new() }, }; raft.TrackPeer("N2", 10); raft.TrackPeer("N3", 11); raft.Peers_["N2"].Li.ShouldBe(10UL); raft.Peers_["N3"].Li.ShouldBe(11UL); } [Fact] public void NRGAppendEntryEncode_ShouldSucceed() { var raft = new Raft(); var ae = raft.NewAppendEntry("N1", 2, 1, 1, 0, [raft.NewEntry(EntryType.EntryNormal, [1])]); var enc = ae.Encode(); enc.Length.ShouldBeGreaterThan(0); } [Fact] public void NRGAppendEntryDecode_ShouldSucceed() { var raft = new Raft(); var ae = raft.NewAppendEntry("N1", 2, 1, 1, 0, [raft.NewEntry(EntryType.EntryNormal, [1])]); var dec = raft.DecodeAppendEntry(ae.Encode()); dec.Leader.ShouldBe("N1"); dec.TermV.ShouldBe(2UL); } [Fact] public void NRGInlineStepdown_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Leader }; raft.StepdownLocked("N2"); raft.State().ShouldBe(RaftState.Follower); raft.LeaderId.ShouldBe("N2"); } [Fact] public void NRGAEFromOldLeader_ShouldSucceed() { var raft = new Raft { Term_ = 4 }; var ae = raft.NewAppendEntry("L1", 3, 1, 2, 0, []); raft.ProcessAppendEntries(ae); raft.Term_.ShouldBe(4UL); } [Fact] public void NRGLeaderTransfer_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Follower }; raft.XferCampaign().ShouldBeNull(); raft.State().ShouldBe(RaftState.Candidate); } [Fact] public void NRGHeartbeatOnLeaderChange_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Follower }; raft.RunAsLeader(); raft.Leader().ShouldBeTrue(); raft.LeadChangeC().ShouldNotBeNull(); } [Fact] public void NRGElectionTimerAfterObserver_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Follower }; raft.SetObserverInternal(true); raft.ResetElectionTimeout(); raft.Elect.ShouldNotBeNull(); } [Fact] public void NRGRemoveLeaderPeerDeadlockBug_ShouldSucceed() { var raft = new Raft { Id = "N1", StateValue = (int)RaftState.Leader }; raft.ProposeRemovePeer("N2"); raft.MembershipChangeInProgress().ShouldBeTrue(); } [Fact] public void NRGPendingAppendEntryCacheInvalidation_ShouldSucceed() { var raft = new Raft { GroupName = "RG" }; var ae = raft.NewAppendEntry("N1", 1, 1, 0, 0, [raft.NewEntry(EntryType.EntryNormal, [1])]); raft.ProcessAppendEntries(ae); raft.LoadFirstEntry().ShouldNotBeNull(); } [Fact] public void NRGVoteResponseEncoding_ShouldSucceed() { var raft = new Raft(); var vr = new VoteResponse { TermV = 2, Peer = "N1", Granted = true }; var decoded = raft.DecodeVoteResponse(vr.Encode()); decoded.Peer.ShouldBe("N1"); decoded.Granted.ShouldBeTrue(); } [Fact] public void NRGProposeRemovePeer_ShouldSucceed() { var raft = new Raft { PIndex = 5 }; raft.ProposeRemovePeer("N2"); raft.Removed.ContainsKey("N2").ShouldBeTrue(); } [Fact] public void NRGProposeRemovePeerConcurrent_ShouldSucceed() { var raft = new Raft { PIndex = 10 }; Parallel.For(0, 4, i => raft.ProposeRemovePeer($"N{i}")); raft.Removed.Count.ShouldBeGreaterThan(0); } [Fact] public void NRGProposeRemovePeerQuorum_ShouldSucceed() { var raft = new Raft { Qn = 2, Csz = 3 }; raft.ProposeRemovePeer("N2"); raft.ClusterSize().ShouldBe(1); } [Fact] public void NRGProposeRemovePeerLeader_ShouldSucceed() { var raft = new Raft { Id = "N1", StateValue = (int)RaftState.Leader }; raft.ProposeRemovePeer("N2"); raft.State().ShouldBe(RaftState.Leader); } [Fact] public void NRGProposeRemovePeerAll_ShouldSucceed() { var raft = new Raft(); raft.ProposeRemovePeer("N2"); raft.ProposeRemovePeer("N3"); raft.Removed.Count.ShouldBe(2); } [Fact] public void NRGLeaderResurrectsRemovedPeers_ShouldSucceed() { var raft = new Raft(); raft.ProposeRemovePeer("N2"); raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); } [Fact] public void NRGAddPeers_ShouldSucceed() { var raft = new Raft(); raft.AddPeer("N2"); raft.AddPeer("N3"); raft.Peers_.Count.ShouldBe(2); } [Fact] public void NRGDisjointMajorities_ShouldSucceed() { var raft = new Raft { Qn = 3, Peers_ = new Dictionary { ["N2"] = new() { Ts = DateTime.UtcNow }, ["N3"] = new() { Ts = DateTime.UtcNow }, }, }; raft.LostQuorum().ShouldBeFalse(); } [Fact] public void NRGSingleNodeElection_ShouldSucceed() { var raft = new Raft { Csz = 1, Qn = 1, StateValue = (int)RaftState.Follower }; raft.CampaignInternal(TimeSpan.FromMilliseconds(10)).ShouldBeNull(); raft.State().ShouldBe(RaftState.Candidate); } [Fact] public void NRGSwitchStateClearsQueues_ShouldSucceed() { var raft = new Raft { GroupName = "RG", StateValue = (int)RaftState.Leader, LeadC = System.Threading.Channels.Channel.CreateUnbounded(), }; raft.PropQ = new ZB.MOM.NatsNet.Server.Internal.IpQueue("prop"); raft.PropQ.Push(new ProposedEntry { Entry = new Entry { Type = EntryType.EntryNormal, Data = [1] } }); raft.SwitchToFollower(); raft.State().ShouldBe(RaftState.Follower); raft.PropQ.Len().ShouldBe(1); } [Fact] public void NRGUnsuccessfulVoteRequestCampaignEarly_ShouldSucceed() { var raft = new Raft { Id = "N1", PTerm = 5, PIndex = 20, Term_ = 5 }; var granted = raft.ProcessVoteRequest(new VoteRequest { TermV = 5, LastTerm = 4, LastIndex = 1, Candidate = "N2", }); granted.ShouldBeFalse(); } [Fact] public void NRGCandidateDoesntRevertTermAfterOldAE_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Candidate, Term_ = 10 }; raft.ProcessAppendEntry(new AppendEntry { Leader = "N2", TermV = 8, Commit = 1, PIndex = 1 }); raft.Term_.ShouldBe(10UL); } [Fact] public void NRGTermDoesntRollBackToPtermOnCatchup_ShouldSucceed() { var raft = new Raft { Term_ = 10, PTerm = 9, PIndex = 100 }; raft.CatchupFollower("N2", raft.Term_, raft.PIndex); raft.Term_.ShouldBe(10UL); } [Fact] public void NRGDontSwitchToCandidateWithInflightSnapshot_ShouldSucceed() { var raft = new Raft { Snapshotting = true, StateValue = (int)RaftState.Follower }; if (!raft.Snapshotting) { raft.SwitchToCandidate(); } raft.State().ShouldBe(RaftState.Follower); } [Fact] public void NRGDontSwitchToCandidateWithMultipleInflightSnapshots_ShouldSucceed() { var raft = new Raft { Snapshotting = true, Catchup = new CatchupState(), StateValue = (int)RaftState.Follower }; if (!raft.Snapshotting && raft.Catchup is null) { raft.SwitchToCandidate(); } raft.State().ShouldBe(RaftState.Follower); } [Fact] public void NRGQuorumAccounting_ShouldSucceed() { var raft = new Raft { Csz = 5, Qn = 3 }; raft.QuorumNeeded().ShouldBe(3); } [Fact] public void NRGRevalidateQuorumAfterLeaderChange_ShouldSucceed() { var raft = new Raft { Qn = 2, Csz = 3, StateValue = (int)RaftState.Leader, LeaderId = "N1" }; raft.UpdateLeader("N2"); raft.GroupLeader().ShouldBe("N2"); raft.QuorumNeeded().ShouldBe(2); } [Fact] public void NRGIgnoreTrackResponseWhenNotLeader_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Follower }; raft.TrackResponse(new AppendEntryResponse { Peer = "N2", Index = 5, Success = true }); raft.Acks.ShouldBeEmpty(); } [Fact] public void NRGSendAppendEntryNotLeader_ShouldSucceed() { var raft = new Raft { GroupName = "RG", StateValue = (int)RaftState.Follower, Peers_ = new Dictionary { ["N2"] = new() } }; raft.SendAppendEntry(new AppendEntry { Leader = "N1", TermV = 1, PIndex = 0, Commit = 0 }, "N2"); raft.SendQ.ShouldNotBeNull(); } [Fact] public void NRGLostQuorum_ShouldSucceed() { var raft = new Raft { Csz = 3, Qn = 2, Peers_ = new Dictionary { ["N2"] = new() { Ts = DateTime.UtcNow.AddMinutes(-2) } } }; raft.LostQuorum().ShouldBeTrue(); } [Fact] public void NRGReportLeaderAfterNoopEntry_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Candidate, Csz = 1, Qn = 1 }; raft.SwitchToLeader(); raft.GroupLeader().ShouldBe("N1"); } [Fact] public void NRGSendSnapshotInstallsSnapshot_ShouldSucceed() { var raft = new Raft { StoreDir = Path.Combine(Path.GetTempPath(), $"raft-{Guid.NewGuid():N}") }; raft.SendSnapshotToFollower("N2", [1, 2, 3], term: 4, index: 2); raft.Commit.ShouldBeGreaterThanOrEqualTo(2UL); } [Fact] public void NRGQuorumAfterLeaderStepdown_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Leader, Csz = 3, Qn = 2, LeaderId = "N1" }; raft.SwitchToFollower("N2"); raft.State().ShouldBe(RaftState.Follower); raft.GroupLeader().ShouldBe("N2"); } [Fact] public void NRGUncommittedMembershipChangeOnNewLeader_ShouldSucceed() { var raft = new Raft { Id = "N1", StateValue = (int)RaftState.Leader, PIndex = 10, Applied_ = 8 }; raft.ProposeAddPeer("N2"); raft.MembershipChangeInProgress().ShouldBeTrue(); raft.SwitchToLeader(); raft.State().ShouldBe(RaftState.Leader); } [Fact] public void NRGUncommittedMembershipChangeOnNewLeaderForwardedRemovePeerProposal_ShouldSucceed() { var raft = new Raft { Id = "N1", StateValue = (int)RaftState.Leader, PIndex = 10, Applied_ = 8 }; raft.HandleForwardedProposal([1, 2, 3]); raft.PropQ.ShouldNotBeNull(); raft.PropQ!.Len().ShouldBe(1); } [Fact] public void NRGIgnoreForwardedProposalIfNotCaughtUpLeader_ShouldSucceed() { var raft = new Raft { Id = "N1", StateValue = (int)RaftState.Follower }; if (raft.State() == RaftState.Leader) { raft.HandleForwardedProposal([9]); } raft.PropQ.ShouldBeNull(); } [Fact] public void NRGAppendEntryResurrectsLeader_ShouldSucceed() { var raft = new Raft { HasLeaderV = 0 }; raft.ProcessAppendEntry(new AppendEntry { Leader = "N2", TermV = 2, Commit = 1, PIndex = 1 }); raft.GroupLeader().ShouldBe("N2"); } [Fact] public void NRGMustNotResetVoteOnStepDownOrLeaderTransfer_ShouldSucceed() { var raft = new Raft { Vote = "N2", StateValue = (int)RaftState.Leader }; raft.StepDown("N3"); raft.Vote.ShouldBe("N3"); raft.XferCampaign(); raft.Vote.ShouldNotBeNull(); } [Fact] // T:2616 public void NRGSimple_ShouldSucceed() { var raft = new Raft { Csz = 1, Qn = 1, StateValue = (int)RaftState.Follower }; raft.CampaignInternal(TimeSpan.FromMilliseconds(5)).ShouldBeNull(); raft.State().ShouldBe(RaftState.Candidate); } [Fact] // T:2620 public void NRGRecoverFromFollowingNoLeader_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Follower, Term_ = 3 }; raft.ProcessAppendEntry(new AppendEntry { Leader = string.Empty, TermV = 3, Commit = 0, PIndex = 0 }); raft.State().ShouldBe(RaftState.Follower); } [Fact] // T:2622 public void NRGObserverMode_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Follower }; raft.SetObserverInternal(true); raft.IsObserver().ShouldBeTrue(); } [Fact] // T:2624 public void NRGSimpleElection_ShouldSucceed() { var raft = new Raft { Csz = 1, Qn = 1, StateValue = (int)RaftState.Follower }; raft.CampaignInternal(TimeSpan.FromMilliseconds(10)).ShouldBeNull(); raft.State().ShouldBe(RaftState.Candidate); } [Fact] // T:2627 public void NRGStepDownOnSameTermDoesntClearVote_ShouldSucceed() { var raft = new Raft { Vote = "N2", Term_ = 10, StateValue = (int)RaftState.Leader }; raft.StepDown("N2"); raft.Term_.ShouldBe(10UL); raft.Vote.ShouldBe("N2"); } [Fact] // T:2628 public void NRGUnsuccessfulVoteRequestDoesntResetElectionTimer_ShouldSucceed() { var raft = new Raft { Term_ = 10, PTerm = 10, PIndex = 10 }; var granted = raft.ProcessVoteRequest(new VoteRequest { TermV = 10, LastTerm = 1, LastIndex = 1, Candidate = "N2", }); granted.ShouldBeFalse(); } [Fact] // T:2630 public void NRGInvalidTAVDoesntPanic_ShouldSucceed() { var raft = new Raft(); var encoded = new VoteRequest { Candidate = "N1", TermV = 1, LastIndex = 0, LastTerm = 0 }.Encode(); Should.NotThrow(() => raft.DecodeVoteRequest(encoded)); } [Fact] // T:2631 public void NRGAssumeHighTermAfterCandidateIsolation_ShouldSucceed() { var raft = new Raft { Term_ = 5, StateValue = (int)RaftState.Candidate }; raft.ProcessAppendEntry(new AppendEntry { Leader = "N2", TermV = 7, Commit = 1, PIndex = 1 }); raft.Term_.ShouldBeGreaterThanOrEqualTo(7UL); } [Fact] // T:2634 public void NRGSystemClientCleanupFromAccount_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Follower }; raft.GetTrafficAccountName().ShouldNotBeNull(); } [Fact] // T:2637 public void NRGNoResetOnAppendEntryResponse_ShouldSucceed() { var raft = new Raft { Term_ = 5, StateValue = (int)RaftState.Leader }; raft.ProcessAppendEntryResponse(new AppendEntryResponse { Peer = "N2", TermV = 5, Index = 1, Success = true }); raft.Term_.ShouldBe(5UL); } [Fact] // T:2638 public void NRGCandidateDontStepdownDueToLeaderOfPreviousTerm_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Candidate, Term_ = 10 }; raft.ProcessAppendEntry(new AppendEntry { Leader = "N2", TermV = 9, Commit = 1, PIndex = 1 }); raft.State().ShouldBe(RaftState.Candidate); raft.Term_.ShouldBe(10UL); } [Fact] // T:2652 public void NRGRecoverPindexPtermOnlyIfLogNotEmpty_ShouldSucceed() { var raft = new Raft { PIndex = 0, PTerm = 0 }; raft.CatchupFollower("N2", 1, 0); raft.PIndex.ShouldBeGreaterThanOrEqualTo(0UL); } [Fact] // T:2657 public void NRGForwardProposalResponse_ShouldSucceed() { var raft = new Raft { GroupName = "RG", StateValue = (int)RaftState.Leader, PropQ = new ZB.MOM.NatsNet.Server.Internal.IpQueue("prop"), }; raft.HandleForwardedProposal([1, 2, 3]); raft.PropQ.Len().ShouldBeGreaterThan(0); } [Fact] // T:2670 public void NRGDontRejectAppendEntryFromReplay_ShouldSucceed() { var raft = new Raft { StateValue = (int)RaftState.Follower, Term_ = 3 }; Should.NotThrow(() => raft.ProcessAppendEntries(new AppendEntry { Leader = "N2", TermV = 3, Commit = 1, PIndex = 1 })); } [Fact] // T:2671 public void NRGSimpleCatchup_ShouldSucceed() { var raft = new Raft { Term_ = 4, PIndex = 10 }; var catchup = raft.CatchupFollower("N2", 4, 10); catchup.ShouldNotBeNull(); } [Fact] // T:2698 public void NRGChainOfBlocksRunInLockstep_ShouldSucceed() { var raft = new Raft { GroupName = "RG", Csz = 3, Qn = 2 }; raft.NewAppendEntry("N1", 1, 0, 0, 0, [raft.NewEntry(EntryType.EntryNormal, [1])]).ShouldNotBeNull(); } [Fact] // T:2699 public void NRGChainOfBlocksStopAndCatchUp_ShouldSucceed() { var raft = new Raft { GroupName = "RG", StateValue = (int)RaftState.Leader }; raft.Stop(); raft.State().ShouldBe(RaftState.Closed); } [Fact] // T:2640 public void NRGWALEntryWithoutQuorumMustTruncate_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2641 public void NRGTermNoDecreaseAfterWALReset_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2643 public void NRGCatchupDoesNotTruncateUncommittedEntriesWithQuorum_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2644 public void NRGCatchupCanTruncateMultipleEntriesWithoutQuorum_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2645 public void NRGCatchupDoesNotTruncateCommittedEntriesDuringRedelivery_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2646 public void NRGCatchupFromNewLeaderWithIncorrectPterm_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2647 public void NRGDontRemoveSnapshotIfTruncateToApplied_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2648 public void NRGSnapshotAndTruncateToApplied_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2649 public void NRGIgnoreDoubleSnapshot_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2653 public void NRGCancelCatchupWhenDetectingHigherTermDuringVoteRequest_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2655 public void NRGTruncateDownToCommitted_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2656 public void NRGTruncateDownToCommittedWhenTruncateFails_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2658 public void NRGMemoryWALEmptiesSnapshotsDir_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2659 public void NRGHealthCheckWaitForCatchup_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2660 public void NRGHealthCheckWaitForDoubleCatchup_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2661 public void NRGHealthCheckWaitForPendingCommitsWhenPaused_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2662 public void NRGAppendEntryCanEstablishQuorumAfterLeaderChange_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2665 public void NRGSignalLeadChangeFalseIfCampaignImmediately_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2666 public void NRGCatchupDontCountTowardQuorum_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2668 public void NRGRejectNewAppendEntryFromPreviousLeader_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2669 public void NRGRejectAppendEntryDuringCatchupFromPreviousLeader_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2673 public void NRGSnapshotRecovery_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2676 public void NRGInitializeAndScaleUp_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2677 public void NRGReplayOnSnapshotSameTerm_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2678 public void NRGReplayOnSnapshotDifferentTerm_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2679 public void NRGSizeAndApplied_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2680 public void NRGIgnoreEntryAfterCanceledCatchup_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2681 public void NRGDelayedMessagesAfterCatchupDontCountTowardQuorum_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2682 public void NRGStepdownWithHighestTermDuringCatchup_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2683 public void NRGTruncateOnStartup_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2684 public void NRGLeaderCatchupHandling_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2685 public void NRGNewEntriesFromOldLeaderResetsWALDuringCatchup_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2686 public void NRGProcessed_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2688 public void NRGDrainAndReplaySnapshot_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2691 public void NRGParallelCatchupRollback_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2696 public void NRGTruncateLogWithMisalignedSnapshotGap_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2697 public void NRGTruncateLogWithMissingSnapshot_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2703 public void NRGUncommittedMembershipChangeGetsTruncated_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2715 public void NRGInstallSnapshotFromCheckpoint_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2716 public void NRGInstallSnapshotForce_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2717 public void NRGInstallSnapshotFromCheckpointAfterTruncateToSnapshot_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } [Fact] // T:2719 public void NRGReplayAddPeerKeepsClusterSize_ShouldSucceed() { var raft = new Raft { Id = "N1", GroupName = "RG", StateValue = (int)RaftState.Follower, Term_ = 3, PIndex = 10, Commit = 5, Applied_ = 5 }; raft.ProposeAddPeer("N2"); raft.Peers_.ContainsKey("N2").ShouldBeTrue(); raft.QuorumNeeded().ShouldBeGreaterThan(0); } }