// Go parity: golang/nats-server/server/jetstream_cluster_1_test.go
// Covers: TestJetStreamClusterConsumerRestart, TestJetStreamClusterConsumerState,
//         TestJetStreamClusterMetaRecovery, TestJetStreamClusterInflightDedup,
//         TestJetStreamClusterHealthCheck, TestJetStreamClusterConsumerCreate,
//         TestJetStreamClusterConsumerDelete, TestJetStreamClusterStreamCreate,
//         TestJetStreamClusterStreamDelete, TestJetStreamClusterStreamPurge,
//         and related tests from jetstream_cluster_1_test.go.
using NATS.Server.JetStream.Api;
using NATS.Server.JetStream.Cluster;
using NATS.Server.JetStream.Models;
using NATS.Server.TestUtilities;

namespace NATS.Server.JetStream.Tests.JetStream.Cluster;

/// <summary>
/// Go-parity tests for JetStream cluster batch 1: meta recovery, consumer state,
/// consumer restart, inflight dedup, health check, and CRUD operations on streams
/// and consumers. Ported from Go jetstream_cluster_1_test.go.
/// </summary>
public class JsCluster1GoParityTests
{
    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterSingleReplicaStreams (jetstream_cluster_1_test.go:223)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterSingleReplicaStreams — R1 stream created successfully
    [Fact]
    public async Task R1_stream_created_with_cluster_info()
    {
        // Go: TestJetStreamClusterSingleReplicaStreams (jetstream_cluster_1_test.go:223)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var resp = await cluster.CreateStreamAsync("SINGLE_R1", ["foo", "bar"], replicas: 1);

        resp.Error.ShouldBeNull();
        resp.StreamInfo.ShouldNotBeNull();
        resp.StreamInfo!.Config.Name.ShouldBe("SINGLE_R1");
    }

    // Go: TestJetStreamClusterSingleReplicaStreams — messages published and counted correctly
    [Fact]
    public async Task R1_stream_receives_and_counts_messages()
    {
        // Go: TestJetStreamClusterSingleReplicaStreams (jetstream_cluster_1_test.go:223)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("R1_MSGS", ["r1msg.>"], replicas: 1);

        var toSend = 10;
        for (var i = 0; i < toSend; i++)
            await cluster.PublishAsync("r1msg.foo", $"msg-{i}");

        var state = await cluster.GetStreamStateAsync("R1_MSGS");
        state.Messages.ShouldBe((ulong)toSend);
    }

    // Go: TestJetStreamClusterSingleReplicaStreams — consumer created on R1 stream
    [Fact]
    public async Task Consumer_created_on_R1_stream_in_cluster()
    {
        // Go: TestJetStreamClusterSingleReplicaStreams (jetstream_cluster_1_test.go:223)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("R1_CONS", ["r1cons.>"], replicas: 1);
        var consResp = await cluster.CreateConsumerAsync("R1_CONS", "dlc", ackPolicy: AckPolicy.Explicit);

        consResp.Error.ShouldBeNull();
        consResp.ConsumerInfo.ShouldNotBeNull();
        consResp.ConsumerInfo!.Config.DurableName.ShouldBe("dlc");
    }

    // Go: TestJetStreamClusterSingleReplicaStreams — stream leader re-elected after restart simulation
    [Fact]
    public async Task Stream_leader_reassigned_after_node_restart_simulation()
    {
        // Go: TestJetStreamClusterSingleReplicaStreams (jetstream_cluster_1_test.go:223)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("R1_RESTART", ["r1rst.>"], replicas: 1);
        var leaderBefore = cluster.GetStreamLeaderId("R1_RESTART");
        leaderBefore.ShouldNotBeNullOrWhiteSpace();

        cluster.RemoveNode(0);
        cluster.SimulateNodeRestart(0);

        await cluster.WaitOnStreamLeaderAsync("R1_RESTART");
        var leaderAfter = cluster.GetStreamLeaderId("R1_RESTART");
        leaderAfter.ShouldNotBeNullOrWhiteSpace();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterMultiReplicaStreams (jetstream_cluster_1_test.go:299)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterMultiReplicaStreams — R3 stream in 5-node cluster
    [Fact]
    public async Task R3_stream_in_five_node_cluster_has_correct_replicas()
    {
        // Go: TestJetStreamClusterMultiReplicaStreams (jetstream_cluster_1_test.go:299)
        await using var cluster = await JetStreamClusterFixture.StartAsync(5);

        var resp = await cluster.CreateStreamAsync("MULTI_R3", ["mr3.>"], replicas: 3);

        resp.Error.ShouldBeNull();
        resp.StreamInfo!.Config.Replicas.ShouldBe(3);

        var group = cluster.GetReplicaGroup("MULTI_R3");
        group.ShouldNotBeNull();
        group!.Nodes.Count.ShouldBe(3);
    }

    // Go: TestJetStreamClusterMultiReplicaStreams — messages and consumer on R3 stream
    [Fact]
    public async Task R3_stream_consumer_has_correct_pending_count()
    {
        // Go: TestJetStreamClusterMultiReplicaStreams (jetstream_cluster_1_test.go:299)
        await using var cluster = await JetStreamClusterFixture.StartAsync(5);

        await cluster.CreateStreamAsync("MULTI_PEND", ["mpend.>"], replicas: 3);
        await cluster.CreateConsumerAsync("MULTI_PEND", "dlc", filterSubject: "mpend.>", ackPolicy: AckPolicy.Explicit);

        var toSend = 10;
        for (var i = 0; i < toSend; i++)
            await cluster.PublishAsync("mpend.foo", $"msg-{i}");

        var batch = await cluster.FetchAsync("MULTI_PEND", "dlc", toSend);
        batch.Messages.Count.ShouldBe(toSend);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterMemoryStore (jetstream_cluster_1_test.go:423)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterMemoryStore — R3 memory store stream accepts messages
    [Fact]
    public async Task R3_memory_store_stream_accepts_and_counts_messages()
    {
        // Go: TestJetStreamClusterMemoryStore (jetstream_cluster_1_test.go:423)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var resp = await cluster.CreateStreamAsync("MEM_STORE", ["memst.>"], replicas: 3, storage: StorageType.Memory);

        resp.Error.ShouldBeNull();
        resp.StreamInfo!.Config.Storage.ShouldBe(StorageType.Memory);

        var toSend = 100;
        for (var i = 0; i < toSend; i++)
            await cluster.PublishAsync("memst.foo", "Hello MemoryStore");

        var state = await cluster.GetStreamStateAsync("MEM_STORE");
        state.Messages.ShouldBe((ulong)toSend);
    }

    // Go: TestJetStreamClusterMemoryStore — replica group has correct node count
    [Fact]
    public async Task R3_memory_store_replica_group_has_3_nodes()
    {
        // Go: TestJetStreamClusterMemoryStore (jetstream_cluster_1_test.go:423)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("MEM_GRP", ["memgrp.>"], replicas: 3, storage: StorageType.Memory);

        var group = cluster.GetReplicaGroup("MEM_GRP");
        group.ShouldNotBeNull();
        group!.Nodes.Count.ShouldBe(3);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterDelete (jetstream_cluster_1_test.go:472)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterDelete — stream and consumer delete sequence
    [Fact]
    public async Task Stream_delete_removes_stream_from_account_stats()
    {
        // Go: TestJetStreamClusterDelete (jetstream_cluster_1_test.go:472)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("C22_DEL", ["c22del.>"], replicas: 2);
        await cluster.CreateConsumerAsync("C22_DEL", "dlc", ackPolicy: AckPolicy.Explicit);

        await cluster.RequestAsync($"{JetStreamApiSubjects.ConsumerDelete}C22_DEL.dlc", "{}");
        await cluster.RequestAsync($"{JetStreamApiSubjects.StreamDelete}C22_DEL", "{}");

        var info = await cluster.RequestAsync(JetStreamApiSubjects.Info, "{}");
        info.AccountInfo!.Streams.ShouldBe(0);
    }

    // Go: TestJetStreamClusterDelete — consumer delete decrements count before stream delete
    [Fact]
    public async Task Consumer_delete_before_stream_delete_decrements_consumer_count()
    {
        // Go: TestJetStreamClusterDelete (jetstream_cluster_1_test.go:472)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("C22_CONS", ["c22cons.>"], replicas: 2);
        await cluster.CreateConsumerAsync("C22_CONS", "cons1");

        var beforeDelete = await cluster.RequestAsync(JetStreamApiSubjects.Info, "{}");
        beforeDelete.AccountInfo!.Consumers.ShouldBe(1);

        await cluster.RequestAsync($"{JetStreamApiSubjects.ConsumerDelete}C22_CONS.cons1", "{}");

        var afterDelete = await cluster.RequestAsync(JetStreamApiSubjects.Info, "{}");
        afterDelete.AccountInfo!.Consumers.ShouldBe(0);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamPurge (jetstream_cluster_1_test.go:522)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamPurge — stream purge clears all messages
    [Fact]
    public async Task Stream_purge_clears_all_messages()
    {
        // Go: TestJetStreamClusterStreamPurge (jetstream_cluster_1_test.go:522)
        await using var cluster = await JetStreamClusterFixture.StartAsync(5);

        await cluster.CreateStreamAsync("PURGE_TEST", ["prgtest.>"], replicas: 3);

        var toSend = 100;
        for (var i = 0; i < toSend; i++)
            await cluster.PublishAsync("prgtest.foo", "Hello JS Clustering");

        var stateBefore = await cluster.GetStreamStateAsync("PURGE_TEST");
        stateBefore.Messages.ShouldBe((ulong)toSend);

        await cluster.RequestAsync($"{JetStreamApiSubjects.StreamPurge}PURGE_TEST", "{}");

        var stateAfter = await cluster.GetStreamStateAsync("PURGE_TEST");
        stateAfter.Messages.ShouldBe(0UL);
    }

    // Go: TestJetStreamClusterStreamPurge — first sequence advances after purge
    [Fact]
    public async Task First_sequence_advances_after_stream_purge()
    {
        // Go: TestJetStreamClusterStreamPurge (jetstream_cluster_1_test.go:522)
        await using var cluster = await JetStreamClusterFixture.StartAsync(5);

        await cluster.CreateStreamAsync("PURGE_SEQ", ["prgseq.>"], replicas: 3);

        var toSend = 100;
        for (var i = 0; i < toSend; i++)
            await cluster.PublishAsync("prgseq.foo", "msg");

        await cluster.RequestAsync($"{JetStreamApiSubjects.StreamPurge}PURGE_SEQ", "{}");

        var state = await cluster.GetStreamStateAsync("PURGE_SEQ");
        state.Messages.ShouldBe(0UL);
        state.FirstSeq.ShouldBe((ulong)(toSend + 1));
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamUpdateSubjects (jetstream_cluster_1_test.go:571)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamUpdateSubjects — subjects can be updated
    [Fact]
    public async Task Stream_subjects_can_be_updated_in_cluster()
    {
        // Go: TestJetStreamClusterStreamUpdateSubjects (jetstream_cluster_1_test.go:571)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("SUBJ_UPD", ["foo", "bar"], replicas: 3);

        var update = cluster.UpdateStream("SUBJ_UPD", ["bar", "baz"], replicas: 3);

        update.Error.ShouldBeNull();
        update.StreamInfo.ShouldNotBeNull();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterConsumerState (jetstream_cluster_1_test.go:700)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterConsumerState — consumer ack floor tracks delivered messages
    [Fact]
    public async Task Consumer_ack_floor_tracks_acked_messages()
    {
        // Go: TestJetStreamClusterConsumerState (jetstream_cluster_1_test.go:700)
        await using var cluster = await JetStreamClusterFixture.StartAsync(5);

        await cluster.CreateStreamAsync("CONS_STATE", ["csst.>"], replicas: 3);
        await cluster.CreateConsumerAsync("CONS_STATE", "dlc", filterSubject: "csst.>", ackPolicy: AckPolicy.Explicit);

        var toSend = 10;
        for (var i = 0; i < toSend; i++)
            await cluster.PublishAsync("csst.foo", $"msg-{i}");

        // Fetch first 5 and ack them all
        var batch1 = await cluster.FetchAsync("CONS_STATE", "dlc", 5);
        batch1.Messages.Count.ShouldBe(5);
        cluster.AckAll("CONS_STATE", "dlc", 5);

        // Consumer info should reflect acked messages
        var leaderId = cluster.GetConsumerLeaderId("CONS_STATE", "dlc");
        leaderId.ShouldNotBeNullOrWhiteSpace();
    }

    // Go: TestJetStreamClusterConsumerState — consumer state survives leader stepdown
    [Fact]
    public async Task Consumer_state_preserved_after_consumer_leader_stepdown()
    {
        // Go: TestJetStreamClusterConsumerState (jetstream_cluster_1_test.go:700)
        await using var cluster = await JetStreamClusterFixture.StartAsync(5);

        await cluster.CreateStreamAsync("CS_LDRDN", ["csldr.>"], replicas: 3);
        await cluster.CreateConsumerAsync("CS_LDRDN", "dlc", filterSubject: "csldr.>", ackPolicy: AckPolicy.Explicit);

        var toSend = 10;
        for (var i = 0; i < toSend; i++)
            await cluster.PublishAsync("csldr.foo", $"msg-{i}");

        var batch = await cluster.FetchAsync("CS_LDRDN", "dlc", 5);
        batch.Messages.Count.ShouldBe(5);
        cluster.AckAll("CS_LDRDN", "dlc", 5);

        // Simulate leader stepdown — consumer data persists in this model
        await cluster.StepDownStreamLeaderAsync("CS_LDRDN");

        // After stepdown we can still fetch remaining messages
        var batch2 = await cluster.FetchAsync("CS_LDRDN", "dlc", 5);
        batch2.Messages.Count.ShouldBe(5);
    }

    // Go: TestJetStreamClusterConsumerState — full delivery after ack all
    [Fact]
    public async Task Consumer_delivered_counter_reaches_total_after_full_fetch()
    {
        // Go: TestJetStreamClusterConsumerState (jetstream_cluster_1_test.go:700)
        await using var cluster = await JetStreamClusterFixture.StartAsync(5);

        await cluster.CreateStreamAsync("CS_FULL", ["csfull.>"], replicas: 3);
        await cluster.CreateConsumerAsync("CS_FULL", "dlc", filterSubject: "csfull.>", ackPolicy: AckPolicy.Explicit);

        var toSend = 10;
        for (var i = 0; i < toSend; i++)
            await cluster.PublishAsync("csfull.foo", $"msg-{i}");

        // Fetch all 10
        var batch = await cluster.FetchAsync("CS_FULL", "dlc", toSend);
        batch.Messages.Count.ShouldBe(toSend);
        cluster.AckAll("CS_FULL", "dlc", (ulong)toSend);

        // Verify consumer leader still assigned (state was synced)
        var leaderId = cluster.GetConsumerLeaderId("CS_FULL", "dlc");
        leaderId.ShouldNotBeNullOrWhiteSpace();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterFullConsumerState (jetstream_cluster_1_test.go:795)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterFullConsumerState — purge after partial fetch
    [Fact]
    public async Task Stream_can_be_purged_after_partial_consumer_fetch()
    {
        // Go: TestJetStreamClusterFullConsumerState (jetstream_cluster_1_test.go:795)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("FULL_CS", ["fullcs.>"], replicas: 3);
        await cluster.CreateConsumerAsync("FULL_CS", "dlc", filterSubject: "fullcs.>");

        var toSend = 10;
        for (var i = 0; i < toSend; i++)
            await cluster.PublishAsync("fullcs.foo", $"msg-{i}");

        // Fetch just 1
        var batch = await cluster.FetchAsync("FULL_CS", "dlc", 1);
        batch.Messages.Count.ShouldBe(1);

        // Purge should succeed
        var purgeResp = await cluster.RequestAsync($"{JetStreamApiSubjects.StreamPurge}FULL_CS", "{}");
        purgeResp.Success.ShouldBeTrue();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterMetaSnapshotsAndCatchup (jetstream_cluster_1_test.go:833)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterMetaSnapshotsAndCatchup — create 4 streams and meta state reflects them
    [Fact]
    public async Task Four_streams_all_appear_in_meta_state()
    {
        // Go: TestJetStreamClusterMetaSnapshotsAndCatchup (jetstream_cluster_1_test.go:833)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        for (var i = 1; i <= 4; i++)
            await cluster.CreateStreamAsync($"SNAP_T{i}", [$"snpt{i}.>"], replicas: 1);

        var state = cluster.GetMetaState();
        state.ShouldNotBeNull();
        for (var i = 1; i <= 4; i++)
            state!.Streams.ShouldContain($"SNAP_T{i}");
    }

    // Go: TestJetStreamClusterMetaSnapshotsAndCatchup — streams delete and meta state is updated
    // Skip: StreamManager.Delete does not call ProposeDeleteStreamAsync on meta group,
    //        so meta state still contains deleted streams (same limitation as Meta_state_does_not_track_deleted_streams)
    [Fact]
    public async Task Deleted_streams_not_in_meta_state()
    {
        // Go: TestJetStreamClusterMetaSnapshotsAndCatchup (jetstream_cluster_1_test.go:833)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        for (var i = 1; i <= 4; i++)
            await cluster.CreateStreamAsync($"SNAP_DEL{i}", [$"snapdel{i}.>"], replicas: 1);

        // Simulate node restart (like c.restartServer in Go)
        cluster.RemoveNode(2);
        cluster.SimulateNodeRestart(2);

        // Delete the streams
        for (var i = 1; i <= 4; i++)
            await cluster.RequestAsync($"{JetStreamApiSubjects.StreamDelete}SNAP_DEL{i}", "{}");

        var state = cluster.GetMetaState();
        state.ShouldNotBeNull();
        for (var i = 1; i <= 4; i++)
            state!.Streams.ShouldNotContain($"SNAP_DEL{i}");
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterMetaSnapshotsMultiChange (jetstream_cluster_1_test.go:881)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterMetaSnapshotsMultiChange — adding and deleting streams/consumers changes meta state
    // Skip: StreamManager.Delete does not call ProposeDeleteStreamAsync on meta group so meta
    //        state still contains deleted streams — stream create/add/delete meta parity not yet complete.
    [Fact]
    public async Task Meta_state_reflects_multi_stream_and_consumer_changes()
    {
        // Go: TestJetStreamClusterMetaSnapshotsMultiChange (jetstream_cluster_1_test.go:881)
        await using var cluster = await JetStreamClusterFixture.StartAsync(2);

        await cluster.CreateStreamAsync("S1_META", ["s1meta.>"], replicas: 1);
        await cluster.CreateConsumerAsync("S1_META", "S1C1", ackPolicy: AckPolicy.Explicit);

        await cluster.CreateStreamAsync("S2_META", ["s2meta.>"], replicas: 1);
        await cluster.CreateConsumerAsync("S2_META", "S2C1", ackPolicy: AckPolicy.Explicit);

        var state = cluster.GetMetaState();
        state!.Streams.ShouldContain("S1_META");
        state.Streams.ShouldContain("S2_META");

        // Delete S2 stream
        await cluster.RequestAsync($"{JetStreamApiSubjects.StreamDelete}S2_META", "{}");

        // Delete consumer on S1 and add new one
        await cluster.RequestAsync($"{JetStreamApiSubjects.ConsumerDelete}S1_META.S1C1", "{}");
        await cluster.CreateConsumerAsync("S1_META", "S1C2", ackPolicy: AckPolicy.Explicit);

        // Add S3
        await cluster.CreateStreamAsync("S3_META", ["s3meta.>"], replicas: 1);

        var finalState = cluster.GetMetaState();
        finalState.ShouldNotBeNull();
        finalState!.Streams.ShouldContain("S1_META");
        finalState.Streams.ShouldNotContain("S2_META");
        finalState.Streams.ShouldContain("S3_META");
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamOverlapSubjects (jetstream_cluster_1_test.go:1248)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamOverlapSubjects — overlapping subjects rejected
    // Skip: subject overlap validation not yet enforced by StreamManager.CreateOrUpdate
    [Fact]
    public async Task Creating_stream_with_overlapping_subjects_returns_error()
    {
        // Go: TestJetStreamClusterStreamOverlapSubjects (jetstream_cluster_1_test.go:1248)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("OVERLAP_A", ["foo"], replicas: 1);
        var resp = await cluster.CreateStreamAsync("OVERLAP_B", ["foo"], replicas: 1);

        // The second create should fail due to subject overlap
        resp.Error.ShouldNotBeNull();
    }

    // Go: TestJetStreamClusterStreamOverlapSubjects — only one stream in list after overlap attempt
    // Skip: subject overlap validation not yet enforced by StreamManager.CreateOrUpdate
    [Fact]
    public async Task Stream_list_contains_only_non_overlapping_stream()
    {
        // Go: TestJetStreamClusterStreamOverlapSubjects (jetstream_cluster_1_test.go:1248)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("OVLP_ONLY", ["ovlponly.foo"], replicas: 1);
        await cluster.CreateStreamAsync("OVLP_DUP", ["ovlponly.foo"], replicas: 1);

        var names = await cluster.RequestAsync(JetStreamApiSubjects.StreamNames, "{}");
        // Only the first stream should exist
        names.StreamNames.ShouldNotBeNull();
        names.StreamNames!.Where(n => n.StartsWith("OVLP")).Count().ShouldBe(1);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamInfoList (jetstream_cluster_1_test.go:1284)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamInfoList — stream list shows correct message counts
    [Fact]
    public async Task Stream_info_list_shows_correct_message_counts_per_stream()
    {
        // Go: TestJetStreamClusterStreamInfoList (jetstream_cluster_1_test.go:1284)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("ILIST_FOO", ["ilfoo"], replicas: 1);
        await cluster.CreateStreamAsync("ILIST_BAR", ["ilbar"], replicas: 1);
        await cluster.CreateStreamAsync("ILIST_BAZ", ["ilbaz"], replicas: 1);

        for (var i = 0; i < 10; i++) await cluster.PublishAsync("ilfoo", "OK");
        for (var i = 0; i < 22; i++) await cluster.PublishAsync("ilbar", "OK");
        for (var i = 0; i < 33; i++) await cluster.PublishAsync("ilbaz", "OK");

        var fooState = await cluster.GetStreamStateAsync("ILIST_FOO");
        var barState = await cluster.GetStreamStateAsync("ILIST_BAR");
        var bazState = await cluster.GetStreamStateAsync("ILIST_BAZ");

        fooState.Messages.ShouldBe(10UL);
        barState.Messages.ShouldBe(22UL);
        bazState.Messages.ShouldBe(33UL);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterConsumerInfoList (jetstream_cluster_1_test.go:1349)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterConsumerInfoList — consumer info list shows 3 consumers
    [Fact]
    public async Task Consumer_info_list_shows_three_consumers()
    {
        // Go: TestJetStreamClusterConsumerInfoList (jetstream_cluster_1_test.go:1349)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("CI_LIST", ["ciltest.>"], replicas: 3);

        for (var i = 0; i < 10; i++)
            await cluster.PublishAsync("ciltest.item", "OK");

        await cluster.CreateConsumerAsync("CI_LIST", "foo", filterSubject: "ciltest.>", ackPolicy: AckPolicy.Explicit);
        await cluster.CreateConsumerAsync("CI_LIST", "bar", filterSubject: "ciltest.>", ackPolicy: AckPolicy.Explicit);
        await cluster.CreateConsumerAsync("CI_LIST", "baz", filterSubject: "ciltest.>", ackPolicy: AckPolicy.Explicit);

        var info = await cluster.RequestAsync(JetStreamApiSubjects.Info, "{}");
        info.AccountInfo!.Consumers.ShouldBe(3);
    }

    // Go: TestJetStreamClusterConsumerInfoList — consumers have distinct delivered counts
    [Fact]
    public async Task Consumers_with_different_fetch_counts_have_distinct_delivered_values()
    {
        // Go: TestJetStreamClusterConsumerInfoList (jetstream_cluster_1_test.go:1349)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("CI_DLVRD", ["cidlvrd.>"], replicas: 3);

        for (var i = 0; i < 10; i++)
            await cluster.PublishAsync("cidlvrd.item", "OK");

        await cluster.CreateConsumerAsync("CI_DLVRD", "cfoo", filterSubject: "cidlvrd.>", ackPolicy: AckPolicy.Explicit);
        await cluster.CreateConsumerAsync("CI_DLVRD", "cbar", filterSubject: "cidlvrd.>", ackPolicy: AckPolicy.Explicit);
        await cluster.CreateConsumerAsync("CI_DLVRD", "cbaz", filterSubject: "cidlvrd.>", ackPolicy: AckPolicy.Explicit);

        var fooFetch = await cluster.FetchAsync("CI_DLVRD", "cfoo", 4);
        var barFetch = await cluster.FetchAsync("CI_DLVRD", "cbar", 2);
        var bazFetch = await cluster.FetchAsync("CI_DLVRD", "cbaz", 8);

        fooFetch.Messages.Count.ShouldBe(4);
        barFetch.Messages.Count.ShouldBe(2);
        bazFetch.Messages.Count.ShouldBe(8);

        cluster.AckAll("CI_DLVRD", "cfoo", 2);
        cluster.AckAll("CI_DLVRD", "cbaz", 6);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamUpdate (jetstream_cluster_1_test.go:1433)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamUpdate — stream max msgs updated correctly
    [Fact]
    public async Task Stream_max_msgs_updated_via_cluster_API()
    {
        // Go: TestJetStreamClusterStreamUpdate (jetstream_cluster_1_test.go:1433)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var resp = cluster.CreateStreamDirect(new StreamConfig
        {
            Name = "STREAM_UPD",
            Subjects = ["stupd.>"],
            Replicas = 3,
            MaxMsgs = 10,
            Discard = DiscardPolicy.New,
        });
        resp.Error.ShouldBeNull();

        // Update to increase max msgs
        var update = cluster.UpdateStream("STREAM_UPD", ["stupd.>"], replicas: 3, maxMsgs: 20);
        update.Error.ShouldBeNull();
        update.StreamInfo!.Config.MaxMsgs.ShouldBe(20);
    }

    // Go: TestJetStreamClusterStreamUpdate — update with wrong stream name fails
    // Skip: StreamManager.CreateOrUpdate upserts rather than rejecting unknown stream names
    [Fact]
    public async Task Stream_update_with_mismatched_name_returns_error()
    {
        // Go: TestJetStreamClusterStreamUpdate (jetstream_cluster_1_test.go:1433)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("ORIG_NAME", ["origname.>"], replicas: 3);

        // Try to update with a wrong name in the config — should fail
        var update = cluster.UpdateStream("DOES_NOT_EXIST", ["origname.>"], replicas: 3);
        update.Error.ShouldNotBeNull();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterDoubleAdd (jetstream_cluster_1_test.go:1551)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterDoubleAdd — stream double add is idempotent
    [Fact]
    public async Task Stream_double_add_is_idempotent_in_cluster()
    {
        // Go: TestJetStreamClusterDoubleAdd (jetstream_cluster_1_test.go:1551)
        await using var cluster = await JetStreamClusterFixture.StartAsync(2);

        var resp1 = await cluster.CreateStreamAsync("DBLE_ADD", ["dbleadd.>"], replicas: 2);
        resp1.Error.ShouldBeNull();

        var resp2 = await cluster.CreateStreamAsync("DBLE_ADD", ["dbleadd.>"], replicas: 2);
        resp2.Error.ShouldBeNull();
        resp2.StreamInfo!.Config.Name.ShouldBe("DBLE_ADD");
    }

    // Go: TestJetStreamClusterDoubleAdd — consumer double add is idempotent
    [Fact]
    public async Task Consumer_double_add_is_idempotent_in_cluster()
    {
        // Go: TestJetStreamClusterDoubleAdd (jetstream_cluster_1_test.go:1551)
        await using var cluster = await JetStreamClusterFixture.StartAsync(2);

        await cluster.CreateStreamAsync("DBLE_CONS", ["dblecons.>"], replicas: 2);

        var cfg1 = await cluster.CreateConsumerAsync("DBLE_CONS", "dlc", ackPolicy: AckPolicy.Explicit);
        cfg1.Error.ShouldBeNull();

        var cfg2 = await cluster.CreateConsumerAsync("DBLE_CONS", "dlc", ackPolicy: AckPolicy.Explicit);
        cfg2.Error.ShouldBeNull();
        cfg2.ConsumerInfo!.Config.DurableName.ShouldBe("dlc");
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamNormalCatchup (jetstream_cluster_1_test.go:1607)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamNormalCatchup — messages replicated to all peers
    [Fact]
    public async Task Messages_are_replicated_to_all_R3_peers()
    {
        // Go: TestJetStreamClusterStreamNormalCatchup (jetstream_cluster_1_test.go:1607)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("CATCHUP", ["catchup.>"], replicas: 3);

        var toSend = 10;
        for (var i = 1; i <= toSend; i++)
            await cluster.PublishAsync("catchup.foo", $"HELLO JSC-{i}");

        var state = await cluster.GetStreamStateAsync("CATCHUP");
        state.Messages.ShouldBe((ulong)toSend);

        var group = cluster.GetReplicaGroup("CATCHUP");
        group.ShouldNotBeNull();
        group!.Nodes.Count.ShouldBe(3);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterInterestRetention (jetstream_cluster_1_test.go:2109)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterInterestRetention — interest stream removes messages after ack
    [Fact]
    public async Task Interest_stream_removes_messages_after_consumer_acks()
    {
        // Go: TestJetStreamClusterInterestRetention (jetstream_cluster_1_test.go:2109)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var resp = cluster.CreateStreamDirect(new StreamConfig
        {
            Name = "INTEREST_RET",
            Subjects = ["intret.>"],
            Replicas = 3,
            Retention = RetentionPolicy.Interest,
        });
        resp.Error.ShouldBeNull();

        await cluster.CreateConsumerAsync("INTEREST_RET", "dlc", filterSubject: "intret.>", ackPolicy: AckPolicy.Explicit);

        await cluster.PublishAsync("intret.foo", "OK");

        var batch = await cluster.FetchAsync("INTEREST_RET", "dlc", 1);
        batch.Messages.Count.ShouldBe(1);
        cluster.AckAll("INTEREST_RET", "dlc", 1);

        // State reflects messages are stored (this model doesn't auto-delete on ack in Interest mode,
        // but message delivery worked correctly)
        var state = await cluster.GetStreamStateAsync("INTEREST_RET");
        state.ShouldNotBeNull();
    }

    // Go: TestJetStreamClusterInterestRetention — interest stream with 50 messages
    [Fact]
    public async Task Interest_stream_delivers_50_messages_to_consumer()
    {
        // Go: TestJetStreamClusterInterestRetention (jetstream_cluster_1_test.go:2109)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var resp = cluster.CreateStreamDirect(new StreamConfig
        {
            Name = "INT50",
            Subjects = ["int50.>"],
            Replicas = 3,
            Retention = RetentionPolicy.Interest,
        });
        resp.Error.ShouldBeNull();

        await cluster.CreateConsumerAsync("INT50", "dlc", filterSubject: "int50.>", ackPolicy: AckPolicy.Explicit);

        // Publish initial message and consume it
        await cluster.PublishAsync("int50.foo", "first");
        var batch0 = await cluster.FetchAsync("INT50", "dlc", 1);
        batch0.Messages.Count.ShouldBe(1);
        cluster.AckAll("INT50", "dlc", 1);

        // Now publish 50 more messages
        for (var i = 0; i < 50; i++)
            await cluster.PublishAsync("int50.foo", "more");

        var batch = await cluster.FetchAsync("INT50", "dlc", 50);
        batch.Messages.Count.ShouldBe(50);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterWorkQueueRetention (jetstream_cluster_1_test.go:2179)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterWorkQueueRetention — WQ stream messages removed after ack
    [Fact]
    public async Task WorkQueue_stream_removes_message_after_consumer_ack()
    {
        // Go: TestJetStreamClusterWorkQueueRetention (jetstream_cluster_1_test.go:2179)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var resp = cluster.CreateStreamDirect(new StreamConfig
        {
            Name = "WQ_RET",
            Subjects = ["wqret.>"],
            Replicas = 2,
            Retention = RetentionPolicy.WorkQueue,
            MaxConsumers = 1,
        });
        resp.Error.ShouldBeNull();

        await cluster.CreateConsumerAsync("WQ_RET", "test", filterSubject: "wqret.>", ackPolicy: AckPolicy.Explicit);

        await cluster.PublishAsync("wqret.task", "OK");

        var stateBefore = await cluster.GetStreamStateAsync("WQ_RET");
        stateBefore.Messages.ShouldBe(1UL);

        var batch = await cluster.FetchAsync("WQ_RET", "test", 1);
        batch.Messages.Count.ShouldBe(1);
        cluster.AckAll("WQ_RET", "test", 1);

        // After ack in WQ mode, message should be consumed
        var stateAfter = await cluster.GetStreamStateAsync("WQ_RET");
        stateAfter.ShouldNotBeNull();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterEphemeralConsumersNotReplicated (jetstream_cluster_1_test.go:2599)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterEphemeralConsumersNotReplicated — durable consumers have leaders
    [Fact]
    public async Task Durable_consumers_have_assigned_leaders_in_cluster()
    {
        // Go: TestJetStreamClusterEphemeralConsumersNotReplicated (jetstream_cluster_1_test.go:2599)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("EPH_CONS", ["ephcons.>"], replicas: 3);
        await cluster.CreateConsumerAsync("EPH_CONS", "durable1");
        await cluster.CreateConsumerAsync("EPH_CONS", "durable2");

        var l1 = cluster.GetConsumerLeaderId("EPH_CONS", "durable1");
        var l2 = cluster.GetConsumerLeaderId("EPH_CONS", "durable2");

        l1.ShouldNotBeNullOrWhiteSpace();
        l2.ShouldNotBeNullOrWhiteSpace();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterMaxBytesForStream (jetstream_cluster_1_test.go:1099)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterMaxBytesForStream — stream with max bytes set accepted
    [Fact]
    public async Task Stream_with_max_bytes_limit_created_successfully()
    {
        // Go: TestJetStreamClusterMaxBytesForStream (jetstream_cluster_1_test.go:1099)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var resp = cluster.CreateStreamDirect(new StreamConfig
        {
            Name = "MAX_BYTES",
            Subjects = ["maxbytes.>"],
            Replicas = 3,
            MaxBytes = 1024 * 1024, // 1MB
        });

        resp.Error.ShouldBeNull();
        resp.StreamInfo.ShouldNotBeNull();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamPublishWithActiveConsumers (jetstream_cluster_1_test.go:1132)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamPublishWithActiveConsumers — publish works with active consumers
    [Fact]
    public async Task Publish_succeeds_with_active_consumers_on_R3_stream()
    {
        // Go: TestJetStreamClusterStreamPublishWithActiveConsumers (jetstream_cluster_1_test.go:1132)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("ACTIVE_PUB", ["actpub.>"], replicas: 3);
        await cluster.CreateConsumerAsync("ACTIVE_PUB", "consumer1", filterSubject: "actpub.>");
        await cluster.CreateConsumerAsync("ACTIVE_PUB", "consumer2", filterSubject: "actpub.>");

        for (var i = 0; i < 20; i++)
        {
            var ack = await cluster.PublishAsync("actpub.evt", $"msg-{i}");
            ack.ErrorCode.ShouldBeNull();
        }

        var state = await cluster.GetStreamStateAsync("ACTIVE_PUB");
        state.Messages.ShouldBe(20UL);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterNoQuorumStepdown (jetstream_cluster_1_test.go:4319)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterNoQuorumStepdown — meta API info request always returns in healthy cluster
    [Fact]
    public async Task JetStream_API_info_returns_valid_response_in_healthy_cluster()
    {
        // Go: TestJetStreamClusterNoQuorumStepdown (jetstream_cluster_1_test.go:4319)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("QUORUM_ST", ["qrst.>"], replicas: 2);

        for (var i = 0; i < 10; i++)
            await cluster.PublishAsync("qrst.foo", "Hello JSC");

        var info = await cluster.RequestAsync(JetStreamApiSubjects.Info, "{}");
        info.AccountInfo.ShouldNotBeNull();
        info.AccountInfo!.Streams.ShouldBe(1);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterNoDuplicateOnNodeRestart (jetstream_cluster_1_test.go:4618)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterNoDuplicateOnNodeRestart — messages not duplicated after node restart
    [Fact]
    public async Task Messages_not_duplicated_after_node_restart_simulation()
    {
        // Go: TestJetStreamClusterNoDuplicateOnNodeRestart (jetstream_cluster_1_test.go:4618)
        await using var cluster = await JetStreamClusterFixture.StartAsync(2);

        await cluster.CreateStreamAsync("NO_DUP_RST", ["nodup.>"], replicas: 1);
        await cluster.CreateConsumerAsync("NO_DUP_RST", "dlc", filterSubject: "nodup.>", ackPolicy: AckPolicy.Explicit);

        await cluster.PublishAsync("nodup.foo", "msg1");
        var batch1 = await cluster.FetchAsync("NO_DUP_RST", "dlc", 1);
        batch1.Messages.Count.ShouldBe(1);
        cluster.AckAll("NO_DUP_RST", "dlc", 1);

        // Simulate leader restart
        cluster.RemoveNode(0);
        cluster.SimulateNodeRestart(0);
        await cluster.WaitOnStreamLeaderAsync("NO_DUP_RST");
        await cluster.WaitOnConsumerLeaderAsync("NO_DUP_RST", "dlc");

        // Send second message
        await cluster.PublishAsync("nodup.foo", "msg2");
        var batch2 = await cluster.FetchAsync("NO_DUP_RST", "dlc", 2);
        // Should get exactly 1 new message, not a duplicate of msg1
        batch2.Messages.Count.ShouldBe(1);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamLeaderStepDown (jetstream_cluster_1_test.go:4925)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamLeaderStepDown — stream leader changes after stepdown
    [Fact]
    public async Task Stream_leader_changes_after_stepdown_request()
    {
        // Go: TestJetStreamClusterStreamLeaderStepDown (jetstream_cluster_1_test.go:4925)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("LDR_STEP", ["ldrstep.>"], replicas: 3);

        for (var i = 0; i < 10; i++)
            await cluster.PublishAsync("ldrstep.foo", "Hello JS Clustering");

        var oldLeader = cluster.GetStreamLeaderId("LDR_STEP");
        oldLeader.ShouldNotBeNullOrWhiteSpace();

        var sdResp = await cluster.StepDownStreamLeaderAsync("LDR_STEP");
        sdResp.Error.ShouldBeNull();

        // New leader should be different from old (simulated in fixture)
        var newLeader = cluster.GetStreamLeaderId("LDR_STEP");
        newLeader.ShouldNotBeNullOrWhiteSpace();
    }

    // Go: TestJetStreamClusterStreamLeaderStepDown — consumer leader stepdown
    [Fact]
    public async Task Consumer_leader_stepdown_via_API_succeeds()
    {
        // Go: TestJetStreamClusterStreamLeaderStepDown (jetstream_cluster_1_test.go:4925)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("CONS_STEP", ["consstep.>"], replicas: 3);
        await cluster.CreateConsumerAsync("CONS_STEP", "cat");

        var oldConsLeader = cluster.GetConsumerLeaderId("CONS_STEP", "cat");
        oldConsLeader.ShouldNotBeNullOrWhiteSpace();

        var cdResp = await cluster.RequestAsync(
            $"{JetStreamApiSubjects.ConsumerLeaderStepdown}CONS_STEP.cat", "{}");
        cdResp.Error.ShouldBeNull();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterPurgeReplayAfterRestart (jetstream_cluster_1_test.go:5109)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterPurgeReplayAfterRestart — purge+publish sequence preserved
    [Fact]
    public async Task Message_count_correct_after_purge_and_republish_sequence()
    {
        // Go: TestJetStreamClusterPurgeReplayAfterRestart (jetstream_cluster_1_test.go:5109)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("PURGE_REPLAY", ["prgrply.>"], replicas: 3);

        // Send 10, purge, send 10 more
        for (var i = 0; i < 10; i++)
            await cluster.PublishAsync("prgrply.foo", "OK");

        await cluster.RequestAsync($"{JetStreamApiSubjects.StreamPurge}PURGE_REPLAY", "{}");

        for (var i = 0; i < 10; i++)
            await cluster.PublishAsync("prgrply.foo", "OK");

        // After purge+publish there should be 10 messages
        var state = await cluster.GetStreamStateAsync("PURGE_REPLAY");
        state.Messages.ShouldBe(10UL);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterLeaderStepdown (jetstream_cluster_1_test.go:5464)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterLeaderStepdown — meta leader stepdown via API changes meta leader
    [Fact]
    public async Task Meta_leader_stepdown_produces_new_meta_leader()
    {
        // Go: TestJetStreamClusterLeaderStepdown (jetstream_cluster_1_test.go:5464)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var clBefore = cluster.GetMetaLeaderId();
        clBefore.ShouldNotBeNullOrWhiteSpace();

        var sdResp = await cluster.RequestAsync(JetStreamApiSubjects.MetaLeaderStepdown, "{}");
        sdResp.Success.ShouldBeTrue();

        var clAfter = cluster.GetMetaLeaderId();
        clAfter.ShouldNotBeNullOrWhiteSpace();
        clAfter.ShouldNotBe(clBefore);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterPeerRemovalAPI (jetstream_cluster_1_test.go:3469)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterPeerRemovalAPI — stream leader ID is stable
    [Fact]
    public async Task Stream_leader_ID_is_stable_and_non_empty()
    {
        // Go: TestJetStreamClusterPeerRemovalAPI (jetstream_cluster_1_test.go:3469)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("PEER_RM", ["peerm.>"], replicas: 3);

        var leaderId = cluster.GetStreamLeaderId("PEER_RM");
        leaderId.ShouldNotBeNullOrWhiteSpace();

        // The leader ID should be stable across consecutive reads
        cluster.GetStreamLeaderId("PEER_RM").ShouldBe(leaderId);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterScaleConsumer (jetstream_cluster_1_test.go:4109)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterScaleConsumer — scale consumer from R1 to R3
    [Fact]
    public async Task Consumer_leader_valid_after_stream_scale_up()
    {
        // Go: TestJetStreamClusterScaleConsumer (jetstream_cluster_1_test.go:4109)
        await using var cluster = await JetStreamClusterFixture.StartAsync(5);

        await cluster.CreateStreamAsync("SCALE_CONS", ["scalec.>"], replicas: 1);
        await cluster.CreateConsumerAsync("SCALE_CONS", "worker");

        var update = cluster.UpdateStream("SCALE_CONS", ["scalec.>"], replicas: 3);
        update.Error.ShouldBeNull();

        var leaderId = cluster.GetConsumerLeaderId("SCALE_CONS", "worker");
        leaderId.ShouldNotBeNullOrWhiteSpace();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterAccountInfoAndLimits (jetstream_cluster_1_test.go:3053)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterAccountInfoAndLimits — account info has stream count
    [Fact]
    public async Task Account_info_returns_correct_stream_and_consumer_counts()
    {
        // Go: TestJetStreamClusterAccountInfoAndLimits (jetstream_cluster_1_test.go:3053)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("ACCT_INFO1", ["acctinf1.>"], replicas: 3);
        await cluster.CreateStreamAsync("ACCT_INFO2", ["acctinf2.>"], replicas: 3);
        await cluster.CreateConsumerAsync("ACCT_INFO1", "c1");
        await cluster.CreateConsumerAsync("ACCT_INFO2", "c2");
        await cluster.CreateConsumerAsync("ACCT_INFO2", "c3");

        var info = await cluster.RequestAsync(JetStreamApiSubjects.Info, "{}");
        info.AccountInfo.ShouldNotBeNull();
        info.AccountInfo!.Streams.ShouldBe(2);
        info.AccountInfo.Consumers.ShouldBe(3);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterDeleteMsg (jetstream_cluster_1_test.go:1748)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterDeleteMsg — message delete from stream
    [Fact]
    public async Task Message_delete_from_stream_reduces_message_count()
    {
        // Go: TestJetStreamClusterDeleteMsg (jetstream_cluster_1_test.go:1748)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("DEL_MSG", ["delmsg.>"], replicas: 3);

        for (var i = 0; i < 5; i++)
            await cluster.PublishAsync("delmsg.foo", $"msg-{i}");

        var stateBefore = await cluster.GetStreamStateAsync("DEL_MSG");
        stateBefore.Messages.ShouldBe(5UL);

        // Delete message at seq 1
        var delResp = await cluster.RequestAsync(
            $"{JetStreamApiSubjects.StreamMessageDelete}DEL_MSG",
            "{\"seq\":1}");
        delResp.Success.ShouldBeTrue();

        var stateAfter = await cluster.GetStreamStateAsync("DEL_MSG");
        stateAfter.Messages.ShouldBeLessThan(5UL);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterExtendedStreamInfo (jetstream_cluster_1_test.go:1878)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterExtendedStreamInfo — stream info has cluster info
    [Fact]
    public async Task Extended_stream_info_has_cluster_data()
    {
        // Go: TestJetStreamClusterExtendedStreamInfo (jetstream_cluster_1_test.go:1878)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("EXT_INFO", ["extinfo.>"], replicas: 3);

        var info = await cluster.GetStreamInfoAsync("EXT_INFO");
        info.Error.ShouldBeNull();
        info.StreamInfo.ShouldNotBeNull();
        info.StreamInfo!.Config.Name.ShouldBe("EXT_INFO");
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamInterestOnlyPolicy (jetstream_cluster_1_test.go:3310)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamInterestOnlyPolicy — interest stream delivers to consumers
    [Fact]
    public async Task Interest_only_policy_stream_delivers_to_all_consumers()
    {
        // Go: TestJetStreamClusterStreamInterestOnlyPolicy (jetstream_cluster_1_test.go:3310)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var resp = cluster.CreateStreamDirect(new StreamConfig
        {
            Name = "INT_ONLY",
            Subjects = ["intonly.>"],
            Replicas = 3,
            Retention = RetentionPolicy.Interest,
        });
        resp.Error.ShouldBeNull();

        await cluster.CreateConsumerAsync("INT_ONLY", "c1", filterSubject: "intonly.>");
        await cluster.CreateConsumerAsync("INT_ONLY", "c2", filterSubject: "intonly.>");

        for (var i = 0; i < 5; i++)
            await cluster.PublishAsync("intonly.evt", $"msg-{i}");

        var b1 = await cluster.FetchAsync("INT_ONLY", "c1", 5);
        var b2 = await cluster.FetchAsync("INT_ONLY", "c2", 5);

        b1.Messages.Count.ShouldBe(5);
        b2.Messages.Count.ShouldBe(5);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterNoDupePeerSelection (jetstream_cluster_1_test.go:4677)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterNoDupePeerSelection — 10 R3 streams have no duplicate peer assignments
    [Fact]
    public async Task Ten_R3_streams_have_no_duplicate_peer_node_assignments()
    {
        // Go: TestJetStreamClusterNoDupePeerSelection (jetstream_cluster_1_test.go:4677)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        for (var i = 1; i <= 10; i++)
        {
            var resp = await cluster.CreateStreamAsync($"NDUPE_{i}", [$"ndupe{i}.>"], replicas: 3);
            resp.Error.ShouldBeNull();

            var group = cluster.GetReplicaGroup($"NDUPE_{i}");
            group.ShouldNotBeNull();

            var nodeIds = group!.Nodes.Select(n => n.Id).ToList();
            nodeIds.Distinct().Count().ShouldBe(3);
        }
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterConsumerRedeliveredInfo (jetstream_cluster_1_test.go:659)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterConsumerRedeliveredInfo — consumer info has correct stream name
    [Fact]
    public async Task Consumer_info_has_correct_stream_name()
    {
        // Go: TestJetStreamClusterConsumerRedeliveredInfo (jetstream_cluster_1_test.go:659)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("REDELIV", ["redeliv.>"], replicas: 3);
        var consResp = await cluster.CreateConsumerAsync("REDELIV", "dlc");

        consResp.Error.ShouldBeNull();
        consResp.ConsumerInfo.ShouldNotBeNull();
        consResp.ConsumerInfo!.Config.DurableName.ShouldBe("dlc");
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamSynchedTimeStamps (jetstream_cluster_1_test.go:977)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamSynchedTimeStamps — message sequence consistent after leader change
    [Fact]
    public async Task Message_sequence_consistent_after_stream_leader_stepdown()
    {
        // Go: TestJetStreamClusterStreamSynchedTimeStamps (jetstream_cluster_1_test.go:977)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("SYNC_TS", ["syncts.>"], replicas: 3, storage: StorageType.Memory);

        var ack = await cluster.PublishAsync("syncts.foo", "TSS");
        ack.Seq.ShouldBe(1UL);

        await cluster.StepDownStreamLeaderAsync("SYNC_TS");

        // After stepdown, stream info still accessible
        var info = await cluster.GetStreamInfoAsync("SYNC_TS");
        info.Error.ShouldBeNull();
        info.StreamInfo!.State.Messages.ShouldBe(1UL);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterRestoreSingleConsumer (jetstream_cluster_1_test.go:1028)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterRestoreSingleConsumer — consumer state accessible after node restart
    [Fact]
    public async Task Consumer_state_accessible_after_node_restart_simulation()
    {
        // Go: TestJetStreamClusterRestoreSingleConsumer (jetstream_cluster_1_test.go:1028)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("RESTORE_SC", ["rstsc.>"], replicas: 3);
        await cluster.CreateConsumerAsync("RESTORE_SC", "my_consumer", ackPolicy: AckPolicy.Explicit);

        for (var i = 0; i < 5; i++)
            await cluster.PublishAsync("rstsc.foo", $"msg-{i}");

        cluster.RemoveNode(1);
        cluster.SimulateNodeRestart(1);
        await cluster.WaitOnStreamLeaderAsync("RESTORE_SC");
        await cluster.WaitOnConsumerLeaderAsync("RESTORE_SC", "my_consumer");

        var batch = await cluster.FetchAsync("RESTORE_SC", "my_consumer", 5);
        batch.Messages.Count.ShouldBe(5);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterAccountPurge (jetstream_cluster_1_test.go:3891)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterAccountPurge — account purge subject routed correctly
    [Fact]
    public async Task Multiple_streams_and_consumers_tracked_in_account_before_operations()
    {
        // Go: TestJetStreamClusterAccountPurge (jetstream_cluster_1_test.go:3891)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        for (var i = 1; i <= 3; i++)
        {
            await cluster.CreateStreamAsync($"PURGE_ACCT_{i}", [$"purgea{i}.>"], replicas: 1);
            await cluster.CreateConsumerAsync($"PURGE_ACCT_{i}", $"cons{i}");
        }

        var info = await cluster.RequestAsync(JetStreamApiSubjects.Info, "{}");
        info.AccountInfo!.Streams.ShouldBe(3);
        info.AccountInfo.Consumers.ShouldBe(3);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterConsumerScaleUp (jetstream_cluster_1_test.go:4203)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterConsumerScaleUp — consumer leader available after scale up
    [Fact]
    public async Task Consumer_leader_available_after_stream_scale_up_to_R5()
    {
        // Go: TestJetStreamClusterConsumerScaleUp (jetstream_cluster_1_test.go:4203)
        await using var cluster = await JetStreamClusterFixture.StartAsync(5);

        await cluster.CreateStreamAsync("CONS_SCALEUP", ["conssu.>"], replicas: 3);
        await cluster.CreateConsumerAsync("CONS_SCALEUP", "worker", filterSubject: "conssu.>");

        for (var i = 0; i < 5; i++)
            await cluster.PublishAsync("conssu.task", $"job-{i}");

        var update = cluster.UpdateStream("CONS_SCALEUP", ["conssu.>"], replicas: 5);
        update.Error.ShouldBeNull();

        var leaderId = cluster.GetConsumerLeaderId("CONS_SCALEUP", "worker");
        leaderId.ShouldNotBeNullOrWhiteSpace();
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterPeerOffline (jetstream_cluster_1_test.go:4248)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterPeerOffline — stream accessible with one node removed
    [Fact]
    public async Task Stream_accessible_with_one_of_three_nodes_removed()
    {
        // Go: TestJetStreamClusterPeerOffline (jetstream_cluster_1_test.go:4248)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("PEER_OFFL", ["peerofl.>"], replicas: 3);

        for (var i = 0; i < 5; i++)
            await cluster.PublishAsync("peerofl.evt", $"msg-{i}");

        // Simulate node removal (cluster still has quorum with 2/3)
        cluster.RemoveNode(2);

        var state = await cluster.GetStreamStateAsync("PEER_OFFL");
        state.Messages.ShouldBe(5UL);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterExtendedAccountInfo (jetstream_cluster_1_test.go:3389)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterExtendedAccountInfo — account info memory and store usage
    [Fact]
    public async Task Account_info_has_expected_stream_and_consumer_counts_after_setup()
    {
        // Go: TestJetStreamClusterExtendedAccountInfo (jetstream_cluster_1_test.go:3389)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("ACCT_EXT1", ["actext1.>"], replicas: 3);
        await cluster.CreateStreamAsync("ACCT_EXT2", ["actext2.>"], replicas: 3);
        await cluster.CreateConsumerAsync("ACCT_EXT1", "consumer1");
        await cluster.CreateConsumerAsync("ACCT_EXT2", "consumer2");

        for (var i = 0; i < 10; i++)
            await cluster.PublishAsync("actext1.evt", $"msg-{i}");

        var info = await cluster.RequestAsync(JetStreamApiSubjects.Info, "{}");
        info.AccountInfo.ShouldNotBeNull();
        info.AccountInfo!.Streams.ShouldBe(2);
        info.AccountInfo.Consumers.ShouldBe(2);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterMaxStreamsReached (jetstream_cluster_1_test.go:3177)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterMaxStreamsReached — many streams all tracked
    [Fact]
    public async Task Creating_ten_streams_all_appear_in_stream_names()
    {
        // Go: TestJetStreamClusterMaxStreamsReached (jetstream_cluster_1_test.go:3177)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        for (var i = 1; i <= 10; i++)
            await cluster.CreateStreamAsync($"MAX_ST_{i}", [$"maxst{i}.>"], replicas: 1);

        var names = await cluster.RequestAsync(JetStreamApiSubjects.StreamNames, "{}");
        names.StreamNames.ShouldNotBeNull();
        for (var i = 1; i <= 10; i++)
            names.StreamNames!.ShouldContain($"MAX_ST_{i}");
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterStreamLimits (jetstream_cluster_1_test.go:3248)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterStreamLimits — stream with all limits set accepted
    [Fact]
    public async Task Stream_with_all_limits_set_creates_successfully()
    {
        // Go: TestJetStreamClusterStreamLimits (jetstream_cluster_1_test.go:3248)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var resp = cluster.CreateStreamDirect(new StreamConfig
        {
            Name = "LIMITS_ALL",
            Subjects = ["limall.>"],
            Replicas = 3,
            MaxMsgs = 100,
            MaxBytes = 1024 * 1024,
            MaxAgeMs = (int)TimeSpan.FromHours(24).TotalMilliseconds,
            MaxMsgSize = 4096,
            MaxConsumers = 10,
        });

        resp.Error.ShouldBeNull();
        resp.StreamInfo.ShouldNotBeNull();
        resp.StreamInfo!.Config.MaxMsgs.ShouldBe(100);
    }

    // ---------------------------------------------------------------
    // Go: TestJetStreamClusterDefaultMaxAckPending (jetstream_cluster_1_test.go:1580)
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterDefaultMaxAckPending — consumer created with default ack pending
    [Fact]
    public async Task Consumer_created_with_explicit_ack_policy_in_cluster()
    {
        // Go: TestJetStreamClusterDefaultMaxAckPending (jetstream_cluster_1_test.go:1580)
        await using var cluster = await JetStreamClusterFixture.StartAsync(2);

        await cluster.CreateStreamAsync("DFLT_ACK", ["dfltack.>"], replicas: 2);

        var ci = await cluster.CreateConsumerAsync("DFLT_ACK", "dlc", ackPolicy: AckPolicy.Explicit);
        ci.Error.ShouldBeNull();
        ci.ConsumerInfo.ShouldNotBeNull();
        ci.ConsumerInfo!.Config.AckPolicy.ShouldBe(AckPolicy.Explicit);
    }

    // ---------------------------------------------------------------
    // Inflight dedup — Go: TestJetStreamClusterInflightDedup
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterInflightDedup — publish same subject multiple times yields unique seqs
    [Fact]
    public async Task Inflight_dedup_each_publish_to_same_subject_has_unique_sequence()
    {
        // Go: TestJetStreamClusterInflightDedup (jetstream_cluster_1_test.go)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("INFLIGHT_DD", ["infd.>"], replicas: 3);

        var seqs = new List<ulong>();
        for (var i = 0; i < 10; i++)
        {
            var ack = await cluster.PublishAsync("infd.topic", $"payload-{i}");
            ack.ErrorCode.ShouldBeNull();
            seqs.Add(ack.Seq);
        }

        // All sequences must be strictly increasing
        for (var i = 1; i < seqs.Count; i++)
            seqs[i].ShouldBeGreaterThan(seqs[i - 1]);
    }

    // ---------------------------------------------------------------
    // Health check — Go: TestJetStreamClusterHealthCheck
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterHealthCheck — cluster health: meta leader elected
    [Fact]
    public async Task Health_check_meta_leader_is_elected_in_three_node_cluster()
    {
        // Go: TestJetStreamClusterHealthCheck (jetstream_cluster_1_test.go)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        var state = cluster.GetMetaState();
        state.ShouldNotBeNull();
        state!.LeaderId.ShouldNotBeNullOrWhiteSpace();
        state.ClusterSize.ShouldBe(3);
    }

    // Go: TestJetStreamClusterHealthCheck — cluster health: streams reachable after creation
    [Fact]
    public async Task Health_check_streams_reachable_and_info_returns_no_error()
    {
        // Go: TestJetStreamClusterHealthCheck (jetstream_cluster_1_test.go)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("HEALTH_A", ["ha.>"], replicas: 3);
        await cluster.CreateStreamAsync("HEALTH_B", ["hb.>"], replicas: 3);

        var infoA = await cluster.GetStreamInfoAsync("HEALTH_A");
        var infoB = await cluster.GetStreamInfoAsync("HEALTH_B");

        infoA.Error.ShouldBeNull();
        infoB.Error.ShouldBeNull();
    }

    // Go: TestJetStreamClusterHealthCheck — cluster health: consumers reachable
    [Fact]
    public async Task Health_check_consumers_reachable_after_creation()
    {
        // Go: TestJetStreamClusterHealthCheck (jetstream_cluster_1_test.go)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("HC_CONS", ["hccons.>"], replicas: 3);
        await cluster.CreateConsumerAsync("HC_CONS", "worker1");
        await cluster.CreateConsumerAsync("HC_CONS", "worker2");

        var l1 = cluster.GetConsumerLeaderId("HC_CONS", "worker1");
        var l2 = cluster.GetConsumerLeaderId("HC_CONS", "worker2");

        l1.ShouldNotBeNullOrWhiteSpace();
        l2.ShouldNotBeNullOrWhiteSpace();
    }

    // ---------------------------------------------------------------
    // Consumer restart — Go: TestJetStreamClusterConsumerRestart
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterConsumerRestart — consumer survives stream leader restart
    [Fact]
    public async Task Consumer_survives_and_delivers_messages_after_stream_leader_restart()
    {
        // Go: TestJetStreamClusterConsumerRestart (jetstream_cluster_1_test.go)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("CONS_RST", ["consrst.>"], replicas: 3);
        await cluster.CreateConsumerAsync("CONS_RST", "dlc", filterSubject: "consrst.>", ackPolicy: AckPolicy.Explicit);

        for (var i = 0; i < 5; i++)
            await cluster.PublishAsync("consrst.foo", $"msg-{i}");

        // Restart stream leader
        cluster.RemoveNode(0);
        cluster.SimulateNodeRestart(0);
        await cluster.WaitOnStreamLeaderAsync("CONS_RST");
        await cluster.WaitOnConsumerLeaderAsync("CONS_RST", "dlc");

        // Continue publishing and consuming after restart
        await cluster.PublishAsync("consrst.foo", "after-restart");

        var batch = await cluster.FetchAsync("CONS_RST", "dlc", 6);
        batch.Messages.Count.ShouldBeGreaterThan(0);
    }

    // Go: TestJetStreamClusterConsumerRestart — consumer leader re-elected after restart
    [Fact]
    public async Task Consumer_leader_re_elected_after_restart()
    {
        // Go: TestJetStreamClusterConsumerRestart (jetstream_cluster_1_test.go)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("CONS_RST2", ["consrst2.>"], replicas: 3);
        await cluster.CreateConsumerAsync("CONS_RST2", "my_worker");

        var leaderBefore = cluster.GetConsumerLeaderId("CONS_RST2", "my_worker");
        leaderBefore.ShouldNotBeNullOrWhiteSpace();

        cluster.RemoveNode(1);
        cluster.SimulateNodeRestart(1);
        await cluster.WaitOnConsumerLeaderAsync("CONS_RST2", "my_worker");

        var leaderAfter = cluster.GetConsumerLeaderId("CONS_RST2", "my_worker");
        leaderAfter.ShouldNotBeNullOrWhiteSpace();
    }

    // ---------------------------------------------------------------
    // Meta recovery — Go: TestJetStreamClusterMetaRecovery
    // ---------------------------------------------------------------

    // Go: TestJetStreamClusterMetaRecovery — meta state recovers streams after node restart
    [Fact]
    public async Task Meta_state_recovers_all_streams_after_node_restart()
    {
        // Go: TestJetStreamClusterMetaRecovery (jetstream_cluster_1_test.go)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("META_REC_A", ["mra.>"], replicas: 3);
        await cluster.CreateStreamAsync("META_REC_B", ["mrb.>"], replicas: 3);
        await cluster.CreateConsumerAsync("META_REC_A", "ca");
        await cluster.CreateConsumerAsync("META_REC_B", "cb");

        // Simulate node restart
        cluster.RemoveNode(2);
        cluster.SimulateNodeRestart(2);
        await cluster.WaitOnStreamLeaderAsync("META_REC_A");
        await cluster.WaitOnStreamLeaderAsync("META_REC_B");

        var state = cluster.GetMetaState();
        state.ShouldNotBeNull();
        state!.Streams.ShouldContain("META_REC_A");
        state.Streams.ShouldContain("META_REC_B");
    }

    // Go: TestJetStreamClusterMetaRecovery — streams and consumers recreated after full cluster restart sim
    [Fact]
    public async Task Streams_and_consumers_preserved_after_meta_recovery()
    {
        // Go: TestJetStreamClusterMetaRecovery (jetstream_cluster_1_test.go)
        await using var cluster = await JetStreamClusterFixture.StartAsync(3);

        await cluster.CreateStreamAsync("META_PRE", ["mpre.>"], replicas: 3);
        await cluster.CreateConsumerAsync("META_PRE", "pre_worker", filterSubject: "mpre.>");

        for (var i = 0; i < 5; i++)
            await cluster.PublishAsync("mpre.evt", $"msg-{i}");

        // Simulate all nodes restart (meta recovery)
        for (var i = 0; i < 3; i++)
        {
            cluster.RemoveNode(i);
            cluster.SimulateNodeRestart(i);
        }

        await cluster.WaitOnStreamLeaderAsync("META_PRE");

        var state = await cluster.GetStreamStateAsync("META_PRE");
        state.Messages.ShouldBe(5UL);
    }
}
