25 tasks across 6 waves targeting ~1,415 new tests: - Wave 2: Internal data structures (AVL, SubjectTree, GSL, TimeHashWheel) - Wave 3: FileStore block engine with 160 tests - Wave 4: RAFT consensus (election, replication, snapshots, membership) - Wave 5: JetStream clustering + NORACE concurrency - Wave 6: Remaining subsystem test suites (config, MQTT, leaf, accounts, gateway, routes, monitoring, client, JetStream API/cluster)
28 KiB
Full Production Parity Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers-extended-cc:executing-plans to implement this plan task-by-task.
Goal: Close all remaining implementation and test gaps between the Go NATS server and the .NET port, achieving full production parity.
Architecture: 6-wave slice-by-slice TDD, ordered by dependency. Waves 2-5 are sequential (each depends on prior wave's production code). Wave 6 subsystem suites are parallel and can begin alongside Wave 2.
Tech Stack: .NET 10 / C# 14, xUnit 3, Shouldly, NSubstitute, System.IO.Pipelines
Task 0: Inventory and Scaffolding
Files:
- Create:
src/NATS.Server/Internal/Avl/SequenceSet.cs - Create:
src/NATS.Server/Internal/SubjectTree/SubjectTree.cs - Create:
src/NATS.Server/Internal/Gsl/GenericSubjectList.cs - Create:
src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs - Create:
src/NATS.Server/JetStream/Storage/IStreamStore.cs - Create:
src/NATS.Server/JetStream/Storage/IConsumerStore.cs - Create:
src/NATS.Server/JetStream/Storage/FileStore/FileStore.cs - Create:
src/NATS.Server/Raft/IRaftNode.cs - Create:
src/NATS.Server/Raft/RaftNode.cs - Create:
src/NATS.Server/Raft/RaftState.cs - Create:
tests/NATS.Server.Tests/Internal/Avl/(directory) - Create:
tests/NATS.Server.Tests/Internal/SubjectTree/(directory) - Create:
tests/NATS.Server.Tests/Internal/Gsl/(directory) - Create:
tests/NATS.Server.Tests/Internal/TimeHashWheel/(directory) - Create:
tests/NATS.Server.Tests/Raft/(directory)
Step 1: Create namespace stub files with minimal type skeletons
Each stub file contains the namespace declaration, a public class/interface with a // TODO: Port from Go comment, and the Go reference file path.
Step 2: Create test directory structure
mkdir -p tests/NATS.Server.Tests/Internal/Avl
mkdir -p tests/NATS.Server.Tests/Internal/SubjectTree
mkdir -p tests/NATS.Server.Tests/Internal/Gsl
mkdir -p tests/NATS.Server.Tests/Internal/TimeHashWheel
mkdir -p tests/NATS.Server.Tests/Raft
Step 3: Verify build succeeds
Run: dotnet build NatsDotNet.slnx
Expected: Build succeeded, 0 errors
Step 4: Commit
git add -A && git commit -m "feat: scaffold namespaces for data structures, FileStore, and RAFT"
Task 1: AVL Tree / SequenceSet
Files:
- Create:
src/NATS.Server/Internal/Avl/SequenceSet.cs - Create:
tests/NATS.Server.Tests/Internal/Avl/SequenceSetTests.cs - Test:
tests/NATS.Server.Tests/Internal/Avl/SequenceSetTests.cs
Go reference: server/avl/seqset.go + server/avl/seqset_test.go (16 test functions)
Public API to port:
namespace NATS.Server.Internal.Avl;
public class SequenceSet
{
public void Insert(ulong seq);
public bool Exists(ulong seq);
public bool Delete(ulong seq);
public void SetInitialMin(ulong min);
public int Size { get; }
public int Nodes { get; }
public void Empty();
public bool IsEmpty { get; }
public void Range(Func<ulong, bool> callback);
public (int Left, int Right) Heights();
public (ulong Min, ulong Max, ulong Num) State();
public (ulong Min, ulong Max) MinMax();
public SequenceSet Clone();
public void Union(params SequenceSet[] others);
public static SequenceSet Union(params SequenceSet[] sets);
public int EncodeLength();
public byte[] Encode();
public static (SequenceSet Set, int BytesRead) Decode(ReadOnlySpan<byte> buf);
}
Step 1: Write failing tests
Port all 16 Go test functions:
TestSeqSetBasics→Basics_InsertExistsDeleteTestSeqSetLeftLean→LeftLean_TreeBalancesCorrectlyTestSeqSetRightLean→RightLean_TreeBalancesCorrectlyTestSeqSetCorrectness→Correctness_RandomInsertDeleteTestSeqSetRange→Range_IteratesInOrderTestSeqSetDelete→Delete_VariousPatternsTestSeqSetInsertAndDeletePedantic→InsertAndDelete_PedanticVerificationTestSeqSetMinMax→MinMax_TracksCorrectlyTestSeqSetClone→Clone_IndependentCopyTestSeqSetUnion→Union_MergesSetsTestSeqSetFirst→First_ReturnsMinimumTestSeqSetDistinctUnion→DistinctUnion_NoOverlapTestSeqSetDecodeV1→DecodeV1_BackwardsCompatibleTestNoRaceSeqSetSizeComparison→SizeComparison_LargeSetTestNoRaceSeqSetEncodeLarge→EncodeLarge_RoundTripsTestNoRaceSeqSetRelativeSpeed→RelativeSpeed_Performance
Step 2: Run tests to verify they fail
Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~SequenceSetTests" -v quiet
Expected: FAIL (types not implemented)
Step 3: Implement SequenceSet
Port the AVL tree from server/avl/seqset.go. Key implementation details:
- Internal
Nodeclass withValue,Height,Left,Right - AVL rotations:
RotateLeft,RotateRight,Balance - Run-length encoding for
Encode/Decode(sequences compress into ranges) - The tree stores ranges
[min, max]in each node, not individual values
Step 4: Run tests to verify they pass
Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~SequenceSetTests" -v quiet
Expected: PASS (16 tests)
Step 5: Commit
git add src/NATS.Server/Internal/Avl/ tests/NATS.Server.Tests/Internal/Avl/
git commit -m "feat: port AVL SequenceSet from Go with 16 tests"
Task 2: Subject Tree (ART)
Files:
- Create:
src/NATS.Server/Internal/SubjectTree/SubjectTree.cs - Create:
src/NATS.Server/Internal/SubjectTree/Node.cs(node4, node10, node16, node48, node256, leaf) - Create:
tests/NATS.Server.Tests/Internal/SubjectTree/SubjectTreeTests.cs
Go reference: server/stree/stree.go + 8 node files + server/stree/stree_test.go (59 test functions)
Public API to port:
namespace NATS.Server.Internal.SubjectTree;
public class SubjectTree<T>
{
public int Size { get; }
public SubjectTree<T> Empty();
public (T? Value, bool Existed) Insert(ReadOnlySpan<byte> subject, T value);
public (T? Value, bool Found) Find(ReadOnlySpan<byte> subject);
public (T? Value, bool Found) Delete(ReadOnlySpan<byte> subject);
public void Match(ReadOnlySpan<byte> filter, Action<byte[], T> callback);
public bool MatchUntil(ReadOnlySpan<byte> filter, Func<byte[], T, bool> callback);
public bool IterOrdered(Func<byte[], T, bool> callback);
public bool IterFast(Func<byte[], T, bool> callback);
}
Step 1: Write failing tests
Port all 59 Go test functions. Key groupings:
- Basic CRUD:
TestSubjectTreeBasics,TestSubjectTreeNoPrefix,TestSubjectTreeEmpty→ 5 tests - Node growth/shrink:
TestSubjectTreeNodeGrow,TestNode256Operations,TestNode256Shrink→ 8 tests - Matching:
TestSubjectTreeMatchLeafOnly,TestSubjectTreeMatchNodes,TestSubjectTreeMatchUntil+ 10 more → 15 tests - Iteration:
TestSubjectTreeIterOrdered,TestSubjectTreeIterFast+ edge cases → 8 tests - Delete:
TestSubjectTreeNodeDelete+ edge cases → 6 tests - Intersection:
TestSubjectTreeLazyIntersect,TestSubjectTreeGSLIntersection→ 3 tests - Edge cases and bug regression: remaining 14 tests
Step 2: Run tests to verify they fail
Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~SubjectTreeTests" -v quiet
Expected: FAIL
Step 3: Implement SubjectTree
Port the Adaptive Radix Tree from server/stree/. Key implementation:
- 5 node types:
Node4,Node10,Node16,Node48,Node256(capacity-tiered) - Generic
Leaf<T>for values Partshelper for subject tokenization (split on.)MatchPartsfor wildcard matching (*single,>multi)- Node interface:
AddChild,FindChild,DeleteChild,IsFull,Grow,Shrink,Iter
Step 4: Run tests to verify they pass
Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~SubjectTreeTests" -v quiet
Expected: PASS (59 tests)
Step 5: Commit
git add src/NATS.Server/Internal/SubjectTree/ tests/NATS.Server.Tests/Internal/SubjectTree/
git commit -m "feat: port ART SubjectTree from Go with 59 tests"
Task 3: Generic Subject List (GSL)
Files:
- Create:
src/NATS.Server/Internal/Gsl/GenericSubjectList.cs - Create:
tests/NATS.Server.Tests/Internal/Gsl/GenericSubjectListTests.cs
Go reference: server/gsl/gsl.go + server/gsl/gsl_test.go (21 test functions)
Public API to port:
namespace NATS.Server.Internal.Gsl;
public class GenericSubjectList<T> where T : IEquatable<T>
{
public void Insert(string subject, T value);
public void Remove(string subject, T value);
public void Match(string subject, Action<T> callback);
public void MatchBytes(ReadOnlySpan<byte> subject, Action<T> callback);
public bool HasInterest(string subject);
public int NumInterest(string subject);
public bool HasInterestStartingIn(string subject);
public uint Count { get; }
}
public class SimpleSubjectList : GenericSubjectList<int> { }
Step 1: Write failing tests
Port all 21 Go test functions:
- Init/count:
TestGenericSublistInit,TestGenericSublistInsertCount - Matching:
TestGenericSublistSimplethroughTestGenericSublistFullWildcard(5 tests) - Remove:
TestGenericSublistRemovethroughTestGenericSublistRemoveCleanupWildcards(4 tests) - Invalid subjects:
TestGenericSublistInvalidSubjectsInsert,TestGenericSublistBadSubjectOnRemove - Edge cases:
TestGenericSublistTwoTokenPubMatchSingleTokenSubthroughTestGenericSublistMatchWithEmptyTokens(3 tests) - Interest:
TestGenericSublistHasInterestthroughTestGenericSublistNumInterest(4 tests)
Step 2: Run tests to verify they fail
Step 3: Implement GenericSubjectList
Trie-based subject matcher with locking (ReaderWriterLockSlim):
- Internal
Node<T>withPsubs(plain),Qsubs(queue),Childrenlevel map *and>wildcard child pointers- Thread-safe via
ReaderWriterLockSlim
Step 4: Run tests to verify they pass
Expected: PASS (21 tests)
Step 5: Commit
git add src/NATS.Server/Internal/Gsl/ tests/NATS.Server.Tests/Internal/Gsl/
git commit -m "feat: port GenericSubjectList from Go with 21 tests"
Task 4: Time Hash Wheel
Files:
- Create:
src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs - Create:
tests/NATS.Server.Tests/Internal/TimeHashWheel/HashWheelTests.cs
Go reference: server/thw/thw.go + server/thw/thw_test.go (8 test functions)
Public API to port:
namespace NATS.Server.Internal.TimeHashWheel;
public class HashWheel
{
public void Add(ulong seq, long expires);
public bool Remove(ulong seq, long expires);
public void Update(ulong seq, long oldExpires, long newExpires);
public void ExpireTasks(Func<ulong, long, bool> callback);
public long GetNextExpiration(long before);
public ulong Count { get; }
public byte[] Encode(ulong highSeq);
public (ulong HighSeq, int BytesRead) Decode(ReadOnlySpan<byte> buf);
}
Step 1: Write failing tests
Port all 8 Go test functions:
TestHashWheelBasics→Basics_AddRemoveCountTestHashWheelUpdate→Update_ChangesExpirationTestHashWheelExpiration→Expiration_FiresCallbackForExpiredTestHashWheelManualExpiration→ManualExpiration_SpecificTimeTestHashWheelExpirationLargerThanWheel→LargerThanWheel_HandlesWrapAroundTestHashWheelNextExpiration→NextExpiration_FindsEarliestTestHashWheelStress→Stress_ConcurrentAddRemoveTestHashWheelEncodeDecode→EncodeDecode_RoundTrips
Step 2: Run tests to verify they fail
Step 3: Implement HashWheel
Fixed-size array of slots. Each slot is a linked list of (seq, expires) entries:
- Slot index =
(expires / tickResolution) % wheelSize ExpireTasksscans all slots whose time has passed- Encode/Decode for persistence (used by FileStore TTL state)
Step 4: Run tests to verify they pass
Expected: PASS (8 tests)
Step 5: Commit
git add src/NATS.Server/Internal/TimeHashWheel/ tests/NATS.Server.Tests/Internal/TimeHashWheel/
git commit -m "feat: port TimeHashWheel from Go with 8 tests"
Task 5: StreamStore / ConsumerStore Interfaces
Files:
- Create:
src/NATS.Server/JetStream/Storage/IStreamStore.cs - Create:
src/NATS.Server/JetStream/Storage/IConsumerStore.cs - Create:
src/NATS.Server/JetStream/Storage/StoreMsg.cs - Create:
src/NATS.Server/JetStream/Storage/StreamState.cs - Create:
src/NATS.Server/JetStream/Storage/ConsumerState.cs - Create:
src/NATS.Server/JetStream/Storage/FileStoreConfig.cs
Go reference: server/store.go (interfaces and types)
Step 1: Define interfaces and value types
Port StreamStore, ConsumerStore interfaces and all supporting types (StoreMsg, StreamState, SimpleState, ConsumerState, FileStoreConfig, StoreCipher, StoreCompression).
Step 2: Verify build
Run: dotnet build NatsDotNet.slnx
Expected: Build succeeded
Step 3: Commit
git add src/NATS.Server/JetStream/Storage/
git commit -m "feat: define StreamStore and ConsumerStore interfaces from Go store.go"
Task 6: FileStore Block Engine — Core
Files:
- Create:
src/NATS.Server/JetStream/Storage/FileStore/FileStore.cs - Create:
src/NATS.Server/JetStream/Storage/FileStore/MessageBlock.cs - Create:
src/NATS.Server/JetStream/Storage/FileStore/BlockCache.cs - Create:
tests/NATS.Server.Tests/JetStream/Storage/FileStoreTests.cs
Go reference: server/filestore.go + server/filestore_test.go (200 test functions)
This is the largest single task. Split into sub-tasks:
Task 6a: Basic CRUD (store/load/remove)
Port: TestFileStoreBasics, TestFileStoreMsgHeaders, TestFileStoreBasicWriteMsgsAndRestore, TestFileStoreWriteAndReadSameBlock, TestFileStoreAndRetrieveMultiBlock
Tests: ~15
Task 6b: Limits Enforcement
Port: TestFileStoreMsgLimit, TestFileStoreBytesLimit, TestFileStoreAgeLimit, TestFileStoreMaxMsgsPerSubject and variants
Tests: ~20
Task 6c: Purge / Compact / Truncate
Port: TestFileStorePurge, TestFileStoreCompact, TestFileStoreSparseCompaction, TestFileStorePurgeExWithSubject, TestFileStoreStreamTruncate and variants
Tests: ~25
Task 6d: Recovery
Port: TestFileStoreAgeLimitRecovery, TestFileStoreRemovePartialRecovery, TestFileStoreFullStateBasics, TestFileStoreRecoverWithRemovesAndNoIndexDB and variants
Tests: ~20
Task 6e: Subject Filtering
Port: TestFileStoreSubjectsTotals, TestFileStoreMultiLastSeqs, TestFileStoreLoadLastWildcard, TestFileStoreNumPendingMulti and variants
Tests: ~15
Task 6f: Encryption
Port: TestFileStoreEncrypted, TestFileStoreRestoreEncryptedWithNoKeyFuncFails, TestFileStoreDoubleCompactWithWriteInBetweenEncryptedBug and variants
Tests: ~10
Task 6g: Compression and TTL
Port: TestFileStoreMessageTTL, TestFileStoreMessageTTLRestart, TestFileStoreMessageSchedule, TestFileStoreCompressionAfterTruncate and variants
Tests: ~15
Task 6h: Skip Messages and Consumer Store
Port: TestFileStoreSkipMsg, TestFileStoreSkipMsgs, TestFileStoreConsumer, TestFileStoreConsumerEncodeDecodeRedelivered
Tests: ~15
Task 6i: Corruption and Edge Cases
Port: TestFileStoreBitRot, TestFileStoreSubjectCorruption, TestFileStoreWriteFullStateDetectCorruptState and remaining edge case tests
Tests: ~15
Task 6j: Performance Tests
Port: TestFileStorePerf, TestFileStoreCompactPerf, TestFileStoreFetchPerf, TestFileStoreReadBackMsgPerf
Tests: ~10
Total FileStore tests: ~160 (of 200 Go tests; remaining 40 are deep internal tests that depend on Go-specific internals)
Commit after each sub-task passes:
git commit -m "feat: FileStore [sub-task] with N tests"
Task 7: RAFT Consensus — Core Types
Files:
- Create:
src/NATS.Server/Raft/IRaftNode.cs - Create:
src/NATS.Server/Raft/RaftState.cs - Create:
src/NATS.Server/Raft/RaftEntry.cs - Create:
src/NATS.Server/Raft/RaftMessages.cs(VoteRequest, VoteResponse, AppendEntry, AppendEntryResponse) - Create:
src/NATS.Server/Raft/Peer.cs - Create:
src/NATS.Server/Raft/CommittedEntry.cs
Go reference: server/raft.go lines 1-200 (types and interfaces)
Step 1: Define types
namespace NATS.Server.Raft;
public enum RaftState : byte
{
Follower = 0,
Leader = 1, // Note: Go ordering — Leader before Candidate
Candidate = 2,
Closed = 3
}
public enum EntryType : byte
{
Normal = 0,
OldSnapshot = 1,
PeerState = 2,
AddPeer = 3,
RemovePeer = 4,
LeaderTransfer = 5,
Snapshot = 6,
Catchup = 7 // internal only
}
public record struct Peer(string Id, bool Current, DateTime Last, ulong Lag);
Step 2: Verify build
Step 3: Commit
git add src/NATS.Server/Raft/
git commit -m "feat: define RAFT core types from Go raft.go"
Task 8: RAFT Consensus — Wire Format
Files:
- Create:
src/NATS.Server/Raft/RaftWireFormat.cs - Create:
tests/NATS.Server.Tests/Raft/RaftWireFormatTests.cs
Go reference: server/raft.go encoding/decoding sections, raft_test.go:
TestNRGAppendEntryEncode,TestNRGAppendEntryDecode,TestNRGVoteResponseEncoding
Step 1: Write failing tests for encode/decode
Port the 3 wire format tests plus additional round-trip tests (~10 tests total).
Wire format details:
- All little-endian binary (
BinaryPrimitives) - Node IDs: exactly 8 bytes
- VoteRequest: 32 bytes (3 × uint64 + 8-byte candidateId)
- VoteResponse: 17 bytes (uint64 term + 8-byte peerId + 1 byte flags)
- AppendEntry: 42-byte header + entries
- AppendEntryResponse: 25 bytes
Step 2: Implement wire format encode/decode
Step 3: Run tests, verify pass
Step 4: Commit
git commit -m "feat: RAFT wire format encode/decode with 10 tests"
Task 9: RAFT Consensus — Election
Files:
- Create:
src/NATS.Server/Raft/RaftNode.cs(state machine core) - Create:
src/NATS.Server/Raft/IRaftTransport.cs - Create:
src/NATS.Server/Raft/InProcessTransport.cs - Create:
tests/NATS.Server.Tests/Raft/RaftElectionTests.cs
Go reference: raft_test.go election tests (~25 functions)
Port tests:
TestNRGSimpleElection→ single/3/5 node electionsTestNRGSingleNodeElection→ single node auto-leaderTestNRGLeaderTransfer→ leadership transferTestNRGInlineStepdown→ step-downTestNRGObserverMode→ observer doesn't voteTestNRGCandidateDoesntRevertTermAfterOldAETestNRGAssumeHighTermAfterCandidateIsolationTestNRGHeartbeatOnLeaderChangeTestNRGElectionTimerAfterObserverTestNRGUnsuccessfulVoteRequestDoesntResetElectionTimerTestNRGStepDownOnSameTermDoesntClearVoteTestNRGMustNotResetVoteOnStepDownOrLeaderTransfer- And more...
Tests: ~25
Commit:
git commit -m "feat: RAFT election with 25 tests"
Task 10: RAFT Consensus — Log Replication
Files:
- Modify:
src/NATS.Server/Raft/RaftNode.cs - Create:
tests/NATS.Server.Tests/Raft/RaftLogReplicationTests.cs
Go reference: raft_test.go log replication and catchup tests (~30 functions)
Port tests:
TestNRGSimple→ basic propose and commitTestNRGAEFromOldLeader→ reject old leader entriesTestNRGWALEntryWithoutQuorumMustTruncateTestNRGCatchupDoesNotTruncateUncommittedEntriesWithQuorumTestNRGCatchupCanTruncateMultipleEntriesWithoutQuorumTestNRGSimpleCatchup→ follower catches upTestNRGChainOfBlocksRunInLockstepTestNRGChainOfBlocksStopAndCatchUpTestNRGAppendEntryCanEstablishQuorumAfterLeaderChangeTestNRGQuorumAccounting- And more...
Tests: ~30
Commit:
git commit -m "feat: RAFT log replication with 30 tests"
Task 11: RAFT Consensus — Snapshots and Membership
Files:
- Modify:
src/NATS.Server/Raft/RaftNode.cs - Create:
tests/NATS.Server.Tests/Raft/RaftSnapshotTests.cs - Create:
tests/NATS.Server.Tests/Raft/RaftMembershipTests.cs
Go reference: raft_test.go snapshot + membership tests (~35 functions)
Port tests:
- Snapshots:
TestNRGSnapshotAndRestart,TestNRGSnapshotCatchup,TestNRGSnapshotRecovery,TestNRGDontRemoveSnapshotIfTruncateToApplied,TestNRGInstallSnapshotFromCheckpoint,TestNRGInstallSnapshotForce, etc. - Membership:
TestNRGProposeRemovePeer,TestNRGProposeRemovePeerConcurrent,TestNRGAddPeers,TestNRGDisjointMajorities,TestNRGLeaderResurrectsRemovedPeers, etc.
Tests: ~35
Commit:
git commit -m "feat: RAFT snapshots and membership with 35 tests"
Task 12: JetStream Clustering — Meta Controller
Files:
- Create:
src/NATS.Server/JetStream/Cluster/JetStreamCluster.cs - Create:
src/NATS.Server/JetStream/Cluster/MetaController.cs - Create:
tests/NATS.Server.Tests/JetStream/Cluster/MetaControllerTests.cs
Go reference: server/jetstream_cluster.go, server/jetstream_cluster_1_test.go
Port: Stream/consumer placement, $JS.API.* routing through meta leader, cluster formation.
Tests: ~30
Task 13: JetStream Clustering — Per-Stream/Consumer RAFT
Files:
- Create:
src/NATS.Server/JetStream/Cluster/StreamRaftGroup.cs - Create:
src/NATS.Server/JetStream/Cluster/ConsumerRaftGroup.cs - Create:
tests/NATS.Server.Tests/JetStream/Cluster/StreamRaftGroupTests.cs - Create:
tests/NATS.Server.Tests/JetStream/Cluster/ConsumerRaftGroupTests.cs
Go reference: server/stream.go (raft field), server/consumer.go (raft field), jetstream_cluster_*_test.go
Port: Per-stream/consumer RAFT groups, leader/follower replication, failover.
Tests: ~40
Task 14: NORACE Concurrency Suite
Files:
- Create:
tests/NATS.Server.Tests/Concurrency/ConcurrencyTests.cs
Go reference: server/norace_1_test.go (100), server/norace_2_test.go (41)
Port a representative subset of Go's -race tests using Task.WhenAll patterns:
- Concurrent publish/subscribe on same stream
- Concurrent consumer creates/deletes
- Concurrent stream purge during publish
- Concurrent RAFT proposals
Tests: ~30 (representative subset of 141 Go tests; full NORACE suite is deeply coupled to Go runtime internals)
Task 15: Config Reload Tests
Files:
- Modify:
tests/NATS.Server.Tests/Configuration/ConfigReloadParityTests.cs
Go reference: server/reload_test.go (73 test functions)
Port remaining ~70 config reload tests (3 already exist). Key areas:
- Max connections, payload, subscriptions reload
- Auth changes (user/pass, token, NKey)
- TLS reload
- Cluster config reload
- JetStream config reload
- Preserve existing connections during reload
Tests: ~70
Commit:
git commit -m "feat: port 70 config reload tests from Go"
Task 16: MQTT Bridge Tests
Files:
- Modify:
tests/NATS.Server.Tests/Mqtt/(existing files) - Create: additional MQTT test files as needed
Go reference: server/mqtt_test.go (123 test functions)
Port remaining ~73 MQTT tests (50 already exist). Key areas:
- Topic mapping (MQTT topics → NATS subjects)
- Retained messages
- Will messages
- MQTT-over-WebSocket
- QoS 2 semantics (if supported)
- MQTT 5.0 properties
Tests: ~73
Task 17: Leaf Node Tests
Files:
- Modify:
tests/NATS.Server.Tests/LeafNodes/ - Create: additional leaf node test files
Go reference: server/leafnode_test.go (110 test functions)
Port remaining ~108 leaf node tests (2 exist). Key areas:
- Hub-spoke forwarding
- Subject filter propagation
- Loop detection (
$LDS.prefix) - Auth on leaf connections
- Reconnection
- JetStream over leaf nodes
Tests: ~108
Task 18: Accounts/Auth Tests
Files:
- Modify:
tests/NATS.Server.Tests/Accounts/
Go reference: server/accounts_test.go (64 test functions)
Port remaining ~49 account tests (15 exist). Key areas:
- Export/import between accounts
- Service latency tracking
- Account limits (connections, payload, subscriptions)
- System account operations
- Account revocations
Tests: ~49
Task 19: Gateway Tests
Files:
- Modify:
tests/NATS.Server.Tests/Gateways/
Go reference: server/gateway_test.go (88 test functions)
Port remaining ~86 gateway tests (2 exist). Key areas:
- Interest-only mode optimization
- Reply subject mapping (
_GR_.prefix) - Gateway reconnection
- Cross-cluster pub/sub
- Gateway auth
Tests: ~86
Task 20: Route Tests
Files:
- Modify:
tests/NATS.Server.Tests/Routes/
Go reference: server/routes_test.go (70 test functions)
Port remaining ~68 route tests (2 exist). Key areas:
- Route pooling (default 3 connections per peer)
- Account-specific dedicated routes
RS+/RS-subscribe propagationRMSGrouted messages- Route reconnection
- Cluster gossip
Tests: ~68
Task 21: Monitoring Tests
Files:
- Modify:
tests/NATS.Server.Tests/Monitoring/
Go reference: server/monitor_test.go (100 test functions)
Port remaining ~93 monitoring tests (7 exist). Key areas:
/varz— server info, memory, connections, messages/connz— connection listing, sorting, filtering/routez— route information/gatewayz— gateway information/subsz— subscription statistics/jsz— JetStream statistics/healthz— health check/accountz— account information
Tests: ~93
Task 22: Client Protocol Tests
Files:
- Modify:
tests/NATS.Server.Tests/ClientTests.csand related files
Go reference: server/client_test.go (82 test functions)
Port remaining ~52 client protocol tests (30 exist). Key areas:
- Max payload enforcement
- Slow consumer detection and eviction
- Permission violations
- Connection info parsing
- Buffer management
- Verbose mode
- Pedantic mode
Tests: ~52
Task 23: JetStream API Tests
Files:
- Modify:
tests/NATS.Server.Tests/JetStream/(multiple files)
Go reference: server/jetstream_test.go (312 test functions)
Port remaining ~292 JetStream API tests (20 exist). Key areas:
- Stream CRUD lifecycle
- Consumer CRUD lifecycle
- Publish acknowledgment and dedup
- Consumer delivery semantics (push, pull, deliver policies)
- Retention policies (limits, interest, work queue)
- Mirror and source streams
- Subject transforms
- Direct get API
- Stream purge variants
- Consumer pause/resume
Tests: ~292 (split across multiple test files by area)
Task 24: JetStream Cluster Tests
Files:
- Create:
tests/NATS.Server.Tests/JetStream/Cluster/(multiple files)
Go reference: server/jetstream_cluster_1_test.go (151), _2_test.go (123), _3_test.go (97), _4_test.go (85), _long_test.go (7) — total 463
Port a representative subset (~100 tests). Many of these require full RAFT + clustering (Waves 4-5). Key areas:
- Clustered stream create/delete
- Leader election and step-down
- Consumer failover
- R1/R3 replication
- Cross-cluster JetStream
- Snapshot/restore in cluster
Tests: ~100 (of 463; remaining require deep cluster integration)
Wave Gate Verification
After each wave completes, run the full test suite:
dotnet test NatsDotNet.slnx --nologo -v quiet
Verify: 0 failures, test count increased by expected amount.
Summary
| Task | Wave | Description | Tests |
|---|---|---|---|
| 0 | 1 | Scaffolding | 0 |
| 1 | 2 | AVL SequenceSet | 16 |
| 2 | 2 | Subject Tree (ART) | 59 |
| 3 | 2 | Generic Subject List | 21 |
| 4 | 2 | Time Hash Wheel | 8 |
| 5 | 3 | StreamStore interfaces | 0 |
| 6 | 3 | FileStore block engine | 160 |
| 7 | 4 | RAFT core types | 0 |
| 8 | 4 | RAFT wire format | 10 |
| 9 | 4 | RAFT election | 25 |
| 10 | 4 | RAFT log replication | 30 |
| 11 | 4 | RAFT snapshots + membership | 35 |
| 12 | 5 | JetStream meta controller | 30 |
| 13 | 5 | JetStream per-stream/consumer RAFT | 40 |
| 14 | 5 | NORACE concurrency | 30 |
| 15 | 6 | Config reload | 70 |
| 16 | 6 | MQTT bridge | 73 |
| 17 | 6 | Leaf nodes | 108 |
| 18 | 6 | Accounts/auth | 49 |
| 19 | 6 | Gateway | 86 |
| 20 | 6 | Routes | 68 |
| 21 | 6 | Monitoring | 93 |
| 22 | 6 | Client protocol | 52 |
| 23 | 6 | JetStream API | 292 |
| 24 | 6 | JetStream cluster | 100 |
| Total | ~1,415 |
Expected final test count: 1,081 + 1,415 = ~2,496 tests