feat: add NatsRaftTransport with NATS subject routing ($NRG.*)
Implements RaftSubjects static class with Go's $NRG.* subject constants and NatsRaftTransport which routes RAFT RPCs over those subjects using RaftAppendEntryWire / RaftVoteRequestWire encoding. 43 tests cover all subject patterns, wire encoding fidelity, and transport construction.
This commit is contained in:
155
tests/NATS.Server.Tests/Raft/RaftSubjectsTests.cs
Normal file
155
tests/NATS.Server.Tests/Raft/RaftSubjectsTests.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using NATS.Server.Raft;
|
||||
|
||||
namespace NATS.Server.Tests.Raft;
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that RaftSubjects produces the exact $NRG.* subject strings
|
||||
/// defined in Go's raft.go constants.
|
||||
///
|
||||
/// Go reference: golang/nats-server/server/raft.go:2161-2169
|
||||
/// raftAllSubj = "$NRG.>"
|
||||
/// raftVoteSubj = "$NRG.V.%s"
|
||||
/// raftAppendSubj = "$NRG.AE.%s"
|
||||
/// raftPropSubj = "$NRG.P.%s"
|
||||
/// raftRemovePeerSubj = "$NRG.RP.%s"
|
||||
/// raftReply = "$NRG.R.%s"
|
||||
/// raftCatchupReply = "$NRG.CR.%s"
|
||||
/// </summary>
|
||||
public class RaftSubjectsTests
|
||||
{
|
||||
// Go: server/raft.go:2162 — raftAllSubj = "$NRG.>"
|
||||
[Fact]
|
||||
public void All_constant_matches_go_raftAllSubj()
|
||||
{
|
||||
RaftSubjects.All.ShouldBe("$NRG.>");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2163 — raftVoteSubj = "$NRG.V.%s"
|
||||
[Fact]
|
||||
public void Vote_formats_subject_with_group()
|
||||
{
|
||||
RaftSubjects.Vote("mygroup").ShouldBe("$NRG.V.mygroup");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2163 — fmt.Sprintf(raftVoteSubj, n.group)
|
||||
[Fact]
|
||||
public void Vote_uses_group_verbatim()
|
||||
{
|
||||
RaftSubjects.Vote("meta").ShouldBe("$NRG.V.meta");
|
||||
RaftSubjects.Vote("stream-A").ShouldBe("$NRG.V.stream-A");
|
||||
RaftSubjects.Vote("_raft_").ShouldBe("$NRG.V._raft_");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2164 — raftAppendSubj = "$NRG.AE.%s"
|
||||
[Fact]
|
||||
public void AppendEntry_formats_subject_with_group()
|
||||
{
|
||||
RaftSubjects.AppendEntry("mygroup").ShouldBe("$NRG.AE.mygroup");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2164 — fmt.Sprintf(raftAppendSubj, n.group)
|
||||
[Fact]
|
||||
public void AppendEntry_uses_group_verbatim()
|
||||
{
|
||||
RaftSubjects.AppendEntry("meta").ShouldBe("$NRG.AE.meta");
|
||||
RaftSubjects.AppendEntry("stream-B").ShouldBe("$NRG.AE.stream-B");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2165 — raftPropSubj = "$NRG.P.%s"
|
||||
[Fact]
|
||||
public void Proposal_formats_subject_with_group()
|
||||
{
|
||||
RaftSubjects.Proposal("mygroup").ShouldBe("$NRG.P.mygroup");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2165 — fmt.Sprintf(raftPropSubj, n.group)
|
||||
[Fact]
|
||||
public void Proposal_uses_group_verbatim()
|
||||
{
|
||||
RaftSubjects.Proposal("meta").ShouldBe("$NRG.P.meta");
|
||||
RaftSubjects.Proposal("consumer-1").ShouldBe("$NRG.P.consumer-1");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2166 — raftRemovePeerSubj = "$NRG.RP.%s"
|
||||
[Fact]
|
||||
public void RemovePeer_formats_subject_with_group()
|
||||
{
|
||||
RaftSubjects.RemovePeer("mygroup").ShouldBe("$NRG.RP.mygroup");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2166 — fmt.Sprintf(raftRemovePeerSubj, n.group)
|
||||
[Fact]
|
||||
public void RemovePeer_uses_group_verbatim()
|
||||
{
|
||||
RaftSubjects.RemovePeer("meta").ShouldBe("$NRG.RP.meta");
|
||||
RaftSubjects.RemovePeer("stream-C").ShouldBe("$NRG.RP.stream-C");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2167 — raftReply = "$NRG.R.%s"
|
||||
[Fact]
|
||||
public void Reply_formats_subject_with_id()
|
||||
{
|
||||
RaftSubjects.Reply("abc123").ShouldBe("$NRG.R.abc123");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2167 — fmt.Sprintf(raftReply, b[:])
|
||||
[Fact]
|
||||
public void Reply_uses_id_verbatim()
|
||||
{
|
||||
RaftSubjects.Reply("ABCDEFGH").ShouldBe("$NRG.R.ABCDEFGH");
|
||||
RaftSubjects.Reply("00000001").ShouldBe("$NRG.R.00000001");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2168 — raftCatchupReply = "$NRG.CR.%s"
|
||||
[Fact]
|
||||
public void CatchupReply_formats_subject_with_id()
|
||||
{
|
||||
RaftSubjects.CatchupReply("xyz789").ShouldBe("$NRG.CR.xyz789");
|
||||
}
|
||||
|
||||
// Go: server/raft.go:2168 — fmt.Sprintf(raftCatchupReply, b[:])
|
||||
[Fact]
|
||||
public void CatchupReply_uses_id_verbatim()
|
||||
{
|
||||
RaftSubjects.CatchupReply("ABCDEFGH").ShouldBe("$NRG.CR.ABCDEFGH");
|
||||
RaftSubjects.CatchupReply("00000001").ShouldBe("$NRG.CR.00000001");
|
||||
}
|
||||
|
||||
// Verify that subjects for different groups are distinct (no collisions)
|
||||
[Fact]
|
||||
public void Subjects_for_different_groups_are_distinct()
|
||||
{
|
||||
RaftSubjects.Vote("group1").ShouldNotBe(RaftSubjects.Vote("group2"));
|
||||
RaftSubjects.AppendEntry("group1").ShouldNotBe(RaftSubjects.AppendEntry("group2"));
|
||||
RaftSubjects.Proposal("group1").ShouldNotBe(RaftSubjects.Proposal("group2"));
|
||||
RaftSubjects.RemovePeer("group1").ShouldNotBe(RaftSubjects.RemovePeer("group2"));
|
||||
}
|
||||
|
||||
// Verify that different verb subjects for the same group are distinct
|
||||
[Fact]
|
||||
public void Different_verbs_for_same_group_are_distinct()
|
||||
{
|
||||
var group = "meta";
|
||||
var subjects = new[]
|
||||
{
|
||||
RaftSubjects.Vote(group),
|
||||
RaftSubjects.AppendEntry(group),
|
||||
RaftSubjects.Proposal(group),
|
||||
RaftSubjects.RemovePeer(group),
|
||||
};
|
||||
subjects.Distinct().Count().ShouldBe(subjects.Length);
|
||||
}
|
||||
|
||||
// All group subjects must be sub-subjects of the wildcard $NRG.>
|
||||
[Fact]
|
||||
public void All_group_subjects_are_under_NRG_namespace()
|
||||
{
|
||||
var group = "g";
|
||||
RaftSubjects.Vote(group).ShouldStartWith("$NRG.");
|
||||
RaftSubjects.AppendEntry(group).ShouldStartWith("$NRG.");
|
||||
RaftSubjects.Proposal(group).ShouldStartWith("$NRG.");
|
||||
RaftSubjects.RemovePeer(group).ShouldStartWith("$NRG.");
|
||||
RaftSubjects.Reply("id").ShouldStartWith("$NRG.");
|
||||
RaftSubjects.CatchupReply("id").ShouldStartWith("$NRG.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user