Files
natsdotnet/docs/plans/2026-03-12-test-project-split-design.md
Joseph Doherty 6941d9275b docs: add design doc for splitting NATS.Server.Tests into feature-focused projects
Splits the 609-file monolithic test project into 10 subsystem test projects
plus a shared TestUtilities library for developer ergonomics.
2026-03-12 14:23:06 -04:00

9.2 KiB

Test Project Split Design

Date: 2026-03-12 Goal: Split NATS.Server.Tests (609 files) into feature-focused test projects for developer ergonomics — easier to run just the tests for the subsystem you're working on.

Project Structure

tests/
  NATS.Server.TestUtilities/           # Shared helpers, fixtures, parity tools (class library)
  NATS.Server.Core.Tests/              # Client, server, parser, config, subscriptions, protocol
  NATS.Server.Auth.Tests/              # Auth, accounts, permissions, JWT, NKeys
  NATS.Server.JetStream.Tests/         # JetStream API, streams, consumers, storage, cluster
  NATS.Server.Raft.Tests/              # RAFT consensus
  NATS.Server.Clustering.Tests/        # Routes, cluster topology, inter-server protocol
  NATS.Server.Gateways.Tests/          # Gateway connections, interest modes
  NATS.Server.LeafNodes.Tests/         # Leaf node connections, hub-spoke
  NATS.Server.Mqtt.Tests/              # MQTT protocol bridge
  NATS.Server.Monitoring.Tests/        # Monitor endpoints, events, system events
  NATS.Server.Transport.Tests/         # WebSocket, TLS, OCSP, IO
  NATS.E2E.Tests/                      # (existing, unchanged)

TestUtilities Contents

NATS.Server.TestUtilities is a class library (not a test project).

Shared helpers (deduplicated)

  • TestPortAllocatorGetFreePort() (currently duplicated in ~51 files)
  • SocketTestHelperReadUntilAsync(), raw socket connect/read patterns (~25 files)
  • ServerTestHelper — common server startup/teardown patterns

Shared fixtures

  • JetStreamApiFixture — moved from root (used by 52 JetStream test files)
  • JetStreamClusterFixture — consolidated from 2 duplicate definitions
  • LeafFixture — consolidated from 3 duplicate definitions

Parity utilities (non-test)

  • NatsCapabilityInventory.cs
  • ParityRowInspector.cs
  • JetStreamParityTruthMatrix.cs

TestData

  • TestData/*.conf files (copied to output directory)

File-to-Project Mapping

NATS.Server.Core.Tests (~75 files)

Root-level: ClientClosedReasonTests, ClientFlagsTests, ClientHeaderTests, ClientKindCommandMatrixTests, ClientKindProtocolRoutingTests, ClientKindTests, ClientLifecycleTests, ClientProtocolParityTests, ClientPubSubTests, ClientServerGoParityTests, ClientSlowConsumerTests, ClientTests, ClientTraceModeTests, ClientTraceTests, ClientUnsubTests, ConfigIntegrationTests, ConfigProcessorTests, ConfigReloadTests, ConfigRuntimeParityTests, FlushCoalescingTests, IntegrationTests, InternalClientTests, LoggingTests, MessageTraceTests, MsgTraceGoParityTests, NatsConfLexerTests, NatsConfParserTests, NatsHeaderParserTests, NatsOptionsTests, NoRespondersTests, ParserTests, ResponseRoutingTests, ResponseTrackerTests, RttTests, ServerConfigTests, ServerStatsTests, ServerTests, SignalHandlerTests, SlopwatchSuppressAttribute, SlowConsumerStallGateTests, StallGateTests, SubjectMatchTests, SubjectTransformIntegrationTests, SubjectTransformTests, SubListTests, VerboseModeTests, WriteLoopTests, WriteTimeoutTests, ConcurrencyStressTests

Subfolders: Configuration/ (14), Internal/ (8), IO/ (4), Protocol/ (7), Server/ (7), SubList/ (6), Subscriptions/ (6), Stress/ (3)

Parity test files (from Parity/ folder): NatsStrictCapabilityInventoryTests, JetStreamParityTruthMatrixTests, GoParityRunnerTests, InfrastructureGoParityTests, DifferencesParityClosureTests

NATS.Server.Auth.Tests (~50 files)

Root-level: AccountIsolationTests, AccountResolverTests, AccountStatsTests, AccountTests, AuthConfigTests, AuthIntegrationTests, AuthProtocolTests, AuthServiceTests, ClientPermissionsTests, JwtAuthenticatorTests, JwtTests, NKeyAuthenticatorTests, NKeyIntegrationTests, PermissionIntegrationTests, PermissionLruCacheTests, PermissionTemplateTests, SimpleUserPasswordAuthenticatorTests, TokenAuthenticatorTests, UserPasswordAuthenticatorTests, ImportExportTests

Subfolders: Auth/ (25), Accounts/ (5)

NATS.Server.JetStream.Tests (~220 files)

Root-level: All JetStream* files at root (~55), plus FileStoreTests, FileStoreEncryptionTests, MemStoreTests, StreamStoreContractTests, MirrorSourceRetryTests, ClusterJetStreamConfigProcessorTests

Subfolders: JetStream/ and all sub-folders (163 files)

NATS.Server.Raft.Tests (~45 files)

Root-level: RaftConsensusAdvancedParityTests, RaftElectionTests, RaftMembershipParityTests, RaftReplicationTests, RaftSafetyContractTests, RaftSnapshotCatchupTests, RaftSnapshotTransferParityTests, RaftTransportPersistenceTests

Subfolders: Raft/ (36)

NATS.Server.Clustering.Tests (~30 files)

Root-level: RouteHandshakeTests, RoutePoolTests, RouteRmsgForwardingTests, RouteSubscriptionPropagationTests, RouteWireSubscriptionProtocolTests, ImplicitDiscoveryTests, InterServerAccountProtocolTests

Subfolders: Routes/ (21), Route/ (1)

NATS.Server.Gateways.Tests (~25 files)

Root-level: GatewayAdvancedRemapRuntimeTests, GatewayAdvancedSemanticsTests, GatewayLeafBootstrapTests, GatewayProtocolTests

Subfolders: Gateways/ (21)

NATS.Server.LeafNodes.Tests (~30 files)

Root-level: LeafAdvancedSemanticsTests, LeafProtocolTests

Subfolders: LeafNodes/ (26), LeafNode/ (1)

NATS.Server.Mqtt.Tests (~30 files)

Root-level: MqttPersistenceTests

Subfolders: Mqtt/ (28)

NATS.Server.Monitoring.Tests (~35 files)

Root-level: EventSystemTests, JszMonitorTests, MonitorClusterEndpointTests, MonitorModelTests, MonitorTests, SubszTests, SystemEventsTests, SystemRequestReplyTests

Subfolders: Monitoring/ (21), Events/ (10)

NATS.Server.Transport.Tests (~25 files)

Root-level: OcspConfigTests, OcspStaplingTests, TlsConnectionWrapperTests, TlsHelperTests, TlsMapAuthenticatorTests, TlsOcspParityBatch1Tests, TlsOcspParityBatch2Tests, TlsRateLimiterTests, TlsServerTests

Subfolders: WebSocket/ (15), Networking/ (1)

Project File Template

Each test project follows the same base pattern:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="coverlet.collector" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" />
    <PackageReference Include="NSubstitute" />
    <PackageReference Include="Shouldly" />
    <PackageReference Include="xunit" />
    <PackageReference Include="xunit.runner.visualstudio" />
  </ItemGroup>

  <ItemGroup>
    <Using Include="Xunit" />
    <Using Include="Shouldly" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\src\NATS.Server\NATS.Server.csproj" />
    <ProjectReference Include="..\NATS.Server.TestUtilities\NATS.Server.TestUtilities.csproj" />
  </ItemGroup>
</Project>

Project-specific package additions:

Project Extra packages
Auth.Tests NATS.NKeys
JetStream.Tests NATS.Client.Core, JETSTREAM_INTEGRATION_MATRIX define constant
Transport.Tests Serilog.Sinks.File (if TLS tests use it)
Core.Tests NATS.Client.Core, Serilog.Sinks.File
Monitoring.Tests NATS.Client.Core

TestUtilities is a plain class library:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="NATS.Client.Core" />
    <PackageReference Include="Shouldly" />
    <PackageReference Include="xunit" />  <!-- for IAsyncLifetime fixtures -->
  </ItemGroup>

  <ItemGroup>
    <None Update="TestData\**\*" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\src\NATS.Server\NATS.Server.csproj" />
  </ItemGroup>
</Project>

Migration Strategy

Phase 1: Create TestUtilities

  • Create NATS.Server.TestUtilities project
  • Extract GetFreePort(), ReadUntilAsync() into shared helper classes
  • Move JetStreamApiFixture, consolidated JetStreamClusterFixture, consolidated LeafFixture
  • Move parity utility files (non-test) and TestData
  • Update the original NATS.Server.Tests to reference TestUtilities
  • Verify build + all tests pass

Phase 2: Split projects one at a time (smallest first)

  1. Transport.Tests (~25 files)
  2. Mqtt.Tests (~30 files)
  3. Gateways.Tests (~25 files)
  4. LeafNodes.Tests (~30 files)
  5. Clustering.Tests (~30 files)
  6. Raft.Tests (~45 files)
  7. Monitoring.Tests (~35 files)
  8. Auth.Tests (~50 files)
  9. JetStream.Tests (~220 files)
  10. Core.Tests (rename remaining original project)

Each step:

  • Create the new .csproj
  • Move files with git mv to preserve history
  • Update namespaces to match new project name
  • Add to solution file
  • Remove files from old project
  • Build + test

Phase 3: Cleanup

  • Delete the original NATS.Server.Tests project (now empty)
  • Verify dotnet test from solution root runs all projects
  • Verify CI still works

Decisions

  • Namespaces updated to match new project names (e.g., NATS.Server.Auth.Tests)
  • Root-level files sorted into matching subsystem projects by prefix/topic
  • Storage files (FileStore, MemStore, StreamStore) → JetStream project
  • ImportExportTests → Auth project
  • InternalClientTests → Core project
  • Parity test files → Core.Tests; parity utility classes → TestUtilities
  • Stress test files → Core.Tests (only 3-4 files, not worth a separate project)
  • Trace files → Core.Tests (tracing is a core feature)