using NATS.Server.JetStream; using NATS.Server.JetStream.Api; using NATS.Server.JetStream.Cluster; using NATS.Server.JetStream.Models; namespace NATS.Server.JetStream.Tests; public class JetStreamMetaGroupTests { [Fact] public async Task Stream_create_requires_meta_group_commit() { await using var fixture = await MetaGroupTestFixture.StartAsync(nodes: 3); var result = await fixture.CreateStreamAsync("ORDERS", replicas: 3); result.Error.ShouldBeNull(); var meta = await fixture.GetMetaStateAsync(); meta.Streams.ShouldContain("ORDERS"); } } internal sealed class MetaGroupTestFixture : IAsyncDisposable { private readonly JetStreamMetaGroup _metaGroup; private readonly StreamManager _streamManager; private readonly ConsumerManager _consumerManager; private readonly JetStreamApiRouter _router; private MetaGroupTestFixture(JetStreamMetaGroup metaGroup, StreamManager streamManager, ConsumerManager consumerManager, JetStreamApiRouter router) { _metaGroup = metaGroup; _streamManager = streamManager; _consumerManager = consumerManager; _router = router; } public static Task StartAsync(int nodes) { var meta = new JetStreamMetaGroup(nodes); var streamManager = new StreamManager(meta); var consumerManager = new ConsumerManager(meta); var router = new JetStreamApiRouter(streamManager, consumerManager, meta); return Task.FromResult(new MetaGroupTestFixture(meta, streamManager, consumerManager, router)); } public Task CreateStreamAsync(string name, int replicas) { var response = _streamManager.CreateOrUpdate(new StreamConfig { Name = name, Subjects = [name.ToLowerInvariant() + ".*"], Replicas = replicas, }); return Task.FromResult(response); } public Task GetMetaStateAsync() => Task.FromResult(_metaGroup.GetState()); public Task RequestAsync(string subject, string payload) { return Task.FromResult(_router.Route(subject, System.Text.Encoding.UTF8.GetBytes(payload))); } public ValueTask DisposeAsync() => ValueTask.CompletedTask; }