Files
natsdotnet/tests/NATS.Server.Tests/WebSocket/WebSocketOptionsValidatorParityBatch2Tests.cs
Joseph Doherty c30e67a69d 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
2026-03-12 14:09:23 -04:00

173 lines
4.8 KiB
C#

using NATS.Server.Auth;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
public class WebSocketOptionsValidatorParityBatch2Tests
{
[Fact]
public void Validate_rejects_tls_listener_without_cert_key_when_not_no_tls()
{
var opts = new NatsOptions
{
WebSocket = new WebSocketOptions
{
Port = 8080,
NoTls = false,
},
};
var result = WebSocketOptionsValidator.Validate(opts);
result.IsValid.ShouldBeFalse();
result.Errors.ShouldContain(e => e.Contains("TLS", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Validate_rejects_invalid_allowed_origins()
{
var opts = new NatsOptions
{
WebSocket = new WebSocketOptions
{
Port = 8080,
NoTls = true,
AllowedOrigins = ["not-a-uri"],
},
};
var result = WebSocketOptionsValidator.Validate(opts);
result.IsValid.ShouldBeFalse();
result.Errors.ShouldContain(e => e.Contains("allowed origin", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Validate_rejects_no_auth_user_not_present_in_configured_users()
{
var opts = new NatsOptions
{
Users = [new User { Username = "alice", Password = "x" }],
WebSocket = new WebSocketOptions
{
Port = 8080,
NoTls = true,
NoAuthUser = "bob",
},
};
var result = WebSocketOptionsValidator.Validate(opts);
result.IsValid.ShouldBeFalse();
result.Errors.ShouldContain(e => e.Contains("NoAuthUser", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Validate_rejects_username_or_token_when_users_or_nkeys_are_set()
{
var opts = new NatsOptions
{
Users = [new User { Username = "alice", Password = "x" }],
WebSocket = new WebSocketOptions
{
Port = 8080,
NoTls = true,
Username = "ws-user",
},
};
var result = WebSocketOptionsValidator.Validate(opts);
result.IsValid.ShouldBeFalse();
result.Errors.ShouldContain(e => e.Contains("users", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Validate_rejects_jwt_cookie_without_trusted_operators()
{
var opts = new NatsOptions
{
WebSocket = new WebSocketOptions
{
Port = 8080,
NoTls = true,
JwtCookie = "jwt",
},
};
var result = WebSocketOptionsValidator.Validate(opts);
result.IsValid.ShouldBeFalse();
result.Errors.ShouldContain(e => e.Contains("JwtCookie", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Validate_rejects_reserved_response_headers_override()
{
var opts = new NatsOptions
{
TrustedKeys = ["OP1"],
WebSocket = new WebSocketOptions
{
Port = 8080,
NoTls = true,
Headers = new Dictionary<string, string>
{
["Sec-WebSocket-Accept"] = "bad",
},
},
};
var result = WebSocketOptionsValidator.Validate(opts);
result.IsValid.ShouldBeFalse();
result.Errors.ShouldContain(e => e.Contains("reserved", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Validate_rejects_tls_pinned_certs_when_websocket_tls_is_disabled()
{
var opts = new NatsOptions
{
TlsPinnedCerts = ["ABCDEF0123"],
WebSocket = new WebSocketOptions
{
Port = 8080,
NoTls = true,
},
};
var result = WebSocketOptionsValidator.Validate(opts);
result.IsValid.ShouldBeFalse();
result.Errors.ShouldContain(e => e.Contains("TLSPinnedCerts", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Validate_accepts_valid_minimal_configuration()
{
var opts = new NatsOptions
{
TrustedKeys = ["OP1"],
Users = [new User { Username = "alice", Password = "x" }],
WebSocket = new WebSocketOptions
{
Port = 8080,
NoTls = true,
NoAuthUser = "alice",
AllowedOrigins = ["https://app.example.com"],
JwtCookie = "jwt",
Headers = new Dictionary<string, string>
{
["X-App-Version"] = "1",
},
},
};
var result = WebSocketOptionsValidator.Validate(opts);
result.IsValid.ShouldBeTrue();
result.Errors.ShouldBeEmpty();
}
}