Files
natsdotnet/tests/NATS.Server.Tests/JetStream/Cluster/JetStreamLeadershipTests.cs
Joseph Doherty 7e4a23a0b7 feat(cluster): implement leadership transition with inflight cleanup
Add ProcessLeaderChange(bool) method and OnLeaderChange event to JetStreamMetaGroup.
Refactor StepDown() to delegate inflight clearing through ProcessLeaderChange,
enabling subscribers to react to leadership transitions.
Go reference: jetstream_cluster.go:7001-7074 processLeaderChange.
2026-02-25 02:08:37 -05:00

98 lines
2.7 KiB
C#

using NATS.Server.JetStream.Cluster;
namespace NATS.Server.Tests.JetStream.Cluster;
public class JetStreamLeadershipTests
{
[Fact]
public void ProcessLeaderChange_clears_inflight_on_step_down()
{
var meta = new JetStreamMetaGroup(3);
meta.TrackInflightStreamProposal("ACC", new StreamAssignment
{
StreamName = "s1",
Group = new RaftGroup { Name = "rg", Peers = ["n1", "n2", "n3"] },
});
meta.ProcessLeaderChange(isLeader: false);
meta.InflightStreamCount.ShouldBe(0);
}
[Fact]
public void ProcessLeaderChange_fires_event_on_become_leader()
{
var meta = new JetStreamMetaGroup(3);
var leaderChanged = false;
meta.OnLeaderChange += (isLeader) => leaderChanged = true;
meta.ProcessLeaderChange(isLeader: true);
leaderChanged.ShouldBeTrue();
}
[Fact]
public void ProcessLeaderChange_fires_event_on_step_down()
{
var meta = new JetStreamMetaGroup(3);
bool? receivedIsLeader = null;
meta.OnLeaderChange += (isLeader) => receivedIsLeader = isLeader;
meta.ProcessLeaderChange(isLeader: false);
receivedIsLeader.ShouldNotBeNull();
receivedIsLeader.Value.ShouldBeFalse();
}
[Fact]
public void StepDown_triggers_leader_change_event()
{
var meta = new JetStreamMetaGroup(3);
bool? receivedIsLeader = null;
meta.OnLeaderChange += (isLeader) => receivedIsLeader = isLeader;
meta.StepDown();
receivedIsLeader.ShouldNotBeNull();
receivedIsLeader.Value.ShouldBeFalse();
}
[Fact]
public void StepDown_clears_inflight_via_process_leader_change()
{
var meta = new JetStreamMetaGroup(3);
meta.TrackInflightStreamProposal("ACC", new StreamAssignment
{
StreamName = "s1",
Group = new RaftGroup { Name = "rg", Peers = ["n1", "n2", "n3"] },
});
meta.TrackInflightConsumerProposal("ACC", "s1", "c1");
meta.StepDown();
meta.InflightStreamCount.ShouldBe(0);
meta.InflightConsumerCount.ShouldBe(0);
}
[Fact]
public void BecomeLeader_makes_IsLeader_true()
{
var meta = new JetStreamMetaGroup(3);
meta.StepDown(); // move leader away from self
meta.IsLeader().ShouldBeFalse();
meta.BecomeLeader();
meta.IsLeader().ShouldBeTrue();
}
[Fact]
public void OnLeaderChange_not_fired_when_no_subscribers()
{
// Should not throw when no handlers attached
var meta = new JetStreamMetaGroup(3);
Should.NotThrow(() => meta.ProcessLeaderChange(isLeader: true));
Should.NotThrow(() => meta.ProcessLeaderChange(isLeader: false));
}
}