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

@@ -4,6 +4,7 @@ using NATS.Server.JetStream.Cluster;
using NATS.Server.JetStream.Consumers;
using NATS.Server.JetStream.Models;
using NATS.Server.JetStream.Storage;
using NATS.Server.JetStream.Validation;
using NATS.Server.Subscriptions;
namespace NATS.Server.JetStream;
@@ -40,6 +41,12 @@ public sealed class ConsumerManager : IDisposable
return JetStreamApiResponse.ErrorResponse(400, "durable name required");
}
if (!JetStreamConfigValidator.IsValidName(config.DurableName))
return JetStreamApiResponse.ErrorResponse(400, "invalid durable name");
if (!JetStreamConfigValidator.IsMetadataWithinLimit(config.Metadata))
return JetStreamApiResponse.ErrorResponse(400, "consumer metadata exceeds maximum size");
if (config.FilterSubjects.Count == 0 && !string.IsNullOrWhiteSpace(config.FilterSubject))
config.FilterSubjects.Add(config.FilterSubject);
@@ -58,6 +65,8 @@ public sealed class ConsumerManager : IDisposable
{
ConsumerInfo = new JetStreamConsumerInfo
{
Name = handle.Config.DurableName,
StreamName = stream,
Config = handle.Config,
},
};
@@ -71,6 +80,8 @@ public sealed class ConsumerManager : IDisposable
{
ConsumerInfo = new JetStreamConsumerInfo
{
Name = handle.Config.DurableName,
StreamName = stream,
Config = handle.Config,
},
};
@@ -95,6 +106,13 @@ public sealed class ConsumerManager : IDisposable
.OrderBy(x => x, StringComparer.Ordinal)
.ToArray();
public IReadOnlyList<JetStreamConsumerInfo> ListConsumerInfos(string stream)
=> _consumers
.Where(kv => string.Equals(kv.Key.Stream, stream, StringComparison.Ordinal))
.OrderBy(kv => kv.Key.Name, StringComparer.Ordinal)
.Select(kv => new JetStreamConsumerInfo { Name = kv.Value.Config.DurableName, StreamName = stream, Config = kv.Value.Config })
.ToList();
public bool Pause(string stream, string durableName, bool paused)
{
if (!_consumers.TryGetValue((stream, durableName), out var handle))