feat: execute full-repo remaining parity closure plan
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
using NATS.Server.JetStream.Cluster;
|
||||
using NATS.Server.JetStream.Models;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
|
||||
public class JetStreamClusterGovernanceRuntimeParityTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Jetstream_cluster_governance_applies_consensus_backed_placement()
|
||||
{
|
||||
var meta = new JetStreamMetaGroup(3);
|
||||
await meta.ProposeCreateStreamAsync(new StreamConfig
|
||||
{
|
||||
Name = "ORDERS",
|
||||
Subjects = ["orders.*"],
|
||||
}, default);
|
||||
|
||||
var planner = new AssetPlacementPlanner(3);
|
||||
var placement = planner.PlanReplicas(2);
|
||||
var replicas = new StreamReplicaGroup("ORDERS", 1);
|
||||
await replicas.ApplyPlacementAsync(placement, default);
|
||||
|
||||
meta.GetState().Streams.ShouldContain("ORDERS");
|
||||
replicas.Nodes.Count.ShouldBe(2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using NATS.Server.JetStream.Consumers;
|
||||
using NATS.Server.JetStream.Models;
|
||||
using NATS.Server.JetStream.Storage;
|
||||
using NATS.Server.JetStream;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
|
||||
public class JetStreamConsumerFlowReplayParityTests
|
||||
{
|
||||
[Fact]
|
||||
public void Push_consumer_enqueues_flow_control_and_heartbeat_frames_when_enabled()
|
||||
{
|
||||
var engine = new PushConsumerEngine();
|
||||
var consumer = new ConsumerHandle("ORDERS", new ConsumerConfig
|
||||
{
|
||||
AckPolicy = AckPolicy.Explicit,
|
||||
FlowControl = true,
|
||||
HeartbeatMs = 1000,
|
||||
RateLimitBps = 1024,
|
||||
});
|
||||
|
||||
engine.Enqueue(consumer, new StoredMessage
|
||||
{
|
||||
Sequence = 1,
|
||||
Subject = "orders.created",
|
||||
Payload = "payload"u8.ToArray(),
|
||||
});
|
||||
|
||||
consumer.PushFrames.Count.ShouldBe(3);
|
||||
consumer.PushFrames.Any(f => f.IsFlowControl).ShouldBeTrue();
|
||||
consumer.PushFrames.Any(f => f.IsHeartbeat).ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using NATS.Server.JetStream.Consumers;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
|
||||
public class JetStreamConsumerRuntimeParityTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Consumer_runtime_honors_ack_all_redelivery_and_max_deliver_limits()
|
||||
{
|
||||
var ack = new AckProcessor();
|
||||
ack.Register(1, ackWaitMs: 1);
|
||||
await Task.Delay(5);
|
||||
ack.TryGetExpired(out var seq, out var deliveries).ShouldBeTrue();
|
||||
seq.ShouldBe((ulong)1);
|
||||
deliveries.ShouldBe(1);
|
||||
|
||||
ack.ScheduleRedelivery(seq, delayMs: 1);
|
||||
await Task.Delay(5);
|
||||
ack.TryGetExpired(out _, out deliveries).ShouldBeTrue();
|
||||
deliveries.ShouldBe(2);
|
||||
|
||||
ack.AckAll(1);
|
||||
ack.HasPending.ShouldBeFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server.Configuration;
|
||||
using NATS.Server.Gateways;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
|
||||
public class JetStreamCrossClusterRuntimeParityTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Jetstream_cross_cluster_messages_are_forward_counted()
|
||||
{
|
||||
var manager = new GatewayManager(
|
||||
new GatewayOptions { Host = "127.0.0.1", Port = 0, Name = "A" },
|
||||
new ServerStats(),
|
||||
"S1",
|
||||
_ => { },
|
||||
_ => { },
|
||||
NullLogger<GatewayManager>.Instance);
|
||||
|
||||
await manager.ForwardJetStreamClusterMessageAsync(
|
||||
new GatewayMessage("$JS.CLUSTER.REPL", null, "x"u8.ToArray()),
|
||||
default);
|
||||
|
||||
manager.ForwardedJetStreamClusterMessages.ShouldBe(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using NATS.Server.JetStream.Storage;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
|
||||
public class JetStreamFileStoreCryptoCompressionTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task File_store_compression_and_encryption_roundtrip_preserves_payload()
|
||||
{
|
||||
var dir = Path.Combine(Path.GetTempPath(), $"natsdotnet-filestore-crypto-{Guid.NewGuid():N}");
|
||||
try
|
||||
{
|
||||
await using var store = new FileStore(new FileStoreOptions
|
||||
{
|
||||
Directory = dir,
|
||||
EnableCompression = true,
|
||||
EnableEncryption = true,
|
||||
EncryptionKey = [1, 2, 3, 4],
|
||||
});
|
||||
|
||||
var payload = Enumerable.Repeat((byte)'a', 512).ToArray();
|
||||
var seq = await store.AppendAsync("orders.created", payload, default);
|
||||
var loaded = await store.LoadAsync(seq, default);
|
||||
loaded.ShouldNotBeNull();
|
||||
loaded.Payload.ToArray().ShouldBe(payload);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(dir))
|
||||
Directory.Delete(dir, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using NATS.Server.JetStream.Storage;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
|
||||
public class JetStreamFileStoreLayoutParityTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task File_store_uses_block_index_layout_with_ttl_prune_invariants()
|
||||
{
|
||||
var dir = Path.Combine(Path.GetTempPath(), $"natsdotnet-filestore-{Guid.NewGuid():N}");
|
||||
try
|
||||
{
|
||||
await using var store = new FileStore(new FileStoreOptions
|
||||
{
|
||||
Directory = dir,
|
||||
BlockSizeBytes = 128,
|
||||
MaxAgeMs = 60_000,
|
||||
});
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
await store.AppendAsync($"orders.{i}", "x"u8.ToArray(), default);
|
||||
|
||||
var state = await store.GetStateAsync(default);
|
||||
state.Messages.ShouldBe((ulong)100);
|
||||
store.BlockCount.ShouldBeGreaterThan(1);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(dir))
|
||||
Directory.Delete(dir, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using NATS.Server.JetStream.MirrorSource;
|
||||
using NATS.Server.JetStream.Models;
|
||||
using NATS.Server.JetStream.Storage;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
|
||||
public class JetStreamMirrorSourceRuntimeParityTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Mirror_source_runtime_tracks_sync_state_and_subject_mapping()
|
||||
{
|
||||
var mirrorTarget = new MemStore();
|
||||
var sourceTarget = new MemStore();
|
||||
var mirror = new MirrorCoordinator(mirrorTarget);
|
||||
var source = new SourceCoordinator(sourceTarget, new StreamSourceConfig
|
||||
{
|
||||
Name = "SRC",
|
||||
SubjectTransformPrefix = "agg.",
|
||||
SourceAccount = "A",
|
||||
});
|
||||
|
||||
var message = new StoredMessage
|
||||
{
|
||||
Sequence = 10,
|
||||
Subject = "orders.created",
|
||||
Payload = "ok"u8.ToArray(),
|
||||
TimestampUtc = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await mirror.OnOriginAppendAsync(message, default);
|
||||
await source.OnOriginAppendAsync(message, default);
|
||||
|
||||
mirror.LastOriginSequence.ShouldBe((ulong)10);
|
||||
source.LastOriginSequence.ShouldBe((ulong)10);
|
||||
|
||||
var sourced = await sourceTarget.LoadAsync(1, default);
|
||||
sourced.ShouldNotBeNull();
|
||||
sourced.Subject.ShouldBe("agg.orders.created");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using NATS.Server.JetStream.Models;
|
||||
using NATS.Server.JetStream.Validation;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
|
||||
public class JetStreamStreamFeatureToggleParityTests
|
||||
{
|
||||
[Fact]
|
||||
public void Stream_feature_toggles_are_preserved_in_config_model_and_validation()
|
||||
{
|
||||
var config = new StreamConfig
|
||||
{
|
||||
Name = "ORDERS",
|
||||
Subjects = ["orders.*"],
|
||||
Sealed = true,
|
||||
DenyDelete = true,
|
||||
DenyPurge = true,
|
||||
AllowDirect = true,
|
||||
MaxMsgSize = 1024,
|
||||
MaxMsgsPer = 10,
|
||||
MaxAgeMs = 5000,
|
||||
};
|
||||
|
||||
JetStreamConfigValidator.Validate(config).IsValid.ShouldBeTrue();
|
||||
config.Sealed.ShouldBeTrue();
|
||||
config.DenyDelete.ShouldBeTrue();
|
||||
config.DenyPurge.ShouldBeTrue();
|
||||
config.AllowDirect.ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using NATS.Server.JetStream.Models;
|
||||
using NATS.Server.JetStream.Publish;
|
||||
using NATS.Server.JetStream.Validation;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
|
||||
public class JetStreamStreamRuntimeParityTests
|
||||
{
|
||||
[Fact]
|
||||
public void Stream_runtime_enforces_retention_and_size_preconditions()
|
||||
{
|
||||
var invalid = new StreamConfig
|
||||
{
|
||||
Name = "ORDERS",
|
||||
Subjects = ["orders.*"],
|
||||
Retention = RetentionPolicy.WorkQueue,
|
||||
MaxConsumers = 0,
|
||||
MaxMsgSize = -1,
|
||||
};
|
||||
|
||||
var result = JetStreamConfigValidator.Validate(invalid);
|
||||
result.IsValid.ShouldBeFalse();
|
||||
|
||||
var preconditions = new PublishPreconditions();
|
||||
preconditions.Record("m1", 1);
|
||||
preconditions.IsDuplicate("m1", duplicateWindowMs: 10_000, out var existing).ShouldBeTrue();
|
||||
existing.ShouldBe((ulong)1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user