# NATS.E2E.Tests Extended Coverage — Design

**Date:** 2026-03-12
**Status:** Approved

## Overview

Extend the existing `NATS.E2E.Tests` project with phased test coverage that progressively exercises more complex NATS server functionality. All tests are black-box: the server runs as a child process, tests connect via `NATS.Client.Core` (and `NATS.Client.JetStream` for Phase 5).

## Infrastructure Extensions

### NatsServerProcess Changes

- Add optional `string[]? extraArgs` to constructor, appended after `-p <port>`
- Add `WithConfigFile(string content)` static factory — writes content to a temp file, passes `-c <path>`, cleans up on dispose
- Add `MonitorPort` property — allocates a second free port when monitoring is needed, passes `-m <port>`

### New Fixtures (Infrastructure/)

Each fixture gets its own `[CollectionDefinition]`:

| Fixture | Collection | Config |
|---------|-----------|--------|
| `NatsServerFixture` (existing) | `E2E` | Default, no auth/TLS/JS |
| `AuthServerFixture` | `E2E-Auth` | Config file with users, tokens, NKeys, permissions |
| `MonitorServerFixture` | `E2E-Monitor` | `-m <port>` for HTTP monitoring |
| `TlsServerFixture` | `E2E-TLS` | Self-signed certs generated at startup, `--tlscert/--tlskey/--tlscacert` |
| `AccountServerFixture` | `E2E-Accounts` | Config with two isolated accounts |
| `JetStreamServerFixture` | `E2E-JetStream` | Config with `jetstream { store_dir: <tmpdir> }` |

### Shared Helper

`E2ETestHelper` static class:
- `CreateClient(int port)` — returns `NatsConnection`
- `Timeout(int seconds = 10)` — returns `CancellationToken`

### NuGet Additions

- `NATS.NKeys` — already in `Directory.Packages.props`, add to E2E csproj
- `NATS.Client.JetStream` — add to `Directory.Packages.props` and E2E csproj

## Phase 1: Core Messaging (11 tests)

**File:** `CoreMessagingTests.cs` — `[Collection("E2E")]`

| Test | Verifies |
|------|----------|
| `WildcardStar_MatchesSingleToken` | Sub `foo.*`, pub `foo.bar` → received |
| `WildcardGreaterThan_MatchesMultipleTokens` | Sub `foo.>`, pub `foo.bar.baz` → received |
| `WildcardStar_DoesNotMatchMultipleTokens` | Sub `foo.*`, pub `foo.bar.baz` → no message |
| `QueueGroup_LoadBalances` | 3 queue subs, 30 msgs → distributed across all 3 |
| `QueueGroup_MixedWithPlainSub` | 1 plain + 2 queue subs → plain gets all, 1 queue gets each |
| `Unsub_StopsDelivery` | Sub, unsub, pub → no message |
| `Unsub_WithMaxMessages` | Auto-unsub after 3, pub 5 → only 3 received |
| `FanOut_MultipleSubscribers` | 3 subs, 1 pub → all 3 receive |
| `EchoOff_PublisherDoesNotReceiveSelf` | `echo: false`, self-pub → no echo |
| `VerboseMode_OkResponses` | Raw socket, `verbose: true` → `+OK` after SUB |
| `NoResponders_Returns503` | `no_responders: true`, request with no subs → 503 |

## Phase 2: Auth & Permissions (12 tests)

**File:** `AuthTests.cs` — `[Collection("E2E-Auth")]`

Config includes: user/pass pair, token, NKey public key, permission-restricted users, `max_subs: 5` user.

| Test | Verifies |
|------|----------|
| `UsernamePassword_ValidCredentials_Connects` | Correct user/pass → connected |
| `UsernamePassword_InvalidPassword_Rejected` | Wrong pass → rejected |
| `UsernamePassword_NoCredentials_Rejected` | No creds to auth server → rejected |
| `TokenAuth_ValidToken_Connects` | Correct token → connected |
| `TokenAuth_InvalidToken_Rejected` | Wrong token → rejected |
| `NKeyAuth_ValidSignature_Connects` | Valid NKey sig → connected |
| `NKeyAuth_InvalidSignature_Rejected` | Wrong NKey sig → rejected |
| `Permission_PublishAllowed_Succeeds` | Pub to allowed subject → delivered |
| `Permission_PublishDenied_NoDelivery` | Pub to denied subject → not delivered |
| `Permission_SubscribeDenied_Rejected` | Sub to denied subject → rejected |
| `MaxSubscriptions_ExceedsLimit_Rejected` | 6th sub on `max_subs: 5` user → rejected |
| `MaxPayload_ExceedsLimit_Disconnected` | Oversized message → disconnected |

## Phase 3: Monitoring & Config (7 tests)

**File:** `MonitoringTests.cs` — `[Collection("E2E-Monitor")]`

Fixture exposes `MonitorPort` and `HttpClient MonitorClient`.

| Test | Verifies |
|------|----------|
| `Healthz_ReturnsOk` | `GET /healthz` → 200, `{"status":"ok"}` |
| `Varz_ReturnsServerInfo` | `GET /varz` → JSON with `server_id`, `version`, `port` |
| `Varz_ReflectsMessageCounts` | Publish msgs, `GET /varz` → `in_msgs > 0` |
| `Connz_ListsActiveConnections` | 2 clients, `GET /connz` → `num_connections: 2` |
| `Connz_SortByParameter` | `GET /connz?sort=bytes_to` → sorted |
| `Connz_LimitAndOffset` | 5 clients, `GET /connz?limit=2&offset=1` → 2 entries |
| `Subz_ReturnsSubscriptionStats` | Subs active, `GET /subz` → count > 0 |

## Phase 4: TLS & Account Isolation (6 tests)

**File:** `TlsTests.cs` — `[Collection("E2E-TLS")]`

Fixture generates self-signed CA + server cert + client cert using `System.Security.Cryptography` at startup.

| Test | Verifies |
|------|----------|
| `Tls_ClientConnectsSecurely` | TLS connect + ping succeeds |
| `Tls_PlainTextConnection_Rejected` | Non-TLS connect → fails |
| `Tls_PubSub_WorksOverEncryptedConnection` | Full pub/sub over TLS |

**File:** `AccountIsolationTests.cs` — `[Collection("E2E-Accounts")]`

Config with `ACCT_A` and `ACCT_B`, each with its own user.

| Test | Verifies |
|------|----------|
| `Accounts_SameAccount_MessageDelivered` | Two `ACCT_A` clients → pub/sub works |
| `Accounts_CrossAccount_MessageNotDelivered` | `ACCT_A` pub, `ACCT_B` sub → no message |
| `Accounts_EachAccountHasOwnNamespace` | Both accounts sub `foo.bar` independently |

## Phase 5: JetStream (10 tests)

**File:** `JetStreamTests.cs` — `[Collection("E2E-JetStream")]`

Fixture enables JetStream with temp `store_dir`.

| Test | Verifies |
|------|----------|
| `Stream_CreateAndInfo` | Create stream, verify info matches config |
| `Stream_ListAndNames` | Create 3 streams, list/names returns all |
| `Stream_Delete` | Create, delete, verify gone |
| `Stream_PublishAndGet` | Publish msgs, get by sequence |
| `Stream_Purge` | Publish, purge, verify count = 0 |
| `Consumer_CreatePullAndConsume` | Pull consumer, publish 5, pull → receive 5 |
| `Consumer_AckExplicit` | Explicit ack, verify no redelivery |
| `Consumer_ListAndDelete` | Create consumers, list, delete, verify |
| `Retention_LimitsMaxMessages` | `max_msgs: 10`, publish 15 → stream has 10 |
| `Retention_MaxAge` | Short `max_age`, verify msgs expire |

## Test Count Summary

| Phase | Tests | New Files |
|-------|-------|-----------|
| 1 — Core Messaging | 11 | `CoreMessagingTests.cs` |
| 2 — Auth & Permissions | 12 | `AuthTests.cs`, `AuthServerFixture.cs` |
| 3 — Monitoring | 7 | `MonitoringTests.cs`, `MonitorServerFixture.cs` |
| 4 — TLS & Accounts | 6 | `TlsTests.cs`, `TlsServerFixture.cs`, `AccountIsolationTests.cs`, `AccountServerFixture.cs` |
| 5 — JetStream | 10 | `JetStreamTests.cs`, `JetStreamServerFixture.cs` |
| **Total** | **46** | |

Plus infrastructure changes: `NatsServerProcess.cs` (edit), `E2ETestHelper.cs` (new).
