// 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<string, Lps> { ["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<string, Lps>
            {
                ["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<bool>(),
        };

        raft.PropQ = new ZB.MOM.NatsNet.Server.Internal.IpQueue<ProposedEntry>("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<string, Lps> { ["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<string, Lps> { ["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<ProposedEntry>("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);
    }

}
