- Fix pull consumer fetch: send original stream subject in HMSG (not inbox) so NATS client distinguishes data messages from control messages - Fix MaxAge expiry: add background timer in StreamManager for periodic pruning - Fix JetStream wire format: Go-compatible anonymous objects with string enums, proper offset-based pagination for stream/consumer list APIs - Add 42 E2E black-box tests (core messaging, auth, TLS, accounts, JetStream) - Add ~1000 parity tests across all subsystems (gaps closure) - Update gap inventory docs to reflect implementation status
5.1 KiB
NATS.E2E.Tests — Implementation Plan
Date: 2026-03-12 Design: 2026-03-12-e2e-tests-design.md
Steps
Step 1: Create the project and add to solution
Files:
tests/NATS.E2E.Tests/NATS.E2E.Tests.csproj(new)NatsDotNet.slnx(edit)
Details:
Create tests/NATS.E2E.Tests/NATS.E2E.Tests.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="NATS.Client.Core" />
<PackageReference Include="Shouldly" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
<Using Include="Shouldly" />
</ItemGroup>
</Project>
No project references — black-box only. All package versions come from Directory.Packages.props (CPM). TFM inherited from Directory.Build.props (net10.0).
Add to NatsDotNet.slnx under the /tests/ folder:
<Project Path="tests/NATS.E2E.Tests/NATS.E2E.Tests.csproj" />
Verify: dotnet build tests/NATS.E2E.Tests succeeds.
Step 2: Implement NatsServerProcess
Files:
tests/NATS.E2E.Tests/Infrastructure/NatsServerProcess.cs(new)
Details:
Class NatsServerProcess : IAsyncDisposable:
- Constructor: Takes no args. Allocates a free TCP port via ephemeral socket bind.
StartAsync():- Resolves the host DLL path: walk up from
AppContext.BaseDirectoryto find the solution root (containsNatsDotNet.slnx), then build pathsrc/NATS.Server.Host/bin/Debug/net10.0/NATS.Server.Host.dll. If not found, rundotnet build src/NATS.Server.Host/NATS.Server.Host.csproj -c Debugfrom solution root first. - Launch
dotnet exec <dll> -p <port>viaSystem.Diagnostics.Process. Redirect stdout/stderr, capture intoStringBuilderfor diagnostics. - Poll TCP connect to
127.0.0.1:<port>every 100ms, timeout after 10s. ThrowTimeoutExceptionwith captured output if server doesn't become ready.
- Resolves the host DLL path: walk up from
DisposeAsync():- If process is running: try
Process.Kill(entireProcessTree: true)(cross-platform in .NET 10). - Wait for exit with 5s timeout, then force kill if still alive.
- Dispose the process.
- If process is running: try
- Properties:
int Port,string Output(captured stdout+stderr for diagnostics).
Verify: Builds without errors.
Step 3: Implement NatsServerFixture
Files:
tests/NATS.E2E.Tests/Infrastructure/NatsServerFixture.cs(new)
Details:
Class NatsServerFixture : IAsyncLifetime:
- Field:
NatsServerProcess _server InitializeAsync(): CreateNatsServerProcess, callStartAsync().DisposeAsync(): Dispose the server process.int Port: Delegates to_server.Port.NatsConnection CreateClient(): Returnsnew NatsConnection(new NatsOpts { Url = $"nats://127.0.0.1:{Port}" }).
Define a collection attribute:
[CollectionDefinition("E2E")]
public class E2ECollection : ICollectionFixture<NatsServerFixture>;
This lets multiple test classes share one server process via [Collection("E2E")].
Verify: Builds without errors.
Step 4: Implement BasicTests
Files:
tests/NATS.E2E.Tests/BasicTests.cs(new)
Details:
[Collection("E2E")]
public class BasicTests(NatsServerFixture fixture)
{
private static CancellationToken Timeout => new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
Test 1 — ConnectAndPing:
- Create client via
fixture.CreateClient() await client.ConnectAsync()await client.PingAsync()— if no exception, PING/PONG succeeded- Assert
client.ConnectionStateisOpen(via Shouldly)
Test 2 — PubSub:
- Create two clients (pub, sub)
- Connect both
- Subscribe sub to
"e2e.test.pubsub" - Flush sub via
PingAsync() - Publish
"hello e2e"on the subject - Read one message from subscription with 10s timeout
- Assert
msg.Data.ShouldBe("hello e2e")
Test 3 — RequestReply:
- Create two clients (requester, responder)
- Connect both
- Subscribe responder to
"e2e.test.rpc", in a background task read messages and reply with"reply: " + msg.Data - Flush responder via
PingAsync() - Send request from requester:
await requester.RequestAsync<string, string>("e2e.test.rpc", "ping") - Assert reply data is
"reply: ping"
All tests use await using for client cleanup.
Verify: dotnet test tests/NATS.E2E.Tests — all 3 tests pass.
Step 5: Verify full solution builds and tests pass
Commands:
dotnet build
dotnet test tests/NATS.E2E.Tests -v normal
Success criteria: Solution builds clean, all 3 E2E tests pass.
Batch Structure
All 5 steps are in a single batch — the project is small and sequential (each step builds on the prior). No parallelization needed.
| Step | Files | Depends On |
|---|---|---|
| 1 | csproj + slnx | — |
| 2 | NatsServerProcess.cs | Step 1 |
| 3 | NatsServerFixture.cs | Step 2 |
| 4 | BasicTests.cs | Step 3 |
| 5 | (verify only) | Step 4 |