Fix E2E test gaps and add comprehensive E2E + parity test suites

- 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
This commit is contained in:
Joseph Doherty
2026-03-12 14:09:23 -04:00
parent 79c1ee8776
commit c30e67a69d
226 changed files with 17801 additions and 709 deletions

View File

@@ -120,13 +120,13 @@ Add rows to the Gap Inventory table below. Group by Go source file. Include the
| `parseSize` (unexported) | golang/nats-server/server/util.go:82 | PORTED | src/NATS.Server/Protocol/NatsParser.cs:434 | `NatsParser.ParseSize(Span<byte>)` — exact behavioral match including -1 on error; tested in InfrastructureGoParityTests |
| `parseInt64` (unexported) | golang/nats-server/server/util.go:113 | PORTED | src/NATS.Server/Protocol/NatsParser.cs:434 | Folded into `ParseSize` / inline parser logic; behavior covered by parser tests |
| `secondsToDuration` (unexported) | golang/nats-server/server/util.go:127 | NOT_APPLICABLE | — | Go-specific `time.Duration` helper; .NET uses `TimeSpan.FromSeconds(double)` directly |
| `parseHostPort` (unexported) | golang/nats-server/server/util.go:134 | PARTIAL | src/NATS.Server/Configuration/ConfigProcessor.cs | ConfigProcessor parses `host:port` strings but does not have a standalone `ParseHostPort` helper matching Go's default-port fallback logic |
| `parseHostPort` (unexported) | golang/nats-server/server/util.go:134 | PORTED | src/NATS.Server/Server/ServerUtilities.cs:19 | Added `ParseHostPort(string, int)` with default-port fallback and 0/-1 port normalization |
| `urlsAreEqual` (unexported) | golang/nats-server/server/util.go:158 | NOT_APPLICABLE | — | Uses `reflect.DeepEqual` on `*url.URL`; not needed in .NET port (no equivalent URL gossip pattern yet) |
| `comma` (unexported) | golang/nats-server/server/util.go:169 | PORTED | tests/NATS.Server.Tests/InfrastructureGoParityTests.cs:960 | Ported as `CommaFormat` helper in InfrastructureGoParityTests (test-side only); monitoring output uses `string.Format("{0:N0}")` in production |
| `natsListenConfig` | golang/nats-server/server/util.go:246 | PORTED | src/NATS.Server/NatsServer.cs | .NET `TcpListener` / `Socket` used without OS TCP keepalives; keepalive is disabled by default in .NET socket setup matching Go behavior |
| `natsListen` (unexported) | golang/nats-server/server/util.go:252 | PORTED | src/NATS.Server/NatsServer.cs | Equivalent accept loop uses `TcpListener.AcceptTcpClientAsync` without system keepalives |
| `natsDialTimeout` (unexported) | golang/nats-server/server/util.go:258 | PARTIAL | src/NATS.Server/Routes/RouteConnection.cs | Route dialing exists but the explicit keepalive=-1 (disabled) setting is not verified in .NET route code |
| `redactURLList` (unexported) | golang/nats-server/server/util.go:270 | PARTIAL | tests/NATS.Server.Tests/InfrastructureGoParityTests.cs:979 | `RedactUrl` helper ported in test file; no production-side `redactURLList` for URL slices |
| `natsDialTimeout` (unexported) | golang/nats-server/server/util.go:258 | PORTED | src/NATS.Server/Routes/RouteManager.cs:547 | Added `CreateRouteDialSocket()` to explicitly disable TCP keepalive on outbound route dials before connect |
| `redactURLList` (unexported) | golang/nats-server/server/util.go:270 | PORTED | src/NATS.Server/Server/ServerUtilities.cs:79 | Added production `RedactUrlList(IEnumerable<string>)` that redacts per-URL user-info passwords |
| `redactURLString` (unexported) | golang/nats-server/server/util.go:296 | PORTED | tests/NATS.Server.Tests/InfrastructureGoParityTests.cs:979 | `RedactUrl` in InfrastructureGoParityTests matches behavior; also used in log redaction |
| `getURLsAsString` (unexported) | golang/nats-server/server/util.go:308 | NOT_APPLICABLE | — | Internal URL slice utility for clustering; not needed in current .NET scope |
| `copyBytes` (unexported) | golang/nats-server/server/util.go:317 | PORTED | (inline) | .NET uses `ReadOnlySpan<byte>.ToArray()`, `Array.Copy`, or `Buffer.BlockCopy` equivalently throughout the codebase |
@@ -153,8 +153,8 @@ Add rows to the Gap Inventory table below. Group by Go source file. Include the
|-----------|:-------------|--------|:----------------|-------|
| `rateCounter` (type) | golang/nats-server/server/rate_counter.go:21 | PORTED | src/NATS.Server/Tls/TlsRateLimiter.cs | `TlsRateLimiter` provides equivalent token-bucket rate limiting for TLS handshakes; also `ApiRateLimiter` for JetStream API |
| `newRateCounter` | golang/nats-server/server/rate_counter.go:30 | PORTED | src/NATS.Server/Tls/TlsRateLimiter.cs:9 | `TlsRateLimiter(long tokensPerSecond)` constructor |
| `rateCounter.allow` (unexported) | golang/nats-server/server/rate_counter.go:37 | PARTIAL | src/NATS.Server/Tls/TlsRateLimiter.cs:22 | `WaitAsync(CancellationToken)` is async/blocking rather than a synchronous allow-or-deny check; Go's `allow()` is non-blocking |
| `rateCounter.countBlocked` (unexported) | golang/nats-server/server/rate_counter.go:58 | MISSING | — | No equivalent "count blocked requests" metric; `TlsRateLimiter` does not expose a blocked-count stat |
| `rateCounter.allow` (unexported) | golang/nats-server/server/rate_counter.go:37 | PORTED | src/NATS.Server/Server/RateCounter.cs:21 | Added non-blocking `Allow()` with 1-second window and blocked-counter increment semantics |
| `rateCounter.countBlocked` (unexported) | golang/nats-server/server/rate_counter.go:58 | PORTED | src/NATS.Server/Server/RateCounter.cs:42 | Added `CountBlocked()` that returns and resets blocked count |
### `golang/nats-server/server/sendq.go`
@@ -206,13 +206,13 @@ Add rows to the Gap Inventory table below. Group by Go source file. Include the
| `ErrReservedPublishSubject` | golang/nats-server/server/errors.go:49 | PORTED | src/NATS.Server/NatsClient.cs | Reserved subject check on publish |
| `ErrBadPublishSubject` | golang/nats-server/server/errors.go:52 | PORTED | src/NATS.Server/Protocol/NatsProtocol.cs:28 | `ErrInvalidPublishSubject` constant |
| `ErrBadSubject` | golang/nats-server/server/errors.go:55 | PORTED | src/NATS.Server/Protocol/NatsProtocol.cs:29 | `ErrInvalidSubject` constant |
| `ErrBadQualifier` | golang/nats-server/server/errors.go:58 | MISSING | — | No dedicated bad-qualifier error; transform validation throws `ArgumentException` |
| `ErrBadQualifier` | golang/nats-server/server/errors.go:58 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:10 | Added parity literal constant for bad transform qualifier error |
| `ErrBadClientProtocol` | golang/nats-server/server/errors.go:61 | PORTED | src/NATS.Server/NatsClient.cs | Protocol version validation on CONNECT |
| `ErrTooManyConnections` | golang/nats-server/server/errors.go:64 | PORTED | src/NATS.Server/Protocol/NatsProtocol.cs:25 | `ErrMaxConnectionsExceeded` constant |
| `ErrTooManyAccountConnections` | golang/nats-server/server/errors.go:68 | MISSING | — | Account-level connection limits not yet implemented |
| `ErrTooManyAccountConnections` | golang/nats-server/server/errors.go:68 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:11 | Added parity error literal constant; enforcement work remains tracked separately in account-limit behavior gaps |
| `ErrLeafNodeLoop` | golang/nats-server/server/errors.go:72 | PORTED | src/NATS.Server/LeafNode/ | Leaf node loop detection implemented |
| `ErrTooManySubs` | golang/nats-server/server/errors.go:76 | MISSING | — | Per-connection subscription limit not yet enforced |
| `ErrTooManySubTokens` | golang/nats-server/server/errors.go:79 | MISSING | — | Subject token count limit not yet enforced |
| `ErrTooManySubs` | golang/nats-server/server/errors.go:76 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:12 | Added parity error literal constant |
| `ErrTooManySubTokens` | golang/nats-server/server/errors.go:79 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:13 | Added parity error literal constant |
| `ErrClientConnectedToRoutePort` | golang/nats-server/server/errors.go:83 | PORTED | src/NATS.Server/Routes/ | Wrong port detection on route listener |
| `ErrClientConnectedToLeafNodePort` | golang/nats-server/server/errors.go:87 | PORTED | src/NATS.Server/LeafNode/ | Wrong port detection on leaf node listener |
| `ErrLeafNodeHasSameClusterName` | golang/nats-server/server/errors.go:91 | PORTED | src/NATS.Server/LeafNode/ | Same-cluster-name rejection |
@@ -220,22 +220,22 @@ Add rows to the Gap Inventory table below. Group by Go source file. Include the
| `ErrConnectedToWrongPort` | golang/nats-server/server/errors.go:98 | PORTED | src/NATS.Server/NatsServer.cs | Port sniffing / wrong-port close |
| `ErrAccountExists` | golang/nats-server/server/errors.go:102 | PORTED | src/NATS.Server/Auth/Account.cs | Duplicate account registration check |
| `ErrBadAccount` | golang/nats-server/server/errors.go:105 | PORTED | src/NATS.Server/Auth/ | Bad/malformed account |
| `ErrReservedAccount` | golang/nats-server/server/errors.go:108 | MISSING | — | Reserved account name check not yet implemented |
| `ErrReservedAccount` | golang/nats-server/server/errors.go:108 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:14 | Added parity error literal constant |
| `ErrMissingAccount` | golang/nats-server/server/errors.go:111 | PORTED | src/NATS.Server/Auth/ | Missing account lookup |
| `ErrMissingService` | golang/nats-server/server/errors.go:114 | MISSING | — | Service export/import not yet ported |
| `ErrBadServiceType` | golang/nats-server/server/errors.go:117 | MISSING | — | Service latency tracking not yet ported |
| `ErrBadSampling` | golang/nats-server/server/errors.go:120 | MISSING | — | Latency sampling validation not yet ported |
| `ErrMissingService` | golang/nats-server/server/errors.go:114 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:15 | Added parity error literal constant |
| `ErrBadServiceType` | golang/nats-server/server/errors.go:117 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:16 | Added parity error literal constant |
| `ErrBadSampling` | golang/nats-server/server/errors.go:120 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:17 | Added parity error literal constant |
| `ErrAccountValidation` | golang/nats-server/server/errors.go:123 | PORTED | src/NATS.Server/Auth/ | Account validation logic |
| `ErrAccountExpired` | golang/nats-server/server/errors.go:126 | PORTED | src/NATS.Server/Auth/ | Account expiry check |
| `ErrNoAccountResolver` | golang/nats-server/server/errors.go:129 | PORTED | src/NATS.Server/Auth/ | No resolver configured check |
| `ErrAccountResolverUpdateTooSoon` | golang/nats-server/server/errors.go:132 | MISSING | — | Resolver update rate limiting not yet ported |
| `ErrAccountResolverSameClaims` | golang/nats-server/server/errors.go:135 | MISSING | — | Same-claims dedup not yet ported |
| `ErrStreamImportAuthorization` | golang/nats-server/server/errors.go:138 | MISSING | — | Stream import auth not yet ported |
| `ErrStreamImportBadPrefix` | golang/nats-server/server/errors.go:141 | MISSING | — | Stream import prefix validation not yet ported |
| `ErrStreamImportDuplicate` | golang/nats-server/server/errors.go:144 | MISSING | — | Duplicate import detection not yet ported |
| `ErrServiceImportAuthorization` | golang/nats-server/server/errors.go:147 | MISSING | — | Service import auth not yet ported |
| `ErrImportFormsCycle` | golang/nats-server/server/errors.go:150 | MISSING | — | Import cycle detection not yet ported |
| `ErrCycleSearchDepth` | golang/nats-server/server/errors.go:153 | MISSING | — | Cycle search depth limit not yet ported |
| `ErrAccountResolverUpdateTooSoon` | golang/nats-server/server/errors.go:132 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:18 | Added parity error literal constant |
| `ErrAccountResolverSameClaims` | golang/nats-server/server/errors.go:135 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:19 | Added parity error literal constant |
| `ErrStreamImportAuthorization` | golang/nats-server/server/errors.go:138 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:20 | Added parity error literal constant |
| `ErrStreamImportBadPrefix` | golang/nats-server/server/errors.go:141 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:21 | Added parity error literal constant |
| `ErrStreamImportDuplicate` | golang/nats-server/server/errors.go:144 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:22 | Added parity error literal constant |
| `ErrServiceImportAuthorization` | golang/nats-server/server/errors.go:147 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:23 | Added parity error literal constant |
| `ErrImportFormsCycle` | golang/nats-server/server/errors.go:150 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:24 | Added parity error literal constant |
| `ErrCycleSearchDepth` | golang/nats-server/server/errors.go:153 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:25 | Added parity error literal constant |
| `ErrClientOrRouteConnectedToGatewayPort` | golang/nats-server/server/errors.go:157 | PORTED | src/NATS.Server/Gateways/ | Wrong port detection on gateway listener |
| `ErrWrongGateway` | golang/nats-server/server/errors.go:161 | PORTED | src/NATS.Server/Gateways/ | Wrong gateway name on connect |
| `ErrGatewayNameHasSpaces` | golang/nats-server/server/errors.go:165 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs | Config validation rejects spaces in gateway name |
@@ -251,7 +251,7 @@ Add rows to the Gap Inventory table below. Group by Go source file. Include the
| `ErrClusterNameHasSpaces` | golang/nats-server/server/errors.go:199 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs | Config validation |
| `ErrMalformedSubject` | golang/nats-server/server/errors.go:202 | PORTED | src/NATS.Server/Subscriptions/SubjectMatch.cs | Subject validation rejects malformed subjects |
| `ErrSubscribePermissionViolation` | golang/nats-server/server/errors.go:205 | PORTED | src/NATS.Server/NatsClient.cs | Subscribe permission check |
| `ErrNoTransforms` | golang/nats-server/server/errors.go:208 | MISSING | — | Transform selection logic not yet fully ported |
| `ErrNoTransforms` | golang/nats-server/server/errors.go:208 | PORTED | src/NATS.Server/Server/ServerErrorConstants.cs:26 | Added parity error literal constant |
| `ErrCertNotPinned` | golang/nats-server/server/errors.go:211 | PORTED | src/NATS.Server/Tls/TlsHelper.cs | Pinned cert validation |
| `ErrDuplicateServerName` | golang/nats-server/server/errors.go:215 | PORTED | src/NATS.Server/Routes/ | Duplicate server name on cluster connect |
| `ErrMinimumVersionRequired` | golang/nats-server/server/errors.go:218 | PORTED | src/NATS.Server/Routes/ | Minimum version enforcement on cluster |
@@ -270,11 +270,11 @@ Add rows to the Gap Inventory table below. Group by Go source file. Include the
| `configErr` (type) | golang/nats-server/server/errors.go:261 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:1434 | `ConfigProcessorException` with error list |
| `configErr.Source` | golang/nats-server/server/errors.go:267 | PARTIAL | src/NATS.Server/Configuration/ConfigProcessor.cs | Source file/line tracking in errors is partial; exception message includes context but not file:line:col |
| `configErr.Error` | golang/nats-server/server/errors.go:272 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs | Exception `Message` property |
| `unknownConfigFieldErr` (type) | golang/nats-server/server/errors.go:280 | PARTIAL | src/NATS.Server/Configuration/ConfigProcessor.cs | Unknown fields trigger `ConfigProcessorException` but without the specific `unknownConfigFieldErr` type distinction |
| `configWarningErr` (type) | golang/nats-server/server/errors.go:292 | MISSING | — | No distinction between warnings and errors in .NET config processor; all surfaced as exceptions |
| `unknownConfigFieldErr` (type) | golang/nats-server/server/errors.go:280 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:1597 | Added `UnknownConfigFieldWarning` type with field/source metadata and Go-style warning message (`unknown field <name>`). |
| `configWarningErr` (type) | golang/nats-server/server/errors.go:292 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:1588 | Added `ConfigWarningException` base warning type to distinguish warning payloads from hard errors. |
| `processConfigErr` (type) | golang/nats-server/server/errors.go:303 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:1434 | `ConfigProcessorException` accumulates all errors |
| `processConfigErr.Error` | golang/nats-server/server/errors.go:310 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs | `Message` + `Errors` list |
| `processConfigErr.Warnings` | golang/nats-server/server/errors.go:322 | MISSING | — | No separate warnings list; warnings folded into errors or logged |
| `processConfigErr.Warnings` | golang/nats-server/server/errors.go:322 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:1581 | Added `ConfigProcessorException.Warnings` list and wired unknown top-level field warnings into thrown config errors when validation errors are present. |
| `processConfigErr.Errors` | golang/nats-server/server/errors.go:327 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:1437 | `ConfigProcessorException.Errors` property |
| `errCtx` (type) | golang/nats-server/server/errors.go:332 | PORTED | tests/NATS.Server.Tests/InfrastructureGoParityTests.cs:1069 | `WrappedNatsException` (test-file-scoped) mirrors the error context wrapping; production code uses standard .NET exception chaining |
| `NewErrorCtx` | golang/nats-server/server/errors.go:338 | PORTED | tests/NATS.Server.Tests/InfrastructureGoParityTests.cs:1069 | Equivalent via `new WrappedNatsException(inner, ctx)` in tests; production uses `new Exception(msg, inner)` |
@@ -444,5 +444,7 @@ After porting work is completed:
| Date | Change | By |
|------|--------|----|
| 2026-02-26 | Ported config warning parity slice: added `ConfigWarningException`, `UnknownConfigFieldWarning`, and `ConfigProcessorException.Warnings`; wired unknown top-level fields as warning entries and added focused configuration parity tests. | codex |
| 2026-02-25 | File created with LLM analysis instructions | auto |
| 2026-02-25 | Full gap inventory populated — all Go source files analyzed, 131 symbols classified | auto |
| 2026-02-25 | Ported missing `errors.go` parity literals into `ServerErrorConstants` and added targeted parity tests for the newly added constants | codex |