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:
@@ -90,7 +90,7 @@ Add rows to the Gap Inventory table below. Group by Go source file. Include the
|
||||
|-----------|:-------------|--------|:----------------|-------|
|
||||
| `wsOpCode` (type) | websocket.go:41 | PORTED | `src/NATS.Server/WebSocket/WsConstants.cs:1` | Int constants replace Go type alias; `WsConstants.TextMessage`, `BinaryMessage`, etc. |
|
||||
| `websocket` (struct) | websocket.go:108 | PORTED | `src/NATS.Server/WebSocket/WsConnection.cs:8` | Fields mapped: `compress`, `maskread`/`maskwrite`, `browser`, `nocompfrag`. Cookie fields moved to `WsUpgradeResult`. `frames`/`fs` buffer management is now in `WsConnection.WriteAsync`. `compressor` (reuse) is NOT pooled — recreated per call. |
|
||||
| `srvWebsocket` (struct) | websocket.go:126 | PARTIAL | `src/NATS.Server/NatsServer.cs:538` | `_wsListener`, `_options.WebSocket` cover port/host/tls; `allowedOrigins` managed via `WsOriginChecker`. `connectURLsMap` ref-count URL set and `authOverride` flag are MISSING — not ported. |
|
||||
| `srvWebsocket` (struct) | websocket.go:126 | PARTIAL | `src/NATS.Server/NatsServer.cs:538` | `_wsListener`, `_options.WebSocket` cover port/host/tls; `allowedOrigins` managed via `WsOriginChecker`; explicit `authOverride` flag is now computed via `WsAuthConfig.Apply(...)`. `connectURLsMap` ref-count URL set is still missing. |
|
||||
| `allowedOrigin` (struct) | websocket.go:145 | PORTED | `src/NATS.Server/WebSocket/WsOriginChecker.cs:80` | Private `AllowedOrigin` record struct with `Scheme` and `Port`. |
|
||||
| `wsUpgradeResult` (struct) | websocket.go:150 | PORTED | `src/NATS.Server/WebSocket/WsUpgrade.cs:346` | `WsUpgradeResult` readonly record struct with equivalent fields. `kind` maps to `WsClientKind` enum. |
|
||||
| `wsReadInfo` (struct) | websocket.go:156 | PORTED | `src/NATS.Server/WebSocket/WsReadInfo.cs:10` | All fields ported: `rem`→`Remaining`, `fs`→`FrameStart`, `ff`→`FirstFrame`, `fc`→`FrameCompressed`, `mask`→`ExpectMask`, `mkpos`→`MaskKeyPos`, `mkey`→`MaskKey`, `cbufs`→`CompressedBuffers`, `coff`→`CompressedOffset`. Extra .NET fields added for control frame output. |
|
||||
@@ -104,7 +104,7 @@ Add rows to the Gap Inventory table below. Group by Go source file. Include the
|
||||
| `decompressorPool` | websocket.go:99 | PARTIAL | `src/NATS.Server/WebSocket/WsCompression.cs:193` | Go uses `sync.Pool` for `flate.Reader` reuse. .NET creates a new `DeflateStream` per decompression call — no pooling. Functional but slightly less efficient under high load. |
|
||||
| `compressLastBlock` | websocket.go:100 | PORTED | `src/NATS.Server/WebSocket/WsConstants.cs:62` | .NET uses 4-byte `DecompressTrailer` (sync marker only); Go uses 9-byte block. Both work correctly — difference is .NET `DeflateStream` does not need the final stored block. |
|
||||
| `wsGUID` | websocket.go:103 | PORTED | `src/NATS.Server/WebSocket/WsUpgrade.cs:177` | Inline string literal in `ComputeAcceptKey`. |
|
||||
| `wsTestRejectNoMasking` | websocket.go:106 | MISSING | — | Test-only hook to force masking rejection. No equivalent test hook in .NET. |
|
||||
| `wsTestRejectNoMasking` | websocket.go:106 | PORTED | `src/NATS.Server/WebSocket/WsUpgrade.cs:14` | Added test hook `RejectNoMaskingForTest`; when set, no-masking leaf upgrade requests are explicitly rejected |
|
||||
|
||||
#### Methods on `wsReadInfo`
|
||||
|
||||
@@ -148,16 +148,16 @@ Add rows to the Gap Inventory table below. Group by Go source file. Include the
|
||||
| `srvWebsocket.checkOrigin()` | websocket.go:933 | PORTED | `src/NATS.Server/WebSocket/WsOriginChecker.cs:32` | `WsOriginChecker.CheckOrigin()` — same-origin and allowed-list checks. Go checks `r.TLS != nil` for TLS detection; .NET uses `isTls` parameter passed at call site. |
|
||||
| `wsGetHostAndPort()` | websocket.go:985 | PORTED | `src/NATS.Server/WebSocket/WsOriginChecker.cs:65` | `WsOriginChecker.GetHostAndPort()` and `ParseHostPort()` — missing-port defaults to 80/443 by TLS flag. |
|
||||
| `wsAcceptKey()` | websocket.go:1004 | PORTED | `src/NATS.Server/WebSocket/WsUpgrade.cs:175` | `WsUpgrade.ComputeAcceptKey()` — SHA-1 of key + GUID, base64 encoded. |
|
||||
| `wsMakeChallengeKey()` | websocket.go:1011 | MISSING | — | Generates a random 16-byte base64 client key for outbound WS connections (leaf node acting as WS client). No .NET equivalent. Needed when .NET server connects outbound as a WS leaf. |
|
||||
| `validateWebsocketOptions()` | websocket.go:1020 | PARTIAL | `src/NATS.Server/WebSocket/WebSocketTlsConfig.cs:24` | `WebSocketTlsConfig.Validate()` checks cert+key pair consistency. Full Go validation (TLS required unless NoTLS, AllowedOrigins parseable, NoAuthUser in users list, Token/Username incompatible with users/nkeys, JWTCookie requires TrustedOperators, TLSPinnedCerts, reserved header names) is MISSING from .NET. |
|
||||
| `wsMakeChallengeKey()` | websocket.go:1011 | PORTED | `src/NATS.Server/WebSocket/WsUpgrade.cs:192` | Added `MakeChallengeKey()` generating a random base64-encoded 16-byte challenge nonce |
|
||||
| `validateWebsocketOptions()` | websocket.go:1020 | PORTED | `src/NATS.Server/WebSocket/WebSocketOptionsValidator.cs:11` | Added `WebSocketOptionsValidator.Validate(NatsOptions)` covering TLS cert/key requirement, allowed-origin URI parsing, `NoAuthUser` membership, username/token conflicts with users/nkeys, `JwtCookie` trusted-operator requirement, TLS pinned-cert validation, and reserved response-header protection. Startup now enforces this validation in `NatsServer.StartAsync()` before opening WS listener (`src/NATS.Server/NatsServer.cs:631`). |
|
||||
| `Server.wsSetOriginOptions()` | websocket.go:1083 | PARTIAL | `src/NATS.Server/WebSocket/WsUpgrade.cs:49` | Origin checking is constructed inline in `TryUpgradeAsync` from `options.SameOrigin` and `options.AllowedOrigins`. The Go method persists parsed origins in `srvWebsocket.allowedOrigins` map and supports hot-reload. .NET constructs a `WsOriginChecker` per request — no hot-reload support, but functionally equivalent for initial config. |
|
||||
| `Server.wsSetHeadersOptions()` | websocket.go:1111 | PORTED | `src/NATS.Server/WebSocket/WsUpgrade.cs:141` | Custom headers applied inline in `TryUpgradeAsync` from `options.Headers`. |
|
||||
| `Server.wsConfigAuth()` | websocket.go:1131 | MISSING | — | Sets `srvWebsocket.authOverride` flag based on username/token/noAuthUser presence. No equivalent flag computation in .NET — `WebSocketOptions` properties are read directly. Functionally equivalent but the explicit override flag is absent. |
|
||||
| `Server.wsConfigAuth()` | websocket.go:1131 | PORTED | `src/NATS.Server/WebSocket/WsAuthConfig.cs:5` | Added explicit auth-override computation (`Username`/`Token`/`NoAuthUser`) and startup application via `NatsServer.StartAsync()` before WS listener initialization |
|
||||
| `Server.startWebsocketServer()` | websocket.go:1137 | PORTED | `src/NATS.Server/NatsServer.cs:538` | `NatsServer.StartAsync()` section at line 538 sets up the WS listener, logs, and launches `RunWebSocketAcceptLoopAsync`. Go uses `http.Server` + mux; .NET uses raw `TcpListener`/`Socket.AcceptAsync`. LEAF and MQTT routing at connection time is PARTIAL — LEAF path is wired (`WsClientKind.Leaf`) but MQTT is not handled in `AcceptWebSocketClientAsync`. `lame-duck` / `ldmCh` signaling is MISSING. |
|
||||
| `Server.wsGetTLSConfig()` | websocket.go:1264 | PARTIAL | `src/NATS.Server/NatsServer.cs:807` | TLS is applied once at accept time via `TlsConnectionWrapper.NegotiateAsync`. Go uses `GetConfigForClient` callback for hot-reload TLS config. .NET does not support hot TLS config reload for WS. |
|
||||
| `Server.createWSClient()` | websocket.go:1273 | PORTED | `src/NATS.Server/NatsServer.cs:799` | `AcceptWebSocketClientAsync()` — creates `WsConnection`, constructs `NatsClient`, wires `IsWebSocket`/`WsInfo`, registers client. Go also sends INFO immediately and sets auth timer; .NET's `NatsClient.RunAsync()` handles INFO send and auth timer. |
|
||||
| `isWSURL()` | websocket.go:1544 | MISSING | — | Helper to detect `ws://` scheme in a URL. Used by leaf node / route URL parsing. No .NET equivalent — not yet needed since outbound WS connections are not implemented. |
|
||||
| `isWSSURL()` | websocket.go:1548 | MISSING | — | Helper to detect `wss://` scheme in a URL. Same as above — not yet needed. |
|
||||
| `isWSURL()` | websocket.go:1544 | PORTED | `src/NATS.Server/WebSocket/WsUpgrade.cs:203` | Added helper to detect absolute `ws://` URLs via URI scheme parsing |
|
||||
| `isWSSURL()` | websocket.go:1548 | PORTED | `src/NATS.Server/WebSocket/WsUpgrade.cs:215` | Added helper to detect absolute `wss://` URLs via URI scheme parsing |
|
||||
|
||||
---
|
||||
|
||||
@@ -184,5 +184,7 @@ After porting work is completed:
|
||||
|
||||
| Date | Change | By |
|
||||
|------|--------|----|
|
||||
| 2026-02-26 | Ported `validateWebsocketOptions()` parity by adding `WebSocketOptionsValidator`, wiring startup enforcement, and adding focused validator tests including TLS pinned cert validation. | codex |
|
||||
| 2026-02-25 | Ported `Server.wsConfigAuth()` parity by adding `WsAuthConfig` auth-override computation and applying it during WS startup; added `WebSocketOptions.AuthOverride` plus focused tests. | codex |
|
||||
| 2026-02-25 | File created with LLM analysis instructions | auto |
|
||||
| 2026-02-25 | Full gap inventory populated: 37 Go symbols classified (26 PORTED, 7 PARTIAL, 4 MISSING, 0 NOT_APPLICABLE, 0 DEFERRED) | auto |
|
||||
|
||||
Reference in New Issue
Block a user