test(batch30): add direct raft node impl backlog tests
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
// 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]
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user