# NATS.E2E.Tests — Implementation Plan
**Date:** 2026-03-12
**Design:** [2026-03-12-e2e-tests-design.md](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`:
```xml
false
```
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:
```xml
```
**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()`**:
1. Resolves the host DLL path: walk up from `AppContext.BaseDirectory` to find the solution root (contains `NatsDotNet.slnx`), then build path `src/NATS.Server.Host/bin/Debug/net10.0/NATS.Server.Host.dll`. If not found, run `dotnet build src/NATS.Server.Host/NATS.Server.Host.csproj -c Debug` from solution root first.
2. Launch `dotnet exec -p ` via `System.Diagnostics.Process`. Redirect stdout/stderr, capture into `StringBuilder` for diagnostics.
3. Poll TCP connect to `127.0.0.1:` every 100ms, timeout after 10s. Throw `TimeoutException` with captured output if server doesn't become ready.
- **`DisposeAsync()`**:
1. If process is running: try `Process.Kill(entireProcessTree: true)` (cross-platform in .NET 10).
2. Wait for exit with 5s timeout, then force kill if still alive.
3. Dispose the process.
- **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()`**: Create `NatsServerProcess`, call `StartAsync()`.
- **`DisposeAsync()`**: Dispose the server process.
- **`int Port`**: Delegates to `_server.Port`.
- **`NatsConnection CreateClient()`**: Returns `new NatsConnection(new NatsOpts { Url = $"nats://127.0.0.1:{Port}" })`.
Define a collection attribute:
```csharp
[CollectionDefinition("E2E")]
public class E2ECollection : ICollectionFixture;
```
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:**
```csharp
[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.ConnectionState` is `Open` (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("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:**
```bash
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 |