Files
natsdotnet/docs/plans/2026-02-23-go-dotnet-test-parity-design.md
Joseph Doherty f6fab376ad docs: add go-to-dotnet systematic test parity design
Systematic mapping of 3,451 Go test functions against .NET port
reveals ~18% coverage. Design defines 4-phase hybrid dependency-first
approach: Foundation (client/routing/sublist) -> Distributed substrate
(RAFT/storage) -> JetStream depth -> Protocol surfaces (MQTT/JWT).
2026-02-23 17:24:57 -05:00

11 KiB

Go-to-.NET Systematic Test Parity Design

Date: 2026-02-23 Status: Approved Scope: Systematically map every Go NATS server test function to .NET equivalents, fill all gaps to achieve behavioral test parity.

1. Current State

The .NET port has all source code implemented (~170 source files, 236 test files, 867 passing tests). However, the Go reference has 3,451+ test functions across 88 test files. Systematic mapping reveals ~18% coverage of Go test intents.

Coverage Matrix

Subsystem Go Tests .NET Tests Coverage Phase
Parser 17 8 47% A
SubList 62 22 35% A
Client 82 3 4% A
Server 44 17 39% A
Routes 70 12 17% A
Gateways 88 9 10% A
Leaf Nodes 110 8 7% A
Auth 12 10 83% A
Accounts 64 18 28% A
JetStream Core 472 82 17% C
JetStream Clustering 503 4 1% C
Storage 269 2 1% B
Config/Reload 159 77 48% B
Monitoring/Events 184 28 15% B
RAFT 104 15 14% B
MQTT 123 8 7% D
WebSocket 61 66 108% -
JWT 88 61 69% D
TOTAL ~2,512 ~454 ~18%

2. Architecture and Approach

Hybrid Dependency-First Phasing

Execute in 4 gated phases ordered by dependency criticality:

  1. Phase A (Foundation): Client, Parser, SubList, Server, Routes, Gateways, Leaf Nodes, Accounts (~440 tests)
  2. Phase B (Distributed Substrate): RAFT, Storage, Config/Reload, Monitoring/Events (~594 tests)
  3. Phase C (JetStream Depth): JetStream Core, JetStream Clustering (~889 tests)
  4. Phase D (Protocol Surfaces): MQTT, Auth/JWT hardening (~135 tests)

Test Philosophy

  • Behavioral equivalence, not 1:1 test count parity. One .NET test may cover multiple Go tests if it preserves the same contract and failure detection power.
  • Every Go test intent mapped. Maintain go-dotnet-test-mapping.md tracking every Go test function to .NET tests or explicit N/A rationale.
  • Primary KPI: Contract coverage by subsystem, not raw test count.
  • Test-first: Write failing test, then implement/fix, then verify pass.

Execution Model

  • Each phase is a gate — must pass full dotnet test before advancing.
  • Within phases, subsystems are independent and run in parallel via subagents.
  • Use model: "sonnet" for straightforward test porting, model: "opus" for complex protocol/concurrency tests.
  • One commit per subsystem batch.
  • JetStream sentinel tests run during Phase A as early-warning canaries.

3. Phase A: Foundation (~440 tests)

A1: Client Tests (79 missing)

Go file: client_test.go (82 tests) Current .NET: ClientTests.cs (3 tests)

Categories to fill:

  • Basic pub/sub (8): ClientSimplePubSub, PubSubNoEcho, PubWithQueueSub, PubSubWithReply
  • Header handling (12): HeaderDeliverMsg, HeaderStrippedMsg, SliceHeader, SetHeaderOrdering
  • UNSUB & auto-unsub (5): ClientUnSub, UnSubMax, AutoUnsubExactReceived
  • Slow consumer (5): NoClientLeakOnSlowConsumer, MaxPending, FlushOutbound
  • TLS connection (8): CloseTLSConnection, TLSHandshakeFirst, TLSFallbackDelay
  • Auth & permissions (6): AuthTimeout, ResponsePermissions, QueueSubscribePermissions
  • Client lifecycle (8): RemoveSubsOnDisconnect, MapRemoval, ConnectionName, Limits
  • Message tracing (5): TraceMsg, TraceMsgHeaders, TraceMsgDelivery
  • Protocol edge cases (10): ConnectProto, TypeString, SplitSubjectQueue, IPv6Address
  • Miscellaneous (12): PingNotSentTooSoon, GWReplyPrefix, ReadloopWarning

New test files:

  • ClientPubSubTests.cs — basic messaging
  • ClientHeaderTests.cs — HPUB/HMSG handling
  • ClientLifecycleTests.cs — connect/disconnect/cleanup
  • ClientSlowConsumerTests.cs — backpressure
  • ClientTlsTests.cs — TLS scenarios

A2: Parser Tests (9 missing)

Go file: parser_test.go (17 tests) Current .NET: ParserTests.cs (8 tests)

Missing: ParsePubSizeOverflow, ParsePubArg, ParsePubBadSize, ParseHeaderPubArg, ParseRoutedHeaderMsg, ParseRouteMsg, ParseMsgSpace, ShouldFail (parametrized negatives), MaxControlLine.

Add to existing ParserTests.cs.

A3: SubList Tests (40 missing)

Go file: sublist_test.go (62 tests) Current .NET: SubListTests.cs + SubjectMatchTests.cs (22 tests)

Categories:

  • No-cache variants (~16): Add as [Theory] data rows
  • Concurrency/race (3): RaceOnRemove, RaceOnInsert, RaceOnMatch — use Task.WhenAll
  • Validation (6): InvalidSubjectsInsert, ValidLiteralSubjects
  • Edge cases (8): LargeSubs, EmptyTokens, WildcardsAsLiterals
  • Remote subs (2): RemoteQueueSubscriptions
  • Utilities (5): SubjectCollide, SubjectToken, IsSubsetMatch

A4: Server Tests (27 missing)

Go file: server_test.go (44 tests) Current .NET: ServerTests.cs + related (17 tests)

Major gaps: TLS version/cipher config (5), advertise URLs (3), max subscriptions (1), lame duck info (1), startup/shutdown edge cases (6), config validation (5), misc (6).

A5: Routes (58 missing)

Go file: routes_test.go (70 tests) Current .NET: Route*Tests.cs (12 tests)

Major gaps: Route config/TLS (10), auth over routes (5), compression variants (15), per-account routing (5), pool management (5), slow consumer/reconnect (8), race conditions (5), misc (5).

A6: Gateways (79 missing)

Go file: gateway_test.go (88 tests) Current .NET: Gateway*Tests.cs (9 tests)

Major gaps: Basic gateway flow (5), auth/TLS (8), service imports/exports (10), queue handling (8), interest tracking (10), reply mapping (5), URL discovery (5), compression (5), error handling (10), misc (13).

A7: Leaf Nodes (102 missing)

Go file: leafnode_test.go (110 tests) Current .NET: Leaf*Tests.cs (8 tests)

Major gaps: Auth (10), TLS (8), loop detection (8), permissions (5), queue distribution (8), compression (5), WebSocket (15), import/export (10), topology (10), slow consumer (3), misc (20).

A8: Accounts (46 missing)

Go file: accounts_test.go (64 tests) Current .NET: Account*Tests.cs (18 tests)

Major gaps: Config parsing (8), import/export (12), subject mapping (6), connection limits (4), service exports (5), latency tracking (3), misc (8).

4. Phase B: Distributed Substrate (~594 tests)

B1: RAFT Consensus (89 missing)

Go file: raft_test.go (104 tests) Current .NET: Raft*Tests.cs (15 tests)

Categories: Election mechanics (15), log replication (20), snapshot (15), membership changes (10), partition handling (10), edge cases (19).

Write .NET-native scenario tests — RAFT timing behavior differs between goroutines and async/await. Focus on state-machine correctness invariants.

B2: Storage (267 missing)

Go files: filestore_test.go (232), memstore_test.go (37) Current .NET: 2 tests

Build a storage contract suite + fault-injection harness:

  • Contract tests: append/read/delete, compaction, retention/TTL, index rebuild, checksums, snapshot/recovery
  • Failure tests: partial writes, fsync failures, restart during compaction, torn metadata, concurrent access
  • Run same contracts against both MemStore and FileStore via IStreamStore
  • Focus on externally visible durability/invariants

Categories: FileStore basics (40), block management (30), retention/limits (25), recovery (35), compression (15), encryption (15), subject indexing (20), MemStore (35), snapshot/restore (15), failure injection (20), performance (17).

B3: Config & Reload (82 missing)

Go files: opts_test.go (86), reload_test.go (73) Current .NET: Config*Tests.cs (77 tests)

Categories: CLI arg parsing (10), config file features (15), subsystem config (15), TLS config (10), auth config (8), hot reload (15), validation (9).

B4: Monitoring & Events (156 missing)

Go files: monitor_test.go (100), events_test.go (51), msgtrace_test.go (33) Current .NET: MonitorTests.cs + EventTests.cs (28 tests)

Categories: /varz (20), /connz (25), /routez+/gatewayz+/leafz (20), /jsz (15), /healthz (5), system events (30), message tracing (25), signal handling (10), rate-limit logging (6).

5. Phase C: JetStream Depth (~889 tests)

C1: JetStream Core (390 missing)

Go files: jetstream_test.go (312), jetstream_consumer_test.go (160) Current .NET: JetStream*Tests.cs (82 tests)

Categories: Stream lifecycle (40), publish semantics (30), consumer delivery (50), ack policies (25), retention policies (20), flow control (15), ordered consumers (10), subject transforms (15), mirror/source (25), API endpoints (30), account/permission (20), error handling (20), batching (15), direct API (10), misc (65).

C2: JetStream Clustering (499 missing)

Go files: jetstream_cluster_[1-4]_test.go (456), jetstream_super_cluster_test.go (47) Current .NET: 4 tests

Write .NET-native cluster tests using in-process multi-server fixtures. Don't attempt 1:1 Go test translation — cover same behavioral contracts.

Categories: Cluster formation (30), multi-replica streams (50), consumer replication (40), leader failover (30), snapshot/catchup (25), super-cluster (45), mirror/source in cluster (30), account isolation (20), stream recovery (30), consumer edge cases (30), scaling (20), config reload (15), integration (134).

6. Phase D: Protocol Surfaces (~135 tests)

D1: MQTT (115 missing)

Go file: mqtt_test.go (123 tests) Current .NET: Mqtt*Tests.cs (8 tests)

Categories: Packet parsing (15), QoS handling (20), session management (15), auth/TLS (10), keep-alive (5), topic handling (15), will messages (5), retain (5), integration (25).

D2: Auth/JWT Hardening (~20 missing)

JWT at 69% — fill claim validation edge cases, token rotation, revocation.

7. Completion Gates

Per-Phase Gates

  • Phase A: dotnet test passes. Client pub/sub, routing, gateway, leaf node basics verified.
  • Phase B: RAFT election/append/snapshot pass. FileStore append/read/recovery pass. Monitor endpoints return valid JSON.
  • Phase C: All JetStream API endpoints work. Stream/consumer lifecycle correct. Cluster failover preserves data.
  • Phase D: MQTT pub/sub works. JWT full claim validation passes.

Final Gate

  • Every Go test intent mapped in go-dotnet-test-mapping.md
  • dotnet test passes with zero failures
  • No subsystem below 60% behavioral coverage
  • differences.md synchronized with verified behavior

8. Error Handling and Testing Strategy

  • Preserve protocol-specific error semantics (NATS, MQTT, JetStream)
  • Fail closed on account/auth/consensus violations
  • Test-first for every capability
  • Happy-path + edge/failure coverage per capability
  • Integration tests for inter-server, RAFT/cluster, and restart durability
  • Unit tests for deterministic parser/state primitives
  • Use Shouldly assertions, xUnit 3, NSubstitute — never Assert.*, FluentAssertions, or Moq