- 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
109 lines
3.4 KiB
C#
109 lines
3.4 KiB
C#
using NATS.Server.JetStream.Api;
|
|
using NATS.Server.JetStream.Models;
|
|
using NATS.Server.JetStream.Validation;
|
|
using NATS.Server.JetStream;
|
|
|
|
namespace NATS.Server.Tests.JetStream.Api;
|
|
|
|
public class JetStreamApiLimitsParityBatch1Tests
|
|
{
|
|
[Fact]
|
|
public void Constants_match_go_reference_values()
|
|
{
|
|
JetStreamApiLimits.JSMaxDescriptionLen.ShouldBe(4_096);
|
|
JetStreamApiLimits.JSMaxMetadataLen.ShouldBe(128 * 1024);
|
|
JetStreamApiLimits.JSMaxNameLen.ShouldBe(255);
|
|
JetStreamApiLimits.JSDefaultRequestQueueLimit.ShouldBe(10_000);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null, false)]
|
|
[InlineData("", false)]
|
|
[InlineData(" ", false)]
|
|
[InlineData("ORDERS", true)]
|
|
[InlineData("ORD ERS", false)]
|
|
[InlineData("ORDERS.*", false)]
|
|
[InlineData("ORDERS.>", false)]
|
|
public void IsValidName_enforces_expected_rules(string? name, bool expected)
|
|
{
|
|
JetStreamConfigValidator.IsValidName(name).ShouldBe(expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void Stream_create_rejects_name_over_max_length()
|
|
{
|
|
var manager = new StreamManager();
|
|
var response = manager.CreateOrUpdate(new StreamConfig
|
|
{
|
|
Name = new string('S', JetStreamApiLimits.JSMaxNameLen + 1),
|
|
Subjects = ["a"],
|
|
});
|
|
|
|
response.Error.ShouldNotBeNull();
|
|
response.Error!.Description.ShouldBe("invalid stream name");
|
|
}
|
|
|
|
[Fact]
|
|
public void Stream_create_rejects_description_over_max_bytes()
|
|
{
|
|
var manager = new StreamManager();
|
|
var response = manager.CreateOrUpdate(new StreamConfig
|
|
{
|
|
Name = "LIMITDESC",
|
|
Subjects = ["a"],
|
|
Description = new string('d', JetStreamApiLimits.JSMaxDescriptionLen + 1),
|
|
});
|
|
|
|
response.Error.ShouldNotBeNull();
|
|
response.Error!.Description.ShouldBe("stream description is too long");
|
|
}
|
|
|
|
[Fact]
|
|
public void Stream_create_rejects_metadata_over_max_bytes()
|
|
{
|
|
var manager = new StreamManager();
|
|
var response = manager.CreateOrUpdate(new StreamConfig
|
|
{
|
|
Name = "LIMITMETA",
|
|
Subjects = ["a"],
|
|
Metadata = new Dictionary<string, string>
|
|
{
|
|
["k"] = new string('m', JetStreamApiLimits.JSMaxMetadataLen),
|
|
},
|
|
});
|
|
|
|
response.Error.ShouldNotBeNull();
|
|
response.Error!.Description.ShouldBe("stream metadata exceeds maximum size");
|
|
}
|
|
|
|
[Fact]
|
|
public void Consumer_create_rejects_durable_name_over_max_length()
|
|
{
|
|
var manager = new ConsumerManager();
|
|
var response = manager.CreateOrUpdate("S", new ConsumerConfig
|
|
{
|
|
DurableName = new string('C', JetStreamApiLimits.JSMaxNameLen + 1),
|
|
});
|
|
|
|
response.Error.ShouldNotBeNull();
|
|
response.Error!.Description.ShouldBe("invalid durable name");
|
|
}
|
|
|
|
[Fact]
|
|
public void Consumer_create_rejects_metadata_over_max_bytes()
|
|
{
|
|
var manager = new ConsumerManager();
|
|
var response = manager.CreateOrUpdate("S", new ConsumerConfig
|
|
{
|
|
DurableName = "C1",
|
|
Metadata = new Dictionary<string, string>
|
|
{
|
|
["k"] = new string('m', JetStreamApiLimits.JSMaxMetadataLen),
|
|
},
|
|
});
|
|
|
|
response.Error.ShouldNotBeNull();
|
|
response.Error!.Description.ShouldBe("consumer metadata exceeds maximum size");
|
|
}
|
|
}
|