// Copyright 2020-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build !skip_js_tests && !skip_js_cluster_tests

package server

import (
	"bytes"
	"context"
	crand "crypto/rand"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"math/rand"
	"os"
	"path/filepath"
	"reflect"
	"slices"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/klauspost/compress/s2"
	"github.com/nats-io/jwt/v2"
	"github.com/nats-io/nats.go"
)

func TestJetStreamClusterConfig(t *testing.T) {
	conf := createConfFile(t, []byte(`
		listen: 127.0.0.1:-1
		jetstream: {max_mem_store: 16GB, max_file_store: 10TB, store_dir: '%s'}
		cluster { listen: 127.0.0.1:-1 }
	`))

	check := func(errStr string) {
		t.Helper()
		opts, err := ProcessConfigFile(conf)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if _, err := NewServer(opts); err == nil || !strings.Contains(err.Error(), errStr) {
			t.Fatalf("Expected an error of `%s`, got `%v`", errStr, err)
		}
	}

	check("requires `server_name`")

	conf = createConfFile(t, []byte(`
		listen: 127.0.0.1:-1
		server_name: "TEST"
		jetstream: {max_mem_store: 16GB, max_file_store: 10TB, store_dir: '%s'}
		cluster { listen: 127.0.0.1:-1 }
	`))

	check("requires `cluster.name`")
}

func TestJetStreamClusterLeader(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "JSC", 3)
	defer c.shutdown()

	// Kill our current leader and force an election.
	c.leader().Shutdown()
	c.waitOnLeader()

	// Now killing our current leader should leave us leaderless.
	c.leader().Shutdown()
	c.expectNoLeader()
}

func TestJetStreamClusterExpand(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "JSC", 2)
	defer c.shutdown()

	c.addInNewServer()
	c.waitOnPeerCount(3)
}

func TestJetStreamClusterAccountInfo(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "JSC", 3)
	defer c.shutdown()

	nc := clientConnectToServer(t, c.randomServer())
	defer nc.Close()

	reply := nats.NewInbox()
	sub, _ := nc.SubscribeSync(reply)

	if err := nc.PublishRequest(JSApiAccountInfo, reply, nil); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	checkSubsPending(t, sub, 1)
	resp, _ := sub.NextMsg(0)

	var info JSApiAccountInfoResponse
	if err := json.Unmarshal(resp.Data, &info); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if info.JetStreamAccountStats == nil || info.Error != nil {
		t.Fatalf("Did not receive correct response: %+v", info.Error)
	}
	// Make sure we only got 1 response.
	// Technically this will always work since its a singleton service export.
	if nmsgs, _, _ := sub.Pending(); nmsgs > 0 {
		t.Fatalf("Expected only a single response, got %d more", nmsgs)
	}
}

func TestJetStreamClusterStreamLimitWithAccountDefaults(t *testing.T) {
	// 2MB memory, 8MB disk
	c := createJetStreamClusterWithTemplate(t, jsClusterLimitsTempl, "R3L", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomNonLeader()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 2,
		MaxBytes: 4 * 1024 * 1024,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = js.AddStream(&nats.StreamConfig{
		Name:     "TEST2",
		Replicas: 2,
		MaxBytes: 15 * 1024 * 1024,
	})
	require_Contains(t, err.Error(), "no suitable peers for placement", "insufficient storage")
}

func TestJetStreamClusterInfoRaftGroup(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R1S", 3)
	defer c.shutdown()

	s := c.randomNonLeader()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	acc := s.GlobalAccount()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Storage:  nats.FileStorage,
		Replicas: 3,
	})
	require_NoError(t, err)

	nfoResp, err := nc.Request("$JS.API.STREAM.INFO.TEST", nil, time.Second)
	require_NoError(t, err)

	var si StreamInfo
	err = json.Unmarshal(nfoResp.Data, &si)
	require_NoError(t, err)

	if si.Cluster == nil {
		t.Fatalf("Expected cluster info, got none")
	}

	stream, err := acc.lookupStream("TEST")
	require_NoError(t, err)

	if si.Cluster.RaftGroup != stream.raftGroup().Name {
		t.Fatalf("Expected raft group %q to equal %q", si.Cluster.RaftGroup, stream.raftGroup().Name)
	}

	var sscfg StreamConfig
	rCfgData, err := os.ReadFile(filepath.Join(s.opts.StoreDir, "jetstream", "$SYS", "_js_", stream.raftGroup().Name, "meta.inf"))
	require_NoError(t, err)
	err = json.Unmarshal(rCfgData, &sscfg)
	require_NoError(t, err)
	if !reflect.DeepEqual(sscfg.Metadata, map[string]string{"account": "$G", "stream": "TEST", "type": "stream"}) {
		t.Fatalf("Invalid raft stream metadata: %v", sscfg.Metadata)
	}

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "DURABLE", Replicas: 3})
	require_NoError(t, err)

	consumer := stream.lookupConsumer("DURABLE")

	var ci ConsumerInfo
	nfoResp, err = nc.Request("$JS.API.CONSUMER.INFO.TEST.DURABLE", nil, time.Second)
	require_NoError(t, err)

	var cscfg ConsumerConfig
	rCfgData, err = os.ReadFile(filepath.Join(s.opts.StoreDir, "jetstream", "$SYS", "_js_", consumer.raftGroup().Name, "meta.inf"))
	require_NoError(t, err)
	err = json.Unmarshal(rCfgData, &cscfg)
	require_NoError(t, err)
	if !reflect.DeepEqual(cscfg.Metadata, map[string]string{"account": "$G", "consumer": "DURABLE", "stream": "TEST", "type": "consumer"}) {
		t.Fatalf("Invalid raft stream metadata: %v", cscfg.Metadata)
	}

	err = json.Unmarshal(nfoResp.Data, &ci)
	require_NoError(t, err)

	if ci.Cluster.RaftGroup != consumer.raftGroup().Name {
		t.Fatalf("Expected raft group %q to equal %q", ci.Cluster.RaftGroup, consumer.raftGroup().Name)
	}
}

func TestJetStreamClusterSingleReplicaStreams(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R1S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomNonLeader()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	si, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.Cluster == nil {
		t.Fatalf("Expected si to have cluster info")
	}
	// Send in 10 messages.
	msg, toSend := []byte("Hello JS Clustering"), 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}
	// Now grab info for this stream.
	si, err = js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si == nil || si.Config.Name != "TEST" {
		t.Fatalf("StreamInfo is not correct %+v", si)
	}
	// Check active state as well, shows that the owner answered.
	if si.State.Msgs != uint64(toSend) {
		t.Fatalf("Expected %d msgs, got bad state: %+v", toSend, si.State)
	}
	// Now create a consumer. This should be pinned to same server that our stream was allocated to.
	// First do a normal sub.
	sub, err := js.SubscribeSync("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkSubsPending(t, sub, toSend)

	// Now create a consumer as well.
	ci, err := js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if ci == nil || ci.Name != "dlc" || ci.Stream != "TEST" {
		t.Fatalf("ConsumerInfo is not correct %+v", ci)
	}

	// Now make sure that if we kill and restart the server that this stream and consumer return.
	sl := c.streamLeader("$G", "TEST")
	sl.Shutdown()
	c.restartServer(sl)

	c.waitOnStreamLeader("$G", "TEST")
	si, err = js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si == nil || si.Config.Name != "TEST" {
		t.Fatalf("StreamInfo is not correct %+v", si)
	}
	// Now durable consumer.
	c.waitOnConsumerLeader("$G", "TEST", "dlc")
	if _, err = js.ConsumerInfo("TEST", "dlc"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
}

func TestJetStreamClusterMultiReplicaStreams(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "RNS", 5)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Send in 10 messages.
	msg, toSend := []byte("Hello JS Clustering"), 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	// Now grab info for this stream.
	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si == nil || si.Config.Name != "TEST" {
		t.Fatalf("StreamInfo is not correct %+v", si)
	}
	// Check active state as well, shows that the owner answered.
	if si.State.Msgs != uint64(toSend) {
		t.Fatalf("Expected %d msgs, got bad state: %+v", toSend, si.State)
	}
	// Now create a consumer. This should be affinitize to the same set of servers as the stream.
	// First do a normal sub.
	sub, err := js.SubscribeSync("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkSubsPending(t, sub, toSend)

	// Now create a consumer as well.
	ci, err := js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if ci == nil || ci.Name != "dlc" || ci.Stream != "TEST" || ci.NumPending != uint64(toSend) {
		t.Fatalf("ConsumerInfo is not correct %+v", ci)
	}
}

func TestJetStreamClusterMultiReplicaStreamsDefaultFileMem(t *testing.T) {
	const testConfig = `
	listen: 127.0.0.1:-1
	server_name: %s
	jetstream: {store_dir: '%s'}

	cluster {
		name: %s
		listen: 127.0.0.1:%d
		routes = [%s]
	}
`
	c := createJetStreamClusterWithTemplate(t, testConfig, "RNS", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
		MaxBytes: 1024,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Send in 10 messages.
	msg, toSend := []byte("Hello JS Clustering"), 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	// Now grab info for this stream.
	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si == nil || si.Config.Name != "TEST" {
		t.Fatalf("StreamInfo is not correct %+v", si)
	}
	// Check active state as well, shows that the owner answered.
	if si.State.Msgs != uint64(toSend) {
		t.Fatalf("Expected %d msgs, got bad state: %+v", toSend, si.State)
	}
	// Now create a consumer. This should be affinitize to the same set of servers as the stream.
	// First do a normal sub.
	sub, err := js.SubscribeSync("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkSubsPending(t, sub, toSend)

	// Now create a consumer as well.
	ci, err := js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if ci == nil || ci.Name != "dlc" || ci.Stream != "TEST" || ci.NumPending != uint64(toSend) {
		t.Fatalf("ConsumerInfo is not correct %+v", ci)
	}
}

func TestJetStreamClusterMemoryStore(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3M", 3)
	defer c.shutdown()

	// Client based API
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
		Storage:  nats.MemoryStorage,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Send in 100 messages.
	msg, toSend := []byte("Hello MemoryStore"), 100
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}
	// Now grab info for this stream.
	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si == nil || si.Config.Name != "TEST" {
		t.Fatalf("StreamInfo is not correct %+v", si)
	}
	if si.Cluster == nil || len(si.Cluster.Replicas) != 2 {
		t.Fatalf("Cluster info is incorrect: %+v", si.Cluster)
	}
	// Check active state as well, shows that the owner answered.
	if si.State.Msgs != uint64(toSend) {
		t.Fatalf("Expected %d msgs, got bad state: %+v", toSend, si.State)
	}
	// Do a normal sub.
	sub, err := js.SubscribeSync("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkSubsPending(t, sub, toSend)
}

func TestJetStreamClusterDelete(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "RNS", 3)
	defer c.shutdown()

	// Client for API requests.
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	cfg := &nats.StreamConfig{
		Name:     "C22",
		Subjects: []string{"foo", "bar", "baz"},
		Replicas: 2,
		Storage:  nats.FileStorage,
		MaxMsgs:  100,
	}
	if _, err := js.AddStream(cfg); err != nil {
		t.Fatalf("Error adding stream: %v", err)
	}

	// Now create a consumer.
	if _, err := js.AddConsumer("C22", &nats.ConsumerConfig{
		Durable:   "dlc",
		AckPolicy: nats.AckExplicitPolicy,
	}); err != nil {
		t.Fatalf("Error adding consumer: %v", err)
	}

	// Now delete the consumer.
	if err := js.DeleteConsumer("C22", "dlc"); err != nil {
		t.Fatalf("Error deleting consumer: %v", err)
	}

	// Now delete the stream.
	if err := js.DeleteStream("C22"); err != nil {
		t.Fatalf("Error deleting stream: %v", err)
	}

	// This will get the current information about usage and limits for this account.
	checkFor(t, time.Second, 15*time.Millisecond, func() error {
		info, err := js.AccountInfo()
		if err != nil {
			return err
		}
		if info.Streams != 0 {
			return fmt.Errorf("Expected no remaining streams, got %d", info.Streams)
		}
		return nil
	})
}

func TestJetStreamClusterStreamPurge(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	s := c.randomServer()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	msg, toSend := []byte("Hello JS Clustering"), 100
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	// Now grab info for this stream.
	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Check active state as well, shows that the owner answered.
	if si.State.Msgs != uint64(toSend) {
		t.Fatalf("Expected %d msgs, got bad state: %+v", toSend, si.State)
	}

	// Now purge the stream.
	if err := js.PurgeStream("TEST"); err != nil {
		t.Fatalf("Unexpected purge error: %v", err)
	}
	si, err = js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if si.State.Msgs != 0 || si.State.FirstSeq != uint64(toSend+1) {
		t.Fatalf("Expected no msgs, got: %+v", si.State)
	}
}

func TestJetStreamClusterStreamUpdateSubjects(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	cfg := &nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	}

	if _, err := js.AddStream(cfg); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Make sure we can update subjects.
	cfg.Subjects = []string{"bar", "baz"}

	si, err := js.UpdateStream(cfg)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si == nil {
		t.Fatalf("Expected a stream info, got none")
	}
	if !reflect.DeepEqual(si.Config.Subjects, cfg.Subjects) {
		t.Fatalf("Expected subjects to be updated: got %+v", si.Config.Subjects)
	}
	// Make sure it registered
	js2, err := nc.JetStream(nats.MaxWait(250 * time.Millisecond))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err = js2.Publish("foo", nil); err == nil {
		t.Fatalf("Expected this to fail")
	}
	if _, err = js2.Publish("baz", nil); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}
}

func TestJetStreamClusterBadStreamUpdate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	cfg := &nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	}

	if _, err := js.AddStream(cfg); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	msg, toSend := []byte("Keep Me"), 50
	for i := 0; i < toSend; i++ {
		if _, err := js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	// Make sure a bad update will not remove our stream.
	cfg.Subjects = []string{"foo..bar"}
	if _, err := js.UpdateStream(cfg); err == nil || err == nats.ErrTimeout {
		t.Fatalf("Expected error but got none or timeout")
	}

	// Make sure we did not delete our original stream.
	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if !reflect.DeepEqual(si.Config.Subjects, []string{"foo", "bar"}) {
		t.Fatalf("Expected subjects to be original ones, got %+v", si.Config.Subjects)
	}
}

func TestJetStreamClusterConsumerRedeliveredInfo(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	cfg := &nats.StreamConfig{Name: "TEST"}
	if _, err := js.AddStream(cfg); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err := js.Publish("TEST", []byte("CI")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	sub, _ := nc.SubscribeSync("R")
	sub.AutoUnsubscribe(2)

	ci, err := js.AddConsumer("TEST", &nats.ConsumerConfig{
		DeliverSubject: "R",
		AckPolicy:      nats.AckExplicitPolicy,
		AckWait:        100 * time.Millisecond,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkSubsPending(t, sub, 2)
	sub.Unsubscribe()

	ci, err = js.ConsumerInfo("TEST", ci.Name)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if ci.NumRedelivered != 1 {
		t.Fatalf("Expected 1 redelivered, got %d", ci.NumRedelivered)
	}
}

func TestJetStreamClusterConsumerState(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 5)
	defer c.shutdown()

	s := c.randomServer()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	msg, toSend := []byte("Hello JS Clustering"), 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	// Make sure we are not connected to any of the stream servers so that we do not do client reconnect
	// when we take out the consumer leader.
	if s.JetStreamIsStreamAssigned("$G", "TEST") {
		nc.Close()
		for _, ns := range c.servers {
			if !ns.JetStreamIsStreamAssigned("$G", "TEST") {
				s = ns
				nc, js = jsClientConnect(t, s)
				defer nc.Close()
				break
			}
		}
	}

	sub, err := js.PullSubscribe("foo", "dlc")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Pull 5 messages and ack.
	for _, m := range fetchMsgs(t, sub, 5, 5*time.Second) {
		m.AckSync()
	}

	// Let state propagate for exact comparison below.
	time.Sleep(200 * time.Millisecond)

	ci, err := sub.ConsumerInfo()
	if err != nil {
		t.Fatalf("Unexpected error getting consumer info: %v", err)
	}
	if ci.AckFloor.Consumer != 5 {
		t.Fatalf("Expected ack floor of %d, got %d", 5, ci.AckFloor.Consumer)
	}

	c.consumerLeader("$G", "TEST", "dlc").Shutdown()
	c.waitOnConsumerLeader("$G", "TEST", "dlc")

	nci, err := sub.ConsumerInfo()
	if err != nil {
		t.Fatalf("Unexpected error getting consumer info: %v", err)
	}
	// nil out timestamp for better comparison
	nci.Delivered.Last, ci.Delivered.Last = nil, nil
	if nci.Delivered != ci.Delivered {
		t.Fatalf("Consumer delivered did not match after leader switch, wanted %+v, got %+v", ci.Delivered, nci.Delivered)
	}
	nci.AckFloor.Last, ci.AckFloor.Last = nil, nil
	if nci.AckFloor != ci.AckFloor {
		t.Fatalf("Consumer ackfloor did not match after leader switch, wanted %+v, got %+v", ci.AckFloor, nci.AckFloor)
	}

	// Now make sure we can receive new messages.
	// Pull last 5.
	for _, m := range fetchMsgs(t, sub, 5, 5*time.Second) {
		m.AckSync()
	}

	nci, _ = sub.ConsumerInfo()
	if nci.Delivered.Consumer != 10 || nci.Delivered.Stream != 10 {
		t.Fatalf("Received bad delivered: %+v", nci.Delivered)
	}
	if nci.AckFloor.Consumer != 10 || nci.AckFloor.Stream != 10 {
		t.Fatalf("Received bad ackfloor: %+v", nci.AckFloor)
	}
	if nci.NumAckPending != 0 {
		t.Fatalf("Received bad ackpending: %+v", nci.NumAckPending)
	}
}

func TestJetStreamClusterFullConsumerState(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	s := c.randomServer()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	msg, toSend := []byte("Hello JS Clustering"), 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	sub, err := js.PullSubscribe("foo", "dlc")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	fetchMsgs(t, sub, 1, 5*time.Second)

	// Now purge the stream.
	if err := js.PurgeStream("TEST"); err != nil {
		t.Fatalf("Unexpected purge error: %v", err)
	}
}

func TestJetStreamClusterMetaSnapshotsAndCatchup(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Shut one down.
	rs := c.randomServer()
	rs.Shutdown()

	c.waitOnLeader()
	s := c.leader()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	numStreams := 4
	// Create 4 streams
	// FIXME(dlc) - R2 make sure we place properly.
	for i := 0; i < numStreams; i++ {
		sn := fmt.Sprintf("T-%d", i+1)
		_, err := js.AddStream(&nats.StreamConfig{Name: sn})
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
	}

	c.leader().JetStreamSnapshotMeta()

	rs = c.restartServer(rs)
	c.checkClusterFormed()
	c.waitOnServerCurrent(rs)

	rs.Shutdown()
	c.waitOnLeader()

	for i := 0; i < numStreams; i++ {
		sn := fmt.Sprintf("T-%d", i+1)
		err := js.DeleteStream(sn)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
	}

	rs = c.restartServer(rs)
	c.checkClusterFormed()
	c.waitOnServerCurrent(rs)
}

func TestJetStreamClusterMetaSnapshotsMultiChange(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 2)
	defer c.shutdown()

	s := c.leader()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	// Add in 2 streams with 1 consumer each.
	if _, err := js.AddStream(&nats.StreamConfig{Name: "S1"}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	c.waitOnStreamLeader(globalAccountName, "S1")
	_, err := js.AddConsumer("S1", &nats.ConsumerConfig{Durable: "S1C1", AckPolicy: nats.AckExplicitPolicy})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	c.waitOnConsumerLeader(globalAccountName, "S1", "S1C1")

	if _, err = js.AddStream(&nats.StreamConfig{Name: "S2"}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	c.waitOnStreamLeader(globalAccountName, "S2")
	_, err = js.AddConsumer("S2", &nats.ConsumerConfig{Durable: "S2C1", AckPolicy: nats.AckExplicitPolicy})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	c.waitOnConsumerLeader(globalAccountName, "S2", "S2C1")

	// Add in a new server to the group. This way we know we can delete the original streams and consumers.
	rs := c.addInNewServer()
	c.waitOnServerCurrent(rs)
	rsn := rs.Name()

	// Shut it down.
	rs.Shutdown()

	// Wait for the peer to be removed.
	checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
		for _, p := range s.JetStreamClusterPeers() {
			if p == rsn {
				return fmt.Errorf("Old server still in peer set")
			}
		}
		return nil
	})

	// We want to make changes here that test each delta scenario for the meta snapshots.
	// Add new stream and consumer.
	if _, err = js.AddStream(&nats.StreamConfig{Name: "S3"}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	c.waitOnStreamLeader(globalAccountName, "S3")
	_, err = js.AddConsumer("S3", &nats.ConsumerConfig{Durable: "S3C1", AckPolicy: nats.AckExplicitPolicy})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	c.waitOnConsumerLeader(globalAccountName, "S3", "S3C1")
	// Delete stream S2
	resp, _ := nc.Request(fmt.Sprintf(JSApiStreamDeleteT, "S2"), nil, time.Second)
	var dResp JSApiStreamDeleteResponse
	if err := json.Unmarshal(resp.Data, &dResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if !dResp.Success || dResp.Error != nil {
		t.Fatalf("Got a bad response %+v", dResp.Error)
	}
	// Delete the consumer on S1 but add another.
	resp, err = nc.Request(fmt.Sprintf(JSApiConsumerDeleteT, "S1", "S1C1"), nil, time.Second)
	require_NoError(t, err)
	require_NotNil(t, resp)
	var cdResp JSApiConsumerDeleteResponse
	if err = json.Unmarshal(resp.Data, &cdResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if !cdResp.Success || cdResp.Error != nil {
		t.Fatalf("Got a bad response %+v", cdResp)
	}
	// Add new consumer on S1
	_, err = js.AddConsumer("S1", &nats.ConsumerConfig{Durable: "S1C2", AckPolicy: nats.AckExplicitPolicy})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	c.waitOnConsumerLeader(globalAccountName, "S1", "S1C2")

	cl := c.leader()
	cl.JetStreamSnapshotMeta()
	c.waitOnServerCurrent(cl)

	rs = c.restartServer(rs)
	c.checkClusterFormed()
	c.waitOnServerCurrent(rs)
}

func TestJetStreamClusterStreamSynchedTimeStamps(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "foo", Storage: nats.MemoryStorage, Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err = js.Publish("foo", []byte("TSS")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	// Grab the message and timestamp from our current leader
	sub, err := js.SubscribeSync("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	m, err := sub.NextMsg(time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	meta, _ := m.Metadata()

	sub.Unsubscribe()

	sl := c.streamLeader("$G", "foo")

	sl.Shutdown()

	c.waitOnLeader()
	c.waitOnStreamLeader("$G", "foo")

	nc, js = jsClientConnect(t, c.leader())
	defer nc.Close()

	sm, err := js.GetMsg("foo", 1)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if !sm.Time.Equal(meta.Timestamp) {
		t.Fatalf("Expected same timestamps, got %v vs %v", sm.Time, meta.Timestamp)
	}
}

// Test to mimic what R.I. was seeing.
func TestJetStreamClusterRestoreSingleConsumer(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "foo"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err = js.Publish("foo", []byte("TSS")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	sub, err := js.SubscribeSync("foo", nats.Durable("dlc"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if m, err := sub.NextMsg(time.Second); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	} else {
		m.AckSync()
	}

	c.stopAll()
	c.restartAll()
	c.waitOnLeader()
	c.waitOnStreamLeader("$G", "foo")

	s = c.randomServer()
	nc, js = jsClientConnect(t, s)
	defer nc.Close()

	var names []string
	for name := range js.StreamNames() {
		names = append(names, name)
	}
	if len(names) != 1 {
		t.Fatalf("Expected only 1 stream but got %d", len(names))
	}

	// Now do detailed version.
	var infos []*nats.StreamInfo
	for info := range js.StreamsInfo() {
		infos = append(infos, info)
	}
	if len(infos) != 1 {
		t.Fatalf("Expected 1 stream but got %d", len(infos))
	}
	si, err := js.StreamInfo("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si == nil || si.Config.Name != "foo" {
		t.Fatalf("StreamInfo is not correct %+v", si)
	}

	// Now check for consumer.
	names = names[:0]
	for name := range js.ConsumerNames("foo") {
		names = append(names, name)
	}
	if len(names) != 1 {
		t.Fatalf("Expected 1 consumer but got %d", len(names))
	}
}

func TestJetStreamClusterMaxBytesForStream(t *testing.T) {
	// Has max_file_store of 2GB
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	info, err := js.AccountInfo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Make sure we still are dynamic.
	if info.Limits.MaxStore != -1 || info.Limits.MaxMemory != -1 {
		t.Fatalf("Expected dynamic limits for the account, got %+v\n", info.Limits)
	}
	// Stream config.
	cfg := &nats.StreamConfig{
		Name:     "TEST",
		Replicas: 2,
		MaxBytes: 2 * 1024 * 1024 * 1024, // 2GB
	}
	_, err = js.AddStream(cfg)
	require_NoError(t, err)

	// Make sure going over the single server limit though is enforced (for now).
	cfg.Name = "TEST2"
	cfg.MaxBytes *= 2
	_, err = js.AddStream(cfg)
	require_Contains(t, err.Error(), "no suitable peers for placement")
}

func TestJetStreamClusterStreamPublishWithActiveConsumers(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	s := c.randomServer()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "foo", Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err = js.Publish("foo", []byte("TSS")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	sub, err := js.SubscribeSync("foo", nats.Durable("dlc"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// FIXME(dlc) - Need to track this down.
	c.waitOnConsumerLeader("$G", "foo", "dlc")

	if m, err := sub.NextMsg(time.Second); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	} else {
		m.AckSync()
	}

	// Send 10 messages.
	for i := 1; i <= 10; i++ {
		payload := []byte(fmt.Sprintf("MSG-%d", i))
		if _, err = js.Publish("foo", payload); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}
	checkSubsPending(t, sub, 10)
	// Sanity check for duplicate deliveries..
	if nmsgs, _, _ := sub.Pending(); nmsgs > 10 {
		t.Fatalf("Expected only %d responses, got %d more", 10, nmsgs)
	}
	for i := 1; i <= 10; i++ {
		m, err := sub.NextMsg(time.Second)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		payload := []byte(fmt.Sprintf("MSG-%d", i))
		if !bytes.Equal(m.Data, payload) {
			t.Fatalf("Did not get expected msg, expected %q, got %q", payload, m.Data)
		}
	}

	var ci *nats.ConsumerInfo
	checkFor(t, time.Second, 100*time.Millisecond, func() error {
		ci, err = sub.ConsumerInfo()
		if err != nil {
			return fmt.Errorf("Unexpected error getting consumer info: %v", err)
		}
		if ci.Delivered.Stream != 11 {
			return fmt.Errorf("expected delivered stream sequence to be %d, got %d", 11, ci.Delivered.Stream)
		}
		return nil
	})

	c.consumerLeader("$G", "foo", "dlc").Shutdown()
	c.waitOnConsumerLeader("$G", "foo", "dlc")

	ci2, err := sub.ConsumerInfo()
	if err != nil {
		t.Fatalf("Unexpected error getting consumer info: %v", err)
	}

	ci.Cluster = nil
	ci2.Cluster = nil

	// nil out timestamp for better comparison
	ci.Delivered.Last, ci2.Delivered.Last = nil, nil
	ci.AckFloor.Last, ci2.AckFloor.Last = nil, nil
	if !reflect.DeepEqual(ci, ci2) {
		t.Fatalf("Consumer info did not match: %+v vs %+v", ci, ci2)
	}

	// In case the server above was also stream leader.
	c.waitOnStreamLeader("$G", "foo")

	// Now send more..
	// Send 10 more messages.
	for i := 11; i <= 20; i++ {
		payload := []byte(fmt.Sprintf("MSG-%d", i))
		if _, err = js.Publish("foo", payload); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	checkSubsPending(t, sub, 10)
	// Sanity check for duplicate deliveries..
	if nmsgs, _, _ := sub.Pending(); nmsgs > 10 {
		t.Fatalf("Expected only %d responses, got %d more", 10, nmsgs)
	}

	for i := 11; i <= 20; i++ {
		m, err := sub.NextMsg(time.Second)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		payload := []byte(fmt.Sprintf("MSG-%d", i))
		if !bytes.Equal(m.Data, payload) {
			t.Fatalf("Did not get expected msg, expected %q, got %q", payload, m.Data)
		}
	}
}

func TestJetStreamClusterStreamOverlapSubjects(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Subjects: []string{"foo"}}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST2", Subjects: []string{"foo"}}); err == nil || err == nats.ErrTimeout {
		t.Fatalf("Expected error but got none or timeout: %v", err)
	}

	// Now grab list of streams and make sure the second is not there.
	var names []string
	for name := range js.StreamNames() {
		names = append(names, name)
	}
	if len(names) != 1 {
		t.Fatalf("Expected only 1 stream but got %d", len(names))
	}

	// Now do a detailed version.
	var infos []*nats.StreamInfo
	for info := range js.StreamsInfo() {
		infos = append(infos, info)
	}
	if len(infos) != 1 {
		t.Fatalf("Expected only 1 stream but got %d", len(infos))
	}
}

func TestJetStreamClusterStreamInfoList(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	createStream := func(name string) {
		t.Helper()
		if _, err := js.AddStream(&nats.StreamConfig{Name: name}); err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
	}

	createStream("foo")
	createStream("bar")
	createStream("baz")

	sendBatch := func(subject string, n int) {
		t.Helper()
		// Send a batch to a given subject.
		for i := 0; i < n; i++ {
			if _, err := js.Publish(subject, []byte("OK")); err != nil {
				t.Fatalf("Unexpected publish error: %v", err)
			}
		}
	}

	sendBatch("foo", 10)
	sendBatch("bar", 22)
	sendBatch("baz", 33)

	// Now get the stream list info.
	var infos []*nats.StreamInfo
	checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
		infos = infos[:0]
		for info := range js.StreamsInfo() {
			infos = append(infos, info)
		}
		if len(infos) != 3 {
			return fmt.Errorf("StreamInfo expected 3 results, got %d", len(infos))
		}
		return nil
	})

	for _, si := range infos {
		switch si.Config.Name {
		case "foo":
			if si.State.Msgs != 10 {
				t.Fatalf("Expected %d msgs but got %d", 10, si.State.Msgs)
			}
		case "bar":
			if si.State.Msgs != 22 {
				t.Fatalf("Expected %d msgs but got %d", 22, si.State.Msgs)
			}
		case "baz":
			if si.State.Msgs != 33 {
				t.Fatalf("Expected %d msgs but got %d", 33, si.State.Msgs)
			}
		}
	}
}

func TestJetStreamClusterConsumerInfoList(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 3}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Place messages so we can generate consumer state.
	for i := 0; i < 10; i++ {
		if _, err := js.Publish("TEST", []byte("OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	createConsumer := func(name string) *nats.Subscription {
		t.Helper()
		sub, err := js.PullSubscribe("TEST", name)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		return sub
	}

	subFoo := createConsumer("foo")
	subBar := createConsumer("bar")
	subBaz := createConsumer("baz")

	// Place consumers in various states.
	for _, ss := range []struct {
		sub   *nats.Subscription
		fetch int
		ack   int
	}{
		{subFoo, 4, 2},
		{subBar, 2, 0},
		{subBaz, 8, 6},
	} {
		msgs := fetchMsgs(t, ss.sub, ss.fetch, 5*time.Second)
		for i := 0; i < ss.ack; i++ {
			msgs[i].AckSync()
		}
	}

	// Now get the consumer list info.
	var infos []*nats.ConsumerInfo
	for info := range js.ConsumersInfo("TEST") {
		infos = append(infos, info)
	}
	if len(infos) != 3 {
		t.Fatalf("ConsumerInfo expected 3 results, got %d", len(infos))
	}
	for _, ci := range infos {
		switch ci.Name {
		case "foo":
			if ci.Delivered.Consumer != 4 {
				t.Fatalf("Expected %d delivered but got %d", 4, ci.Delivered.Consumer)
			}
			if ci.AckFloor.Consumer != 2 {
				t.Fatalf("Expected %d for ack floor but got %d", 2, ci.AckFloor.Consumer)
			}
		case "bar":
			if ci.Delivered.Consumer != 2 {
				t.Fatalf("Expected %d delivered but got %d", 2, ci.Delivered.Consumer)
			}
			if ci.AckFloor.Consumer != 0 {
				t.Fatalf("Expected %d for ack floor but got %d", 0, ci.AckFloor.Consumer)
			}
		case "baz":
			if ci.Delivered.Consumer != 8 {
				t.Fatalf("Expected %d delivered but got %d", 8, ci.Delivered.Consumer)
			}
			if ci.AckFloor.Consumer != 6 {
				t.Fatalf("Expected %d for ack floor but got %d", 6, ci.AckFloor.Consumer)
			}
		}
	}
}

func TestJetStreamClusterStreamUpdate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	sc := &nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
		MaxMsgs:  10,
		Discard:  DiscardNew,
	}

	if _, err := js.AddStream(sc); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	for i := 1; i <= int(sc.MaxMsgs); i++ {
		msg := []byte(fmt.Sprintf("HELLO JSC-%d", i))
		if _, err := js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	// Expect error here.
	if _, err := js.Publish("foo", []byte("fail")); err == nil {
		t.Fatalf("Expected publish to fail")
	}

	// Now update MaxMsgs, select non-leader
	s = c.randomNonStreamLeader("$G", "TEST")
	nc, js = jsClientConnect(t, s)
	defer nc.Close()

	sc.MaxMsgs = 20
	si, err := js.UpdateStream(sc)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.Config.MaxMsgs != 20 {
		t.Fatalf("Expected to have config updated with max msgs of %d, got %d", 20, si.Config.MaxMsgs)
	}

	// Do one that will fail. Wait and make sure we only are getting one response.
	sc.Name = "TEST22"

	rsub, _ := nc.SubscribeSync(nats.NewInbox())
	defer rsub.Unsubscribe()
	nc.Flush()

	req, _ := json.Marshal(sc)
	if err := nc.PublishRequest(fmt.Sprintf(JSApiStreamUpdateT, "TEST"), rsub.Subject, req); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Wait incase more than one reply sent.
	time.Sleep(250 * time.Millisecond)

	if nmsgs, _, _ := rsub.Pending(); err != nil || nmsgs != 1 {
		t.Fatalf("Expected only one response, got %d", nmsgs)
	}

	m, err := rsub.NextMsg(time.Second)
	if err != nil {
		t.Fatalf("Error getting message: %v", err)
	}

	var scResp JSApiStreamCreateResponse
	if err := json.Unmarshal(m.Data, &scResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if scResp.StreamInfo != nil || scResp.Error == nil {
		t.Fatalf("Did not receive correct response: %+v", scResp)
	}
}

func TestJetStreamClusterStreamExtendedUpdates(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	cfg := &nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	}
	if _, err := js.AddStream(cfg); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	updateStream := func() *nats.StreamInfo {
		si, err := js.UpdateStream(cfg)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		return si
	}

	// Subjects can be updated
	cfg.Subjects = []string{"bar", "baz"}
	if si := updateStream(); !reflect.DeepEqual(si.Config.Subjects, cfg.Subjects) {
		t.Fatalf("Did not get expected stream info: %+v", si)
	}
	// Mirror changes are not supported for now
	cfg.Subjects = nil
	cfg.Mirror = &nats.StreamSource{Name: "ORDERS"}
	_, err := js.UpdateStream(cfg)
	require_Error(t, err, NewJSStreamMirrorNotUpdatableError())
}

func TestJetStreamClusterDoubleAdd(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R32", 2)
	defer c.shutdown()

	s := c.randomServer()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Streams should allow double add.
	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Check Consumers.
	cfg := &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy}
	if _, err := js.AddConsumer("TEST", cfg); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Check double add ok.
	if _, err := js.AddConsumer("TEST", cfg); err != nil {
		t.Fatalf("Expected no error but got: %v", err)
	}
}

func TestJetStreamClusterDefaultMaxAckPending(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R32", 2)
	defer c.shutdown()

	s := c.randomServer()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Do Consumers too.
	cfg := &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy}
	ci, err := js.AddConsumer("TEST", cfg)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Check that we have a default set now for the max ack pending.
	if ci.Config.MaxAckPending != JsDefaultMaxAckPending {
		t.Fatalf("Expected a default for max ack pending of %d, got %d", JsDefaultMaxAckPending, ci.Config.MaxAckPending)
	}
}

func TestJetStreamClusterStreamNormalCatchup(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	toSend := 10
	for i := 1; i <= toSend; i++ {
		msg := []byte(fmt.Sprintf("HELLO JSC-%d", i))
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	sl := c.streamLeader("$G", "TEST")
	sl.Shutdown()
	c.waitOnStreamLeader("$G", "TEST")

	// Send 10 more while one replica offline.
	for i := toSend; i <= toSend*2; i++ {
		msg := []byte(fmt.Sprintf("HELLO JSC-%d", i))
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	// Delete the first from the second batch.
	dreq := JSApiMsgDeleteRequest{Seq: uint64(toSend)}
	dreqj, err := json.Marshal(dreq)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	resp, _ := nc.Request(fmt.Sprintf(JSApiMsgDeleteT, "TEST"), dreqj, time.Second)
	var delMsgResp JSApiMsgDeleteResponse
	if err = json.Unmarshal(resp.Data, &delMsgResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if !delMsgResp.Success || delMsgResp.Error != nil {
		t.Fatalf("Got a bad response %+v", delMsgResp.Error)
	}

	sl = c.restartServer(sl)
	c.checkClusterFormed()

	c.waitOnServerCurrent(sl)
	c.waitOnStreamCurrent(sl, "$G", "TEST")
}

func TestJetStreamClusterStreamSnapshotCatchup(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	s := c.randomServer()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	pseq := uint64(1)
	sendBatch := func(n int) {
		t.Helper()
		// Send a batch.
		for i := 0; i < n; i++ {
			msg := []byte(fmt.Sprintf("HELLO JSC-%d", pseq))
			if _, err = js.Publish("foo", msg); err != nil {
				t.Fatalf("Unexpected publish error: %v", err)
			}
			pseq++
		}
	}

	sendBatch(2)

	sl := c.streamLeader("$G", "TEST")
	sl.Shutdown()
	c.waitOnStreamLeader("$G", "TEST")

	sendBatch(100)

	deleteMsg := func(seq uint64) {
		if err := js.DeleteMsg("TEST", seq); err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
	}

	// Delete the first from the second batch.
	deleteMsg(pseq / 2)
	// Delete the next one too.
	deleteMsg(pseq/2 + 1)

	nsl := c.streamLeader("$G", "TEST")
	nsl.JetStreamSnapshotStream("$G", "TEST")

	// Do some activity post snapshot as well.
	// Delete next to last.
	deleteMsg(pseq - 2)
	// Send another batch.
	sendBatch(100)

	mset, err := nsl.GlobalAccount().lookupStream("TEST")
	require_NoError(t, err)
	ostate := mset.stateWithDetail(true)

	sl = c.restartServer(sl)
	c.checkClusterFormed()

	c.waitOnServerCurrent(sl)
	c.waitOnStreamCurrent(sl, "$G", "TEST")

	mset, err = sl.GlobalAccount().lookupStream("TEST")
	require_NoError(t, err)

	checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
		if nstate := mset.stateWithDetail(true); !reflect.DeepEqual(ostate, nstate) {
			return fmt.Errorf("States do not match after recovery: %+v vs %+v", ostate, nstate)
		}
		return nil
	})
}

func TestJetStreamClusterDeleteMsg(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	// R=1 make sure delete works.
	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	toSend := 10
	for i := 1; i <= toSend; i++ {
		msg := []byte(fmt.Sprintf("HELLO JSC-%d", i))
		if _, err = js.Publish("TEST", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	deleteMsg := func(seq uint64) {
		if err := js.DeleteMsg("TEST", seq); err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
	}

	deleteMsg(1)

	// Also make sure purge of R=1 works too.
	if err := js.PurgeStream("TEST"); err != nil {
		t.Fatalf("Unexpected purge error: %v", err)
	}
}

func TestJetStreamClusterDeleteMsgAndRestart(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	// R=1 make sure delete works.
	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 2})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	toSend := 10
	for i := 1; i <= toSend; i++ {
		msg := []byte(fmt.Sprintf("HELLO JSC-%d", i))
		if _, err = js.Publish("TEST", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	deleteMsg := func(seq uint64) {
		if err := js.DeleteMsg("TEST", seq); err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
	}

	deleteMsg(1)

	c.stopAll()
	c.restartAll()

	c.waitOnStreamLeader("$G", "TEST")
}

func TestJetStreamClusterStreamSnapshotCatchupWithPurge(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	s := c.randomServer()

	// Client based API
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	sl := c.streamLeader("$G", "TEST")

	sl.Shutdown()
	c.waitOnStreamLeader("$G", "TEST")

	toSend := 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", []byte("OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	nsl := c.streamLeader("$G", "TEST")
	if err := nsl.JetStreamSnapshotStream("$G", "TEST"); err != nil {
		t.Fatalf("Error snapshotting stream: %v", err)
	}
	time.Sleep(250 * time.Millisecond)

	sl = c.restartServer(sl)
	c.checkClusterFormed()

	// Now purge the stream while we are recovering.
	if err := js.PurgeStream("TEST"); err != nil {
		t.Fatalf("Unexpected purge error: %v", err)
	}

	c.waitOnServerCurrent(sl)
	c.waitOnStreamCurrent(sl, "$G", "TEST")

	nsl.Shutdown()
	c.waitOnStreamLeader("$G", "TEST")

	if _, err := js.StreamInfo("TEST"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
}

func TestJetStreamClusterExtendedStreamInfo(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	toSend := 50
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", []byte("OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	leader := c.streamLeader("$G", "TEST").Name()

	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.Cluster == nil {
		t.Fatalf("Expected cluster info")
	}
	if si.Cluster.Name != c.name {
		t.Fatalf("Expected cluster name of %q, got %q", c.name, si.Cluster.Name)
	}

	if si.Cluster.Leader != leader {
		t.Fatalf("Expected leader of %q, got %q", leader, si.Cluster.Leader)
	}
	if len(si.Cluster.Replicas) != 2 {
		t.Fatalf("Expected %d replicas, got %d", 2, len(si.Cluster.Replicas))
	}

	// Make sure that returned array is ordered
	for i := 0; i < 50; i++ {
		si, err := js.StreamInfo("TEST")
		require_NoError(t, err)
		require_True(t, len(si.Cluster.Replicas) == 2)
		s1 := si.Cluster.Replicas[0].Name
		s2 := si.Cluster.Replicas[1].Name
		if s1 > s2 {
			t.Fatalf("Expected replicas to be ordered, got %s then %s", s1, s2)
		}
	}

	// We may need to wait a bit for peers to catch up.
	checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
		return checkState(t, c, "$G", "TEST")
	})

	// Shutdown the leader.
	oldLeader := c.streamLeader("$G", "TEST")
	oldLeader.Shutdown()

	c.waitOnStreamLeader("$G", "TEST")

	// Re-request.
	leader = c.streamLeader("$G", "TEST").Name()
	si, err = js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.Cluster == nil {
		t.Fatalf("Expected cluster info")
	}
	if si.Cluster.Leader != leader {
		t.Fatalf("Expected leader of %q, got %q", leader, si.Cluster.Leader)
	}
	if len(si.Cluster.Replicas) != 2 {
		t.Fatalf("Expected %d replicas, got %d", 2, len(si.Cluster.Replicas))
	}
	for _, peer := range si.Cluster.Replicas {
		if peer.Name == oldLeader.Name() {
			if peer.Current {
				t.Fatalf("Expected old leader to be reported as not current: %+v", peer)
			}
		} else if !peer.Current {
			t.Fatalf("Expected replica to be current: %+v", peer)
		}
	}

	// Now send a few more messages then restart the oldLeader.
	for i := 0; i < 10; i++ {
		if _, err = js.Publish("foo", []byte("OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	oldLeader = c.restartServer(oldLeader)
	c.checkClusterFormed()

	c.waitOnStreamLeader("$G", "TEST")
	c.waitOnStreamCurrent(oldLeader, "$G", "TEST")

	// Re-request.
	leader = c.streamLeader("$G", "TEST").Name()
	si, err = js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.Cluster == nil {
		t.Fatalf("Expected cluster info")
	}
	if si.Cluster.Leader != leader {
		t.Fatalf("Expected leader of %q, got %q", leader, si.Cluster.Leader)
	}
	if len(si.Cluster.Replicas) != 2 {
		t.Fatalf("Expected %d replicas, got %d", 2, len(si.Cluster.Replicas))
	}

	// We may need to wait a bit for peers to catch up.
	checkFor(t, 10*time.Second, 100*time.Millisecond, func() error {
		return checkState(t, c, "$G", "TEST")
	})

	nc, js = jsClientConnect(t, c.randomServer())
	defer nc.Close()

	// Now do consumer.
	sub, err := js.PullSubscribe("foo", "dlc")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer sub.Unsubscribe()
	fetchMsgs(t, sub, 10, 5*time.Second)

	leader = c.consumerLeader("$G", "TEST", "dlc").Name()
	ci, err := sub.ConsumerInfo()
	if err != nil {
		t.Fatalf("Unexpected error getting consumer info: %v", err)
	}

	if ci.Cluster.Leader != leader {
		t.Fatalf("Expected leader of %q, got %q", leader, ci.Cluster.Leader)
	}
	if len(ci.Cluster.Replicas) != 2 {
		t.Fatalf("Expected %d replicas, got %d", 2, len(ci.Cluster.Replicas))
	}
	checkFor(t, 10*time.Second, 100*time.Millisecond, func() error {
		return checkState(t, c, "$G", "TEST")
	})
}

func TestJetStreamClusterExtendedStreamInfoSingleReplica(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	toSend := 50
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", []byte("OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	leader := c.streamLeader("$G", "TEST").Name()

	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.Cluster == nil {
		t.Fatalf("Expected cluster info")
	}
	if si.Cluster.Name != c.name {
		t.Fatalf("Expected cluster name of %q, got %q", c.name, si.Cluster.Name)
	}
	if si.Cluster.Leader != leader {
		t.Fatalf("Expected leader of %q, got %q", leader, si.Cluster.Leader)
	}
	if len(si.Cluster.Replicas) != 0 {
		t.Fatalf("Expected no replicas but got %d", len(si.Cluster.Replicas))
	}

	// Make sure we can grab consumer lists from any
	var infos []*nats.ConsumerInfo
	for info := range js.ConsumersInfo("TEST") {
		infos = append(infos, info)
	}
	if len(infos) != 0 {
		t.Fatalf("ConsumerInfo expected no paged results, got %d", len(infos))
	}

	// Now add in a consumer.
	cfg := &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy}
	if _, err := js.AddConsumer("TEST", cfg); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	infos = infos[:0]
	for info := range js.ConsumersInfo("TEST") {
		infos = append(infos, info)
	}
	if len(infos) != 1 {
		t.Fatalf("ConsumerInfo expected 1 result, got %d", len(infos))
	}

	// Now do direct names list as well.
	var names []string
	for name := range js.ConsumerNames("TEST") {
		names = append(names, name)
	}
	if len(names) != 1 {
		t.Fatalf("Expected only 1 consumer but got %d", len(names))
	}
}

func TestJetStreamClusterInterestRetention(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "foo", Retention: nats.InterestPolicy, Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	sub, err := js.SubscribeSync("foo", nats.Durable("dlc"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	sl := c.streamLeader("$G", "foo")
	cl := c.consumerLeader("$G", "foo", "dlc")
	if sl == cl {
		_, err := nc.Request(fmt.Sprintf(JSApiStreamLeaderStepDownT, "foo"), nil, time.Second)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		c.waitOnStreamLeader("$G", "foo")
	}

	if _, err = js.Publish("foo", []byte("OK")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	m, err := sub.NextMsg(time.Second)
	if err != nil {
		t.Fatalf("Unexpected error getting msg: %v", err)
	}
	m.AckSync()

	waitForZero := func() {
		checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
			si, err := js.StreamInfo("foo")
			if err != nil {
				t.Fatalf("Unexpected error: %v", err)
			}
			if si.State.Msgs != 0 {
				return fmt.Errorf("Expected 0 msgs, got state: %+v", si.State)
			}
			return nil
		})
	}

	waitForZero()

	// Add in 50 messages.
	for i := 0; i < 50; i++ {
		if _, err = js.Publish("foo", []byte("more")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}
	checkSubsPending(t, sub, 50)

	// Now delete the consumer and make sure the stream goes to zero.
	if err := js.DeleteConsumer("foo", "dlc"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	waitForZero()
}

// https://github.com/nats-io/nats-server/issues/2243
func TestJetStreamClusterWorkQueueRetention(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "FOO",
		Subjects:  []string{"foo.*"},
		Replicas:  2,
		Retention: nats.WorkQueuePolicy,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	sub, err := js.PullSubscribe("foo.test", "test")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err = js.Publish("foo.test", []byte("OK")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}
	si, err := js.StreamInfo("FOO")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.State.Msgs != 1 {
		t.Fatalf("Expected 1 msg, got state: %+v", si.State)
	}

	// Fetch from our pull consumer and ack.
	for _, m := range fetchMsgs(t, sub, 1, 5*time.Second) {
		m.AckSync()
	}

	// Make sure the messages are removed.
	checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
		si, err := js.StreamInfo("FOO")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if si.State.Msgs != 0 {
			return fmt.Errorf("Expected 0 msgs, got state: %+v", si.State)
		}
		return nil
	})

}

func TestJetStreamClusterMirrorAndSourceWorkQueues(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "WQ", 3)
	defer c.shutdown()

	// Client for API requests.
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "WQ22",
		Subjects:  []string{"foo"},
		Replicas:  2,
		Retention: nats.WorkQueuePolicy,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = js.AddStream(&nats.StreamConfig{
		Name:     "M",
		Replicas: 2,
		Mirror:   &nats.StreamSource{Name: "WQ22"},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = js.AddStream(&nats.StreamConfig{
		Name:     "S",
		Replicas: 2,
		Sources:  []*nats.StreamSource{{Name: "WQ22"}},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Allow direct sync consumers to connect.
	time.Sleep(1500 * time.Millisecond)

	if _, err = js.Publish("foo", []byte("ok")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	checkFor(t, 5*time.Second, 250*time.Millisecond, func() error {
		if si, _ := js.StreamInfo("WQ22"); si.State.Msgs != 0 {
			return fmt.Errorf("Expected no msgs for %q, got %d", "WQ22", si.State.Msgs)
		}
		if si, _ := js.StreamInfo("M"); si.State.Msgs != 1 {
			return fmt.Errorf("Expected 1 msg for %q, got %d", "M", si.State.Msgs)
		}
		if si, _ := js.StreamInfo("S"); si.State.Msgs != 1 {
			return fmt.Errorf("Expected 1 msg for %q, got %d", "S", si.State.Msgs)
		}
		return nil
	})

}

func TestJetStreamClusterMirrorAndSourceInterestPolicyStream(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "WQ", 3)
	defer c.shutdown()

	// Client for API requests.
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "IP22",
		Subjects:  []string{"foo"},
		Replicas:  3,
		Retention: nats.InterestPolicy,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = js.AddStream(&nats.StreamConfig{
		Name:     "M",
		Replicas: 2,
		Mirror:   &nats.StreamSource{Name: "IP22"},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = js.AddStream(&nats.StreamConfig{
		Name:     "S",
		Replicas: 2,
		Sources:  []*nats.StreamSource{{Name: "IP22"}},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Allow sync consumers to connect.
	time.Sleep(1500 * time.Millisecond)

	if _, err = js.Publish("foo", []byte("ok")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	checkFor(t, 5*time.Second, 250*time.Millisecond, func() error {
		// This one will be 0 since no other interest exists.
		if si, _ := js.StreamInfo("IP22"); si.State.Msgs != 0 {
			return fmt.Errorf("Expected no msgs for %q, got %d", "IP22", si.State.Msgs)
		}
		if si, _ := js.StreamInfo("M"); si.State.Msgs != 1 {
			return fmt.Errorf("Expected 1 msg for %q, got %d", "M", si.State.Msgs)
		}
		if si, _ := js.StreamInfo("S"); si.State.Msgs != 1 {
			return fmt.Errorf("Expected 1 msg for %q, got %d", "S", si.State.Msgs)
		}
		return nil
	})

	// Now create other interest on IP22.
	sub, err := js.SubscribeSync("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer sub.Unsubscribe()
	// Allow consumer state to propagate.
	time.Sleep(500 * time.Millisecond)

	if _, err = js.Publish("foo", []byte("ok")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	checkFor(t, 5*time.Second, 250*time.Millisecond, func() error {
		// This one will be 0 since no other interest exists.
		if si, _ := js.StreamInfo("IP22"); si.State.Msgs != 1 {
			return fmt.Errorf("Expected 1 msg for %q, got %d", "IP22", si.State.Msgs)
		}
		if si, _ := js.StreamInfo("M"); si.State.Msgs != 2 {
			return fmt.Errorf("Expected 2 msgs for %q, got %d", "M", si.State.Msgs)
		}
		if si, _ := js.StreamInfo("S"); si.State.Msgs != 2 {
			return fmt.Errorf("Expected 2 msgs for %q, got %d", "S", si.State.Msgs)
		}
		return nil
	})
}

func TestJetStreamClusterInterestRetentionWithFilteredConsumers(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Subjects: []string{"*"}, Retention: nats.InterestPolicy, Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	fsub, err := js.SubscribeSync("foo", nats.Durable("d1"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer fsub.Unsubscribe()

	bsub, err := js.SubscribeSync("bar", nats.Durable("d2"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer bsub.Unsubscribe()

	msg := []byte("FILTERED")
	sendMsg := func(subj string) {
		t.Helper()
		if _, err = js.Publish(subj, msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	getAndAck := func(sub *nats.Subscription) {
		t.Helper()
		m, err := sub.NextMsg(time.Second)
		if err != nil {
			t.Fatalf("Unexpected error getting msg: %v", err)
		}
		m.AckSync()
	}

	jsq, err := nc.JetStream(nats.MaxWait(250 * time.Millisecond))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkState := func(expected uint64) {
		t.Helper()
		checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
			t.Helper()
			si, err := jsq.StreamInfo("TEST")
			if err != nil {
				t.Fatalf("Unexpected error: %v", err)
			}
			if si.State.Msgs != expected {
				return fmt.Errorf("Expected %d msgs, got %d", expected, si.State.Msgs)
			}
			return nil
		})
	}

	sendMsg("foo")
	checkState(1)
	getAndAck(fsub)
	checkState(0)
	sendMsg("bar")
	sendMsg("foo")
	checkState(2)
	getAndAck(bsub)
	checkState(1)
	getAndAck(fsub)
	checkState(0)

	// Now send a bunch of messages and then delete the consumer.
	for i := 0; i < 10; i++ {
		sendMsg("foo")
		sendMsg("bar")
	}
	checkState(20)

	if err := js.DeleteConsumer("TEST", "d1"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if err := js.DeleteConsumer("TEST", "d2"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	checkState(0)

	// Now make sure pull based consumers work same.
	if _, err := js.PullSubscribe("foo", "dlc"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Now send a bunch of messages and then delete the consumer.
	for i := 0; i < 10; i++ {
		sendMsg("foo")
		sendMsg("bar")
	}
	checkState(10)

	if err := js.DeleteConsumer("TEST", "dlc"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	checkState(0)
}

func TestJetStreamClusterEphemeralConsumerNoImmediateInterest(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// We want to relax the strict interest requirement.
	ci, err := js.AddConsumer("TEST", &nats.ConsumerConfig{DeliverSubject: "r"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	cl := c.consumerLeader("$G", "TEST", ci.Name)
	mset, err := cl.GlobalAccount().lookupStream("TEST")
	if err != nil {
		t.Fatalf("Expected to find a stream for %q", "TEST")
	}
	o := mset.lookupConsumer(ci.Name)
	if o == nil {
		t.Fatalf("Error looking up consumer %q", ci.Name)
	}
	o.setInActiveDeleteThreshold(500 * time.Millisecond)

	// Make sure the consumer goes away though eventually.
	// Should be 5 seconds wait.
	checkFor(t, 5*time.Second, 100*time.Millisecond, func() error {
		if _, err := js.ConsumerInfo("TEST", ci.Name); err != nil {
			return nil
		}
		return fmt.Errorf("Consumer still present")
	})
}

func TestJetStreamClusterEphemeralConsumerCleanup(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "foo", Replicas: 2})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	sub, err := js.Subscribe("foo", func(m *nats.Msg) {})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	ci, _ := sub.ConsumerInfo()
	if ci == nil {
		t.Fatalf("Unexpected error: no consumer info")
	}

	// We will look up by hand this consumer to set inactive threshold lower for this test.
	cl := c.consumerLeader("$G", "foo", ci.Name)
	if cl == nil {
		t.Fatalf("Could not find consumer leader")
	}
	mset, err := cl.GlobalAccount().lookupStream("foo")
	if err != nil {
		t.Fatalf("Expected to find a stream for %q", "foo")
	}
	o := mset.lookupConsumer(ci.Name)
	if o == nil {
		t.Fatalf("Error looking up consumer %q", ci.Name)
	}
	o.setInActiveDeleteThreshold(10 * time.Millisecond)

	msg, toSend := []byte("Hello JS Clustering"), 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	getConsumers := func() []string {
		ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
		defer cancel()

		var names []string
		for name := range js.ConsumerNames("foo", nats.Context(ctx)) {
			names = append(names, name)
		}
		return names
	}

	checkConsumer := func(expected int) {
		consumers := getConsumers()
		if len(consumers) != expected {
			t.Fatalf("Expected %d consumers but got %d", expected, len(consumers))
		}
	}

	checkConsumer(1)

	// Now Unsubscribe, since this is ephemeral this will make this go away.
	sub.Unsubscribe()

	checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
		if consumers := getConsumers(); len(consumers) == 0 {
			return nil
		} else {
			return fmt.Errorf("Still %d consumers remaining", len(consumers))
		}
	})
}

func TestJetStreamClusterEphemeralConsumersNotReplicated(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "foo", Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	sub, err := js.SubscribeSync("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	ci, _ := sub.ConsumerInfo()
	if ci == nil {
		t.Fatalf("Unexpected error: no consumer info")
	}

	if _, err = js.Publish("foo", []byte("OK")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}
	checkSubsPending(t, sub, 1)
	sub.NextMsg(0)

	if ci.Cluster == nil || len(ci.Cluster.Replicas) != 0 {
		t.Fatalf("Expected ephemeral to be R=1, got %+v", ci.Cluster)
	}
	scl := c.serverByName(ci.Cluster.Leader)
	if scl == nil {
		t.Fatalf("Could not select server where ephemeral consumer is running")
	}

	// Test migrations. If we are also metadata leader will not work so skip.
	if scl == c.leader() {
		return
	}

	scl.Shutdown()
	c.waitOnStreamLeader("$G", "foo")

	if _, err = js.Publish("foo", []byte("OK")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	if _, err := sub.NextMsg(500 * time.Millisecond); err != nil {
		t.Logf("Expected to see another message, but behavior is optimistic so can fail")
	}
}

func TestJetStreamClusterUserSnapshotAndRestore(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 2,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	toSend := 200

	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("foo", []byte("OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	// Create consumer with no state.
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "rip", AckPolicy: nats.AckExplicitPolicy})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Create another consumer as well and give it a non-simplistic state.
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy, AckWait: 10 * time.Second})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	jsub, err := js.PullSubscribe("foo", "dlc")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Ack first 50.
	for _, m := range fetchMsgs(t, jsub, 50, 5*time.Second) {
		require_NoError(t, m.AckSync())
	}
	// Now ack every third message for next 50.
	for i, m := range fetchMsgs(t, jsub, 50, 5*time.Second) {
		if i%3 == 0 {
			require_NoError(t, m.AckSync())
		}
	}

	// Snapshot consumer info.
	ci, err := jsub.ConsumerInfo()
	if err != nil {
		t.Fatalf("Unexpected error getting consumer info: %v", err)
	}

	sreq := &JSApiStreamSnapshotRequest{
		DeliverSubject: nats.NewInbox(),
		ChunkSize:      512,
	}

	req, _ := json.Marshal(sreq)
	rmsg, err := nc.Request(fmt.Sprintf(JSApiStreamSnapshotT, "TEST"), req, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error on snapshot request: %v", err)
	}

	var resp JSApiStreamSnapshotResponse
	json.Unmarshal(rmsg.Data, &resp)
	if resp.Error != nil {
		t.Fatalf("Did not get correct error response: %+v", resp.Error)
	}

	// Grab state for comparison.
	state := *resp.State
	config := *resp.Config

	var snapshot []byte
	done := make(chan bool)

	sub, _ := nc.Subscribe(sreq.DeliverSubject, func(m *nats.Msg) {
		// EOF
		if len(m.Data) == 0 {
			done <- true
			return
		}
		// Could be writing to a file here too.
		snapshot = append(snapshot, m.Data...)
		// Flow ack
		m.Respond(nil)
	})
	defer sub.Unsubscribe()

	// Wait to receive the snapshot.
	select {
	case <-done:
	case <-time.After(5 * time.Second):
		t.Fatalf("Did not receive our snapshot in time")
	}

	var rresp JSApiStreamRestoreResponse
	rreq := &JSApiStreamRestoreRequest{
		Config: config,
		State:  state,
	}
	req, _ = json.Marshal(rreq)

	// Make sure a restore to an existing stream fails.
	rmsg, err = nc.Request(fmt.Sprintf(JSApiStreamRestoreT, "TEST"), req, 2*time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	json.Unmarshal(rmsg.Data, &rresp)
	if !IsNatsErr(rresp.Error, JSStreamNameExistRestoreFailedErr) {
		t.Fatalf("Did not get correct error response: %+v", rresp.Error)
	}

	if _, err := js.StreamInfo("TEST"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Now make sure a restore will work.
	// Delete our stream first.
	if err := js.DeleteStream("TEST"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.StreamInfo("TEST"); err == nil || !strings.Contains(err.Error(), "not found") {
		t.Fatalf("Expected not found error: %v", err)
	}

	// This should work properly.
	rmsg, err = nc.Request(fmt.Sprintf(JSApiStreamRestoreT, "TEST"), req, 5*time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	rresp.Error = nil
	json.Unmarshal(rmsg.Data, &rresp)
	if rresp.Error != nil {
		t.Fatalf("Got an unexpected error response: %+v", rresp.Error)
	}
	if rresp.DeliverSubject == _EMPTY_ {
		t.Fatalf("No deliver subject set on response: %+v", rresp)
	}
	// Send our snapshot back in to restore the stream.
	// Can be any size message.
	var chunk [1024]byte
	for r := bytes.NewReader(snapshot); ; {
		n, err := r.Read(chunk[:])
		if err != nil {
			break
		}
		nc.Request(rresp.DeliverSubject, chunk[:n], time.Second)
	}
	rmsg, err = nc.Request(rresp.DeliverSubject, nil, 2*time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	rresp.Error = nil
	json.Unmarshal(rmsg.Data, &rresp)
	if rresp.Error != nil {
		t.Fatalf("Got an unexpected error response: %+v", rresp.Error)
	}

	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si == nil || si.Config.Name != "TEST" || si.State.Msgs != uint64(toSend) {
		t.Fatalf("StreamInfo is not correct %+v", si)
	}

	// Make sure the replicas become current eventually. They will be doing catchup.
	checkFor(t, 10*time.Second, 100*time.Millisecond, func() error {
		return checkState(t, c, "$G", "TEST")
	})

	// Wait on the system to elect a leader for the restored consumer.
	c.waitOnConsumerLeader("$G", "TEST", "dlc")

	// Now check for the consumer being recreated.
	nci, err := js.ConsumerInfo("TEST", "dlc")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// nil out timestamp for better comparison
	nci.Delivered.Last, ci.Delivered.Last = nil, nil
	if nci.Delivered != ci.Delivered {
		t.Fatalf("Delivered states do not match %+v vs %+v", nci.Delivered, ci.Delivered)
	}
	nci.AckFloor.Last, ci.AckFloor.Last = nil, nil
	if nci.AckFloor != ci.AckFloor {
		t.Fatalf("Ack floors did not match %+v vs %+v", nci.AckFloor, ci.AckFloor)
	}

	// Make sure consumer works.
	// It should pick up with the next delivery spot, so check for that as first message.
	// We should have all the messages for first delivery delivered.
	wantSeq := 101
	for _, m := range fetchMsgs(t, jsub, 100, 5*time.Second) {
		meta, err := m.Metadata()
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if meta.Sequence.Stream != uint64(wantSeq) {
			t.Fatalf("Expected stream sequence of %d, but got %d", wantSeq, meta.Sequence.Stream)
		}
		require_NoError(t, m.AckSync())
		wantSeq++
	}

	// Check that redelivered come in now..
	redelivered := 50/3 + 1
	fetchMsgs(t, jsub, redelivered, 15*time.Second)

	// Now make sure the other server was properly caughtup.
	// Need to call this by hand for now.
	rmsg, err = nc.Request(fmt.Sprintf(JSApiStreamLeaderStepDownT, "TEST"), nil, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	var sdResp JSApiStreamLeaderStepDownResponse
	if err := json.Unmarshal(rmsg.Data, &sdResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if sdResp.Error != nil {
		t.Fatalf("Unexpected error: %+v", sdResp.Error)
	}

	c.waitOnStreamLeader("$G", "TEST")
	si, err = js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %+v", err)
	}
	if si.State.Msgs != uint64(toSend) {
		t.Fatalf("Unexpected stream info: %+v", si)
	}

	// Check idle consumer
	c.waitOnConsumerLeader("$G", "TEST", "rip")

	// Now check for the consumer being recreated.
	if _, err := js.ConsumerInfo("TEST", "rip"); err != nil {
		t.Fatalf("Unexpected error: %+v", err)
	}
}

func TestJetStreamClusterUserSnapshotAndRestoreConfigChanges(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	// FIXME(dlc) - Do case with R=1
	cfg := &nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 2,
	}

	if _, err := js.AddStream(cfg); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	toSend := 10
	for i := 0; i < toSend; i++ {
		if _, err := js.Publish("foo", []byte("OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	getSnapshot := func() ([]byte, *StreamState) {
		t.Helper()
		sreq := &JSApiStreamSnapshotRequest{
			DeliverSubject: nats.NewInbox(),
			ChunkSize:      1024,
		}

		req, _ := json.Marshal(sreq)
		rmsg, err := nc.Request(fmt.Sprintf(JSApiStreamSnapshotT, "TEST"), req, time.Second)
		if err != nil {
			t.Fatalf("Unexpected error on snapshot request: %v", err)
		}

		var resp JSApiStreamSnapshotResponse
		json.Unmarshal(rmsg.Data, &resp)
		if resp.Error != nil {
			t.Fatalf("Did not get correct error response: %+v", resp.Error)
		}

		var snapshot []byte
		done := make(chan bool)

		sub, _ := nc.Subscribe(sreq.DeliverSubject, func(m *nats.Msg) {
			// EOF
			if len(m.Data) == 0 {
				done <- true
				return
			}
			// Could be writing to a file here too.
			snapshot = append(snapshot, m.Data...)
			// Flow ack
			m.Respond(nil)
		})
		defer sub.Unsubscribe()

		// Wait to receive the snapshot.
		select {
		case <-done:
		case <-time.After(5 * time.Second):
			t.Fatalf("Did not receive our snapshot in time")
		}
		return snapshot, resp.State
	}

	restore := func(cfg *StreamConfig, state *StreamState, snap []byte) *nats.StreamInfo {
		rreq := &JSApiStreamRestoreRequest{
			Config: *cfg,
			State:  *state,
		}
		req, err := json.Marshal(rreq)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		rmsg, err := nc.Request(fmt.Sprintf(JSApiStreamRestoreT, cfg.Name), req, 5*time.Second)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		var rresp JSApiStreamRestoreResponse
		json.Unmarshal(rmsg.Data, &rresp)
		if rresp.Error != nil {
			t.Fatalf("Got an unexpected error response: %+v", rresp.Error)
		}
		if rresp.DeliverSubject == _EMPTY_ {
			t.Fatalf("No deliver subject set on response: %+v", rresp)
		}
		// Send our snapshot back in to restore the stream.
		// Can be any size message.
		var chunk [1024]byte
		for r := bytes.NewReader(snap); ; {
			n, err := r.Read(chunk[:])
			if err != nil {
				break
			}
			nc.Request(rresp.DeliverSubject, chunk[:n], time.Second)
		}
		rmsg, err = nc.Request(rresp.DeliverSubject, nil, 2*time.Second)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		rresp.Error = nil
		json.Unmarshal(rmsg.Data, &rresp)
		if rresp.Error != nil {
			t.Fatalf("Got an unexpected error response: %+v", rresp.Error)
		}
		si, err := js.StreamInfo(cfg.Name)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		return si
	}

	snap, state := getSnapshot()

	if err := js.DeleteStream("TEST"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Now change subjects.
	ncfg := &StreamConfig{
		Name:     "TEST",
		Subjects: []string{"bar", "baz"},
		Storage:  FileStorage,
		Replicas: 2,
	}
	if si := restore(ncfg, state, snap); !reflect.DeepEqual(si.Config.Subjects, ncfg.Subjects) {
		t.Fatalf("Did not get expected stream info: %+v", si)
	}
	if err := js.DeleteStream("TEST"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Storage
	ncfg.Storage = MemoryStorage
	if si := restore(ncfg, state, snap); !reflect.DeepEqual(si.Config.Subjects, ncfg.Subjects) {
		t.Fatalf("Did not get expected stream info: %+v", si)
	}
	if err := js.DeleteStream("TEST"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Now replicas
	ncfg.Replicas = 3
	if si := restore(ncfg, state, snap); !reflect.DeepEqual(si.Config.Subjects, ncfg.Subjects) {
		t.Fatalf("Did not get expected stream info: %+v", si)
	}
}

func TestJetStreamClusterAccountInfoAndLimits(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	// Adjust our limits.
	c.updateLimits("$G", map[string]JetStreamAccountLimits{
		_EMPTY_: {
			MaxMemory:    1024,
			MaxStore:     8000,
			MaxStreams:   3,
			MaxConsumers: 2,
		},
	})

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "foo", Replicas: 1}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.AddStream(&nats.StreamConfig{Name: "bar", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.AddStream(&nats.StreamConfig{Name: "baz", Replicas: 3}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Create with same config is idempotent, and must not exceed max streams as it already exists.
	if _, err := js.AddStream(&nats.StreamConfig{Name: "baz", Replicas: 3}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	sendBatch := func(subject string, n int) {
		t.Helper()
		for i := 0; i < n; i++ {
			if _, err := js.Publish(subject, []byte("JSC-OK")); err != nil {
				t.Fatalf("Unexpected publish error: %v", err)
			}
		}
	}

	sendBatch("foo", 25)
	sendBatch("bar", 75)
	sendBatch("baz", 10)

	accountStats := func() *nats.AccountInfo {
		t.Helper()

		info, err := js.AccountInfo()
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		return info
	}

	// If subject is not 3 letters or payload not 2 this needs to change.
	const msgSize = uint64(22 + 3 + 6 + 8)

	stats := accountStats()
	if stats.Streams != 3 {
		t.Fatalf("Should have been tracking 3 streams, found %d", stats.Streams)
	}
	expectedSize := 25*msgSize + 75*msgSize*2 + 10*msgSize*3
	// This may lag.
	checkFor(t, 5*time.Second, 500*time.Millisecond, func() error {
		if stats.Store != expectedSize {
			err := fmt.Errorf("Expected store size to be %d, got %+v\n", expectedSize, stats)
			stats = accountStats()
			return err

		}
		return nil
	})

	// Check limit enforcement.
	if _, err := js.AddStream(&nats.StreamConfig{Name: "fail", Replicas: 3}); err == nil {
		t.Fatalf("Expected an error but got none")
	}

	// We should be at 7995 at the moment with a limit of 8000, so any message will go over.
	if _, err := js.Publish("baz", []byte("JSC-NOT-OK")); err == nil {
		t.Fatalf("Expected publish error but got none")
	}

	// Check consumers
	_, err := js.AddConsumer("foo", &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Create (with explicit create API) for the same consumer must be idempotent, and not trigger limit.
	obsReq := CreateConsumerRequest{
		Stream: "foo",
		Config: ConsumerConfig{Durable: "bar"},
		Action: ActionCreate,
	}
	req, err := json.Marshal(obsReq)
	require_NoError(t, err)

	msg, err := nc.Request(fmt.Sprintf(JSApiDurableCreateT, "foo", "bar"), req, time.Second)
	require_NoError(t, err)
	var resp JSApiConsumerInfoResponse
	require_NoError(t, json.Unmarshal(msg.Data, &resp))
	if resp.Error != nil {
		t.Fatalf("Unexpected error: %v", resp.Error)
	}

	msg, err = nc.Request(fmt.Sprintf(JSApiDurableCreateT, "foo", "bar"), req, time.Second)
	require_NoError(t, err)
	var resp2 JSApiConsumerInfoResponse
	require_NoError(t, json.Unmarshal(msg.Data, &resp2))
	if resp2.Error != nil {
		t.Fatalf("Unexpected error: %v", resp2.Error)
	}

	// This should fail.
	_, err = js.AddConsumer("foo", &nats.ConsumerConfig{Durable: "dlc22", AckPolicy: nats.AckExplicitPolicy})
	if err == nil {
		t.Fatalf("Expected error but got none")
	}
}

func TestJetStreamClusterMaxStreamsReached(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomNonLeader())
	defer nc.Close()

	// Adjust our limits.
	c.updateLimits("$G", map[string]JetStreamAccountLimits{
		_EMPTY_: {
			MaxMemory:  1024,
			MaxStore:   1024,
			MaxStreams: 1,
		},
	})

	// Many stream creations in parallel for the same stream should not result in
	// maximum number of streams reached error. All should have a successful response.
	var wg sync.WaitGroup
	for i := 0; i < 15; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			_, err := js.AddStream(&nats.StreamConfig{
				Name:     "TEST",
				Subjects: []string{"foo"},
				Replicas: 1,
			})
			require_NoError(t, err)
		}()
	}
	wg.Wait()
	require_NoError(t, js.DeleteStream("TEST"))

	// Adjust our limits.
	c.updateLimits("$G", map[string]JetStreamAccountLimits{
		_EMPTY_: {
			MaxMemory:  1024,
			MaxStore:   1024,
			MaxStreams: 2,
		},
	})

	// Setup streams beforehand.
	for d := 0; d < 2; d++ {
		_, err := js.AddStream(&nats.StreamConfig{
			Name:     fmt.Sprintf("TEST-%d", d),
			Subjects: []string{fmt.Sprintf("foo.%d", d)},
			Replicas: 1,
		})
		require_NoError(t, err)
	}

	// Many stream creations in parallel for streams that already exist should not result in
	// maximum number of streams reached error. All should have a successful response.
	for i := 0; i < 15; i++ {
		wg.Add(1)
		d := i % 2
		go func() {
			defer wg.Done()
			_, err := js.AddStream(&nats.StreamConfig{
				Name:     fmt.Sprintf("TEST-%d", d),
				Subjects: []string{fmt.Sprintf("foo.%d", d)},
				Replicas: 1,
			})
			require_NoError(t, err)
		}()
	}
	wg.Wait()
}

func TestJetStreamClusterStreamLimits(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	// Check that large R will fail.
	if _, err := js.AddStream(&nats.StreamConfig{Name: "foo", Replicas: 5}); err == nil {
		t.Fatalf("Expected error but got none")
	}

	maxMsgs := 5

	_, err := js.AddStream(&nats.StreamConfig{
		Name:       "foo",
		Replicas:   3,
		Retention:  nats.LimitsPolicy,
		Discard:    DiscardNew,
		MaxMsgSize: 11,
		MaxMsgs:    int64(maxMsgs),
		MaxAge:     250 * time.Millisecond,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Large message should fail.
	if _, err := js.Publish("foo", []byte("0123456789ZZZ")); err == nil {
		t.Fatalf("Expected publish to fail")
	}

	for i := 0; i < maxMsgs; i++ {
		if _, err := js.Publish("foo", []byte("JSC-OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	// These should fail.
	if _, err := js.Publish("foo", []byte("JSC-OK")); err == nil {
		t.Fatalf("Expected publish to fail")
	}

	// Make sure when space frees up we can send more.
	checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
		si, err := js.StreamInfo("foo")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if si.State.Msgs != 0 {
			return fmt.Errorf("Expected 0 msgs, got state: %+v", si.State)
		}
		return nil
	})

	if _, err := js.Publish("foo", []byte("ROUND2")); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
}

func TestJetStreamClusterStreamInterestOnlyPolicy(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "foo",
		Replicas:  3,
		Retention: nats.InterestPolicy,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	toSend := 10

	// With no interest these should be no-ops.
	for i := 0; i < toSend; i++ {
		if _, err := js.Publish("foo", []byte("JSC-OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	si, err := js.StreamInfo("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.State.Msgs != 0 {
		t.Fatalf("Expected no messages with no interest, got %d", si.State.Msgs)
	}

	// Now create a consumer.
	sub, err := js.SubscribeSync("foo", nats.Durable("dlc"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	for i := 0; i < toSend; i++ {
		if _, err := js.Publish("foo", []byte("JSC-OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}
	checkSubsPending(t, sub, toSend)

	si, err = js.StreamInfo("foo")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.State.Msgs != uint64(toSend) {
		t.Fatalf("Expected %d messages with interest, got %d", toSend, si.State.Msgs)
	}
	if si.State.FirstSeq != uint64(toSend+1) {
		t.Fatalf("Expected first sequence of %d, got %d", toSend+1, si.State.FirstSeq)
	}

	// Now delete the consumer.
	sub.Unsubscribe()
	// That should make it go away.
	if _, err := js.ConsumerInfo("foo", "dlc"); err == nil {
		t.Fatalf("Expected not found error, got none")
	}

	// Wait for the messages to be purged.
	checkFor(t, 5*time.Second, 20*time.Millisecond, func() error {
		si, err := js.StreamInfo("foo")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if si.State.Msgs == 0 {
			return nil
		}
		return fmt.Errorf("Wanted 0 messages, got %d", si.State.Msgs)
	})
}

func TestJetStreamClusterExtendedAccountInfo(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	sendBatch := func(subject string, n int) {
		t.Helper()
		for i := 0; i < n; i++ {
			if _, err := js.Publish(subject, []byte("JSC-OK")); err != nil {
				t.Fatalf("Unexpected publish error: %v", err)
			}
		}
	}

	// Add in some streams with msgs and consumers.
	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST-1", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.SubscribeSync("TEST-1"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	sendBatch("TEST-1", 25)

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST-2", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.SubscribeSync("TEST-2"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	sendBatch("TEST-2", 50)

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST-3", Replicas: 3, Storage: nats.MemoryStorage}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.SubscribeSync("TEST-3"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	sendBatch("TEST-3", 100)

	// Go client will lag so use direct for now.
	getAccountInfo := func() *nats.AccountInfo {
		t.Helper()

		info, err := js.AccountInfo()
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		return info
	}

	checkFor(t, 5*time.Second, 250*time.Millisecond, func() error {
		ai := getAccountInfo()
		if ai.Streams != 3 || ai.Consumers != 3 {
			return fmt.Errorf("AccountInfo not correct: %+v", ai)
		}
		if ai.API.Total < 7 {
			return fmt.Errorf("Expected at least 7 total API calls, got %d", ai.API.Total)
		}
		return nil
	})

	// Now do a failure to make sure we track API errors.
	js.StreamInfo("NO-STREAM")
	js.ConsumerInfo("TEST-1", "NO-CONSUMER")
	js.ConsumerInfo("TEST-2", "NO-CONSUMER")
	js.ConsumerInfo("TEST-3", "NO-CONSUMER")

	checkFor(t, 5*time.Second, 250*time.Millisecond, func() error {
		ai := getAccountInfo()
		if ai.API.Errors != 4 {
			return fmt.Errorf("Expected 4 API calls to be errors, got %d", ai.API.Errors)
		}
		return nil
	})
}

func TestJetStreamClusterPeerRemovalAPI(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	// Client based API
	ml := c.leader()
	nc, err := nats.Connect(ml.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
	if err != nil {
		t.Fatalf("Failed to create system client: %v", err)
	}
	defer nc.Close()

	// Expect error if unknown peer
	req := &JSApiMetaServerRemoveRequest{Server: "S-9"}
	jsreq, err := json.Marshal(req)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	rmsg, err := nc.Request(JSApiRemoveServer, jsreq, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	var resp JSApiMetaServerRemoveResponse
	if err := json.Unmarshal(rmsg.Data, &resp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if resp.Error == nil {
		t.Fatalf("Expected an error, got none")
	}

	sub, err := nc.SubscribeSync(JSAdvisoryServerRemoved)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	rs := c.randomNonLeader()
	req = &JSApiMetaServerRemoveRequest{Server: rs.Name()}
	jsreq, err = json.Marshal(req)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	rmsg, err = nc.Request(JSApiRemoveServer, jsreq, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	resp.Error = nil
	if err := json.Unmarshal(rmsg.Data, &resp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if resp.Error != nil {
		t.Fatalf("Unexpected error: %+v", resp.Error)
	}
	c.waitOnLeader()
	ml = c.leader()

	checkSubsPending(t, sub, 1)
	madv, _ := sub.NextMsg(0)
	var adv JSServerRemovedAdvisory
	if err := json.Unmarshal(madv.Data, &adv); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if adv.Server != rs.Name() {
		t.Fatalf("Expected advisory about %s being removed, got %+v", rs.Name(), adv)
	}

	checkFor(t, 5*time.Second, 250*time.Millisecond, func() error {
		for _, s := range ml.JetStreamClusterPeers() {
			if s == rs.Name() {
				return fmt.Errorf("Still in the peer list")
			}
		}
		return nil
	})
}

func TestJetStreamClusterPeerRemovalAndStreamReassignment(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	// Client based API
	s := c.randomNonLeader()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	si, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Admin based API
	ml := c.leader()
	nc, err = nats.Connect(ml.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
	if err != nil {
		t.Fatalf("Failed to create system client: %v", err)
	}
	defer nc.Close()

	// Select the non-leader server for the stream to remove.
	if len(si.Cluster.Replicas) < 2 {
		t.Fatalf("Not enough replicas found: %+v", si.Cluster)
	}
	toRemove, cl := si.Cluster.Replicas[0].Name, c.leader()
	if toRemove == cl.Name() {
		toRemove = si.Cluster.Replicas[1].Name
	}

	req := &JSApiMetaServerRemoveRequest{Server: toRemove}
	jsreq, err := json.Marshal(req)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	rmsg, err := nc.Request(JSApiRemoveServer, jsreq, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	var resp JSApiMetaServerRemoveResponse
	if err := json.Unmarshal(rmsg.Data, &resp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if resp.Error != nil {
		t.Fatalf("Unexpected error: %+v", resp.Error)
	}
	// In case that server was also meta-leader.
	c.waitOnLeader()

	checkFor(t, 10*time.Second, 250*time.Millisecond, func() error {
		for _, s := range ml.JetStreamClusterPeers() {
			if s == toRemove {
				return fmt.Errorf("Server still in the peer list")
			}
		}
		return nil
	})

	// Now wait until the stream is now current.
	checkFor(t, 10*time.Second, 100*time.Millisecond, func() error {
		si, err := js.StreamInfo("TEST", nats.MaxWait(time.Second))
		if err != nil {
			return fmt.Errorf("Could not fetch stream info: %v", err)
		}
		// We should not see the old server at all.
		for _, p := range si.Cluster.Replicas {
			if p.Name == toRemove {
				t.Fatalf("Peer not removed yet: %+v", toRemove)
			}
			if !p.Current {
				return fmt.Errorf("Expected replica to be current: %+v", p)
			}
		}
		if len(si.Cluster.Replicas) != 2 {
			return fmt.Errorf("Expected 2 replicas, got %d", len(si.Cluster.Replicas))
		}
		return nil
	})
}

func TestJetStreamClusterPeerRemovalAndStreamReassignmentWithoutSpace(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomNonLeader()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	si, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Admin based API
	ml := c.leader()
	nc, err = nats.Connect(ml.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
	if err != nil {
		t.Fatalf("Failed to create system client: %v", err)
	}
	defer nc.Close()

	// Select the non-leader server for the stream to remove.
	if len(si.Cluster.Replicas) < 2 {
		t.Fatalf("Not enough replicas found: %+v", si.Cluster)
	}
	toRemove, cl := si.Cluster.Replicas[0].Name, c.leader()
	if toRemove == cl.Name() {
		toRemove = si.Cluster.Replicas[1].Name
	}

	req := &JSApiMetaServerRemoveRequest{Server: toRemove}
	jsreq, err := json.Marshal(req)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	rmsg, err := nc.Request(JSApiRemoveServer, jsreq, 2*time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	var resp JSApiMetaServerRemoveResponse
	if err := json.Unmarshal(rmsg.Data, &resp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if resp.Error != nil {
		t.Fatalf("Unexpected error: %+v", resp.Error)
	}
	checkFor(t, 10*time.Second, 250*time.Millisecond, func() error {
		for _, s := range ml.JetStreamClusterPeers() {
			if s == toRemove {
				return fmt.Errorf("Server still in the peer list")
			}
		}
		return nil
	})
	// Make sure only 2 peers at this point.
	c.waitOnPeerCount(2)

	// Now wait until the stream is now current.
	streamCurrent := func(nr int) {
		checkFor(t, 10*time.Second, 100*time.Millisecond, func() error {
			si, err := js.StreamInfo("TEST", nats.MaxWait(time.Second))
			if err != nil {
				return fmt.Errorf("Could not fetch stream info: %v", err)
			}
			// We should not see the old server at all.
			for _, p := range si.Cluster.Replicas {
				if p.Name == toRemove {
					return fmt.Errorf("Peer not removed yet: %+v", toRemove)
				}
				if !p.Current {
					return fmt.Errorf("Expected replica to be current: %+v", p)
				}
			}
			if len(si.Cluster.Replicas) != nr {
				return fmt.Errorf("Expected %d replicas, got %d", nr, len(si.Cluster.Replicas))
			}
			return nil
		})
	}

	// Make sure the peer was removed from the stream and that we did not fill the new spot.
	streamCurrent(1)

	// Now add in a new server and make sure it gets added to our stream.
	c.addInNewServer()
	c.waitOnPeerCount(3)

	streamCurrent(2)
}

func TestJetStreamClusterPeerRemovalAndServerBroughtBack(t *testing.T) {
	// Speed up for this test
	peerRemoveTimeout = 2 * time.Second
	defer func() {
		peerRemoveTimeout = peerRemoveTimeoutDefault
	}()

	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	// Client based API
	ml := c.leader()
	nc, err := nats.Connect(ml.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
	if err != nil {
		t.Fatalf("Failed to create system client: %v", err)
	}
	defer nc.Close()

	getPeersCount := func() int {
		js := ml.getJetStream()
		if js == nil {
			return 0
		}
		js.mu.RLock()
		defer js.mu.RUnlock()

		cc := js.cluster
		if !cc.isLeader() || cc.meta == nil {
			return 0
		}
		return len(cc.meta.Peers())
	}

	checkFor(t, 2*time.Second, 250*time.Millisecond, func() error {
		if l := getPeersCount(); l != 5 {
			return fmt.Errorf("expected 5 peers, got %d", l)
		}
		return nil
	})

	// Shutdown server first.
	rs := c.randomNonLeader()
	rs.Shutdown()

	// Peers should still remain the same, even if one server is shut down.
	checkFor(t, 2*time.Second, 250*time.Millisecond, func() error {
		if l := getPeersCount(); l != 5 {
			return fmt.Errorf("expected 5 peers, got %d", l)
		}
		return nil
	})

	// Peer-remove after shutdown.
	req := &JSApiMetaServerRemoveRequest{Server: rs.Name()}
	jsreq, err := json.Marshal(req)
	require_NoError(t, err)
	rmsg, err := nc.Request(JSApiRemoveServer, jsreq, time.Second)
	require_NoError(t, err)

	var resp JSApiMetaServerRemoveResponse
	require_NoError(t, json.Unmarshal(rmsg.Data, &resp))
	if resp.Error != nil {
		t.Fatalf("Unexpected error: %+v", resp.Error)
	}

	// Peer should be removed.
	checkFor(t, 2*time.Second, 250*time.Millisecond, func() error {
		if l := getPeersCount(); l != 4 {
			return fmt.Errorf("expected 4 peers, got %d", l)
		}
		return nil
	})

	// Bringing back the server should re-add to peers after peer-remove timeout.
	c.restartServer(rs)
	checkFor(t, 5*time.Second, 250*time.Millisecond, func() error {
		if l := getPeersCount(); l != 5 {
			return fmt.Errorf("expected 5 peers, got %d", l)
		}
		return nil
	})
}

func TestJetStreamClusterPeerExclusionTag(t *testing.T) {
	c := createJetStreamClusterWithTemplateAndModHook(t, jsClusterTempl, "C", 3,
		func(serverName, clusterName, storeDir, conf string) string {
			switch serverName {
			case "S-1":
				return fmt.Sprintf("%s\nserver_tags: [server:%s, intersect, %s]", conf, serverName, jsExcludePlacement)
			case "S-2":
				return fmt.Sprintf("%s\nserver_tags: [server:%s, intersect]", conf, serverName)
			default:
				return fmt.Sprintf("%s\nserver_tags: [server:%s]", conf, serverName)
			}
		})
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	for i, c := range []nats.StreamConfig{
		{Replicas: 1, Placement: &nats.Placement{Tags: []string{"server:S-1"}}},
		{Replicas: 2, Placement: &nats.Placement{Tags: []string{"intersect"}}},
		{Replicas: 3}, // not enough server without !jetstream
	} {
		c.Name = fmt.Sprintf("TEST%d", i)
		c.Subjects = []string{c.Name}
		_, err := js.AddStream(&c)
		require_Error(t, err)
		require_Contains(t, err.Error(), "no suitable peers for placement", "exclude tag set")
	}

	// Test update failure
	cfg := &nats.StreamConfig{Name: "TEST", Subjects: []string{"foo"}, Replicas: 2}
	_, err := js.AddStream(cfg)
	require_NoError(t, err)

	cfg.Replicas = 3
	_, err = js.UpdateStream(cfg)
	require_Error(t, err)
	require_Contains(t, err.Error(), "no suitable peers for placement", "exclude tag set")
	// Test tag reload removing !jetstream tag, and allowing placement again

	srv := c.serverByName("S-1")

	v, err := srv.Varz(nil)
	require_NoError(t, err)
	require_True(t, v.Tags.Contains(jsExcludePlacement))
	content, err := os.ReadFile(srv.configFile)
	require_NoError(t, err)
	newContent := strings.ReplaceAll(string(content), fmt.Sprintf(", %s]", jsExcludePlacement), "]")
	changeCurrentConfigContentWithNewContent(t, srv.configFile, []byte(newContent))

	require_NoError(t, srv.Reload())
	srv.sendStatszUpdate()

	v, err = srv.Varz(nil)
	require_NoError(t, err)
	require_True(t, !v.Tags.Contains(jsExcludePlacement))

	checkFor(t, 20*time.Second, 500*time.Millisecond, func() error {
		for _, s := range c.servers {
			var err error
			s.nodeToInfo.Range(func(sn any, value any) bool {
				ni := value.(nodeInfo)
				if ni.name != "S-1" {
					return true
				}
				if ni.tags.Contains("!jetstream") {
					err = fmt.Errorf("%s still has jetstream exclude tag", sn)
					return false
				}
				return true
			})
			if err != nil {
				return err
			}
		}
		return nil
	})

	cfg.Replicas = 3
	_, err = js.UpdateStream(cfg)
	require_NoError(t, err)
}

func TestJetStreamClusterAccountPurge(t *testing.T) {
	sysKp, syspub := createKey(t)
	sysJwt := encodeClaim(t, jwt.NewAccountClaims(syspub), syspub)
	sysCreds := newUser(t, sysKp)
	accKp, accpub := createKey(t)
	accClaim := jwt.NewAccountClaims(accpub)
	accClaim.Limits.JetStreamLimits.DiskStorage = 1024 * 1024 * 5
	accClaim.Limits.JetStreamLimits.MemoryStorage = 1024 * 1024 * 5
	accJwt := encodeClaim(t, accClaim, accpub)
	accCreds := newUser(t, accKp)

	tmlp := `
		listen: 127.0.0.1:-1
		server_name: %s
		jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'}
		leaf {
			listen: 127.0.0.1:-1
		}
		cluster {
			name: %s
			listen: 127.0.0.1:%d
			routes = [%s]
		}
	`
	c := createJetStreamClusterWithTemplateAndModHook(t, tmlp, "cluster", 3,
		func(serverName, clustername, storeDir, conf string) string {
			return conf + fmt.Sprintf(`
				operator: %s
				system_account: %s
				resolver: {
					type: full
					dir: '%s/jwt'
					timeout: "10ms"
				}`, ojwt, syspub, storeDir)
		})
	defer c.shutdown()

	c.waitOnLeader()

	updateJwt(t, c.randomServer().ClientURL(), sysCreds, sysJwt, 3)
	updateJwt(t, c.randomServer().ClientURL(), sysCreds, accJwt, 3)

	c.waitOnAccount(accpub)

	createTestData := func(t *testing.T) {
		nc, js := jsClientConnect(t, c.randomNonLeader(), nats.UserCredentials(accCreds))
		defer nc.Close()

		// Prevent 'nats: JetStream not enabled for account' when creating the first stream.
		c.waitOnAccount(accpub)

		_, err := js.AddStream(&nats.StreamConfig{
			Name:     "TEST1",
			Subjects: []string{"foo"},
			Replicas: 3,
		})
		require_NoError(t, err)
		c.waitOnStreamLeader(accpub, "TEST1")

		ci, err := js.AddConsumer("TEST1",
			&nats.ConsumerConfig{Durable: "DUR1",
				AckPolicy: nats.AckExplicitPolicy})
		require_NoError(t, err)
		require_True(t, ci.Config.Replicas == 0)

		ci, err = js.AddConsumer("TEST1",
			&nats.ConsumerConfig{Durable: "DUR2",
				AckPolicy: nats.AckExplicitPolicy,
				Replicas:  1})
		require_NoError(t, err)
		require_True(t, ci.Config.Replicas == 1)

		toSend := uint64(1_000)
		for i := uint64(0); i < toSend; i++ {
			_, err = js.Publish("foo", nil)
			require_NoError(t, err)
		}

		_, err = js.AddStream(&nats.StreamConfig{
			Name:     "TEST2",
			Subjects: []string{"bar"},
			Replicas: 1,
		})
		require_NoError(t, err)

		ci, err = js.AddConsumer("TEST2",
			&nats.ConsumerConfig{Durable: "DUR1",
				AckPolicy: nats.AckExplicitPolicy,
				Replicas:  0})
		require_NoError(t, err)
		require_True(t, ci.Config.Replicas == 0)

		for i := uint64(0); i < toSend; i++ {
			_, err = js.Publish("bar", nil)
			require_NoError(t, err)
		}
	}

	inspectDirs := func(t *testing.T, sysTotal, accTotal int) error {
		t.Helper()
		sysDirs := 0
		accDirs := 0
		for _, s := range c.servers {
			files, err := os.ReadDir(filepath.Join(s.getOpts().StoreDir, "jetstream", syspub, "_js_"))
			require_NoError(t, err)
			sysDirs += len(files) - 1 // sub 1 for _meta_
			files, err = os.ReadDir(filepath.Join(s.getOpts().StoreDir, "jetstream", accpub, "streams"))
			if err == nil || err.(*os.PathError).Error() == "no such file or directory" {
				accDirs += len(files)
			}
		}
		if sysDirs != sysTotal || accDirs != accTotal {
			return fmt.Errorf("expected directory count does not match %d == %d, %d == %d",
				sysDirs, sysTotal, accDirs, accTotal)
		}
		return nil
	}

	checkForDirs := func(t *testing.T, sysTotal, accTotal int) {
		t.Helper()
		checkFor(t, 20*time.Second, 250*time.Millisecond, func() error {
			return inspectDirs(t, sysTotal, accTotal)
		})
	}

	purge := func(t *testing.T) {
		t.Helper()
		ncsys, err := nats.Connect(c.randomServer().ClientURL(), nats.UserCredentials(sysCreds))
		require_NoError(t, err)
		defer ncsys.Close()

		request := func() error {
			var resp JSApiAccountPurgeResponse
			m, err := ncsys.Request(fmt.Sprintf(JSApiAccountPurgeT, accpub), nil, time.Second)
			if err != nil {
				return err
			}
			if err := json.Unmarshal(m.Data, &resp); err != nil {
				return err
			}
			if !resp.Initiated {
				return fmt.Errorf("not started")
			}
			return nil
		}
		checkFor(t, 30*time.Second, 250*time.Millisecond, request)
	}

	t.Run("startup-cleanup", func(t *testing.T) {
		_, newCleanupAcc1 := createKey(t)
		_, newCleanupAcc2 := createKey(t)
		for _, s := range c.servers {
			os.MkdirAll(filepath.Join(s.getOpts().StoreDir, JetStreamStoreDir, newCleanupAcc1, streamsDir), defaultDirPerms)
			os.MkdirAll(filepath.Join(s.getOpts().StoreDir, JetStreamStoreDir, newCleanupAcc2), defaultDirPerms)
		}
		createTestData(t)
		checkForDirs(t, 6, 4)
		c.stopAll()
		c.restartAll()
		for _, s := range c.servers {
			accDir := filepath.Join(s.getOpts().StoreDir, JetStreamStoreDir, newCleanupAcc1)
			_, e := os.Stat(filepath.Join(accDir, streamsDir))
			require_Error(t, e)
			require_True(t, os.IsNotExist(e))
			_, e = os.Stat(accDir)
			require_Error(t, e)
			require_True(t, os.IsNotExist(e))
			_, e = os.Stat(filepath.Join(s.getOpts().StoreDir, JetStreamStoreDir, newCleanupAcc2))
			require_Error(t, e)
			require_True(t, os.IsNotExist(e))
		}
		checkForDirs(t, 6, 4)
		// Make sure we have a leader for all assets before moving to the next test
		c.waitOnStreamLeader(accpub, "TEST1")
		c.waitOnConsumerLeader(accpub, "TEST1", "DUR1")
		c.waitOnConsumerLeader(accpub, "TEST1", "DUR2")
		c.waitOnStreamLeader(accpub, "TEST2")
		c.waitOnConsumerLeader(accpub, "TEST2", "DUR1")
	})

	t.Run("purge-with-restart", func(t *testing.T) {
		createTestData(t)
		checkForDirs(t, 6, 4)
		purge(t)
		checkForDirs(t, 0, 0)
		c.stopAll()
		c.restartAll()
		checkForDirs(t, 0, 0)
	})

	t.Run("purge-with-reuse", func(t *testing.T) {
		createTestData(t)
		checkForDirs(t, 6, 4)
		purge(t)
		checkForDirs(t, 0, 0)
		createTestData(t)
		checkForDirs(t, 6, 4)
		purge(t)
		checkForDirs(t, 0, 0)
	})

	t.Run("purge-deleted-account", func(t *testing.T) {
		createTestData(t)
		checkForDirs(t, 6, 4)
		c.stopAll()
		for _, s := range c.servers {
			require_NoError(t, os.Remove(s.getOpts().StoreDir+"/jwt/"+accpub+".jwt"))
		}
		c.restartAll()
		checkForDirs(t, 6, 4)
		purge(t)
		checkForDirs(t, 0, 0)
		c.stopAll()
		c.restartAll()
		checkForDirs(t, 0, 0)
	})
}

func TestJetStreamClusterScaleConsumer(t *testing.T) {
	c := createJetStreamClusterWithTemplate(t, jsClusterTempl, "C", 3)
	defer c.shutdown()

	srv := c.randomNonLeader()
	nc, js := jsClientConnect(t, srv)
	defer nc.Close()

	si, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	durCfg := &nats.ConsumerConfig{Durable: "DUR", AckPolicy: nats.AckExplicitPolicy}
	ci, err := js.AddConsumer("TEST", durCfg)
	require_NoError(t, err)
	require_True(t, ci.Config.Replicas == 0)

	toSend := uint64(1_000)
	for i := uint64(0); i < toSend; i++ {
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
	}

	s, err := js.PullSubscribe("foo", "DUR")
	require_NoError(t, err)

	consumeOne := func(expSeq uint64) error {
		if ci, err := js.ConsumerInfo("TEST", "DUR"); err != nil {
			return err
		} else if ci.Delivered.Stream != expSeq {
			return fmt.Errorf("pre: not expected delivered stream %d, got %d", expSeq, ci.Delivered.Stream)
		} else if ci.Delivered.Consumer != expSeq {
			return fmt.Errorf("pre: not expected delivered consumer %d, got %d", expSeq, ci.Delivered.Consumer)
		} else if ci.AckFloor.Stream != expSeq {
			return fmt.Errorf("pre: not expected ack stream %d, got %d", expSeq, ci.AckFloor.Stream)
		} else if ci.AckFloor.Consumer != expSeq {
			return fmt.Errorf("pre: not expected ack consumer %d, got %d", expSeq, ci.AckFloor.Consumer)
		}
		if m, err := s.Fetch(1); err != nil {
			return err
		} else if err := m[0].AckSync(); err != nil {
			return err
		}
		expSeq = expSeq + 1
		if ci, err := js.ConsumerInfo("TEST", "DUR"); err != nil {
			return err
		} else if ci.Delivered.Stream != expSeq {
			return fmt.Errorf("post: not expected delivered stream %d, got %d", expSeq, ci.Delivered.Stream)
		} else if ci.Delivered.Consumer != expSeq {
			return fmt.Errorf("post: not expected delivered consumer %d, got %d", expSeq, ci.Delivered.Consumer)
		} else if ci.AckFloor.Stream != expSeq {
			return fmt.Errorf("post: not expected ack stream %d, got %d", expSeq, ci.AckFloor.Stream)
		} else if ci.AckFloor.Consumer != expSeq {
			return fmt.Errorf("post: not expected ack consumer %d, got %d", expSeq, ci.AckFloor.Consumer)
		}
		return nil
	}

	require_NoError(t, consumeOne(0))

	// scale down, up, down and up to default == 3 again
	for i, r := range []int{1, 3, 1, 0} {
		durCfg.Replicas = r
		if r == 0 {
			r = si.Config.Replicas
		}
		js.UpdateConsumer("TEST", durCfg)

		checkFor(t, time.Second*30, time.Millisecond*250, func() error {
			if ci, err = js.ConsumerInfo("TEST", "DUR"); err != nil {
				return err
			} else if ci.Cluster == nil {
				return errors.New("no cluster info")
			} else if ci.Cluster.Leader == _EMPTY_ {
				return errors.New("no leader")
			} else if len(ci.Cluster.Replicas) != r-1 {
				return fmt.Errorf("not enough replica, got %d wanted %d", len(ci.Cluster.Replicas), r-1)
			} else {
				for _, r := range ci.Cluster.Replicas {
					if !r.Current || r.Offline || r.Lag != 0 {
						return fmt.Errorf("replica %s not current %t offline %t lag %d", r.Name, r.Current, r.Offline, r.Lag)
					}
				}
			}
			return nil
		})

		require_NoError(t, consumeOne(uint64(i+1)))
	}
}

func TestJetStreamClusterConsumerScaleUp(t *testing.T) {
	c := createJetStreamCluster(t, jsClusterTempl, "HUB", _EMPTY_, 3, 22020, true)
	defer c.shutdown()

	// Client based API
	srv := c.randomNonLeader()
	nc, js := jsClientConnect(t, srv)
	defer nc.Close()

	scfg := nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 1,
	}
	_, err := js.AddStream(&scfg)
	require_NoError(t, err)
	defer js.DeleteStream("TEST")

	dcfg := nats.ConsumerConfig{
		Durable:   "DUR",
		AckPolicy: nats.AckExplicitPolicy,
		Replicas:  0}
	_, err = js.AddConsumer("TEST", &dcfg)
	require_NoError(t, err)

	for i := 0; i < 100; i++ {
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
	}

	scfg.Replicas = 2
	_, err = js.UpdateStream(&scfg)
	require_NoError(t, err)

	// The scale up issue shows itself as permanent loss of consumer leadership
	// So give it some time for the change to propagate to new consumer peers and the quorum to disrupt
	// 2 seconds is a value arrived by experimentally, no sleep or a sleep of 1sec always had the test pass a lot.
	time.Sleep(2 * time.Second)

	c.waitOnStreamLeader("$G", "TEST")

	// There is also a timing component to the issue triggering.
	c.waitOnConsumerLeader("$G", "TEST", "DUR")
}

func TestJetStreamClusterPeerOffline(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	ml := c.leader()
	rs := c.randomNonLeader()

	checkPeer := func(ml, rs *Server, shouldBeOffline bool) {
		t.Helper()

		checkFor(t, 5*time.Second, 50*time.Millisecond, func() error {
			var found bool
			for _, s := range ml.JetStreamClusterPeers() {
				if s == rs.Name() {
					found = true
					break
				}
			}
			if !shouldBeOffline && !found {
				return fmt.Errorf("Server %q not in the peers list", rs.Name())
			} else if shouldBeOffline && found {
				return fmt.Errorf("Server %q should not be in the peers list", rs.Name())
			}

			var ok bool
			ml.nodeToInfo.Range(func(k, v any) bool {
				if si := v.(nodeInfo); si.name == rs.Name() {
					if shouldBeOffline && si.offline || !shouldBeOffline && !si.offline {
						ok = true
						return false
					}
				}
				return true
			})
			if !ok {
				if shouldBeOffline {
					return fmt.Errorf("Server %q should be marked as online", rs.Name())
				}
				return fmt.Errorf("Server %q is still marked as online", rs.Name())
			}
			return nil
		})
	}

	// Shutdown the server and make sure that it is now showing as offline.
	rs.Shutdown()
	checkPeer(ml, rs, true)

	// Now restart that server and check that is no longer offline.
	oldrs := rs
	rs, _ = RunServerWithConfig(rs.getOpts().ConfigFile)
	defer rs.Shutdown()

	// Replaced old with new server
	for i := 0; i < len(c.servers); i++ {
		if c.servers[i] == oldrs {
			c.servers[i] = rs
		}
	}

	// Wait for cluster to be formed
	checkClusterFormed(t, c.servers...)

	// Make sure that we have a leader (there can always be a re-election)
	c.waitOnLeader()
	ml = c.leader()

	// Now check that rs is not offline
	checkPeer(ml, rs, false)
}

func TestJetStreamClusterNoQuorumStepdown(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	// Setup subscription for leader elected.
	lesub, err := nc.SubscribeSync(JSAdvisoryStreamLeaderElectedPre + ".*")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err := js.AddStream(&nats.StreamConfig{Name: "NO-Q", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Make sure we received our leader elected advisory.
	leadv, _ := lesub.NextMsg(0)
	if leadv == nil {
		t.Fatalf("Expected to receive a leader elected advisory")
	}
	var le JSStreamLeaderElectedAdvisory
	if err := json.Unmarshal(leadv.Data, &le); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if ln := c.streamLeader("$G", "NO-Q").Name(); le.Leader != ln {
		t.Fatalf("Expected to have leader %q in elect advisory, got %q", ln, le.Leader)
	}

	payload := []byte("Hello JSC")
	for i := 0; i < 10; i++ {
		if _, err := js.Publish("NO-Q", payload); err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
	}

	// Setup subscription for leader elected.
	clesub, err := nc.SubscribeSync(JSAdvisoryConsumerLeaderElectedPre + ".*.*")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Make durable to have R match Stream.
	sub, err := js.SubscribeSync("NO-Q", nats.Durable("rr"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	ci, err := sub.ConsumerInfo()
	if err != nil || ci == nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Make sure we received our consumer leader elected advisory.
	leadv, _ = clesub.NextMsg(0)
	if leadv == nil {
		t.Fatalf("Expected to receive a consumer leader elected advisory")
	}

	// Shutdown the non-leader.
	c.randomNonStreamLeader("$G", "NO-Q").Shutdown()

	// This should eventually have us stepdown as leader since we would have lost quorum with R=2.
	checkFor(t, 5*time.Second, 500*time.Millisecond, func() error {
		if sl := c.streamLeader("$G", "NO-Q"); sl == nil {
			return nil
		}
		return fmt.Errorf("Still have leader for stream")
	})

	notAvailableErr := func(err error) bool {
		return err != nil && (strings.Contains(err.Error(), "unavailable") || err == context.DeadlineExceeded)
	}

	checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
		if cl := c.consumerLeader("$G", "NO-Q", ci.Name); cl == nil {
			return nil
		}
		return fmt.Errorf("Still have leader for consumer")
	})

	if _, err = js.ConsumerInfo("NO-Q", ci.Name); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	if _, err := sub.ConsumerInfo(); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}

	// Now let's take out the other non meta-leader
	// We should get same error for general API calls.
	c.randomNonLeader().Shutdown()
	c.expectNoLeader()

	// Now make sure the general JS API responds with system unavailable.
	if _, err = js.AccountInfo(); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	if _, err := js.AddStream(&nats.StreamConfig{Name: "NO-Q33", Replicas: 2}); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	if _, err := js.UpdateStream(&nats.StreamConfig{Name: "NO-Q33", Replicas: 2}); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	if err := js.DeleteStream("NO-Q"); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	if err := js.PurgeStream("NO-Q"); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	if err := js.DeleteMsg("NO-Q", 1); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	// Consumer
	if _, err := js.AddConsumer("NO-Q", &nats.ConsumerConfig{Durable: "dlc", AckPolicy: nats.AckExplicitPolicy}); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	if err := js.DeleteConsumer("NO-Q", "dlc"); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	if _, err := js.ConsumerInfo("NO-Q", "dlc"); !notAvailableErr(err) {
		t.Fatalf("Expected an 'unavailable' error, got %v", err)
	}
	// Listers
	for info := range js.StreamsInfo() {
		t.Fatalf("Unexpected stream info, got %v", info)
	}
	for info := range js.ConsumersInfo("NO-Q") {
		t.Fatalf("Unexpected consumer info, got %v", info)
	}
}

func TestJetStreamClusterCreateResponseAdvisoriesHaveSubject(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	sub, err := nc.SubscribeSync("$JS.EVENT.ADVISORY.API")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer sub.Unsubscribe()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.SubscribeSync("TEST", nats.Durable("DLC")); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if err := js.PurgeStream("TEST"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if err := js.DeleteStream("TEST"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkSubsPending(t, sub, 6)

	for m, err := sub.NextMsg(0); err == nil; m, err = sub.NextMsg(0) {
		var audit JSAPIAudit
		if err := json.Unmarshal(m.Data, &audit); err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if audit.Subject == _EMPTY_ {
			t.Fatalf("Expected subject, got nothing")
		}
	}
}

func TestJetStreamClusterRestartAndRemoveAdvisories(t *testing.T) {
	// FIXME(dlc) - Flaky on Travis, skip for now.
	skip(t)

	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	sub, err := nc.SubscribeSync("$JS.EVENT.ADVISORY.API")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer sub.Unsubscribe()

	csub, err := nc.SubscribeSync("$JS.EVENT.ADVISORY.*.CREATED.>")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer csub.Unsubscribe()
	nc.Flush()

	sendBatch := func(subject string, n int) {
		t.Helper()
		for i := 0; i < n; i++ {
			if _, err := js.Publish(subject, []byte("JSC-OK")); err != nil {
				t.Fatalf("Unexpected publish error: %v", err)
			}
		}
	}

	// Add in some streams with msgs and consumers.
	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST-1", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.SubscribeSync("TEST-1", nats.Durable("DC")); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	sendBatch("TEST-1", 25)

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST-2", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.SubscribeSync("TEST-2", nats.Durable("DC")); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	sendBatch("TEST-2", 50)

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST-3", Replicas: 3, Storage: nats.MemoryStorage}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.SubscribeSync("TEST-3", nats.Durable("DC")); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	sendBatch("TEST-3", 100)

	drainSub := func(sub *nats.Subscription) {
		for _, err := sub.NextMsg(0); err == nil; _, err = sub.NextMsg(0) {
		}
	}

	// Wait for the advisories for all streams and consumers.
	checkSubsPending(t, sub, 12) // 3 streams, 3*2 consumers, 3 stream names lookups for creating consumers.
	drainSub(sub)

	// Created audit events.
	checkSubsPending(t, csub, 6)
	drainSub(csub)

	usub, err := nc.SubscribeSync("$JS.EVENT.ADVISORY.*.UPDATED.>")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer usub.Unsubscribe()
	nc.Flush()

	checkSubsPending(t, csub, 0)
	checkSubsPending(t, sub, 0)
	checkSubsPending(t, usub, 0)

	// Now restart the other two servers we are not connected to.
	for _, cs := range c.servers {
		if cs != s {
			cs.Shutdown()
			c.restartServer(cs)
		}
	}
	c.waitOnAllCurrent()

	checkSubsPending(t, csub, 0)
	checkSubsPending(t, sub, 0)
	checkSubsPending(t, usub, 0)

	dsub, err := nc.SubscribeSync("$JS.EVENT.ADVISORY.*.DELETED.>")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer dsub.Unsubscribe()
	nc.Flush()

	c.waitOnConsumerLeader("$G", "TEST-1", "DC")
	c.waitOnLeader()

	// Now check delete advisories as well.
	if err := js.DeleteConsumer("TEST-1", "DC"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkSubsPending(t, csub, 0)
	checkSubsPending(t, dsub, 1)
	checkSubsPending(t, sub, 1)
	checkSubsPending(t, usub, 0)
	drainSub(dsub)

	if err := js.DeleteStream("TEST-3"); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkSubsPending(t, dsub, 2) // Stream and the consumer underneath.
	checkSubsPending(t, sub, 2)
}

func TestJetStreamClusterNoDuplicateOnNodeRestart(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "ND", 2)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	sl := c.streamLeader("$G", "TEST")
	if s == sl {
		nc.Close()
		nc, js = jsClientConnect(t, s)
		defer nc.Close()
	}

	sub, err := js.SubscribeSync("foo", nats.Durable("dlc"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	js.Publish("foo", []byte("msg1"))
	if m, err := sub.NextMsg(time.Second); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	} else {
		m.AckSync()
	}

	sl.Shutdown()
	c.restartServer(sl)
	c.waitOnStreamLeader("$G", "TEST")
	c.waitOnConsumerLeader("$G", "TEST", "dlc")

	// Send second msg
	js.Publish("foo", []byte("msg2"))
	msg, err := sub.NextMsg(5 * time.Second)
	if err != nil {
		t.Fatalf("Error getting message: %v", err)
	}
	if string(msg.Data) != "msg2" {
		t.Fatalf("Unexpected message: %s", msg.Data)
	}
	msg.AckSync()

	// Make sure we don't get a duplicate.
	msg, err = sub.NextMsg(250 * time.Millisecond)
	if err == nil {
		t.Fatalf("Should have gotten an error, got %s", msg.Data)
	}
}

func TestJetStreamClusterNoDupePeerSelection(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "NDP", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	// Create 10 streams. Make sure none of them have a replica
	// that is the same as the leader.
	for i := 1; i <= 10; i++ {
		si, err := js.AddStream(&nats.StreamConfig{
			Name:     fmt.Sprintf("TEST-%d", i),
			Replicas: 3,
		})
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if si.Cluster == nil || si.Cluster.Leader == "" || len(si.Cluster.Replicas) != 2 {
			t.Fatalf("Unexpected cluster state for stream info: %+v\n", si.Cluster)
		}
		// Make sure that the replicas are not same as the leader.
		for _, pi := range si.Cluster.Replicas {
			if pi.Name == si.Cluster.Leader {
				t.Fatalf("Found replica that is same as leader, meaning 2 nodes placed on same server")
			}
		}
		// Now do a consumer and check same thing.
		sub, err := js.SubscribeSync(si.Config.Name)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		ci, err := sub.ConsumerInfo()
		if err != nil {
			t.Fatalf("Unexpected error getting consumer info: %v", err)
		}
		for _, pi := range ci.Cluster.Replicas {
			if pi.Name == ci.Cluster.Leader {
				t.Fatalf("Found replica that is same as leader, meaning 2 nodes placed on same server")
			}
		}
	}
}

func TestJetStreamClusterStreamRemovePeer(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "RNS", 5)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Send in 10 messages.
	msg, toSend := []byte("Hello JS Clustering"), 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("TEST", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	sub, err := js.SubscribeSync("TEST", nats.Durable("cat"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	checkSubsPending(t, sub, toSend)

	// Do ephemeral too.
	esub, err := js.SubscribeSync("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	checkSubsPending(t, esub, toSend)

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "pull", AckPolicy: nats.AckNonePolicy})
	require_NoError(t, err)

	pullSub, err := js.PullSubscribe("TEST", "pull")
	require_NoError(t, err)

	// First fetch the messages that are already there.
	msgs, err := pullSub.Fetch(toSend, nats.MaxWait(500*time.Millisecond))
	require_NoError(t, err)
	require_Equal(t, toSend, len(msgs))

	// Now prepare a check to see if we get unwated `Consumer Deleted` error on peer remove.
	pullResults := make(chan error, 1)
	go func() {
		_, err := pullSub.Fetch(1, nats.MaxWait(30*time.Second))
		// Let's check if we get unwted `Consumer Deleted` error on peer remove.
		// Everything else is fine (Leader Changed, Timeout, etc.)
		if err != nats.ErrConsumerDeleted {
			close(pullResults)
		} else {
			pullResults <- err
		}
	}()

	ci, err := esub.ConsumerInfo()
	if err != nil {
		t.Fatalf("Could not fetch consumer info: %v", err)
	}
	// Capture ephemeral's server and name.
	es, en := ci.Cluster.Leader, ci.Name

	// Grab stream info.
	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	peers := []string{si.Cluster.Leader}
	for _, p := range si.Cluster.Replicas {
		peers = append(peers, p.Name)
	}
	// Pick a truly random server to remove.
	rand.Shuffle(len(peers), func(i, j int) { peers[i], peers[j] = peers[j], peers[i] })
	toRemove := peers[0]

	// First test bad peer.
	req := &JSApiStreamRemovePeerRequest{Peer: "NOT VALID"}
	jsreq, err := json.Marshal(req)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Need to call this by hand for now.
	resp, err := nc.Request(fmt.Sprintf(JSApiStreamRemovePeerT, "TEST"), jsreq, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	var rpResp JSApiStreamRemovePeerResponse
	if err := json.Unmarshal(resp.Data, &rpResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if rpResp.Error == nil || !strings.Contains(rpResp.Error.Description, "peer not a member") {
		t.Fatalf("Expected error for bad peer, got %+v", rpResp.Error)
	}
	rpResp.Error = nil

	req = &JSApiStreamRemovePeerRequest{Peer: toRemove}
	jsreq, err = json.Marshal(req)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	resp, err = nc.Request(fmt.Sprintf(JSApiStreamRemovePeerT, "TEST"), jsreq, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if err := json.Unmarshal(resp.Data, &rpResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if rpResp.Error != nil {
		t.Fatalf("Unexpected error: %+v", rpResp.Error)
	}

	c.waitOnStreamLeader("$G", "TEST")

	checkFor(t, 10*time.Second, 100*time.Millisecond, func() error {
		si, err := js.StreamInfo("TEST", nats.MaxWait(time.Second))
		if err != nil {
			return fmt.Errorf("Could not fetch stream info: %v", err)
		}
		if len(si.Cluster.Replicas) != 2 {
			return fmt.Errorf("Expected 2 replicas, got %d", len(si.Cluster.Replicas))
		}
		for _, peer := range si.Cluster.Replicas {
			if !peer.Current {
				return fmt.Errorf("Expected replica to be current: %+v", peer)
			}
		}
		if si.Cluster.Leader == toRemove {
			return fmt.Errorf("Peer not removed yet: %+v", toRemove)
		}
		for _, p := range si.Cluster.Replicas {
			if p.Name == toRemove {
				return fmt.Errorf("Peer not removed yet: %+v", toRemove)
			}
		}
		return nil
	})

	c.waitOnConsumerLeader("$G", "TEST", "cat")
	c.waitOnConsumerLeader("$G", "TEST", "pull")

	// Now check consumer info as well.
	checkFor(t, 10*time.Second, 100*time.Millisecond, func() error {
		ci, err := js.ConsumerInfo("TEST", "cat", nats.MaxWait(time.Second))
		if err != nil {
			return fmt.Errorf("Could not fetch consumer info: %v", err)
		}
		if len(ci.Cluster.Replicas) != 2 {
			return fmt.Errorf("Expected 2 replicas, got %d", len(ci.Cluster.Replicas))
		}
		for _, peer := range ci.Cluster.Replicas {
			if !peer.Current {
				return fmt.Errorf("Expected replica to be current: %+v", peer)
			}
		}
		if ci.Cluster.Leader == toRemove {
			return fmt.Errorf("Peer not removed yet: %+v", toRemove)
		}
		for _, p := range ci.Cluster.Replicas {
			if p.Name == toRemove {
				return fmt.Errorf("Peer not removed yet: %+v", toRemove)
			}
		}
		return nil
	})

	// Check if we got the `Consumer Deleted` error on the pull consumer.
	select {
	case err := <-pullResults:
		if err != nil {
			t.Fatalf("Expected timeout error or nil, got %v", err)
		}
	default:
	}

	// Now check ephemeral consumer info.
	// Make sure we did not stamp same new group into the ephemeral where R=1.
	ci, err = esub.ConsumerInfo()
	// If the leader was same as what we just removed, this should fail.
	if es == toRemove {
		if err != nats.ErrConsumerNotFound {
			t.Fatalf("Expected a not found error, got %v", err)
		}
		// Also make sure this was removed all together.
		// We may proactively move things in the future.
		for cn := range js.ConsumerNames("TEST") {
			if cn == en {
				t.Fatalf("Expected ephemeral consumer to be deleted since we removed its only peer")
			}
		}
	} else {
		if err != nil {
			t.Fatalf("Could not fetch consumer info: %v", err)
		}
		if len(ci.Cluster.Replicas) != 0 {
			t.Fatalf("Expected no replicas for ephemeral, got %d", len(ci.Cluster.Replicas))
		}
	}

	// Confirm we can peer-remove by the peer ID instead of the server name too.
	rs := c.randomNonStreamLeader(globalAccountName, "TEST")
	nodeName := rs.Node()
	require_Equal(t, nodeName, getHash(rs.Name()))
	req = &JSApiStreamRemovePeerRequest{Peer: nodeName}
	jsreq, err = json.Marshal(req)
	require_NoError(t, err)
	resp, err = nc.Request(fmt.Sprintf(JSApiStreamRemovePeerT, "TEST"), jsreq, time.Second)
	require_NoError(t, err)
	rpResp = JSApiStreamRemovePeerResponse{}
	require_NoError(t, json.Unmarshal(resp.Data, &rpResp))
	require_True(t, rpResp.Success)
}

func TestJetStreamClusterStreamLeaderStepDown(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "RNS", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Send in 10 messages.
	msg, toSend := []byte("Hello JS Clustering"), 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("TEST", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	sub, err := js.SubscribeSync("TEST", nats.Durable("cat"))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	defer sub.Unsubscribe()

	oldLeader := c.streamLeader("$G", "TEST").Name()

	// Need to call this by hand for now.
	resp, err := nc.Request(fmt.Sprintf(JSApiStreamLeaderStepDownT, "TEST"), nil, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	var sdResp JSApiStreamLeaderStepDownResponse
	if err := json.Unmarshal(resp.Data, &sdResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if sdResp.Error != nil {
		t.Fatalf("Unexpected error: %+v", sdResp.Error)
	}

	// Grab shorter timeout jetstream context.
	js, err = nc.JetStream(nats.MaxWait(250 * time.Millisecond))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkFor(t, 2*time.Second, 50*time.Millisecond, func() error {
		si, err := js.StreamInfo("TEST")
		if err != nil {
			return fmt.Errorf("Could not fetch stream info: %v", err)
		}
		if si.Cluster.Leader == oldLeader {
			return fmt.Errorf("Still have old leader")
		}
		if len(si.Cluster.Replicas) != 2 {
			return fmt.Errorf("Expected 2 replicas, got %d", len(si.Cluster.Replicas))
		}
		for _, peer := range si.Cluster.Replicas {
			if !peer.Current {
				return fmt.Errorf("Expected replica to be current: %+v", peer)
			}
		}
		return nil
	})

	// Now do consumer.
	oldLeader = c.consumerLeader("$G", "TEST", "cat").Name()

	// Need to call this by hand for now.
	resp, err = nc.Request(fmt.Sprintf(JSApiConsumerLeaderStepDownT, "TEST", "cat"), nil, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	var cdResp JSApiConsumerLeaderStepDownResponse
	if err := json.Unmarshal(resp.Data, &cdResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if cdResp.Error != nil {
		t.Fatalf("Unexpected error: %+v", cdResp.Error)
	}

	checkFor(t, 2*time.Second, 50*time.Millisecond, func() error {
		ci, err := js.ConsumerInfo("TEST", "cat")
		if err != nil {
			return fmt.Errorf("Could not fetch consumer info: %v", err)
		}
		if ci.Cluster.Leader == oldLeader {
			return fmt.Errorf("Still have old leader")
		}
		if len(ci.Cluster.Replicas) != 2 {
			return fmt.Errorf("Expected 2 replicas, got %d", len(ci.Cluster.Replicas))
		}
		for _, peer := range ci.Cluster.Replicas {
			if !peer.Current {
				return fmt.Errorf("Expected replica to be current: %+v", peer)
			}
		}
		return nil
	})
}

func TestJetStreamClusterRemoveServer(t *testing.T) {
	skip(t)

	c := createJetStreamClusterExplicit(t, "RNS", 5)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Send in 10 messages.
	msg, toSend := []byte("Hello JS Clustering"), 10
	for i := 0; i < toSend; i++ {
		if _, err = js.Publish("TEST", msg); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}
	sub, err := js.SubscribeSync("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	checkSubsPending(t, sub, toSend)
	ci, err := sub.ConsumerInfo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	cname := ci.Name

	sl := c.streamLeader("$G", "TEST")
	c.removeJetStream(sl)

	c.waitOnLeader()
	c.waitOnStreamLeader("$G", "TEST")

	// Faster timeout since we loop below checking for condition.
	js, err = nc.JetStream(nats.MaxWait(250 * time.Millisecond))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Check the stream info is eventually correct.
	checkFor(t, 20*time.Second, 100*time.Millisecond, func() error {
		si, err := js.StreamInfo("TEST")
		if err != nil {
			return fmt.Errorf("Could not fetch stream info: %v", err)
		}
		if len(si.Cluster.Replicas) != 2 {
			return fmt.Errorf("Expected 2 replicas, got %d", len(si.Cluster.Replicas))
		}
		for _, peer := range si.Cluster.Replicas {
			if !peer.Current {
				return fmt.Errorf("Expected replica to be current: %+v", peer)
			}
		}
		return nil
	})

	// Now do consumer.
	c.waitOnConsumerLeader("$G", "TEST", cname)
	checkFor(t, 20*time.Second, 50*time.Millisecond, func() error {
		ci, err := js.ConsumerInfo("TEST", cname)
		if err != nil {
			return fmt.Errorf("Could not fetch consumer info: %v", err)
		}
		if len(ci.Cluster.Replicas) != 2 {
			return fmt.Errorf("Expected 2 replicas, got %d", len(ci.Cluster.Replicas))
		}
		for _, peer := range ci.Cluster.Replicas {
			if !peer.Current {
				return fmt.Errorf("Expected replica to be current: %+v", peer)
			}
		}
		return nil
	})
}

func TestJetStreamClusterPurgeReplayAfterRestart(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "P3F", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomNonLeader()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 3}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	sendBatch := func(n int) {
		t.Helper()
		// Send a batch to a given subject.
		for i := 0; i < n; i++ {
			if _, err := js.Publish("TEST", []byte("OK")); err != nil {
				t.Fatalf("Unexpected publish error: %v", err)
			}
		}
	}

	sendBatch(10)
	if err := js.PurgeStream("TEST"); err != nil {
		t.Fatalf("Unexpected purge error: %v", err)
	}
	sendBatch(10)

	c.stopAll()
	c.restartAll()

	c.waitOnStreamLeader("$G", "TEST")

	s = c.randomServer()
	nc, js = jsClientConnect(t, s)
	defer nc.Close()

	si, err := js.StreamInfo("TEST")
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if si.State.Msgs != 10 {
		t.Fatalf("Expected 10 msgs after restart, got %d", si.State.Msgs)
	}
}

func TestJetStreamClusterStreamGetMsg(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3F", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST"}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.Publish("TEST", []byte("OK")); err != nil {
		t.Fatalf("Unexpected publish error: %v", err)
	}

	mreq := &JSApiMsgGetRequest{Seq: 1}
	req, err := json.Marshal(mreq)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	rmsg, err := nc.Request(fmt.Sprintf(JSApiMsgGetT, "TEST"), req, time.Second)
	if err != nil {
		t.Fatalf("Could not retrieve stream message: %v", err)
	}
	if err != nil {
		t.Fatalf("Could not retrieve stream message: %v", err)
	}

	var resp JSApiMsgGetResponse
	err = json.Unmarshal(rmsg.Data, &resp)
	if err != nil {
		t.Fatalf("Could not parse stream message: %v", err)
	}
	if resp.Message == nil || resp.Error != nil {
		t.Fatalf("Did not receive correct response: %+v", resp.Error)
	}
}

func TestJetStreamClusterStreamDirectGetMsg(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3F", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, _ := jsClientConnect(t, s)
	defer nc.Close()

	// Do by hand for now.
	cfg := &StreamConfig{
		Name:        "TEST",
		Subjects:    []string{"foo"},
		Storage:     MemoryStorage,
		Replicas:    3,
		MaxMsgsPer:  1,
		AllowDirect: true,
	}
	addStream(t, nc, cfg)
	sendStreamMsg(t, nc, "foo", "bar")

	getSubj := fmt.Sprintf(JSDirectMsgGetT, "TEST")
	getMsg := func(req *JSApiMsgGetRequest) *nats.Msg {
		var b []byte
		var err error
		if req != nil {
			b, err = json.Marshal(req)
			require_NoError(t, err)
		}
		m, err := nc.Request(getSubj, b, time.Second)
		require_NoError(t, err)
		return m
	}

	m := getMsg(&JSApiMsgGetRequest{LastFor: "foo"})
	require_True(t, string(m.Data) == "bar")
	require_True(t, m.Header.Get(JSStream) == "TEST")
	require_True(t, m.Header.Get(JSSequence) == "1")
	require_True(t, m.Header.Get(JSSubject) == "foo")
	require_True(t, m.Subject != "foo")
	require_True(t, m.Header.Get(JSTimeStamp) != _EMPTY_)
}

func TestJetStreamClusterStreamPerf(t *testing.T) {
	// Comment out to run, holding place for now.
	skip(t)

	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	numConnections := 4
	var conns []nats.JetStream
	for i := 0; i < numConnections; i++ {
		s := c.randomServer()
		nc, js := jsClientConnect(t, s)
		defer nc.Close()
		conns = append(conns, js)
	}

	toSend := 100000
	numProducers := 8

	payload := []byte("Hello JSC")

	startCh := make(chan bool)
	var wg sync.WaitGroup

	for n := 0; n < numProducers; n++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			js := conns[rand.Intn(numConnections)]
			<-startCh
			for i := 0; i < int(toSend)/numProducers; i++ {
				if _, err = js.Publish("foo", payload); err != nil {
					t.Errorf("Unexpected publish error: %v", err)
				}
			}
		}()
	}

	// Wait for Go routines.
	time.Sleep(250 * time.Millisecond)

	start := time.Now()
	close(startCh)
	wg.Wait()

	tt := time.Since(start)
	fmt.Printf("Took %v to send %d msgs with %d producers and R=3!\n", tt, toSend, numProducers)
	fmt.Printf("%.0f msgs/sec\n\n", float64(toSend)/tt.Seconds())
}

func TestJetStreamClusterConsumerPerf(t *testing.T) {
	// Comment out to run, holding place for now.
	skip(t)

	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 3})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	toSend := 500000
	msg := make([]byte, 64)
	crand.Read(msg)

	for i := 0; i < toSend; i++ {
		nc.Publish("TEST", msg)
	}
	nc.Flush()

	checkFor(t, 10*time.Second, 250*time.Millisecond, func() error {
		si, err := js.StreamInfo("TEST")
		if err != nil {
			return fmt.Errorf("Unexpected error: %v", err)
		}
		if si.State.Msgs != uint64(toSend) {
			return fmt.Errorf("Expected to have %d messages, got %d", toSend, si.State.Msgs)
		}
		return nil
	})

	received := int32(0)
	deliverTo := "r"
	done := make(chan bool)
	total := int32(toSend)
	var start time.Time

	nc.Subscribe(deliverTo, func(m *nats.Msg) {
		if r := atomic.AddInt32(&received, 1); r >= total {
			done <- true
		} else if r == 1 {
			start = time.Now()
		}
	})

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{DeliverSubject: deliverTo, Durable: "gf"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	select {
	case <-done:
	case <-time.After(10 * time.Second):
		t.Fatalf("Timed out?")
	}
	tt := time.Since(start)
	fmt.Printf("Took %v to receive %d msgs\n", tt, toSend)
	fmt.Printf("%.0f msgs/sec\n\n", float64(toSend)/tt.Seconds())
}

// This test creates a queue consumer for the delivery subject,
// and make sure it connects to the server that is not the leader
// of the stream. A bug was not stripping the $JS.ACK reply subject
// correctly, which means that ack sent on the reply subject was
// dropped by the route.
func TestJetStreamClusterQueueSubConsumer(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R2S", 2)
	defer c.shutdown()

	// Client based API
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo.>"},
		Replicas: 1,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	inbox := nats.NewInbox()
	obsReq := CreateConsumerRequest{
		Stream: "TEST",
		Config: ConsumerConfig{
			Durable:        "ivan",
			DeliverSubject: inbox,
			DeliverGroup:   "queue",
			AckPolicy:      AckExplicit,
			AckWait:        100 * time.Millisecond,
		},
	}
	req, err := json.Marshal(obsReq)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	resp, err := nc.Request(fmt.Sprintf(JSApiDurableCreateT, "TEST", "ivan"), req, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	var ccResp JSApiConsumerCreateResponse
	if err = json.Unmarshal(resp.Data, &ccResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if ccResp.Error != nil {
		t.Fatalf("Unexpected error, got %+v", ccResp.Error)
	}

	ci, err := js.ConsumerInfo("TEST", "ivan")
	if err != nil {
		t.Fatalf("Error getting consumer info: %v", err)
	}

	// Now create a client that does NOT connect to the stream leader.
	// Start with url from first server in the cluster.
	u := c.servers[0].ClientURL()
	// If leader is "S-1", then use S-2 to connect to, which is at servers[1].
	if ci.Cluster.Leader == "S-1" {
		u = c.servers[1].ClientURL()
	}
	qsubnc, err := nats.Connect(u)
	if err != nil {
		t.Fatalf("Error connecting: %v", err)
	}
	defer qsubnc.Close()

	ch := make(chan struct{}, 2)
	if _, err := qsubnc.QueueSubscribe(inbox, "queue", func(m *nats.Msg) {
		m.Respond(nil)
		ch <- struct{}{}
	}); err != nil {
		t.Fatalf("Error creating sub: %v", err)
	}

	// Use the other connection to publish a message
	if _, err := js.Publish("foo.bar", []byte("hello")); err != nil {
		t.Fatalf("Error on publish: %v", err)
	}

	// Wait that we receive the message first.
	select {
	case <-ch:
	case <-time.After(time.Second):
		t.Fatal("Did not receive message")
	}

	// Message should be ack'ed and not redelivered.
	select {
	case <-ch:
		t.Fatal("Message redelivered!!!")
	case <-time.After(250 * time.Millisecond):
		// OK
	}
}

func TestJetStreamClusterLeaderStepdown(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "JSC", 3)
	defer c.shutdown()

	c.waitOnLeader()
	cl := c.leader()
	// Now ask the system account to have the leader stepdown.
	s := c.randomNonLeader()
	nc, err := nats.Connect(s.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
	if err != nil {
		t.Fatalf("Failed to create system client: %v", err)
	}
	defer nc.Close()

	resp, err := nc.Request(JSApiLeaderStepDown, nil, time.Second)
	if err != nil {
		t.Fatalf("Error on stepdown request: %v", err)
	}
	var sdr JSApiLeaderStepDownResponse
	if err := json.Unmarshal(resp.Data, &sdr); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if sdr.Error != nil || !sdr.Success {
		t.Fatalf("Unexpected error for leader stepdown: %+v", sdr.Error)
	}

	c.waitOnLeader()
	if cl == c.leader() {
		t.Fatalf("Expected a new metaleader, got same")
	}
}

func TestJetStreamClusterSourcesFilteringAndUpdating(t *testing.T) {
	owt := srcConsumerWaitTime
	srcConsumerWaitTime = 2 * time.Second
	defer func() { srcConsumerWaitTime = owt }()

	c := createJetStreamClusterExplicit(t, "MSR", 5)
	defer c.shutdown()

	// Client for API requests.
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	sendBatch := func(subject string, n int) {
		t.Helper()
		// Send a batch to a given subject.
		for i := 0; i < n; i++ {
			if _, err := js.Publish(subject, []byte("OK")); err != nil {
				t.Fatalf("Unexpected publish error: %v", err)
			}
		}
	}

	checkSync := func(msgsTest, msgsM uint64) {
		t.Helper()
		checkFor(t, 30*time.Second, time.Second, func() error {
			if tsi, err := js.StreamInfo("TEST"); err != nil {
				return err
			} else if msi, err := js.StreamInfo("M"); err != nil {
				return err
			} else if tsi.State.Msgs != msgsTest {
				return fmt.Errorf("received %d msgs from TEST, expected %d", tsi.State.Msgs, msgsTest)
			} else if msi.State.Msgs != msgsM {
				return fmt.Errorf("received %d msgs from M, expected %d", msi.State.Msgs, msgsM)
			}
			return nil
		})
	}

	// Origin
	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 2,
	})
	require_NoError(t, err)
	defer js.DeleteStream("TEST")

	// Create M stream with a single source on "foo"
	_, err = js.AddStream(&nats.StreamConfig{
		Name:     "M",
		Sources:  []*nats.StreamSource{{Name: "TEST", FilterSubject: "foo"}},
		Replicas: 2,
	})
	require_NoError(t, err)
	defer js.DeleteStream("M")

	// check a message on "bar" doesn't get sourced
	sendBatch("bar", 100)
	checkSync(100, 0)
	// check a message on "foo" does get sourced
	sendBatch("foo", 100)
	checkSync(200, 100)

	// change remove the source on "foo" and add a new source on "bar"
	_, err = js.UpdateStream(&nats.StreamConfig{
		Name:     "M",
		Sources:  []*nats.StreamSource{{Name: "TEST", FilterSubject: "bar"}},
		Replicas: 2,
	})
	require_NoError(t, err)

	// As it is a new source (never been sourced before) it starts sourcing at the start of TEST
	// and therefore sources the message on "bar" that is in TEST
	checkSync(200, 200)

	// new messages on "foo" are being filtered as it's not being currently sourced
	sendBatch("foo", 100)
	checkSync(300, 200)
	// new messages on "bar" are being sourced
	sendBatch("bar", 100)
	checkSync(400, 300)

	// re-add the source for "foo" keep the source on "bar"
	_, err = js.UpdateStream(&nats.StreamConfig{
		Name:     "M",
		Sources:  []*nats.StreamSource{{Name: "TEST", FilterSubject: "bar"}, {Name: "TEST", FilterSubject: "foo"}},
		Replicas: 2,
	})
	require_NoError(t, err)

	// check the 'backfill' of messages on "foo" that were published while the source was inactive
	checkSync(400, 400)

	// causes startingSequenceForSources() to be called
	nc.Close()
	c.stopAll()
	c.restartAll()
	c.waitOnStreamLeader("$G", "TEST")
	c.waitOnStreamLeader("$G", "M")

	nc, js = jsClientConnect(t, c.randomServer())
	defer nc.Close()

	// check that it restarted the sources' consumers at the right place
	checkSync(400, 400)

	// check both sources are still active
	sendBatch("bar", 100)
	checkSync(500, 500)
	sendBatch("foo", 100)
	checkSync(600, 600)

	// Check that purging the stream and does not cause the re-sourcing of the messages
	js.PurgeStream("M")
	checkSync(600, 0)

	// Even after a leader change or restart
	nc.Close()
	c.stopAll()
	c.restartAll()
	c.waitOnStreamLeader("$G", "TEST")
	c.waitOnStreamLeader("$G", "M")

	// Allow direct sync consumers to connect.
	// This test could pass if we do not hook up and try to deliver the messages when we should not.
	time.Sleep(1500 * time.Millisecond)

	nc, js = jsClientConnect(t, c.randomServer())
	defer nc.Close()

	checkSync(600, 0)
}

func TestJetStreamClusterSourcesUpdateOriginError(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "MSR", 5)
	defer c.shutdown()

	// Client for API requests.
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	sendBatch := func(subject string, n int) {
		t.Helper()
		// Send a batch to a given subject.
		for i := 0; i < n; i++ {
			if _, err := js.Publish(subject, []byte("OK")); err != nil {
				t.Fatalf("Unexpected publish error: %v", err)
			}
		}
	}

	checkSync := func(msgsTest, msgsM uint64) {
		t.Helper()
		checkFor(t, 10*time.Second, 500*time.Millisecond, func() error {
			if tsi, err := js.StreamInfo("TEST"); err != nil {
				return err
			} else if msi, err := js.StreamInfo("M"); err != nil {
				return err
			} else if tsi.State.Msgs != msgsTest {
				return fmt.Errorf("received %d msgs from TEST, expected %d", tsi.State.Msgs, msgsTest)
			} else if msi.State.Msgs != msgsM {
				return fmt.Errorf("received %d msgs from M, expected %d", msi.State.Msgs, msgsM)
			}
			return nil
		})
	}

	// Origin
	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 2,
	})
	require_NoError(t, err)

	_, err = js.AddStream(&nats.StreamConfig{
		Name:     "M",
		Sources:  []*nats.StreamSource{{Name: "TEST", FilterSubject: "foo"}},
		Replicas: 2,
	})

	require_NoError(t, err)

	// Send 100 msgs.
	sendBatch("foo", 100)
	checkSync(100, 100)

	// update makes source invalid
	_, err = js.UpdateStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"bar"},
		Replicas: 2,
	})
	require_NoError(t, err)

	// TODO check for downstream error propagation

	_, err = js.Publish("foo", nil)
	require_Error(t, err)

	sendBatch("bar", 100)
	// The source stream remains at 100 msgs as it still uses foo as it's filter
	checkSync(200, 100)

	nc.Close()
	c.stopAll()
	c.restartAll()
	c.waitOnStreamLeader("$G", "TEST")
	c.waitOnStreamLeader("$G", "M")

	nc, js = jsClientConnect(t, c.randomServer())
	defer nc.Close()

	checkSync(200, 100)

	_, err = js.Publish("foo", nil)
	require_Error(t, err)
	require_Equal(t, err.Error(), "nats: no response from stream")

	sendBatch("bar", 100)
	// The source stream remains at 100 msgs as it still uses foo as it's filter
	checkSync(300, 100)
}

func TestJetStreamClusterMirrorAndSourcesClusterRestart(t *testing.T) {
	owt := srcConsumerWaitTime
	srcConsumerWaitTime = 2 * time.Second
	defer func() { srcConsumerWaitTime = owt }()

	test := func(t *testing.T, mirror bool, filter bool) {
		c := createJetStreamClusterExplicit(t, "MSR", 5)
		defer c.shutdown()

		// Client for API requests.
		nc, js := jsClientConnect(t, c.randomServer())
		defer nc.Close()

		// Origin
		_, err := js.AddStream(&nats.StreamConfig{
			Name:     "TEST",
			Subjects: []string{"foo", "bar", "baz.*"},
			Replicas: 2,
		})
		require_NoError(t, err)

		filterSubj := _EMPTY_
		if filter {
			filterSubj = "foo"
		}

		// Create Mirror/Source now.
		if mirror {
			_, err = js.AddStream(&nats.StreamConfig{
				Name:     "M",
				Mirror:   &nats.StreamSource{Name: "TEST", FilterSubject: filterSubj},
				Replicas: 2,
			})
		} else {
			_, err = js.AddStream(&nats.StreamConfig{
				Name:     "M",
				Sources:  []*nats.StreamSource{{Name: "TEST", FilterSubject: filterSubj}},
				Replicas: 2,
			})
		}
		require_NoError(t, err)

		expectedMsgCount := uint64(0)

		sendBatch := func(subject string, n int) {
			t.Helper()
			if subject == "foo" || !filter {
				expectedMsgCount += uint64(n)
			}
			// Send a batch to a given subject.
			for i := 0; i < n; i++ {
				if _, err := js.Publish(subject, []byte("OK")); err != nil {
					t.Fatalf("Unexpected publish error: %v", err)
				}
			}
		}

		checkSync := func(msgsTest, msgsM uint64) {
			t.Helper()
			checkFor(t, 40*time.Second, time.Second, func() error {
				if tsi, err := js.StreamInfo("TEST"); err != nil {
					return err
				} else if msi, err := js.StreamInfo("M"); err != nil {
					return err
				} else if tsi.State.Msgs != msgsTest {
					return fmt.Errorf("received %d msgs from TEST, expected %d", tsi.State.Msgs, msgsTest)
				} else if msi.State.Msgs != msgsM {
					return fmt.Errorf("received %d msgs from M, expected %d", msi.State.Msgs, msgsM)
				}
				return nil
			})
		}

		sendBatch("foo", 100)
		checkSync(100, expectedMsgCount)
		sendBatch("bar", 100)
		checkSync(200, expectedMsgCount)

		nc.Close()
		c.stopAll()
		c.restartAll()
		c.waitOnStreamLeader("$G", "TEST")
		c.waitOnStreamLeader("$G", "M")

		nc, js = jsClientConnect(t, c.randomServer())
		defer nc.Close()

		checkSync(200, expectedMsgCount)
		sendBatch("foo", 100)
		checkSync(300, expectedMsgCount)
		sendBatch("bar", 100)
		checkSync(400, expectedMsgCount)
	}
	t.Run("mirror-filter", func(t *testing.T) {
		test(t, true, true)
	})
	t.Run("mirror-nofilter", func(t *testing.T) {
		test(t, true, false)
	})
	t.Run("source-filter", func(t *testing.T) {
		test(t, false, true)
	})
	t.Run("source-nofilter", func(t *testing.T) {
		test(t, false, false)
	})
}

func TestJetStreamClusterMirrorAndSourcesFilteredConsumers(t *testing.T) {
	c := createJetStreamClusterWithTemplate(t, jsClusterMirrorSourceImportsTempl, "MS5", 5)
	defer c.shutdown()

	// Client for API requests.
	s := c.randomServer()
	nc, js := jsClientConnect(t, s)
	defer nc.Close()

	// Origin
	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar", "baz.*"},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	// Create Mirror now.
	_, err = js.AddStream(&nats.StreamConfig{
		Name:   "M",
		Mirror: &nats.StreamSource{Name: "TEST"},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	dsubj := nats.NewInbox()
	nc.SubscribeSync(dsubj)
	nc.Flush()

	createConsumer := func(sn, fs string) {
		t.Helper()
		_, err = js.AddConsumer(sn, &nats.ConsumerConfig{DeliverSubject: dsubj, FilterSubject: fs})
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
	}
	expectFail := func(sn, fs string) {
		t.Helper()
		_, err = js.AddConsumer(sn, &nats.ConsumerConfig{DeliverSubject: dsubj, FilterSubject: fs})
		if err == nil {
			t.Fatalf("Expected error but got none")
		}
	}

	createConsumer("M", "foo")
	createConsumer("M", "bar")
	createConsumer("M", "baz.foo")
	expectFail("M", ".")
	expectFail("M", ">.foo")

	// Make sure wider scoped subjects work as well.
	createConsumer("M", "*")
	createConsumer("M", ">")

	// Now do some sources.
	if _, err := js.AddStream(&nats.StreamConfig{Name: "O1", Subjects: []string{"foo.*"}}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err := js.AddStream(&nats.StreamConfig{Name: "O2", Subjects: []string{"bar.*"}}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Create downstream now.
	_, err = js.AddStream(&nats.StreamConfig{
		Name:    "S",
		Sources: []*nats.StreamSource{{Name: "O1"}, {Name: "O2"}},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	createConsumer("S", "foo.1")
	createConsumer("S", "bar.1")

	// Now cross account stuff.
	nc2, js2 := jsClientConnect(t, s, nats.UserInfo("rip", "pass"))
	defer nc2.Close()

	if _, err := js2.AddStream(&nats.StreamConfig{Name: "ORIGIN", Subjects: []string{"foo.*"}}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	cfg := StreamConfig{
		Name:    "SCA",
		Storage: FileStorage,
		Sources: []*StreamSource{{
			Name: "ORIGIN",
			External: &ExternalStream{
				ApiPrefix:     "RI.JS.API",
				DeliverPrefix: "RI.DELIVER.SYNC.SOURCES",
			},
		}},
	}
	req, err := json.Marshal(cfg)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	resp, err := nc.Request(fmt.Sprintf(JSApiStreamCreateT, cfg.Name), req, time.Second)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	var scResp JSApiStreamCreateResponse
	if err := json.Unmarshal(resp.Data, &scResp); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if scResp.StreamInfo == nil || scResp.Error != nil {
		t.Fatalf("Did not receive correct response: %+v", scResp.Error)
	}

	// Externals skip the checks for now.
	createConsumer("SCA", "foo.1")
	createConsumer("SCA", "bar.1")
	createConsumer("SCA", "baz")
}

func TestJetStreamClusterCrossAccountMirrorsAndSources(t *testing.T) {
	c := createJetStreamClusterWithTemplate(t, jsClusterMirrorSourceImportsTempl, "C1", 3)
	defer c.shutdown()

	// Create source stream under RI account.
	s := c.randomServer()
	nc, js := jsClientConnect(t, s, nats.UserInfo("rip", "pass"))
	defer nc.Close()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 2}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// use large number to tease out FC issues
	toSend := 3000
	for i := 0; i < toSend; i++ {
		if _, err := js.Publish("TEST", []byte("OK")); err != nil {
			t.Fatalf("Unexpected publish error: %v", err)
		}
	}

	nc2, js2 := jsClientConnect(t, s)
	defer nc2.Close()

	// Have to do this direct until we get Go client support.
	// Need to match jsClusterMirrorSourceImportsTempl imports.
	_, err := js2.AddStream(&nats.StreamConfig{
		Name: "MY_MIRROR_TEST",
		Mirror: &nats.StreamSource{
			Name: "TEST",
			External: &nats.ExternalStream{
				APIPrefix:     "RI.JS.API",
				DeliverPrefix: "RI.DELIVER.SYNC.MIRRORS",
			},
		},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkFor(t, 20*time.Second, 500*time.Millisecond, func() error {
		si, err := js2.StreamInfo("MY_MIRROR_TEST")
		if err != nil {
			t.Fatalf("Could not retrieve stream info: %s", err)
		}
		if si.State.Msgs != uint64(toSend) {
			return fmt.Errorf("Expected %d msgs, got state: %+v", toSend, si.State)
		}
		return nil
	})

	// Now do sources as well.
	_, err = js2.AddStream(&nats.StreamConfig{
		Name: "MY_SOURCE_TEST",
		Sources: []*nats.StreamSource{
			{
				Name: "TEST",
				External: &nats.ExternalStream{
					APIPrefix:     "RI.JS.API",
					DeliverPrefix: "RI.DELIVER.SYNC.SOURCES",
				},
			},
		},
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	checkFor(t, 20*time.Second, 100*time.Millisecond, func() error {
		si, err := js2.StreamInfo("MY_SOURCE_TEST")
		if err != nil {
			t.Fatalf("Could not retrieve stream info")
		}
		if si.State.Msgs != uint64(toSend) {
			return fmt.Errorf("Expected %d msgs, got state: %+v", toSend, si.State)
		}
		return nil
	})

}

func TestJetStreamClusterFailMirrorsAndSources(t *testing.T) {
	c := createJetStreamClusterWithTemplate(t, jsClusterMirrorSourceImportsTempl, "C1", 3)
	defer c.shutdown()

	// Create source stream under RI account.
	s := c.randomServer()
	nc, js := jsClientConnect(t, s, nats.UserInfo("rip", "pass"))
	defer nc.Close()

	if _, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 2, Subjects: []string{"test.>"}}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	nc2, _ := jsClientConnect(t, s, nats.UserInfo("rip", "pass"))
	defer nc2.Close()

	testPrefix := func(testName string, id ErrorIdentifier, cfg StreamConfig) {
		t.Run(testName, func(t *testing.T) {
			req, err := json.Marshal(cfg)
			if err != nil {
				t.Fatalf("Unexpected error: %v", err)
			}
			resp, err := nc2.Request(fmt.Sprintf(JSApiStreamCreateT, cfg.Name), req, time.Second)
			if err != nil {
				t.Fatalf("Unexpected error: %v", err)
			}
			var scResp JSApiStreamCreateResponse
			if err := json.Unmarshal(resp.Data, &scResp); err != nil {
				t.Fatalf("Unexpected error: %v", err)
			}
			if scResp.Error == nil {
				t.Fatalf("Did expect an error but got none")
			} else if !IsNatsErr(scResp.Error, id) {
				t.Fatalf("Expected different error: %s", scResp.Error.Description)
			}
		})
	}

	testPrefix("mirror-bad-apiprefix", JSStreamExternalApiOverlapErrF, StreamConfig{
		Name:    "MY_MIRROR_TEST",
		Storage: FileStorage,
		Mirror: &StreamSource{
			Name: "TEST",
			External: &ExternalStream{
				ApiPrefix:     "$JS.API",
				DeliverPrefix: "here",
			},
		},
	})
	testPrefix("source-bad-apiprefix", JSStreamExternalApiOverlapErrF, StreamConfig{
		Name:    "MY_SOURCE_TEST",
		Storage: FileStorage,
		Sources: []*StreamSource{{
			Name: "TEST",
			External: &ExternalStream{
				ApiPrefix:     "$JS.API",
				DeliverPrefix: "here",
			},
		},
		},
	})
}

func TestJetStreamClusterConsumerDeliveredSyncReporting(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Retention: nats.WorkQueuePolicy,
		Subjects:  []string{"foo.*"},
		Replicas:  3,
	})
	require_NoError(t, err)

	sub, err := js.PullSubscribe("foo.bar", "mc")
	require_NoError(t, err)

	// Make us match first, but not next 10.
	_, err = js.Publish("foo.bar", nil)
	require_NoError(t, err)
	for i := 0; i < 10; i++ {
		_, err = js.Publish("foo.baz", nil)
		require_NoError(t, err)
	}

	opts := &JSzOptions{Accounts: true, Streams: true, Consumer: true}
	for _, s := range c.servers {
		jsz, err := s.Jsz(opts)
		require_NoError(t, err)
		ci := jsz.AccountDetails[0].Streams[0].Consumer[0]
		require_Equal(t, ci.Delivered.Consumer, 0)
		require_Equal(t, ci.Delivered.Stream, 0)
	}

	msgs, err := sub.Fetch(1)
	require_NoError(t, err)
	require_Equal(t, len(msgs), 1)
	meta, err := msgs[0].Metadata()
	require_NoError(t, err)
	require_Equal(t, meta.Sequence.Consumer, 1)
	require_Equal(t, meta.Sequence.Stream, 1)

	// Allow some time for the state to propagate.
	maxWait := 200 * time.Millisecond
	time.Sleep(maxWait)

	for _, s := range c.servers {
		jsz, err := s.Jsz(opts)
		require_NoError(t, err)
		ci := jsz.AccountDetails[0].Streams[0].Consumer[0]
		require_Equal(t, ci.Delivered.Consumer, 1)
		require_Equal(t, ci.Delivered.Stream, 1)
	}

	// Now we want to make sure that jsz reporting will show the same
	// state, including delivered, which will have skipped to the end.
	// The skip can happen on several factors, but for here we just send
	// another pull request which we will let fail.
	_, err = sub.Fetch(1, nats.MaxWait(maxWait))
	require_Error(t, err)

	for _, s := range c.servers {
		jsz, err := s.Jsz(opts)
		require_NoError(t, err)
		ci := jsz.AccountDetails[0].Streams[0].Consumer[0]
		require_Equal(t, ci.Delivered.Consumer, 1)
		require_Equal(t, ci.Delivered.Stream, 11)
	}
}

// This is to test follower ack fill logic when pending not empty.
// There was a bug that would update p.Sequence in the stores (mem & file)
// that would cause the logic to fail. Redeliveries were required to trigger.
func TestJetStreamClusterConsumerAckSyncReporting(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Retention: nats.WorkQueuePolicy,
		Subjects:  []string{"foo.*"},
		Replicas:  3,
	})
	require_NoError(t, err)

	sub, err := js.PullSubscribe("foo.bar", "mc", nats.AckWait(250*time.Millisecond))
	require_NoError(t, err)

	for i := 0; i < 10; i++ {
		_, err = js.Publish("foo.bar", nil)
		require_NoError(t, err)
	}

	// Wait for all servers to converge on the same state. Stream and consumer leader could be different.
	checkFor(t, time.Second, 100*time.Millisecond, func() error {
		return checkState(t, c, globalAccountName, "TEST")
	})

	msgs, err := sub.Fetch(10)
	require_NoError(t, err)
	require_Equal(t, len(msgs), 10)
	// Let redeliveries kick in.
	time.Sleep(time.Second)
	msgs, err = sub.Fetch(10)
	require_NoError(t, err)
	require_Equal(t, len(msgs), 10)

	// Randomize
	rand.Shuffle(len(msgs), func(i, j int) { msgs[i], msgs[j] = msgs[j], msgs[i] })

	dontAck := uint64(7)
	var skipped, last *nats.Msg
	for _, m := range msgs {
		meta, err := m.Metadata()
		require_NoError(t, err)
		if meta.Sequence.Stream == dontAck {
			skipped = m
			continue
		}
		if meta.Sequence.Stream == 10 {
			last = m
			continue
		}
		err = m.AckSync()
		require_NoError(t, err)
	}
	require_NotNil(t, skipped)
	require_NotNil(t, last)

	checkAckFloor := func(consumer, stream uint64) {
		opts := &JSzOptions{Accounts: true, Streams: true, Consumer: true}
		checkFor(t, 3*time.Second, 200*time.Millisecond, func() error {
			for _, s := range c.servers {
				jsz, err := s.Jsz(opts)
				if err != nil {
					return err
				}
				ci := jsz.AccountDetails[0].Streams[0].Consumer[0]
				if ci.AckFloor.Consumer != consumer {
					return fmt.Errorf("AckFloor.Consumer is not %d: %v", consumer, ci.AckFloor.Consumer)
				}
				if ci.AckFloor.Stream != stream {
					return fmt.Errorf("AckFloor.Stream is not %d: %v", stream, ci.AckFloor.Stream)
				}
			}
			return nil
		})
	}

	// Now we want to make sure that jsz reporting will show the same
	// state for ack floor.
	checkAckFloor(dontAck-1, dontAck-1)

	// Now ack the skipped message
	err = skipped.AckSync()
	require_NoError(t, err)
	checkAckFloor(9, 9)

	// Now ack the last message
	err = last.AckSync()
	require_NoError(t, err)
	checkAckFloor(20, 10)
}

func TestJetStreamClusterConsumerDeleteInterestPolicyMultipleConsumers(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Retention: nats.InterestPolicy,
		Subjects:  []string{"foo.*"},
		Replicas:  3,
	})
	require_NoError(t, err)

	// Make the first sequence high. We already protect against it but for extra sanity.
	err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Sequence: 100_000_000})
	require_NoError(t, err)

	// Create 2 consumers.
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:   "C1",
		AckPolicy: nats.AckExplicitPolicy,
	})
	require_NoError(t, err)

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:   "C2",
		AckPolicy: nats.AckExplicitPolicy,
	})
	require_NoError(t, err)

	for i := 0; i < 100; i++ {
		js.PublishAsync("foo.bar", []byte("ok"))
	}
	select {
	case <-js.PublishAsyncComplete():
	case <-time.After(5 * time.Second):
		t.Fatalf("Did not receive completion signal")
	}

	expectedStreamMsgs := func(msgs uint64) {
		t.Helper()
		checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
			si, err := js.StreamInfo("TEST")
			if err != nil {
				return err
			}
			if si.State.Msgs != msgs {
				return fmt.Errorf("require uint64 equal, but got: %d != %d", si.State.Msgs, msgs)
			}
			return nil
		})
	}
	expectedStreamMsgs(100)

	sub, err := js.PullSubscribe("foo.bar", "C1")
	require_NoError(t, err)
	msgs, err := sub.Fetch(50)
	require_NoError(t, err)
	require_Equal(t, len(msgs), 50)
	for _, m := range msgs {
		require_NoError(t, m.AckSync())
	}

	// Now delete second one and make sure accounting correct.
	err = js.DeleteConsumer("TEST", "C2")
	require_NoError(t, err)

	expectedStreamMsgs(50)
}

func TestJetStreamClusterConsumerAckNoneInterestPolicyShouldNotRetainAfterDelivery(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Retention: nats.InterestPolicy,
		Subjects:  []string{"foo.*"},
		Replicas:  3,
	})
	require_NoError(t, err)

	// Make the first sequence high. We already protect against it but for extra sanity.
	err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Sequence: 100_000_000})
	require_NoError(t, err)

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:   "C1",
		AckPolicy: nats.AckNonePolicy,
	})
	require_NoError(t, err)

	for i := 0; i < 100; i++ {
		js.PublishAsync("foo.bar", []byte("ok"))
	}
	select {
	case <-js.PublishAsyncComplete():
	case <-time.After(5 * time.Second):
		t.Fatalf("Did not receive completion signal")
	}

	si, err := js.StreamInfo("TEST")
	require_NoError(t, err)
	require_Equal(t, si.State.Msgs, 100)

	sub, err := js.PullSubscribe("foo.bar", "C1")
	require_NoError(t, err)
	msgs, err := sub.Fetch(100)
	require_NoError(t, err)
	require_Equal(t, len(msgs), 100)

	checkFor(t, time.Second, 100*time.Millisecond, func() error {
		si, err = js.StreamInfo("TEST")
		if err != nil {
			return err
		}
		if si.State.Msgs != 0 {
			return fmt.Errorf("require uint64 equal, but got: %d != 0", si.State.Msgs)
		}
		return nil
	})
}

func TestJetStreamClusterConsumerDeleteAckNoneInterestPolicyWithOthers(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Retention: nats.InterestPolicy,
		Subjects:  []string{"foo.*"},
		Replicas:  3,
	})
	require_NoError(t, err)

	// Make the first sequence high. We already protect against it but for extra sanity.
	err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Sequence: 100_000_000})
	require_NoError(t, err)

	// Create 2 consumers.
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:   "C1",
		AckPolicy: nats.AckExplicitPolicy,
	})
	require_NoError(t, err)

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:   "C2",
		AckPolicy: nats.AckNonePolicy,
	})
	require_NoError(t, err)

	for i := 0; i < 100; i++ {
		js.PublishAsync("foo.bar", []byte("ok"))
	}
	select {
	case <-js.PublishAsyncComplete():
	case <-time.After(5 * time.Second):
		t.Fatalf("Did not receive completion signal")
	}

	expectedStreamMsgs := func(msgs uint64) {
		t.Helper()
		checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
			si, err := js.StreamInfo("TEST")
			if err != nil {
				return err
			}
			if si.State.Msgs != msgs {
				return fmt.Errorf("require uint64 equal, but got: %d != %d", si.State.Msgs, msgs)
			}
			return nil
		})
	}
	expectedStreamMsgs(100)

	sub, err := js.PullSubscribe("foo.bar", "C1")
	require_NoError(t, err)
	msgs, err := sub.Fetch(100)
	require_NoError(t, err)
	require_Equal(t, len(msgs), 100)
	for _, m := range msgs {
		require_NoError(t, m.AckSync())
	}
	// AckNone will hold.
	expectedStreamMsgs(100)

	// Now delete second one and make sure accounting correct.
	err = js.DeleteConsumer("TEST", "C2")
	require_NoError(t, err)

	expectedStreamMsgs(0)
}

// Make sure to not allow non-system accounts to move meta leader.
func TestJetStreamClusterMetaStepdownFromNonSysAccount(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	s := c.randomServer()

	// Client based API
	nc, _ := jsClientConnect(t, s)
	defer nc.Close()

	ml := c.leader()

	_, err := nc.Request(JSApiLeaderStepDown, nil, time.Second)
	require_Error(t, err, nats.ErrTimeout)

	// Make sure we did not move.
	c.waitOnLeader()
	require_Equal(t, ml, c.leader())

	// System user can move it.
	snc, _ := jsClientConnect(t, c.randomServer(), nats.UserInfo("admin", "s3cr3t!"))
	defer snc.Close()

	resp, err := snc.Request(JSApiLeaderStepDown, nil, time.Second)
	require_NoError(t, err)

	var sdr JSApiLeaderStepDownResponse
	require_NoError(t, json.Unmarshal(resp.Data, &sdr))
	require_True(t, sdr.Success)
	require_Equal(t, sdr.Error, nil)

	// Make sure we did move this time.
	c.waitOnLeader()
	require_NotEqual(t, ml, c.leader())
}

func TestJetStreamClusterMaxDeliveriesOnInterestStreams(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Client based API
	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo.*"},
		Retention: nats.InterestPolicy,
	})
	require_NoError(t, err)

	sub1, err := js.PullSubscribe("foo.*", "c1", nats.AckWait(10*time.Millisecond), nats.MaxDeliver(1))
	require_NoError(t, err)

	sub2, err := js.PullSubscribe("foo.*", "c2", nats.AckWait(10*time.Millisecond), nats.MaxDeliver(1))
	require_NoError(t, err)

	_, err = js.Publish("foo.bar", []byte("HELLO"))
	require_NoError(t, err)

	si, err := js.StreamInfo("TEST")
	require_NoError(t, err)
	require_Equal(t, si.State.Msgs, 1)

	msgs, err := sub1.Fetch(1)
	require_NoError(t, err)
	require_Equal(t, len(msgs), 1)

	msgs, err = sub2.Fetch(1)
	require_NoError(t, err)
	require_Equal(t, len(msgs), 1)

	// Wait for redelivery to both consumers which will do nothing.
	time.Sleep(250 * time.Millisecond)

	// Now check that stream and consumer infos are correct.
	si, err = js.StreamInfo("TEST")
	require_NoError(t, err)
	// Messages that are skipped due to max deliveries should NOT remove messages.
	require_Equal(t, si.State.Msgs, 1)
	require_Equal(t, si.State.Consumers, 2)

	for _, cname := range []string{"c1", "c2"} {
		ci, err := js.ConsumerInfo("TEST", cname)
		require_NoError(t, err)
		require_Equal(t, ci.Delivered.Consumer, 1)
		require_Equal(t, ci.Delivered.Stream, 1)
		require_Equal(t, ci.AckFloor.Consumer, 0)
		require_Equal(t, ci.AckFloor.Stream, 0)
		require_Equal(t, ci.NumAckPending, 0)
		require_Equal(t, ci.NumRedelivered, 0)
		require_Equal(t, ci.NumPending, 0)
	}
}

func TestJetStreamClusterMetaRecoveryUpdatesDeletesConsumers(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	js := c.leader().getJetStream()

	create := []*Entry{
		{EntryNormal, encodeAddStreamAssignment(&streamAssignment{
			Config: &StreamConfig{Name: "TEST", Storage: FileStorage},
		})},
		{EntryNormal, encodeAddConsumerAssignment(&consumerAssignment{
			Stream: "TEST",
			Config: &ConsumerConfig{Name: "consumer"},
		})},
	}

	delete := []*Entry{
		{EntryNormal, encodeDeleteStreamAssignment(&streamAssignment{
			Config: &StreamConfig{Name: "TEST", Storage: FileStorage},
		})},
	}

	// Need to be recovering so that we accumulate recoveryUpdates.
	js.setMetaRecovering()
	ru := &recoveryUpdates{
		removeStreams:   make(map[string]*streamAssignment),
		removeConsumers: make(map[string]map[string]*consumerAssignment),
		addStreams:      make(map[string]*streamAssignment),
		updateStreams:   make(map[string]*streamAssignment),
		updateConsumers: make(map[string]map[string]*consumerAssignment),
	}

	// Push recovery entries that create the stream & consumer.
	_, _, err := js.applyMetaEntries(create, ru)
	require_NoError(t, err)
	require_Len(t, len(ru.updateConsumers), 1)

	// Now push another recovery entry that deletes the stream. The
	// entry that creates the consumer should now be gone.
	_, _, err = js.applyMetaEntries(delete, ru)
	require_NoError(t, err)
	require_Len(t, len(ru.removeStreams), 1)
	require_Len(t, len(ru.updateConsumers), 0)
}

func TestJetStreamClusterMetaRecoverySnapshotReconcilesStagedUpdates(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	ml := c.leader()
	require_NotNil(t, ml)
	js := ml.getJetStream()

	// Helpers to build the recovery log entries that stage operations.
	addStream := func(name string) *Entry {
		return &Entry{EntryNormal, encodeAddStreamAssignment(&streamAssignment{
			Client: &ClientInfo{Account: globalAccountName},
			Config: &StreamConfig{Name: name, Storage: FileStorage},
		})}
	}
	updateStream := func(name string) *Entry {
		return &Entry{EntryNormal, encodeUpdateStreamAssignment(&streamAssignment{
			Client: &ClientInfo{Account: globalAccountName},
			Config: &StreamConfig{Name: name, Storage: FileStorage},
		})}
	}
	addConsumer := func(stream, name string) *Entry {
		return &Entry{EntryNormal, encodeAddConsumerAssignment(&consumerAssignment{
			Client: &ClientInfo{Account: globalAccountName}, Stream: stream, Name: name,
			Config: &ConsumerConfig{Name: name},
		})}
	}
	delConsumer := func(stream, name string) *Entry {
		return &Entry{EntryNormal, encodeDeleteConsumerAssignment(&consumerAssignment{
			Client: &ClientInfo{Account: globalAccountName}, Stream: stream, Name: name,
			Config: &ConsumerConfig{Name: name},
		})}
	}

	// snapStream describes a stream (and its consumers) present in the incoming compacted snapshot.
	type snapStream struct {
		name      string
		consumers []string
	}
	encodeSnapshot := func(t *testing.T, streams ...snapStream) []*Entry {
		t.Helper()
		asa := map[string]*streamAssignment{}
		for _, s := range streams {
			cfg := &StreamConfig{Name: s.name, Storage: FileStorage}
			cfgJSON, err := json.Marshal(cfg)
			require_NoError(t, err)
			sa := &streamAssignment{Client: &ClientInfo{Account: globalAccountName}, Config: cfg, ConfigJSON: cfgJSON}
			for _, cn := range s.consumers {
				if sa.consumers == nil {
					sa.consumers = map[string]*consumerAssignment{}
				}
				ccfg := &ConsumerConfig{Name: cn}
				ccfgJSON, err := json.Marshal(ccfg)
				require_NoError(t, err)
				sa.consumers[cn] = &consumerAssignment{
					Client: &ClientInfo{Account: globalAccountName}, Stream: s.name, Name: cn,
					Config: ccfg, ConfigJSON: ccfgJSON,
				}
			}
			asa[s.name] = sa
		}
		snap, _, _, err := js.encodeMetaSnapshot(map[string]map[string]*streamAssignment{globalAccountName: asa})
		require_NoError(t, err)
		return []*Entry{{EntrySnapshot, snap}}
	}

	// Collect the names staged in each part of ru, sorted for stable comparison.
	streamNames := func(m map[string]*streamAssignment) []string {
		var out []string
		for _, sa := range m {
			out = append(out, sa.Config.Name)
		}
		slices.Sort(out)
		return out
	}
	consumerNames := func(m map[string]map[string]*consumerAssignment) []string {
		var out []string
		for _, cs := range m {
			for _, ca := range cs {
				out = append(out, ca.Name)
			}
		}
		slices.Sort(out)
		return out
	}
	assertNames := func(t *testing.T, label string, got, want []string) {
		t.Helper()
		want = slices.Clone(want)
		slices.Sort(want)
		if !slices.Equal(got, want) {
			t.Fatalf("%s: got %v, want %v", label, got, want)
		}
	}

	for _, test := range []struct {
		name string
		// Entries staged during recovery, before the snapshot arrives.
		entries []*Entry
		// Streams (and their consumers) the compacted snapshot contains.
		snapshot []snapStream
		// Staged state expected after the entries, before the snapshot reconciles them.
		preStreams   []string
		preUpdates   []string
		preConsumers []string
		preRemoves   []string
		// Staged state expected after the snapshot has reconciled the staged updates.
		wantStreams   []string
		wantUpdates   []string
		wantConsumers []string
		wantRemoves   []string
	}{
		{
			// A stream staged for add that the snapshot omits is a phantom and must be dropped;
			// a stream the snapshot contains is (re)staged as an add.
			name:        "deleted stream",
			entries:     []*Entry{addStream("TEST")},
			snapshot:    []snapStream{{name: "KEEP"}},
			preStreams:  []string{"TEST"},
			wantStreams: []string{"KEEP"},
		},
		{
			// When the stream itself is superseded, its staged consumer add (CA) and remove (CB)
			// must be dropped along with it.
			name: "deleted stream with consumers",
			entries: []*Entry{
				addStream("TEST"),
				addConsumer("TEST", "CA"),
				addConsumer("TEST", "CB"),
				delConsumer("TEST", "CB"),
			},
			snapshot:     []snapStream{{name: "KEEP"}},
			preStreams:   []string{"TEST"},
			preConsumers: []string{"CA"},
			preRemoves:   []string{"CB"},
			wantStreams:  []string{"KEEP"},
		},
		{
			// The stream survives, but a staged consumer (C2) the snapshot omits is a phantom and
			// must be dropped; the consumer the snapshot still contains (C1) is kept.
			name: "deleted consumer",
			entries: []*Entry{
				addStream("TEST"),
				addConsumer("TEST", "C1"),
				addConsumer("TEST", "C2"),
			},
			snapshot:      []snapStream{{name: "TEST", consumers: []string{"C1"}}},
			preStreams:    []string{"TEST"},
			preConsumers:  []string{"C1", "C2"},
			wantStreams:   []string{"TEST"},
			wantConsumers: []string{"C1"},
		},
		{
			// Nothing is a phantom: every staged consumer is present in the snapshot, and the
			// snapshot also (re)stages its own consumers (C2 here was not staged via an entry).
			name: "snapshot adds consumer",
			entries: []*Entry{
				addStream("TEST"),
				addConsumer("TEST", "C1"),
			},
			snapshot:      []snapStream{{name: "TEST", consumers: []string{"C1", "C2"}}},
			preStreams:    []string{"TEST"},
			preConsumers:  []string{"C1"},
			wantStreams:   []string{"TEST"},
			wantConsumers: []string{"C1", "C2"},
		},
		{
			// Both paths at once: stream KEEP (and consumer KC) survive, while stream GONE (and
			// its consumer GC) are superseded and dropped.
			name: "mixed stream delete",
			entries: []*Entry{
				addStream("KEEP"),
				addConsumer("KEEP", "KC"),
				addStream("GONE"),
				addConsumer("GONE", "GC"),
			},
			snapshot:      []snapStream{{name: "KEEP", consumers: []string{"KC"}}},
			preStreams:    []string{"GONE", "KEEP"},
			preConsumers:  []string{"GC", "KC"},
			wantStreams:   []string{"KEEP"},
			wantConsumers: []string{"KC"},
		},
		{
			// An empty snapshot (all streams deleted) supersedes everything staged.
			name: "empty snapshot",
			entries: []*Entry{
				addStream("TEST"),
				addConsumer("TEST", "C1"),
			},
			snapshot:     nil,
			preStreams:   []string{"TEST"},
			preConsumers: []string{"C1"},
		},
		{
			// A stream created and then updated during recovery is staged in both addStreams and
			// updateStreams. When superseded, the addStreams loop must clear the updateStreams entry
			// too (so the update is not later applied against a stream that no longer exists).
			name: "added-and-updated stream",
			entries: []*Entry{
				addStream("TEST"),
				updateStream("TEST"),
			},
			snapshot:    []snapStream{{name: "KEEP"}},
			preStreams:  []string{"TEST"},
			preUpdates:  []string{"TEST"},
			wantStreams: []string{"KEEP"},
		},
		{
			// A stream created and then updated during recovery is staged in both addStreams and
			// updateStreams. When the snapshot keeps the stream it re-stages it as an add, which must
			// clear the now-stale staged update. Otherwise recovery completion applies the add and then
			// reapplies the older update (adds run before updates), rolling the stream config back away
			// from the snapshot state.
			name: "added-and-updated stream kept",
			entries: []*Entry{
				addStream("TEST"),
				updateStream("TEST"),
			},
			snapshot:    []snapStream{{name: "TEST"}},
			preStreams:  []string{"TEST"},
			preUpdates:  []string{"TEST"},
			wantStreams: []string{"TEST"},
			// wantUpdates intentionally empty: the snapshot's re-add supersedes the stale staged update.
		},
		{
			// A stream staged only in updateStreams (its add is outside this recovery batch) is
			// reconciled by the dedicated updateStreams loop: when superseded it and its staged
			// consumer ops are dropped. This is the path the addStreams loop does not cover.
			name: "update-only stream",
			entries: []*Entry{
				updateStream("GONE"),
				addConsumer("GONE", "GC"),
			},
			snapshot:     []snapStream{{name: "KEEP"}},
			preUpdates:   []string{"GONE"},
			preConsumers: []string{"GC"},
			wantStreams:  []string{"KEEP"},
		},
		{
			// A stream staged only via an update, that the snapshot keeps: the snapshot re-stages it as
			// an add which supersedes the now-stale staged update (so it is not reapplied after the add
			// at recovery completion). The phantom consumer under it (C2) is still dropped; C1 survives.
			name: "update-only stream kept",
			entries: []*Entry{
				updateStream("TEST"),
				addConsumer("TEST", "C1"),
				addConsumer("TEST", "C2"),
			},
			snapshot:     []snapStream{{name: "TEST", consumers: []string{"C1"}}},
			preUpdates:   []string{"TEST"},
			preConsumers: []string{"C1", "C2"},
			wantStreams:  []string{"TEST"},
			// wantUpdates intentionally empty: the snapshot's re-add supersedes the stale staged update.
			wantConsumers: []string{"C1"},
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			// Need to be recovering so that we accumulate recoveryUpdates.
			js.setMetaRecovering()
			ru := &recoveryUpdates{
				removeStreams:   make(map[string]*streamAssignment),
				removeConsumers: make(map[string]map[string]*consumerAssignment),
				addStreams:      make(map[string]*streamAssignment),
				updateStreams:   make(map[string]*streamAssignment),
				updateConsumers: make(map[string]map[string]*consumerAssignment),
			}

			// Stage the recovery entries, then verify what they staged (so the snapshot assertions
			// below are not vacuously satisfied by entries that never staged anything).
			_, _, err := js.applyMetaEntries(test.entries, ru)
			require_NoError(t, err)
			assertNames(t, "pre stream adds", streamNames(ru.addStreams), test.preStreams)
			assertNames(t, "pre stream updates", streamNames(ru.updateStreams), test.preUpdates)
			assertNames(t, "pre consumers", consumerNames(ru.updateConsumers), test.preConsumers)
			assertNames(t, "pre removes", consumerNames(ru.removeConsumers), test.preRemoves)

			// Apply the compacted snapshot, which reconciles the staged updates against it.
			_, _, err = js.applyMetaEntries(encodeSnapshot(t, test.snapshot...), ru)
			require_NoError(t, err)
			assertNames(t, "stream adds", streamNames(ru.addStreams), test.wantStreams)
			assertNames(t, "stream updates", streamNames(ru.updateStreams), test.wantUpdates)
			assertNames(t, "consumer adds", consumerNames(ru.updateConsumers), test.wantConsumers)
			assertNames(t, "consumer removes", consumerNames(ru.removeConsumers), test.wantRemoves)
		})
	}
}

func TestJetStreamClusterMetaRecoveryRecreateFileStreamAsMemory(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	js := c.leader().getJetStream()

	createFileStream := []*Entry{
		{EntryNormal, encodeAddStreamAssignment(&streamAssignment{
			Config: &StreamConfig{Name: "TEST", Storage: FileStorage},
		})},
	}

	deleteFileStream := []*Entry{
		{EntryNormal, encodeDeleteStreamAssignment(&streamAssignment{
			Config: &StreamConfig{Name: "TEST", Storage: FileStorage},
		})},
	}

	createMemoryStream := []*Entry{
		{EntryNormal, encodeAddStreamAssignment(&streamAssignment{
			Config: &StreamConfig{Name: "TEST", Storage: FileStorage},
		})},
	}

	createConsumer := []*Entry{
		{EntryNormal, encodeAddConsumerAssignment(&consumerAssignment{
			Stream: "TEST",
			Config: &ConsumerConfig{Name: "consumer"},
		})},
	}

	// Need to be recovering so that we accumulate recoveryUpdates.
	js.setMetaRecovering()
	ru := &recoveryUpdates{
		removeStreams:   make(map[string]*streamAssignment),
		removeConsumers: make(map[string]map[string]*consumerAssignment),
		addStreams:      make(map[string]*streamAssignment),
		updateStreams:   make(map[string]*streamAssignment),
		updateConsumers: make(map[string]map[string]*consumerAssignment),
	}

	// We created a file-based stream first, but deleted it shortly after.
	_, _, err := js.applyMetaEntries(createFileStream, ru)
	require_NoError(t, err)
	require_Len(t, len(ru.addStreams), 1)
	require_Len(t, len(ru.removeStreams), 0)

	// Now push another recovery entry that deletes the stream.
	// The file-based stream should not have been created.
	_, _, err = js.applyMetaEntries(deleteFileStream, ru)
	require_NoError(t, err)
	require_Len(t, len(ru.addStreams), 0)
	require_Len(t, len(ru.removeStreams), 1)

	// Now stage a memory-based stream to be created.
	_, _, err = js.applyMetaEntries(createMemoryStream, ru)
	require_NoError(t, err)
	require_Len(t, len(ru.addStreams), 1)
	require_Len(t, len(ru.removeStreams), 1)
	require_Len(t, len(ru.updateConsumers), 0)

	// Also create a consumer on that memory-based stream.
	_, _, err = js.applyMetaEntries(createConsumer, ru)
	require_NoError(t, err)
	require_Len(t, len(ru.addStreams), 1)
	require_Len(t, len(ru.removeStreams), 1)
	require_Len(t, len(ru.updateConsumers), 1)
}

func TestJetStreamClusterMetaRecoveryConsumerCreateAndRemove(t *testing.T) {
	tests := []struct {
		title                       string
		encodeAddConsumerAssignment func(ca *consumerAssignment) []byte
	}{
		{title: "simple", encodeAddConsumerAssignment: encodeAddConsumerAssignment},
		{title: "compressed", encodeAddConsumerAssignment: encodeAddConsumerAssignmentCompressed},
	}
	for _, test := range tests {
		t.Run(test.title, func(t *testing.T) {
			c := createJetStreamClusterExplicit(t, "R3S", 3)
			defer c.shutdown()

			js := c.leader().getJetStream()

			ca := &consumerAssignment{Stream: "TEST", Name: "consumer"}
			createConsumer := []*Entry{{EntryNormal, test.encodeAddConsumerAssignment(ca)}}
			deleteConsumer := []*Entry{{EntryNormal, encodeDeleteConsumerAssignment(ca)}}

			// Need to be recovering so that we accumulate recoveryUpdates.
			js.setMetaRecovering()
			ru := &recoveryUpdates{
				removeStreams:   make(map[string]*streamAssignment),
				removeConsumers: make(map[string]map[string]*consumerAssignment),
				addStreams:      make(map[string]*streamAssignment),
				updateStreams:   make(map[string]*streamAssignment),
				updateConsumers: make(map[string]map[string]*consumerAssignment),
			}

			// Creating the consumer should append to update consumers list.
			_, _, err := js.applyMetaEntries(createConsumer, ru)
			require_NoError(t, err)
			require_Len(t, len(ru.updateConsumers[":TEST"]), 1)
			require_Len(t, len(ru.removeConsumers), 0)

			// Deleting the consumer should append to remove consumers list and remove from update list.
			_, _, err = js.applyMetaEntries(deleteConsumer, ru)
			require_NoError(t, err)
			require_Len(t, len(ru.removeConsumers[":TEST"]), 1)
			require_Len(t, len(ru.updateConsumers[":TEST"]), 0)

			// When re-creating the consumer, add to update list.
			_, _, err = js.applyMetaEntries(createConsumer, ru)
			require_NoError(t, err)
			require_Len(t, len(ru.removeConsumers[":TEST"]), 1)
			require_Len(t, len(ru.updateConsumers[":TEST"]), 1)
		})
	}
}

func TestJetStreamClusterMetaRecoveryAddAndUpdateStream(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	js := c.leader().getJetStream()
	ci := &ClientInfo{Account: globalAccountName, Cluster: "R3S"}
	cfg := &StreamConfig{Name: "TEST", Storage: FileStorage, Replicas: 3}
	rg, perr := js.createGroupForStream(ci, cfg)
	if perr != nil {
		require_NoError(t, perr)
	}
	sa := &streamAssignment{Client: ci, Config: cfg, Group: rg}

	// Need to be recovering so that we accumulate recoveryUpdates.
	js.setMetaRecovering()
	ru := &recoveryUpdates{
		removeStreams:   make(map[string]*streamAssignment),
		removeConsumers: make(map[string]map[string]*consumerAssignment),
		addStreams:      make(map[string]*streamAssignment),
		updateStreams:   make(map[string]*streamAssignment),
		updateConsumers: make(map[string]map[string]*consumerAssignment),
	}

	// We create a new stream, and we'll update it shortly after.
	entries := []*Entry{{EntryNormal, encodeAddStreamAssignment(sa)}}
	_, _, err := js.applyMetaEntries(entries, ru)
	require_NoError(t, err)
	require_Len(t, len(ru.addStreams), 1)
	require_Len(t, len(ru.updateStreams), 0)
	require_Len(t, len(ru.removeStreams), 0)

	// Now update the stream. The recovery updates should contain both the add and update.
	// If only the update would exist, the stream would not be created below.
	sa.Config.Subjects = []string{"foo"}
	entries = []*Entry{{EntryNormal, encodeUpdateStreamAssignment(sa)}}
	_, _, err = js.applyMetaEntries(entries, ru)
	require_NoError(t, err)
	require_Len(t, len(ru.addStreams), 1)
	require_Len(t, len(ru.updateStreams), 1)
	require_Len(t, len(ru.removeStreams), 0)

	// Check the stream is properly added.
	for _, sa := range ru.addStreams {
		js.processStreamAssignment(sa)
	}
	sa = js.streamAssignment(globalAccountName, "TEST")
	require_NotNil(t, sa)
	require_Len(t, len(sa.Config.Subjects), 0)

	// Check the stream is properly updated.
	for _, sa := range ru.updateStreams {
		js.processUpdateStreamAssignment(sa)
	}
	sa = js.streamAssignment(globalAccountName, "TEST")
	require_NotNil(t, sa)
	require_Len(t, len(sa.Config.Subjects), 1)
}

// https://github.com/nats-io/nats-server/issues/7229
func TestJetStreamClusterProcessClusterUpdateStreamNilStreamAssignment(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Subjects: []string{"foo"}, Replicas: 3})
	require_NoError(t, err)

	s := c.randomServer()
	acc, err := s.lookupAccount(globalAccountName)
	require_NoError(t, err)
	mset, err := acc.lookupStream("TEST")
	require_NoError(t, err)

	// Capture osa and build a sa that reflects the post-restart meta-replay
	// conditions: a scale-up path where both the old and new raftGroup nodes
	// are nil on this server (alreadyRunning=false, needsNode=true).
	sjs := s.getJetStream()
	sjs.mu.Lock()
	realOsa := sjs.streamAssignment(globalAccountName, "TEST")
	if realOsa == nil {
		sjs.mu.Unlock()
		t.Fatal("stream assignment not found")
	}

	// Intentionally omit Group.node so that in processClusterUpdateStream
	// alreadyRunning (osa.Group.node != nil) is false and needsNode
	// (sa.Group.node == nil) is true, taking the scale-up branch.
	cloneGroup := func() *raftGroup {
		return &raftGroup{
			Name:    realOsa.Group.Name,
			Peers:   append([]string{}, realOsa.Group.Peers...),
			Storage: realOsa.Group.Storage,
		}
	}
	osa := realOsa.clone()
	osa.Group = cloneGroup()
	sa := osa.clone()
	cfg := *realOsa.Config
	cfg.MaxMsgs = 1_000
	sa.Config = &cfg
	sa.Group = cloneGroup()
	sjs.mu.Unlock()

	// Simulate the state right after recoverStream() before meta catchup:
	// no stream assignment, no raft node, no cluster subs registered yet.
	mset.mu.Lock()
	mset.sa = nil
	mset.node = nil
	if mset.syncSub != nil {
		mset.srv.sysUnsubscribe(mset.syncSub)
		mset.syncSub = nil
	}
	mset.mu.Unlock()

	sjs.processClusterUpdateStream(acc, osa, sa)

	mset.mu.RLock()
	got := mset.sa
	mset.mu.RUnlock()
	require_NotNil(t, got)
}

// Make sure if we received acks that are out of bounds, meaning past our
// last sequence or before our first that they are ignored and errored if applicable.
func TestJetStreamClusterConsumerAckOutOfBounds(t *testing.T) {
	testJetStreamClusterConsumerAckOutOfBounds(t, false)
}

func TestJetStreamClusterConsumerAckV2OutOfBounds(t *testing.T) {
	testJetStreamClusterConsumerAckOutOfBounds(t, true)
}

func testJetStreamClusterConsumerAckOutOfBounds(t *testing.T, ackV2 bool) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	for _, s := range c.servers {
		opts := *s.getOpts()
		opts.FeatureFlags = map[string]bool{FeatureFlagJsAckFormatV2: ackV2}
		s.setOpts(&opts)
	}

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo.*"},
		Retention: nats.WorkQueuePolicy,
		Replicas:  3,
	})
	require_NoError(t, err)

	for i := 0; i < 10; i++ {
		_, err = js.Publish("foo.bar", []byte("OK"))
		require_NoError(t, err)
	}

	sub, err := js.PullSubscribe("foo.*", "C")
	require_NoError(t, err)

	msgs, err := sub.Fetch(1)
	require_NoError(t, err)
	require_Equal(t, len(msgs), 1)
	msgs[0].AckSync()

	// Now ack way past the last sequence.
	cl := c.consumerLeader(globalAccountName, "TEST", "C")
	require_NotNil(t, cl)
	mset, err := cl.globalAccount().lookupStream("TEST")
	require_NoError(t, err)
	o := mset.lookupConsumer("C")
	require_NotNil(t, o)
	o.mu.RLock()
	ackReply := o.ackReply(10000000000, 0, 1, 0, 0)
	o.mu.RUnlock()
	if ackV2 {
		require_Equal(t, ackReply, "$JS.ACK._.szMpdrwD.TEST.C.1.10000000000.0.0.0")
	} else {
		require_Equal(t, ackReply, "$JS.ACK.TEST.C.1.10000000000.0.0.0")
	}
	_, err = nc.Request(ackReply, nil, 250*time.Millisecond)
	require_Error(t, err, nats.ErrTimeout)

	// Make sure that now changes happened to our state.
	ci, err := js.ConsumerInfo("TEST", "C")
	require_NoError(t, err)
	require_Equal(t, ci.Delivered.Consumer, 1)
	require_Equal(t, ci.Delivered.Stream, 1)
	require_Equal(t, ci.AckFloor.Consumer, 1)
	require_Equal(t, ci.AckFloor.Stream, 1)

	s := c.consumerLeader("$G", "TEST", "C")
	s.Shutdown()
	s.WaitForShutdown()
	c.restartServer(s)
	c.waitOnConsumerLeader(globalAccountName, "TEST", "C")

	// Confirm new leader has same state for delivered and ack floor.
	ci, err = js.ConsumerInfo("TEST", "C")
	require_NoError(t, err)
	require_Equal(t, ci.Delivered.Consumer, 1)
	require_Equal(t, ci.Delivered.Stream, 1)
	require_Equal(t, ci.AckFloor.Consumer, 1)
	require_Equal(t, ci.AckFloor.Stream, 1)
}

func TestJetStreamClusterCatchupLoadNextMsgTooManyDeletes(t *testing.T) {
	tests := []struct {
		title          string
		catchupRequest *streamSyncRequest
		setup          func(js nats.JetStreamContext)
		assert         func(sub *nats.Subscription)
	}{
		{
			title: "within-delete-gap",
			setup: func(js nats.JetStreamContext) {},
		},
		{
			title: "EOF",
			setup: func(js nats.JetStreamContext) {
				err := js.DeleteMsg("TEST", 100)
				require_NoError(t, err)
			},
		},
	}

	for _, test := range tests {
		t.Run(test.title, func(t *testing.T) {
			c := createJetStreamClusterExplicit(t, "R3S", 3)
			defer c.shutdown()

			nc, js := jsClientConnect(t, c.randomServer())
			defer nc.Close()

			_, err := js.AddStream(&nats.StreamConfig{
				Name:     "TEST",
				Subjects: []string{"foo", "bar"},
				Replicas: 3,
			})
			require_NoError(t, err)

			// Starts and ends with subject "foo", we'll purge so there's a large gap of deletes in the middle.
			// This should force runCatchup to use LoadNextMsg instead of LoadMsg.
			for i := 0; i < 100; i++ {
				subject := "bar"
				if i == 0 || i == 99 {
					subject = "foo"
				}
				_, err = js.Publish(subject, nil)
				require_NoError(t, err)
			}
			err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Subject: "bar"})
			require_NoError(t, err)

			// Optionally run some extra setup.
			test.setup(js)

			// Reconnect to stream leader.
			l := c.streamLeader(globalAccountName, "TEST")
			nc.Close()
			nc, _ = jsClientConnect(t, l, nats.UserInfo("admin", "s3cr3t!"))
			defer nc.Close()

			// Setup wiretap and grab stream.
			sendSubject := "test-wiretap"
			sub, err := nc.SubscribeSync(sendSubject)
			require_NoError(t, err)
			err = nc.Flush() // Must flush, otherwise our subscription could be too late.
			require_NoError(t, err)
			acc, err := l.lookupAccount(globalAccountName)
			require_NoError(t, err)
			mset, err := acc.lookupStream("TEST")
			require_NoError(t, err)

			// Run custom catchup request and the test's asserts.
			sreq := &streamSyncRequest{Peer: "peer", FirstSeq: 5, LastSeq: 5, DeleteRangesOk: true}
			require_True(t, mset.srv.startGoRoutine(func() { mset.runCatchup(sendSubject, sreq) }))

			// Our first message should be a skip msg.
			msg, err := sub.NextMsg(time.Second)
			require_NoError(t, err)
			require_Equal(t, entryOp(msg.Data[0]), streamMsgOp)
			subj, _, _, _, seq, ts, _, err := decodeStreamMsg(msg.Data[1:])
			require_NoError(t, err)
			require_Equal(t, seq, 5)
			require_Equal(t, subj, _EMPTY_)
			require_Equal(t, ts, 0)

			// And end with EOF.
			msg, err = sub.NextMsg(time.Second)
			require_NoError(t, err)
			require_Len(t, len(msg.Data), 0)
		})
	}
}

func TestJetStreamClusterCatchupMustStallWhenBehindOnApplies(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	_, err = js.Publish("foo", nil)
	require_NoError(t, err)

	// Reconnect to stream leader.
	l := c.streamLeader(globalAccountName, "TEST")
	nc.Close()
	nc, _ = jsClientConnect(t, l, nats.UserInfo("admin", "s3cr3t!"))
	defer nc.Close()

	// Setup wiretap and grab stream.
	sendSubject := "test-wiretap"
	sub, err := nc.SubscribeSync(sendSubject)
	require_NoError(t, err)
	err = nc.Flush() // Must flush, otherwise our subscription could be too late.
	require_NoError(t, err)
	acc, err := l.lookupAccount(globalAccountName)
	require_NoError(t, err)
	mset, err := acc.lookupStream("TEST")
	require_NoError(t, err)

	// We have a message at sequence 1, so expect a successful catchup.
	sreq1 := &streamSyncRequest{Peer: "peer", FirstSeq: 1, LastSeq: 1, DeleteRangesOk: true}
	require_True(t, mset.srv.startGoRoutine(func() { mset.runCatchup(sendSubject, sreq1) }))
	// Expect the message at sequence 1.
	msg, err := sub.NextMsg(time.Second)
	require_NoError(t, err)
	require_Equal(t, entryOp(msg.Data[0]), streamMsgOp)
	subj, _, _, _, seq, _, _, err := decodeStreamMsg(msg.Data[1:])
	require_NoError(t, err)
	require_Equal(t, seq, 1)
	require_Equal(t, subj, "foo")
	// And end with EOF.
	msg, err = sub.NextMsg(time.Second)
	require_NoError(t, err)
	require_Len(t, len(msg.Data), 0)

	// Add one additional entry into the log that's not applied yet.
	n := mset.node.(*raft)
	n.Lock()
	ae := n.buildAppendEntry(nil)
	err = n.storeToWAL(ae)
	n.Unlock()
	index, commit, applied := n.Progress()
	require_NoError(t, err)
	require_LessThan(t, applied, index)
	require_Equal(t, commit, applied)
	// We have a message at sequence 1, but we haven't applied as many append entries.
	// We can't fulfill the request right now as we don't know yet if
	// that message will be deleted as part of upcoming append entries.
	sreq2 := &streamSyncRequest{Peer: "peer", FirstSeq: 1, LastSeq: 1, DeleteRangesOk: true, MinApplied: index}
	require_True(t, mset.srv.startGoRoutine(func() { mset.runCatchup(sendSubject, sreq2) }))
	_, err = sub.NextMsg(time.Second)
	require_Error(t, err, nats.ErrTimeout)

	// We have a message at sequence 1, but we haven't applied as many append entries.
	// Also, we seem to have a log that doesn't contain enough entries, even though we became leader.
	// Something has already gone wrong and got the logs to desync.
	// Value availability here and just fulfill the request.
	sreq3 := &streamSyncRequest{Peer: "peer", FirstSeq: 1, LastSeq: 1, DeleteRangesOk: true, MinApplied: 100}
	require_True(t, mset.srv.startGoRoutine(func() { mset.runCatchup(sendSubject, sreq3) }))
	// Expect the message at sequence 1.
	msg, err = sub.NextMsg(time.Second)
	require_NoError(t, err)
	require_Equal(t, entryOp(msg.Data[0]), streamMsgOp)
	subj, _, _, _, seq, _, _, err = decodeStreamMsg(msg.Data[1:])
	require_NoError(t, err)
	require_Equal(t, seq, 1)
	require_Equal(t, subj, "foo")
	// And end with EOF.
	msg, err = sub.NextMsg(time.Second)
	require_NoError(t, err)
	require_Len(t, len(msg.Data), 0)
}

func TestJetStreamClusterConsumerInfoAfterCreate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nl := c.randomNonLeader()
	nc, js := jsClientConnect(t, nl)
	defer nc.Close()

	cfg := &nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	}
	si, err := js.AddStream(cfg)
	require_NoError(t, err)

	// We want to ensure the consumer can be created and be applied.
	// On the non-meta-leader server we'll pause applies, so need to make
	// sure the consumer is not created on that server.
	if si.Cluster.Leader == nl.Name() {
		ml := c.leader()
		jreq, err := json.Marshal(&JSApiLeaderStepdownRequest{Placement: &Placement{Preferred: ml.Name()}})
		require_NoError(t, err)
		resp, err := nc.Request(fmt.Sprintf(JSApiStreamLeaderStepDownT, "TEST"), jreq, time.Second)
		require_NoError(t, err)
		var sdr JSApiLeaderStepDownResponse
		require_NoError(t, json.Unmarshal(resp.Data, &sdr))
	}

	// Wait some time for the leader to settle.
	time.Sleep(500 * time.Millisecond)

	// Scale down to ensure the consumer gets created on this server.
	cfg.Replicas = 1
	si, err = js.UpdateStream(cfg)
	require_NoError(t, err)
	require_NotEqual(t, si.Cluster.Leader, nl.Name())

	// We pause applies for the server we're connected to.
	// This is fine for the RAFT log and allowing the consumer to be created,
	// but we will not be able to apply the consumer assignment for some time.
	mjs := nl.getJetStream()
	require_NotNil(t, js)
	mg := mjs.getMetaGroup()
	require_NotNil(t, mg)
	err = mg.(*raft).PauseApply()
	require_NoError(t, err)

	// Add consumer.
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "CONSUMER"})
	require_NoError(t, err)

	// Consumer info should not fail, this server should not short-circuit because
	// it was not able to apply the consumer assignment.
	_, err = js.ConsumerInfo("TEST", "CONSUMER")
	require_NoError(t, err)

	// Resume applies.
	mg.(*raft).ResumeApply()

	// Check consumer info still works.
	_, err = js.ConsumerInfo("TEST", "CONSUMER")
	require_NoError(t, err)
}

func TestJetStreamClusterStreamUpscalePeersAfterDownscale(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	checkPeerSet := func() {
		t.Helper()
		checkFor(t, 5*time.Second, 500*time.Millisecond, func() error {
			for _, s := range c.servers {
				acc, err := s.lookupAccount(globalAccountName)
				if err != nil {
					return err
				}
				mset, err := acc.lookupStream("TEST")
				if err != nil {
					return err
				}
				peers := mset.raftNode().Peers()
				if len(peers) != 5 {
					return fmt.Errorf("expected 5 peers, got %d", len(peers))
				}
			}
			return nil
		})
	}

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 5,
	})
	require_NoError(t, err)

	checkPeerSet()

	_, err = js.UpdateStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	_, err = js.UpdateStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 5,
	})
	require_NoError(t, err)

	checkPeerSet()
}

func TestJetStreamClusterClearAllPreAcksOnRemoveMsg(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo"},
		Replicas:  3,
		Retention: nats.WorkQueuePolicy,
	})
	require_NoError(t, err)

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:   "CONSUMER",
		AckPolicy: nats.AckExplicitPolicy,
	})
	require_NoError(t, err)

	for i := 0; i < 3; i++ {
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
	}

	// Wait for all servers to converge on the same state.
	checkFor(t, 5*time.Second, 500*time.Millisecond, func() error {
		return checkState(t, c, globalAccountName, "TEST")
	})

	// Register pre-acks on all servers.
	// Normally this can't happen as the stream leader will have the message that's acked available, just for testing.
	for _, s := range c.servers {
		acc, err := s.lookupAccount(globalAccountName)
		require_NoError(t, err)
		mset, err := acc.lookupStream("TEST")
		require_NoError(t, err)
		o := mset.lookupConsumer("CONSUMER")
		require_NotNil(t, o)

		// Register pre-acks for the 3 messages.
		mset.registerPreAckLock(o, 1)
		mset.registerPreAckLock(o, 2)
		mset.registerPreAckLock(o, 3)
	}

	// Check there's an expected amount of pre-acks, and there are no pre-acks for the given sequence.
	checkPreAcks := func(seq uint64, expected int) {
		t.Helper()
		checkFor(t, 5*time.Second, time.Second, func() error {
			for _, s := range c.servers {
				acc, err := s.lookupAccount(globalAccountName)
				if err != nil {
					return err
				}
				mset, err := acc.lookupStream("TEST")
				if err != nil {
					return err
				}
				mset.mu.RLock()
				numPreAcks := len(mset.preAcks)
				numSeqPreAcks := len(mset.preAcks[seq])
				mset.mu.RUnlock()
				if numPreAcks != expected {
					return fmt.Errorf("expected %d pre-acks, got %d", expected, numPreAcks)
				}
				if seq > 0 && numSeqPreAcks != 0 {
					return fmt.Errorf("expected 0 pre-acks for seq %d, got %d", seq, numSeqPreAcks)
				}
			}
			return nil
		})
	}
	// Check all pre-acks were registered.
	checkPreAcks(0, 3)

	// Deleting the message should clear the pre-ack.
	err = js.DeleteMsg("TEST", 1)
	require_NoError(t, err)
	checkPreAcks(1, 2)

	// Erasing the message should clear the pre-ack.
	err = js.SecureDeleteMsg("TEST", 2)
	require_NoError(t, err)
	checkPreAcks(2, 1)

	// Purging should clear all pre-acks below the purged floor.
	err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Sequence: 4})
	require_NoError(t, err)
	checkPreAcks(3, 0)
}

func TestJetStreamClusterStreamHealthCheckMustNotRecreate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	waitForStreamAssignments := func() {
		t.Helper()
		checkFor(t, 5*time.Second, time.Second, func() error {
			for _, s := range c.servers {
				js := s.getJetStream()
				js.mu.RLock()
				sa := js.streamAssignment(globalAccountName, "TEST")
				js.mu.RUnlock()
				if sa == nil {
					return fmt.Errorf("stream assignment not found on %s", s.Name())
				}
			}
			return nil
		})
	}
	waitForNoStreamAssignments := func() {
		t.Helper()
		checkFor(t, 5*time.Second, time.Second, func() error {
			for _, s := range c.servers {
				js := s.getJetStream()
				js.mu.RLock()
				sa := js.streamAssignment(globalAccountName, "TEST")
				js.mu.RUnlock()
				if sa != nil {
					return fmt.Errorf("stream assignment still available on %s", s.Name())
				}
			}
			return nil
		})
	}
	getStreamAssignment := func(rs *Server) (*jetStream, *Account, *streamAssignment, *stream) {
		acc, err := rs.lookupAccount(globalAccountName)
		require_NoError(t, err)
		mset, err := acc.lookupStream("TEST")
		require_NotNil(t, err)

		sjs := rs.getJetStream()
		sjs.mu.Lock()
		defer sjs.mu.Unlock()

		sas := sjs.cluster.streams[globalAccountName]
		require_True(t, sas != nil)
		sa := sas["TEST"]
		require_True(t, sa != nil)
		sa.Created = time.Time{}
		return sjs, acc, sa, mset
	}
	checkNodeIsClosed := func(sa *streamAssignment) {
		t.Helper()
		require_True(t, sa.Group != nil)
		rg := sa.Group
		require_True(t, rg.node != nil)
		n := rg.node
		require_Equal(t, n.State(), Closed)
	}

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)
	waitForStreamAssignments()

	// We manually stop the RAFT node and ensure it doesn't get restarted.
	rs := c.randomNonStreamLeader(globalAccountName, "TEST")
	sjs, acc, sa, mset := getStreamAssignment(rs)
	require_True(t, sa.Group != nil)
	rg := sa.Group
	require_True(t, rg.node != nil)
	n := rg.node
	n.Stop()
	n.WaitForStop()

	// We wait for the monitor to exit, so we can set the flag back manually.
	checkFor(t, 5*time.Second, time.Second, func() error {
		mset.mu.RLock()
		defer mset.mu.RUnlock()
		if mset.inMonitor {
			return errors.New("waiting for monitor to stop")
		}
		return nil
	})
	mset.mu.Lock()
	mset.inMonitor = true
	mset.mu.Unlock()

	// The RAFT node should be closed. Checking health must not change that.
	// Simulates a race condition where we're shutting down, but we're still in the stream monitor.
	checkNodeIsClosed(sa)
	sjs.isStreamHealthy(acc, sa)
	checkNodeIsClosed(sa)

	err = js.DeleteStream("TEST")
	require_NoError(t, err)
	waitForNoStreamAssignments()

	// Underlying layer would be aware the health check made a copy.
	// So we sneakily set these values back, which simulates a race condition where
	// the health check is called while the deletion is in progress. This could happen
	// depending on how the locks are used.
	sjs.mu.Lock()
	sjs.cluster.streams = make(map[string]map[string]*streamAssignment)
	sjs.cluster.streams[globalAccountName] = make(map[string]*streamAssignment)
	sjs.cluster.streams[globalAccountName]["TEST"] = sa
	sa.Group.node = n
	sjs.mu.Unlock()

	// The underlying stream has been deleted. Checking health must not recreate the stream.
	checkNodeIsClosed(sa)
	sjs.isStreamHealthy(acc, sa)
	checkNodeIsClosed(sa)
}

func TestJetStreamClusterStreamHealthCheckMustNotDeleteEarly(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	waitForStreamAssignments := func() {
		t.Helper()
		checkFor(t, 5*time.Second, time.Second, func() error {
			for _, s := range c.servers {
				js := s.getJetStream()
				js.mu.RLock()
				sa := js.streamAssignment(globalAccountName, "TEST")
				js.mu.RUnlock()
				if sa == nil {
					return fmt.Errorf("stream assignment not found on %s", s.Name())
				}
			}
			return nil
		})
	}
	getStreamAssignment := func(rs *Server) (*jetStream, *Account, *streamAssignment, *stream) {
		acc, err := rs.lookupAccount(globalAccountName)
		require_NoError(t, err)
		mset, err := acc.lookupStream("TEST")
		require_NotNil(t, err)

		sjs := rs.getJetStream()
		sjs.mu.Lock()
		defer sjs.mu.Unlock()

		sas := sjs.cluster.streams[globalAccountName]
		require_True(t, sas != nil)
		sa := sas["TEST"]
		require_True(t, sa != nil)
		sa.Created = time.Time{}
		return sjs, acc, sa, mset
	}

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)
	waitForStreamAssignments()

	// We manually clear the node on the stream.
	rs := c.randomNonStreamLeader(globalAccountName, "TEST")
	sjs, acc, sa, mset := getStreamAssignment(rs)
	mset.mu.Lock()
	mset.node = nil
	mset.mu.Unlock()
	sjs.mu.Lock()
	group := sa.Group
	if group == nil {
		sjs.mu.Unlock()
		t.Fatal("sa.Group not initialized")
	}
	node := group.node
	if node == nil {
		sjs.mu.Unlock()
		t.Fatal("sa.Group.node not initialized")
	}
	sjs.mu.Unlock()

	// The health check gets the Raft node of the assignment and checks it against the
	// Raft node of the stream. We simulate a race condition where the stream's Raft node
	// is not yet initialized. The health check MUST NOT delete the node.
	sjs.isStreamHealthy(acc, sa)
	require_Equal(t, node.State(), Follower)
}

func TestJetStreamClusterStreamHealthCheckOnlyReportsSkew(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	waitForStreamAssignments := func() {
		t.Helper()
		checkFor(t, 5*time.Second, time.Second, func() error {
			for _, s := range c.servers {
				js := s.getJetStream()
				js.mu.RLock()
				sa := js.streamAssignment(globalAccountName, "TEST")
				js.mu.RUnlock()
				if sa == nil {
					return fmt.Errorf("stream assignment not found on %s", s.Name())
				}
			}
			return nil
		})
	}
	getStreamAssignment := func(rs *Server) (*jetStream, *Account, *streamAssignment, *stream) {
		acc, err := rs.lookupAccount(globalAccountName)
		require_NoError(t, err)
		mset, err := acc.lookupStream("TEST")
		require_NotNil(t, err)

		sjs := rs.getJetStream()
		sjs.mu.Lock()
		defer sjs.mu.Unlock()

		sas := sjs.cluster.streams[globalAccountName]
		require_True(t, sas != nil)
		sa := sas["TEST"]
		require_True(t, sa != nil)
		sa.Created = time.Time{}
		return sjs, acc, sa, mset
	}

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)
	waitForStreamAssignments()

	// Confirm the stream and assignment Raft nodes are equal.
	rs := c.randomNonStreamLeader(globalAccountName, "TEST")
	sjs, acc, sa, mset := getStreamAssignment(rs)
	mset.mu.RLock()
	msetNode := mset.node
	mset.mu.RUnlock()
	sjs.mu.Lock()
	group := sa.Group
	if group == nil {
		sjs.mu.Unlock()
		t.Fatal("sa.Group not initialized")
	}
	node := group.node
	if node == nil {
		sjs.mu.Unlock()
		t.Fatal("sa.Group.node not initialized")
	}
	sjs.mu.Unlock()
	require_Equal(t, msetNode, node)

	// Simulate stopping and restarting a new instance.
	node.Stop()
	node.WaitForStop()
	node, err = sjs.createRaftGroup(globalAccountName, group, false, FileStorage, pprofLabels{})
	require_NoError(t, err)
	require_NotEqual(t, node.State(), Closed)

	// The health check gets the Raft node of the assignment and checks it against the
	// Raft node of the stream. We simulate a race condition where the assignment's Raft node
	// is re-newed, but the stream's node is still the old instance.
	// The health check MUST NOT delete the node.
	require_Error(t, sjs.isStreamHealthy(acc, sa), errors.New("cluster node skew detected"))
	require_NotEqual(t, node.State(), Closed)
}

func TestJetStreamClusterStreamHealthCheckStreamCatchup(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	sl := c.streamLeader(globalAccountName, "TEST")
	sjs := sl.getJetStream()
	acc := sl.globalAccount()
	mset, err := acc.lookupStream("TEST")
	require_NoError(t, err)
	sjs.mu.Lock()
	sa := sjs.streamAssignment(globalAccountName, "TEST")
	sjs.mu.Unlock()

	require_NoError(t, sjs.isStreamHealthy(acc, sa))

	// Check we can report unhealthy.
	n := mset.raftNode().(*raft)
	n.Lock()
	n.commit = 0
	n.Unlock()
	require_Error(t, sjs.isStreamHealthy(acc, sa), errors.New("group node unhealthy"))

	// Catching up should have precedence.
	mset.setCatchingUp()
	require_True(t, mset.isCatchingUp())
	require_Error(t, sjs.isStreamHealthy(acc, sa), errors.New("stream catching up"))
}

func TestJetStreamClusterConsumerHealthCheckMustNotRecreate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	waitForConsumerAssignments := func() {
		t.Helper()
		checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
			for _, s := range c.servers {
				sjs := s.getJetStream()
				sjs.mu.RLock()
				ca := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
				sjs.mu.RUnlock()
				if ca == nil {
					return fmt.Errorf("stream assignment not found on %s", s.Name())
				}
			}
			return nil
		})
	}
	waitForNoConsumerAssignments := func() {
		t.Helper()
		checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
			for _, s := range c.servers {
				sjs := s.getJetStream()
				sjs.mu.RLock()
				ca := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
				sjs.mu.RUnlock()
				if ca != nil {
					return fmt.Errorf("stream assignment still available on %s", s.Name())
				}
			}
			return nil
		})
	}
	getConsumerAssignment := func(rs *Server) (*jetStream, *streamAssignment, *consumerAssignment, *stream) {
		acc, err := rs.lookupAccount(globalAccountName)
		require_NoError(t, err)
		mset, err := acc.lookupStream("TEST")
		require_NotNil(t, err)

		sjs := rs.getJetStream()
		sjs.mu.Lock()
		defer sjs.mu.Unlock()

		sas := sjs.cluster.streams[globalAccountName]
		require_True(t, sas != nil)
		sa := sas["TEST"]
		require_True(t, sa != nil)
		ca := sa.consumers["CONSUMER"]
		require_True(t, ca != nil)
		ca.Created = time.Time{}
		return sjs, sa, ca, mset
	}
	checkNodeIsClosed := func(ca *consumerAssignment) {
		t.Helper()
		require_True(t, ca.Group != nil)
		rg := ca.Group
		require_True(t, rg.node != nil)
		n := rg.node
		require_Equal(t, n.State(), Closed)
	}

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo"},
		Replicas:  3,
		Retention: nats.InterestPolicy, // Replicated consumers by default
	})
	require_NoError(t, err)
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "CONSUMER"})
	require_NoError(t, err)
	waitForConsumerAssignments()

	// We manually stop the RAFT node and ensure it doesn't get restarted.
	rs := c.randomNonConsumerLeader(globalAccountName, "TEST", "CONSUMER")
	sjs, sa, ca, mset := getConsumerAssignment(rs)
	require_True(t, ca.Group != nil)
	rg := ca.Group
	require_True(t, rg.node != nil)
	n := rg.node
	n.Stop()
	n.WaitForStop()

	o := mset.lookupConsumer("CONSUMER")
	require_NotNil(t, o)
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		if o.isMonitorRunning() {
			return errors.New("monitor goroutine still running")
		}
		return nil
	})

	// The RAFT node should be closed. Checking health must not change that.
	// Simulates a race condition where we're shutting down.
	checkNodeIsClosed(ca)
	require_Error(t, sjs.isConsumerHealthy(mset, "CONSUMER", ca), errors.New("monitor goroutine not running"))
	checkNodeIsClosed(ca)

	// We create a new RAFT group, the health check should detect this skew.
	_, err = sjs.createRaftGroup(globalAccountName, ca.Group, false, MemoryStorage, pprofLabels{})
	require_NoError(t, err)
	sjs.mu.Lock()
	// We set creating to now, since previously it would delete all data but NOT restart if created within <10s.
	ca.Created = time.Now()
	sjs.mu.Unlock()
	require_Error(t, sjs.isConsumerHealthy(mset, "CONSUMER", ca), errors.New("cluster node skew detected"))

	err = js.DeleteConsumer("TEST", "CONSUMER")
	require_NoError(t, err)
	waitForNoConsumerAssignments()

	// Underlying layer would be aware the health check made a copy.
	// So we sneakily set these values back, which simulates a race condition where
	// the health check is called while the deletion is in progress. This could happen
	// depending on how the locks are used.
	sjs.mu.Lock()
	sjs.cluster.streams = make(map[string]map[string]*streamAssignment)
	sjs.cluster.streams[globalAccountName] = make(map[string]*streamAssignment)
	sjs.cluster.streams[globalAccountName]["TEST"] = sa
	ca.Created = time.Time{}
	ca.Group.node = n
	sjs.mu.Unlock()

	// The underlying consumer has been deleted. Checking health must not recreate the consumer.
	checkNodeIsClosed(ca)
	require_Error(t, sjs.isConsumerHealthy(mset, "CONSUMER", ca), errors.New("consumer not found"))
	checkNodeIsClosed(ca)
}

func TestJetStreamClusterConsumerHealthCheckMustNotDeleteEarly(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	waitForConsumerAssignments := func() {
		t.Helper()
		checkFor(t, 5*time.Second, time.Second, func() error {
			for _, s := range c.servers {
				sjs := s.getJetStream()
				sjs.mu.RLock()
				ca := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
				sjs.mu.RUnlock()
				if ca == nil {
					return fmt.Errorf("stream assignment not found on %s", s.Name())
				}
			}
			return nil
		})
	}
	getConsumerAssignment := func(rs *Server) (*jetStream, *consumerAssignment, *stream, *consumer) {
		acc, err := rs.lookupAccount(globalAccountName)
		require_NoError(t, err)
		mset, err := acc.lookupStream("TEST")
		require_NotNil(t, err)
		o := mset.lookupConsumer("CONSUMER")

		sjs := rs.getJetStream()
		sjs.mu.Lock()
		defer sjs.mu.Unlock()

		sas := sjs.cluster.streams[globalAccountName]
		require_True(t, sas != nil)
		sa := sas["TEST"]
		require_True(t, sa != nil)
		ca := sa.consumers["CONSUMER"]
		require_True(t, ca != nil)
		ca.Created = time.Time{}
		return sjs, ca, mset, o
	}

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo"},
		Replicas:  3,
		Retention: nats.InterestPolicy, // Replicated consumers by default
	})
	require_NoError(t, err)
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "CONSUMER"})
	require_NoError(t, err)
	waitForConsumerAssignments()

	// We manually clear the node on the consumer.
	rs := c.randomNonConsumerLeader(globalAccountName, "TEST", "CONSUMER")
	sjs, ca, mset, o := getConsumerAssignment(rs)
	o.mu.Lock()
	o.node = nil
	o.mu.Unlock()
	sjs.mu.Lock()
	group := ca.Group
	if group == nil {
		sjs.mu.Unlock()
		t.Fatal("ca.Group not initialized")
	}
	node := group.node
	if node == nil {
		sjs.mu.Unlock()
		t.Fatal("ca.Group.node not initialized")
	}
	sjs.mu.Unlock()

	// The health check gets the Raft node of the assignment and checks it against the
	// Raft node of the consumer. We simulate a race condition where the consumer's Raft node
	// is not yet initialized. The health check MUST NOT delete the node.
	sjs.isConsumerHealthy(mset, "CONSUMER", ca)
	require_Equal(t, node.State(), Follower)
}

func TestJetStreamClusterConsumerHealthCheckOnlyReportsSkew(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	waitForConsumerAssignments := func() {
		t.Helper()
		checkFor(t, 5*time.Second, time.Second, func() error {
			for _, s := range c.servers {
				sjs := s.getJetStream()
				sjs.mu.RLock()
				ca := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
				sjs.mu.RUnlock()
				if ca == nil {
					return fmt.Errorf("stream assignment not found on %s", s.Name())
				}
			}
			return nil
		})
	}
	getConsumerAssignment := func(rs *Server) (*jetStream, *consumerAssignment, *stream, *consumer) {
		acc, err := rs.lookupAccount(globalAccountName)
		require_NoError(t, err)
		mset, err := acc.lookupStream("TEST")
		require_NotNil(t, err)
		o := mset.lookupConsumer("CONSUMER")

		sjs := rs.getJetStream()
		sjs.mu.Lock()
		defer sjs.mu.Unlock()

		sas := sjs.cluster.streams[globalAccountName]
		require_True(t, sas != nil)
		sa := sas["TEST"]
		require_True(t, sa != nil)
		ca := sa.consumers["CONSUMER"]
		require_True(t, ca != nil)
		ca.Created = time.Time{}
		return sjs, ca, mset, o
	}

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo"},
		Replicas:  3,
		Retention: nats.InterestPolicy, // Replicated consumers by default
	})
	require_NoError(t, err)
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "CONSUMER"})
	require_NoError(t, err)
	waitForConsumerAssignments()

	// Confirm the consumer and assignment Raft nodes are equal.
	rs := c.randomNonConsumerLeader(globalAccountName, "TEST", "CONSUMER")
	sjs, ca, mset, o := getConsumerAssignment(rs)
	o.mu.RLock()
	oNode := o.node
	o.mu.RUnlock()
	sjs.mu.Lock()
	group := ca.Group
	if group == nil {
		sjs.mu.Unlock()
		t.Fatal("ca.Group not initialized")
	}
	node := group.node
	if node == nil {
		sjs.mu.Unlock()
		t.Fatal("ca.Group.node not initialized")
	}
	sjs.mu.Unlock()
	require_Equal(t, oNode, node)

	// Simulate stopping and restarting a new instance.
	node.Stop()
	node.WaitForStop()
	node, err = sjs.createRaftGroup(globalAccountName, group, false, FileStorage, pprofLabels{})
	require_NoError(t, err)
	require_NotEqual(t, node.State(), Closed)

	// The health check gets the Raft node of the assignment and checks it against the
	// Raft node of the consumer. We simulate a race condition where the assignment's Raft node
	// is re-newed, but the consumer's node is still the old instance.
	// The health check MUST NOT delete the node.
	require_Error(t, sjs.isConsumerHealthy(mset, "CONSUMER", ca), errors.New("cluster node skew detected"))
	require_NotEqual(t, node.State(), Closed)
}

// https://github.com/nats-io/nats-server/issues/7149
func TestJetStreamClusterConsumerHealthCheckDeleted(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 1,
	})
	require_NoError(t, err)
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "CONSUMER"})
	require_NoError(t, err)

	cl := c.consumerLeader(globalAccountName, "TEST", "CONSUMER")
	require_NotNil(t, cl)
	mset, err := cl.globalAccount().lookupStream("TEST")
	require_NoError(t, err)

	sjs := cl.getJetStream()
	sjs.mu.Lock()
	ca := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
	if ca == nil {
		sjs.mu.Unlock()
		t.Fatal("ca not found")
	}
	// Reset created time, simulating the consumer existed already for a while.
	ca.Created = time.Time{}
	sjs.mu.Unlock()

	// The health check gathers all assignments and does checking after.
	// If the consumer was deleted in the meantime, it should not report an error.
	require_NoError(t, js.DeleteConsumer("TEST", "CONSUMER"))
	require_Error(t, sjs.isConsumerHealthy(mset, "CONSUMER", ca), errors.New("consumer not found"))

	// The health check could run earlier than we're able to create the consumer.
	// In that case, wait before erroring.
	sjs.mu.Lock()
	ca.Created = time.Now()
	sjs.mu.Unlock()
	require_NoError(t, sjs.isConsumerHealthy(mset, "CONSUMER", ca))
}

func TestJetStreamClusterStreamHealthCheckSurfacesAssignmentErr(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	sl := c.streamLeader(globalAccountName, "TEST")
	sjs := sl.getJetStream()
	acc := sl.globalAccount()

	sjs.mu.Lock()
	sa := sjs.streamAssignment(globalAccountName, "TEST")
	sjs.mu.Unlock()
	require_True(t, sa != nil)

	// Baseline: healthy.
	require_NoError(t, sjs.isStreamHealthy(acc, sa))

	// Persisted assignment-level error must surface via the health check.
	wantErr := errors.New("synthetic stream assignment failure")
	sjs.mu.Lock()
	sa.err = wantErr
	sjs.mu.Unlock()

	err = sjs.isStreamHealthy(acc, sa)
	require_Error(t, err)
	require_Contains(t, err.Error(), wantErr.Error())

	// Clearing the err brings health back.
	sjs.mu.Lock()
	sa.err = nil
	sjs.mu.Unlock()
	require_NoError(t, sjs.isStreamHealthy(acc, sa))
}

func TestJetStreamClusterConsumerHealthCheckSurfacesAssignmentErr(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "CONSUMER"})
	require_NoError(t, err)

	cl := c.consumerLeader(globalAccountName, "TEST", "CONSUMER")
	require_NotNil(t, cl)
	mset, err := cl.globalAccount().lookupStream("TEST")
	require_NoError(t, err)

	sjs := cl.getJetStream()
	sjs.mu.Lock()
	ca := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
	sjs.mu.Unlock()
	require_True(t, ca != nil)

	// Baseline: healthy.
	require_NoError(t, sjs.isConsumerHealthy(mset, "CONSUMER", ca))

	// Persisted assignment-level error must surface via the health check.
	wantErr := errors.New("synthetic consumer assignment failure")
	sjs.mu.Lock()
	ca.err = wantErr
	sjs.mu.Unlock()

	err = sjs.isConsumerHealthy(mset, "CONSUMER", ca)
	require_Error(t, err)
	require_Contains(t, err.Error(), wantErr.Error())

	// Clearing the err brings health back.
	sjs.mu.Lock()
	ca.err = nil
	sjs.mu.Unlock()
	require_NoError(t, sjs.isConsumerHealthy(mset, "CONSUMER", ca))
}

func TestJetStreamClusterConsumerHealthCheckStreamMissingNoLockLeak(t *testing.T) {
	js := &jetStream{}
	ca := &consumerAssignment{}
	err := js.isConsumerHealthy(nil, "consumer", ca)
	require_Error(t, err, errors.New("stream missing"))

	// The JS lock must have been released. Attempt to acquire the write lock
	// from a separate goroutine with a timeout so a leaked lock does not hang
	// the test forever.
	done := make(chan struct{})
	go func() {
		js.mu.Lock()
		defer js.mu.Unlock()
		close(done)
	}()

	select {
	case <-done:
		// Lock acquired, no leak.
	case <-time.After(2 * time.Second):
		t.Fatal("js.mu lock leaked: could not acquire write lock after isConsumerHealthy returned")
	}
}

func TestJetStreamClusterStreamHealthCheckRecoversAfterSuccessfulUpdate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	sl := c.streamLeader(globalAccountName, "TEST")
	sjs := sl.getJetStream()
	acc := sl.globalAccount()

	sjs.mu.Lock()
	sa := sjs.streamAssignment(globalAccountName, "TEST")
	sjs.mu.Unlock()
	require_True(t, sa != nil)

	// Plant a transient assignment-level error. Health check must now fail.
	wantErr := errors.New("synthetic stream assignment failure")
	sjs.mu.Lock()
	sa.err = wantErr
	sjs.mu.Unlock()
	err = sjs.isStreamHealthy(acc, sa)
	require_Error(t, err)
	require_Contains(t, err.Error(), wantErr.Error())

	// A successful update should clear it.
	_, err = js.UpdateStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo", "bar"},
		Replicas: 3,
	})
	require_NoError(t, err)

	sjs.mu.RLock()
	cur := sjs.streamAssignment(globalAccountName, "TEST")
	var gotErr error
	if cur != nil {
		gotErr = cur.err
	}
	sjs.mu.RUnlock()
	require_True(t, cur != nil)
	require_NoError(t, gotErr)
	require_NoError(t, sjs.isStreamHealthy(acc, cur))
}

func TestJetStreamClusterConsumerHealthCheckRecoversAfterSuccessfulUpdate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:       "CONSUMER",
		AckPolicy:     nats.AckExplicitPolicy,
		MaxAckPending: 100,
	})
	require_NoError(t, err)

	cl := c.consumerLeader(globalAccountName, "TEST", "CONSUMER")
	require_NotNil(t, cl)
	mset, err := cl.globalAccount().lookupStream("TEST")
	require_NoError(t, err)

	sjs := cl.getJetStream()
	sjs.mu.Lock()
	ca := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
	sjs.mu.Unlock()
	require_True(t, ca != nil)

	// Plant a transient assignment-level error. Health check must now fail.
	wantErr := errors.New("synthetic consumer assignment failure")
	sjs.mu.Lock()
	ca.err = wantErr
	sjs.mu.Unlock()
	err = sjs.isConsumerHealthy(mset, "CONSUMER", ca)
	require_Error(t, err)
	require_Contains(t, err.Error(), wantErr.Error())

	// A successful update should clear it.
	_, err = js.UpdateConsumer("TEST", &nats.ConsumerConfig{
		Durable:       "CONSUMER",
		AckPolicy:     nats.AckExplicitPolicy,
		MaxAckPending: 200,
	})
	require_NoError(t, err)

	sjs.mu.RLock()
	cur := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
	var gotErr error
	if cur != nil {
		gotErr = cur.err
	}
	sjs.mu.RUnlock()
	require_True(t, cur != nil)
	require_NoError(t, gotErr)
	require_NoError(t, sjs.isConsumerHealthy(mset, "CONSUMER", cur))
}

func TestJetStreamClusterRespectConsumerStartSeq(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	// Create replicated stream.
	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	// We could have published messages into the stream that have not yet been applied on the follower.
	// If we create a consumer with a starting sequence in the future, we must respect it.
	ci, err := js.AddConsumer("TEST", &nats.ConsumerConfig{
		DeliverPolicy: nats.DeliverByStartSequencePolicy,
		OptStartSeq:   20,
	})
	require_NoError(t, err)
	require_Equal(t, ci.Delivered.Stream, 19)

	// Same thing if the first sequence is not 0.
	err = js.PurgeStream("TEST", &nats.StreamPurgeRequest{Sequence: 10})
	require_NoError(t, err)

	// Ensure all servers are up-to-date.
	checkFor(t, 2*time.Second, 500*time.Millisecond, func() error {
		return checkState(t, c, globalAccountName, "TEST")
	})

	ci, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		DeliverPolicy: nats.DeliverByStartSequencePolicy,
		OptStartSeq:   20,
	})
	require_NoError(t, err)
	require_Equal(t, ci.Delivered.Stream, 19)

	// Only if we're requested to start at a sequence that's not available anymore
	// can we safely move it up. That data is gone already, so can't do anything else.
	ci, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		DeliverPolicy: nats.DeliverByStartSequencePolicy,
		OptStartSeq:   5,
	})
	require_NoError(t, err)
	require_Equal(t, ci.Delivered.Stream, 9)
}

func TestJetStreamClusterPeerRemoveStreamConsumerDesync(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	// Must have at least one message in the stream.
	_, err = js.Publish("foo", nil)
	require_NoError(t, err)

	sl := c.streamLeader(globalAccountName, "TEST")
	acc, err := sl.lookupAccount(globalAccountName)
	require_NoError(t, err)
	mset, err := acc.lookupStream("TEST")
	require_NoError(t, err)

	rs := c.randomNonStreamLeader(globalAccountName, "TEST")
	peer := rs.Name()
	rn := mset.raftNode()
	mset.mu.Lock()
	esm := encodeStreamMsgAllowCompress("foo", _EMPTY_, nil, nil, mset.clseq, time.Now().UnixNano(), false)
	mset.clseq++
	mset.mu.Unlock()
	// Propose both remove peer and a normal entry within the same append entry.
	err = rn.ProposeMulti([]*Entry{
		newEntry(EntryRemovePeer, []byte(peer)),
		newEntry(EntryNormal, esm),
	})
	require_NoError(t, err)

	// If the previous normal entry was skipped, we'd get a seq mismatch error here.
	_, err = js.Publish("foo", nil)
	require_NoError(t, err)

	// Now check the same but for a consumer.
	_, err = js.PullSubscribe("foo", "CONSUMER")
	require_NoError(t, err)

	cl := c.consumerLeader(globalAccountName, "TEST", "CONSUMER")
	acc, err = cl.lookupAccount(globalAccountName)
	require_NoError(t, err)
	mset, err = acc.lookupStream("TEST")
	require_NoError(t, err)
	o := mset.lookupConsumer("CONSUMER")
	require_NotNil(t, o)

	updateDeliveredBuffer := func() []byte {
		var b [4*binary.MaxVarintLen64 + 1]byte
		b[0] = byte(updateDeliveredOp)
		n := 1
		n += binary.PutUvarint(b[n:], 100)
		n += binary.PutUvarint(b[n:], 100)
		n += binary.PutUvarint(b[n:], 1)
		n += binary.PutVarint(b[n:], time.Now().UnixNano())
		return b[:n]
	}

	rs = c.randomNonConsumerLeader(globalAccountName, "TEST", "CONSUMER")
	peer = rs.Name()
	rn = o.raftNode()
	// Propose both remove peer and a normal entry within the same append entry.
	err = rn.ProposeMulti([]*Entry{
		newEntry(EntryRemovePeer, []byte(peer)),
		newEntry(EntryNormal, updateDeliveredBuffer()),
	})
	require_NoError(t, err)

	// Check the normal entry was applied.
	checkFor(t, 2*time.Second, 500*time.Millisecond, func() error {
		o.mu.Lock()
		defer o.mu.Unlock()
		cs, err := o.store.State()
		if err != nil {
			return err
		}
		if cs.Delivered.Consumer != 100 || cs.Delivered.Stream != 100 {
			return fmt.Errorf("expected sequence 100, got: %v", cs.Delivered)
		}
		return nil
	})
}

func TestJetStreamClusterStuckConsumerAfterLeaderChangeWithUnknownDeliveries(t *testing.T) {
	testJetStreamClusterStuckConsumerAfterLeaderChangeWithUnknownDeliveries(t, false)
}

func TestJetStreamClusterStuckConsumerV2AfterLeaderChangeWithUnknownDeliveries(t *testing.T) {
	testJetStreamClusterStuckConsumerAfterLeaderChangeWithUnknownDeliveries(t, true)
}

func testJetStreamClusterStuckConsumerAfterLeaderChangeWithUnknownDeliveries(t *testing.T, ackV2 bool) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	for _, s := range c.servers {
		opts := *s.getOpts()
		opts.FeatureFlags = map[string]bool{FeatureFlagJsAckFormatV2: ackV2}
		s.setOpts(&opts)
	}

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	// Publish some messages into the stream.
	for i := 0; i < 3; i++ {
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
	}

	// Ensure all servers are up-to-date.
	checkFor(t, 2*time.Second, 500*time.Millisecond, func() error {
		return checkState(t, c, globalAccountName, "TEST")
	})

	sub, err := js.PullSubscribe("foo", "CONSUMER")
	require_NoError(t, err)
	defer sub.Unsubscribe()

	// We only fetch 1 message here, since the condition is hard to trigger otherwise.
	// But, we're simulating fetching 3 messages and the consumer leader changing while
	// deliveries are happening. This will result in the new consumer leader not knowing
	// that the last two messages were also delivered (since we don't wait for quorum before delivering).
	msgs, err := sub.Fetch(1)
	require_NoError(t, err)
	require_Len(t, len(msgs), 1)

	cl := c.consumerLeader(globalAccountName, "TEST", "CONSUMER")
	require_NotNil(t, cl)
	mset, err := cl.globalAccount().lookupStream("TEST")
	require_NoError(t, err)
	o := mset.lookupConsumer("CONSUMER")
	require_NotNil(t, o)

	// The client could send an acknowledgement, while the new consumer leader doesn't know about it
	// ever being delivered. It must NOT adjust any state and ignore the request to remain consistent.
	o.mu.RLock()
	ackReply := o.ackReply(3, 3, 1, 0, 0)
	o.mu.RUnlock()
	if ackV2 {
		require_Equal(t, ackReply, "$JS.ACK._.szMpdrwD.TEST.CONSUMER.1.3.3.0.0")
	} else {
		require_Equal(t, ackReply, "$JS.ACK.TEST.CONSUMER.1.3.3.0.0")
	}
	_, err = nc.Request(ackReply, nil, time.Second)
	require_Error(t, err, nats.ErrTimeout)

	// Acknowledging a message that is known to be delivered is accepted still.
	o.mu.RLock()
	ackReply = o.ackReply(1, 1, 1, 0, 0)
	o.mu.RUnlock()
	if ackV2 {
		require_Equal(t, ackReply, "$JS.ACK._.szMpdrwD.TEST.CONSUMER.1.1.1.0.0")
	} else {
		require_Equal(t, ackReply, "$JS.ACK.TEST.CONSUMER.1.1.1.0.0")
	}
	_, err = nc.Request(ackReply, nil, time.Second)
	require_NoError(t, err)

	// Check for consistent consumer info.
	ci, err := js.ConsumerInfo("TEST", "CONSUMER")
	require_NoError(t, err)
	require_Equal(t, ci.Delivered.Consumer, 1)
	require_Equal(t, ci.Delivered.Stream, 1)
	require_Equal(t, ci.AckFloor.Consumer, 1)
	require_Equal(t, ci.AckFloor.Stream, 1)

	// Fetching for new messages MUST return the two messages the new consumer leader didn't
	// know were delivered before. If we wouldn't deliver these we'd have a stuck consumer.
	msgs, err = sub.Fetch(2)
	require_NoError(t, err)
	require_Len(t, len(msgs), 2)
	for _, msg := range msgs {
		require_NoError(t, msg.AckSync())
	}

	// Check for consistent consumer info.
	ci, err = js.ConsumerInfo("TEST", "CONSUMER")
	require_NoError(t, err)
	require_Equal(t, ci.Delivered.Consumer, 3)
	require_Equal(t, ci.Delivered.Stream, 3)
	require_Equal(t, ci.AckFloor.Consumer, 3)
	require_Equal(t, ci.AckFloor.Stream, 3)
}

// This is for when we are still using $SYS for NRG replication but we want to make sure
// we track this in something visible to the end user.
func TestJetStreamClusterAccountStatsForReplicatedStreams(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 5,
	})
	require_NoError(t, err)

	// Let's connect to stream leader to make sent messages predictable, otherwise we get those to come up based on routing to stream leader.
	s := c.streamLeader(globalAccountName, "TEST")
	require_True(t, s != nil)

	nc, js = jsClientConnect(t, s)
	defer nc.Close()

	// NRG traffic can be compressed, so make this unique so we can check stats correctly.
	msg := make([]byte, 1024*1024)
	crand.Read(msg)

	// Publish some messages into the stream.
	for i := 0; i < 10; i++ {
		_, err = js.Publish("foo", msg)
		require_NoError(t, err)
	}
	time.Sleep(250 * time.Millisecond)

	// Now grab the account stats for us and make sure we account for the replicated messages.

	// Opts to grab our account.
	opts := &AccountStatzOptions{
		Accounts: []string{globalAccountName},
	}
	as, err := s.AccountStatz(opts)
	require_NoError(t, err)
	require_Equal(t, len(as.Accounts), 1)

	accStats := as.Accounts[0]

	// We need to account for possibility that the stream create was also on this server, hence the >= vs strict ==.
	require_True(t, accStats.Received.Msgs >= 10)
	require_True(t, accStats.Received.Bytes >= 1024*1024)
	// For sent, we will have 10 pub acks, and then should have 40 extra messages that are sent and accounted for
	// during the nrg propsal to the R5 peers.
	require_True(t, accStats.Sent.Msgs >= 50)
	require_True(t, accStats.Sent.Bytes >= accStats.Received.Bytes*4)
}

func TestJetStreamClusterRecreateConsumerFromMetaSnapshot(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	// Initial setup.
	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)
	_, err = js.Publish("foo", nil)
	require_NoError(t, err)
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "CONSUMER"})
	require_NoError(t, err)

	// Wait for all servers to be fully up-to-date.
	checkFor(t, 2*time.Second, 500*time.Millisecond, func() error {
		if err = checkState(t, c, globalAccountName, "TEST"); err != nil {
			return err
		}
		for _, s := range c.servers {
			if acc, err := s.lookupAccount(globalAccountName); err != nil {
				return err
			} else if mset, err := acc.lookupStream("TEST"); err != nil {
				return err
			} else if o := mset.lookupConsumer("CONSUMER"); o == nil {
				return errors.New("consumer doesn't exist")
			}
		}
		return nil
	})

	// Shutdown a random server.
	rs := c.randomServer()
	rs.Shutdown()
	rs.WaitForShutdown()

	// Recreate connection, since we could have shutdown the server we were connected to.
	nc.Close()
	c.waitOnLeader()
	nc, js = jsClientConnect(t, c.randomServer())
	defer nc.Close()

	// Recreate consumer.
	require_NoError(t, js.DeleteConsumer("TEST", "CONSUMER"))
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "CONSUMER"})
	require_NoError(t, err)

	// Wait for all servers (except for the one that's down) to have recreated the consumer.
	var consumerRg string
	checkFor(t, 2*time.Second, 500*time.Millisecond, func() error {
		consumerRg = _EMPTY_
		for _, s := range c.servers {
			if s == rs {
				continue
			}
			if acc, err := s.lookupAccount(globalAccountName); err != nil {
				return err
			} else if mset, err := acc.lookupStream("TEST"); err != nil {
				return err
			} else if o := mset.lookupConsumer("CONSUMER"); o == nil {
				return errors.New("consumer doesn't exist")
			} else if ccrg := o.raftNode().Group(); consumerRg == _EMPTY_ {
				consumerRg = ccrg
			} else if consumerRg != ccrg {
				return errors.New("consumer raft groups don't match")
			}
		}
		return nil
	})

	// Install snapshots on all remaining servers to "hide" the intermediate consumer recreate requests.
	for _, s := range c.servers {
		if s != rs {
			sjs := s.getJetStream()
			require_NotNil(t, sjs)
			snap, _, _, err := sjs.metaSnapshot()
			require_NoError(t, err)
			sjs.mu.RLock()
			meta := sjs.cluster.meta
			sjs.mu.RUnlock()
			require_NoError(t, meta.InstallSnapshot(snap, false))
		}
	}

	// Restart the server, it should receive a meta snapshot and recognize the consumer recreation.
	rs = c.restartServer(rs)
	checkFor(t, 2*time.Second, 500*time.Millisecond, func() error {
		consumerRg = _EMPTY_
		for _, s := range c.servers {
			if acc, err := s.lookupAccount(globalAccountName); err != nil {
				return err
			} else if mset, err := acc.lookupStream("TEST"); err != nil {
				return err
			} else if o := mset.lookupConsumer("CONSUMER"); o == nil {
				return errors.New("consumer doesn't exist")
			} else if rn := o.raftNode(); rn == nil {
				return errors.New("consumer raft node doesn't exist")
			} else if ccrg := rn.Group(); ccrg == _EMPTY_ {
				return errors.New("consumer raft group doesn't exist")
			} else if consumerRg == _EMPTY_ {
				consumerRg = ccrg
			} else if consumerRg != ccrg {
				return errors.New("consumer raft groups don't match")
			}
		}
		return nil
	})
}

func TestJetStreamClusterUpgradeStreamVersioning(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	ml := c.leader()
	sjs := ml.getJetStream()
	rn := sjs.getMetaGroup()
	acc, err := ml.lookupAccount(globalAccountName)
	require_NoError(t, err)

	// Create stream config.
	cfg, apiErr := ml.checkStreamCfg(&StreamConfig{Name: "TEST", Subjects: []string{"foo"}}, acc, false)
	require_True(t, apiErr == nil)

	// Create and propose stream assignment.
	ci := &ClientInfo{Cluster: "R3S", Account: globalAccountName}
	rg, perr := sjs.createGroupForStream(ci, &cfg)
	require_True(t, perr == nil)
	sa := &streamAssignment{Group: rg, Sync: syncSubjForStream(), Config: &cfg, Client: ci, Created: time.Now().UTC()}
	require_NoError(t, rn.Propose(encodeAddStreamAssignment(sa)))

	// Wait for the stream assignment to have gone through.
	checkFor(t, 2*time.Second, 500*time.Millisecond, func() error {
		sjs.mu.RLock()
		defer sjs.mu.RUnlock()
		if sjs.streamAssignment(globalAccountName, "TEST") == nil {
			return errors.New("stream assignment does not exist yet")
		}
		return nil
	})

	for _, create := range []bool{true, false} {
		title := "create"
		if !create {
			title = "update"
		}
		t.Run(title, func(t *testing.T) {
			if create {
				// Create on 2.11+ should be idempotent with previous create on 2.10-.
				mcfg := &StreamConfig{}
				setStaticStreamMetadata(mcfg)
				si, err := js.AddStream(&nats.StreamConfig{
					Name:     "TEST",
					Subjects: []string{"foo"},
					Metadata: setDynamicStreamMetadata(mcfg).Metadata,
				})
				require_NoError(t, err)
				deleteDynamicMetadata(si.Config.Metadata)
				require_Len(t, len(si.Config.Metadata), 0)
			} else {
				// Update populates the versioning metadata.
				si, err := js.UpdateStream(&nats.StreamConfig{Name: "TEST", Subjects: []string{"foo"}})
				require_NoError(t, err)
				require_Len(t, len(si.Config.Metadata), 3)
			}
		})
	}
}

func TestJetStreamClusterUpgradeConsumerVersioning(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo"},
		Retention: nats.LimitsPolicy,
		Replicas:  1,
	})
	require_NoError(t, err)

	ml := c.leader()
	sjs := ml.getJetStream()

	// Wait for the stream assignment to have gone through.
	checkFor(t, 2*time.Second, 500*time.Millisecond, func() error {
		sjs.mu.RLock()
		defer sjs.mu.RUnlock()
		if sjs.streamAssignment(globalAccountName, "TEST") == nil {
			return errors.New("stream assignment does not exist yet")
		}
		return nil
	})

	rn := sjs.getMetaGroup()
	sa := sjs.streamAssignment(globalAccountName, "TEST")
	require_NotNil(t, sa)
	acc, err := ml.lookupAccount(globalAccountName)
	require_NoError(t, err)

	// Create consumer config.
	cfg := &ConsumerConfig{Durable: "CONSUMER", Name: "CONSUMER"}
	streamCfg, ok := sjs.clusterStreamConfig(acc.Name, "TEST")
	if !ok {
		require_NoError(t, NewJSStreamNotFoundError())
	}
	selectedLimits, _, _, apiErr := acc.selectLimits(cfg.replicas(&streamCfg))
	if apiErr != nil {
		require_NoError(t, apiErr)
	}
	srvLim := &ml.getOpts().JetStreamLimits
	apiErr = setConsumerConfigDefaults(cfg, &streamCfg, srvLim, selectedLimits, false)
	if apiErr != nil {
		require_NoError(t, apiErr)
	}

	// Create and propose consumer assignment.
	ci := &ClientInfo{Cluster: "R3S", Account: globalAccountName}
	rg := sjs.cluster.createGroupForConsumer(cfg, sa)
	ca := &consumerAssignment{Group: rg, Stream: "TEST", Name: "CONSUMER", Config: cfg, Client: ci, Created: time.Now().UTC()}
	require_NoError(t, rn.Propose(encodeAddConsumerAssignment(ca)))

	// Wait for the consumer assignment to have gone through.
	checkFor(t, 2*time.Second, 500*time.Millisecond, func() error {
		sjs.mu.RLock()
		defer sjs.mu.RUnlock()
		if sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER") == nil {
			return errors.New("consumer assignment does not exist yet")
		}
		return nil
	})

	for _, create := range []bool{true, false} {
		title := "create"
		if !create {
			title = "update"
		}
		t.Run(title, func(t *testing.T) {
			createConsumerRequest := func(obsReq CreateConsumerRequest) (*JSApiConsumerInfoResponse, error) {
				req, err := json.Marshal(obsReq)
				if err != nil {
					return nil, err
				}
				msg, err := nc.Request(fmt.Sprintf(JSApiDurableCreateT, "TEST", "CONSUMER"), req, time.Second)
				if err != nil {
					return nil, err
				}
				var resp JSApiConsumerInfoResponse
				require_NoError(t, json.Unmarshal(msg.Data, &resp))
				if resp.Error != nil {
					return nil, resp.Error
				}
				return &resp, nil
			}

			if create {
				// Create on 2.11+ should be idempotent with previous create on 2.10-.
				ncfg := &ConsumerConfig{Durable: "CONSUMER"}
				setStaticConsumerMetadata(ncfg)
				obsReq := CreateConsumerRequest{
					Stream: "TEST",
					Config: *setDynamicConsumerMetadata(ncfg),
					Action: ActionCreate,
				}
				resp, err := createConsumerRequest(obsReq)
				require_NoError(t, err)
				deleteDynamicMetadata(resp.Config.Metadata)
				require_Len(t, len(resp.Config.Metadata), 0)
			} else {
				// Update populates the versioning metadata.
				obsReq := CreateConsumerRequest{
					Stream: "TEST",
					Config: ConsumerConfig{Durable: "CONSUMER"},
					Action: ActionUpdate,
				}
				resp, err := createConsumerRequest(obsReq)
				require_NoError(t, err)
				require_Len(t, len(resp.Config.Metadata), 3)
			}
		})
	}
}

func TestJetStreamClusterInterestPolicyAckAll(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Retention: nats.InterestPolicy,
		Subjects:  []string{"foo"},
		Replicas:  3,
	})
	require_NoError(t, err)

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:   "CONSUMER",
		AckPolicy: nats.AckAllPolicy,
	})
	require_NoError(t, err)

	for i := 0; i < 100; i++ {
		_, err = js.Publish("foo", []byte("ok"))
		require_NoError(t, err)
	}

	expectedStreamMsgs := func(msgs uint64) {
		t.Helper()
		checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
			si, err := js.StreamInfo("TEST")
			if err != nil {
				return err
			}
			if si.State.Msgs != msgs {
				return fmt.Errorf("require uint64 equal, but got: %d != %d", si.State.Msgs, msgs)
			}
			return nil
		})
	}
	expectedStreamMsgs(100)

	for _, s := range c.servers {
		acc, err := s.lookupAccount(globalAccountName)
		require_NoError(t, err)
		mset, err := acc.lookupStream("TEST")
		require_NoError(t, err)
		o := mset.lookupConsumer("CONSUMER")
		require_NotNil(t, o)
		o.mu.Lock()
		// Ensure o.checkStateForInterestStream can't hide that the issue happened.
		o.chkflr = 1000
		o.mu.Unlock()
	}

	sub, err := js.PullSubscribe("foo", "CONSUMER")
	require_NoError(t, err)
	msgs, err := sub.Fetch(50)
	require_NoError(t, err)
	require_True(t, len(msgs) == 50)
	require_NoError(t, msgs[49].AckSync())

	expectedStreamMsgs(50)
}

func TestJetStreamClusterPreserveRedeliveredWithLaggingStream(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo"},
		Retention: nats.LimitsPolicy,
		Replicas:  3,
	})
	require_NoError(t, err)

	_, err = js.Publish("foo", nil)
	require_NoError(t, err)

	sub, err := js.PullSubscribe("foo", "CONSUMER", nats.AckWait(500*time.Millisecond))
	require_NoError(t, err)
	defer sub.Unsubscribe()

	msgs, err := sub.Fetch(1)
	require_NoError(t, err)
	require_Len(t, len(msgs), 1)
	require_NoError(t, msgs[0].AckSync())

	rsf := c.randomNonStreamLeader(globalAccountName, "TEST")
	acc, err := rsf.lookupAccount(globalAccountName)
	require_NoError(t, err)
	mset, err := acc.lookupStream("TEST")
	require_NoError(t, err)
	require_NoError(t, mset.raftNode().PauseApply())

	_, err = js.Publish("foo", nil)
	require_NoError(t, err)

	// Move consumer leader to equal stream leader.
	sl := c.streamLeader(globalAccountName, "TEST")
	req := JSApiLeaderStepdownRequest{Placement: &Placement{Preferred: sl.Name()}}
	data, err := json.Marshal(req)
	require_NoError(t, err)
	_, err = nc.Request(fmt.Sprintf(JSApiConsumerLeaderStepDownT, "TEST", "CONSUMER"), data, time.Second)
	require_NoError(t, err)
	c.waitOnConsumerLeader(globalAccountName, "TEST", "CONSUMER")
	require_Equal(t, c.consumerLeader(globalAccountName, "TEST", "CONSUMER").Name(), sl.Name())

	// Get the message to be stored in redelivered state.
	msgs, err = sub.Fetch(1)
	require_NoError(t, err)
	require_Len(t, len(msgs), 1)
	meta, err := msgs[0].Metadata()
	require_NoError(t, err)
	require_Equal(t, meta.NumDelivered, 1)

	msgs, err = sub.Fetch(1)
	require_NoError(t, err)
	require_Len(t, len(msgs), 1)
	meta, err = msgs[0].Metadata()
	require_NoError(t, err)
	require_Equal(t, meta.NumDelivered, 2)

	// Move consumer leader to a different server.
	req = JSApiLeaderStepdownRequest{Placement: &Placement{Preferred: rsf.Name()}}
	data, err = json.Marshal(req)
	require_NoError(t, err)
	_, err = nc.Request(fmt.Sprintf(JSApiConsumerLeaderStepDownT, "TEST", "CONSUMER"), data, time.Second)
	require_NoError(t, err)
	c.waitOnConsumerLeader(globalAccountName, "TEST", "CONSUMER")
	require_Equal(t, c.consumerLeader(globalAccountName, "TEST", "CONSUMER").Name(), rsf.Name())

	// Confirming the stream contents of the consumer leader don't contain the message yet.
	msgs, err = sub.Fetch(1, nats.MaxWait(time.Second))
	require_Error(t, err, nats.ErrTimeout)
	require_Len(t, len(msgs), 0)

	// Now let it apply the missing message.
	mset.raftNode().ResumeApply()

	// Should preserve delivered state properly, otherwise the client would observe NumDelivered rolled back.
	msgs, err = sub.Fetch(1)
	require_NoError(t, err)
	require_Len(t, len(msgs), 1)
	meta, err = msgs[0].Metadata()
	require_NoError(t, err)
	require_Equal(t, meta.NumDelivered, 3)
}

func TestJetStreamClusterInvalidJSACKOverRoute(t *testing.T) {
	test := func(t *testing.T, stream, consumer, subject string) {
		c := createJetStreamClusterExplicit(t, "R3S", 3)
		defer c.shutdown()

		nc, js := jsClientConnect(t, c.randomServer())
		defer nc.Close()

		_, err := js.AddStream(&nats.StreamConfig{
			Name:      stream,
			Subjects:  []string{"foo.>"},
			Retention: nats.LimitsPolicy,
			Replicas:  3,
		})
		require_NoError(t, err)

		_, err = js.AddConsumer(stream, &nats.ConsumerConfig{
			Durable:   consumer,
			AckPolicy: nats.AckExplicitPolicy,
		})
		require_NoError(t, err)

		// Reconnect to consumer follower.
		nc.Close()
		rs := c.randomNonConsumerLeader(globalAccountName, stream, consumer)
		nc, js = jsClientConnect(t, rs)
		defer nc.Close()

		_, err = js.Publish(subject, nil)
		require_NoError(t, err)

		checkFor(t, time.Second, 100*time.Millisecond, func() error {
			return checkState(t, c, globalAccountName, stream)
		})

		sub, err := js.PullSubscribe("foo.>", consumer)
		require_NoError(t, err)
		defer sub.Unsubscribe()

		// The message went over a route and should have the correct subject, and working ACK.
		msgs, err := sub.Fetch(1)
		require_NoError(t, err)
		require_Len(t, len(msgs), 1)
		require_Equal(t, msgs[0].Subject, subject)
		require_NoError(t, msgs[0].AckSync())
	}

	for _, s := range []string{"StreamNoAt", "StreamWith@At"} {
		for _, c := range []string{"ConsumerNoAt", "ConsumerWith@At"} {
			for _, subject := range []string{"foo.no.at", "foo.with@at"} {
				t.Run(fmt.Sprintf("%s/%s/%s", s, c, subject), func(t *testing.T) {
					test(t, s, c, subject)
				})
			}
		}
	}
}

func TestJetStreamClusterConsumerOnlyDeliverMsgAfterQuorum(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo"},
		Retention: nats.LimitsPolicy,
		Replicas:  3,
	})
	require_NoError(t, err)

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:   "CONSUMER",
		AckPolicy: nats.AckExplicitPolicy,
		Replicas:  3,
		AckWait:   2 * time.Second,
	})
	require_NoError(t, err)

	_, err = js.Publish("foo", nil)
	require_NoError(t, err)

	checkFor(t, time.Second, 100*time.Millisecond, func() error {
		return checkState(t, c, globalAccountName, "TEST")
	})

	cl := c.consumerLeader(globalAccountName, "TEST", "CONSUMER")
	acc, err := cl.lookupAccount(globalAccountName)
	require_NoError(t, err)
	mset, err := acc.lookupStream("TEST")
	require_NoError(t, err)
	o := mset.lookupConsumer("CONSUMER")
	require_NotNil(t, o)
	rn := o.raftNode().(*raft)

	// Force the leader to not be able to make proposals.
	rn.Lock()
	rn.werr = errors.New("block proposals")
	rn.Unlock()

	sub, err := js.PullSubscribe("foo", "CONSUMER")
	require_NoError(t, err)
	defer sub.Unsubscribe()

	// We must only receive a message AFTER quorum was met for updating delivered state.
	// This should time out since proposals are blocked.
	msgs, err := sub.Fetch(1, nats.MaxWait(2*time.Second))
	require_Error(t, err, nats.ErrTimeout)
	require_Len(t, len(msgs), 0)

	// Allow proposals to be made again.
	rn.Lock()
	rn.werr = nil
	rn.Unlock()

	// Now it should pass.
	msgs, err = sub.Fetch(1, nats.MaxWait(2*time.Second))
	require_NoError(t, err)
	require_Len(t, len(msgs), 1)
	require_NoError(t, msgs[0].AckSync())
}

func TestJetStreamClusterConsumerResetPendingDeliveriesOnMaxAckPendingUpdate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo"},
		Retention: nats.LimitsPolicy,
		Replicas:  3,
	})
	require_NoError(t, err)

	cfg := &nats.ConsumerConfig{
		Durable:       "CONSUMER",
		AckPolicy:     nats.AckExplicitPolicy,
		Replicas:      3,
		AckWait:       2 * time.Second,
		MaxAckPending: 100,
	}
	_, err = js.AddConsumer("TEST", cfg)
	require_NoError(t, err)

	cl := c.consumerLeader(globalAccountName, "TEST", "CONSUMER")
	acc, err := cl.lookupAccount(globalAccountName)
	require_NoError(t, err)
	mset, err := acc.lookupStream("TEST")
	require_NoError(t, err)
	o := mset.lookupConsumer("CONSUMER")
	require_NotNil(t, o)

	o.mu.Lock()
	o.pendingDeliveries = map[uint64]*jsPubMsg{0: getJSPubMsgFromPool()}
	o.mu.Unlock()

	// Increasing does not reset pending deliveries.
	cfg.MaxAckPending++
	_, err = js.UpdateConsumer("TEST", cfg)
	require_NoError(t, err)

	o.mu.Lock()
	l := len(o.pendingDeliveries)
	o.mu.Unlock()
	require_Equal(t, l, 1)

	// Decreasing does reset pending deliveries, so we can shrink the map.
	cfg.MaxAckPending--
	_, err = js.UpdateConsumer("TEST", cfg)
	require_NoError(t, err)

	o.mu.Lock()
	l = len(o.pendingDeliveries)
	o.mu.Unlock()
	require_Equal(t, l, 0)
}

func TestJetStreamClusterConsumerActiveAfterDidNotDeliverOverRoute(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	pubAck, err := js.Publish("foo", nil)
	require_NoError(t, err)
	require_Equal(t, pubAck.Sequence, 1)

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:        "CONSUMER",
		Replicas:       3,
		DeliverSubject: "deliver_subject",
	})
	require_NoError(t, err)

	ci, err := js.ConsumerInfo("TEST", "CONSUMER")
	require_NoError(t, err)
	require_False(t, ci.PushBound)

	cl := c.consumerLeader(globalAccountName, "TEST", "CONSUMER")
	rs := c.randomNonConsumerLeader(globalAccountName, "TEST", "CONSUMER")
	ncRs, jsRs := jsClientConnect(t, rs)
	defer ncRs.Close()

	// Just noop callback.
	cb := func(msg *nats.Msg) {}
	_, err = jsRs.Subscribe("foo", cb, nats.Bind("TEST", "CONSUMER"))
	require_NoError(t, err)

	// Eventually the sub should be known to the leader, and report active/bound.
	checkFor(t, 2*time.Second, 100*time.Millisecond, func() error {
		ci, err = js.ConsumerInfo("TEST", "CONSUMER")
		if err != nil {
			return err
		}
		if !ci.PushBound {
			return errors.New("push not bound")
		}
		return nil
	})

	// Simulate failed delivery over route connection.
	acc, err := cl.lookupAccount(globalAccountName)
	require_NoError(t, err)
	mset, err := acc.lookupStream("TEST")
	require_NoError(t, err)
	o := mset.lookupConsumer("CONSUMER")
	require_NotNil(t, o)
	o.didNotDeliver(1, "foo")

	// Consumer should still be active/bound.
	ci, err = js.ConsumerInfo("TEST", "CONSUMER")
	require_NoError(t, err)
	require_True(t, ci.PushBound)
}

func TestJetStreamClusterOfflineR1StreamDenyUpdate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	cfg := &nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 1,
	}
	_, err := js.AddStream(cfg)
	require_NoError(t, err)

	// Stop current R1 stream leader.
	sl := c.streamLeader(globalAccountName, "TEST")
	sl.Shutdown()
	nc.Close()
	nc, js = jsClientConnect(t, c.randomServer())
	defer nc.Close()

	// Wait for meta leader, so we can send an update.
	c.waitOnLeader()

	_, err = js.StreamInfo("TEST")
	require_Error(t, err, NewJSStreamOfflineError())

	cfg.Replicas = 3
	_, err = js.UpdateStream(cfg)
	require_Error(t, err, NewJSStreamOfflineError())
}

func TestJetStreamClusterOfflineR1ConsumerDenyUpdate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	cfg := &nats.ConsumerConfig{
		Durable:  "CONSUMER",
		Replicas: 1,
	}
	_, err = js.AddConsumer("TEST", cfg)
	require_NoError(t, err)

	// Stop current R1 consumer leader.
	cl := c.consumerLeader(globalAccountName, "TEST", "CONSUMER")
	cl.Shutdown()
	nc.Close()
	nc, js = jsClientConnect(t, c.randomServer())
	defer nc.Close()

	// Wait for meta leader, so we can send an update.
	c.waitOnLeader()

	_, err = js.ConsumerInfo("TEST", "CONSUMER")
	require_Error(t, err, NewJSConsumerOfflineError())

	cfg.Replicas = 3
	_, err = js.UpdateConsumer("TEST", cfg)
	require_Error(t, err, NewJSConsumerOfflineError())
}

func TestJetStreamClusterSnapshotStreamAssetOnShutdown(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	var sds []string
	for _, s := range c.servers {
		sds = append(sds, s.StoreDir())
	}

	for _, sd := range sds {
		matches, err := filepath.Glob(filepath.Join(sd, "$SYS", "_js_", "*", snapshotsDir, "*"))
		require_NoError(t, err)
		require_True(t, len(matches) > 0)
		for _, match := range matches {
			require_NoError(t, os.RemoveAll(match))
		}
	}

	// Publish, so we have something new to snapshot.
	_, err = js.Publish("foo", nil)
	require_NoError(t, err)
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		return checkState(t, c, globalAccountName, "TEST")
	})

	// Shutdown servers, and check if all made stream snapshots.
	for _, s := range c.servers {
		s.Shutdown()
	}
	for _, sd := range sds {
		matches, err := filepath.Glob(filepath.Join(sd, "$SYS", "_js_", "*", snapshotsDir))
		require_NoError(t, err)
		// Matches _meta_ and stream raft groups.
		require_Len(t, len(matches), 2)
		var foundStream bool
		for _, match := range matches {
			if !strings.Contains(match, "S-R3F") {
				continue
			}
			foundStream = true
			dirs, err := os.ReadDir(match)
			require_NoError(t, err)
			if len(dirs) != 1 {
				t.Errorf("Missing snapshot for %s", match)
			}
		}
		require_True(t, foundStream)
	}
}

func TestJetStreamClusterDontReviveRemovedStream(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	cfg := &nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	}
	_, err := js.AddStream(cfg)
	require_NoError(t, err)

	// Wait for stream to be assigned on all servers.
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		for _, s := range c.servers {
			if !s.JetStreamIsStreamAssigned(globalAccountName, "TEST") {
				return fmt.Errorf("stream not yet assigned on %s", s.Name())
			}
		}
		return nil
	})

	sl := c.streamLeader(globalAccountName, "TEST")
	rs := c.randomNonStreamLeader(globalAccountName, "TEST")
	mset, err := rs.globalAccount().lookupStream("TEST")
	require_NoError(t, err)

	// Simulate the stream being scaled down, quickly after it was scaled up.
	cfg.Replicas = 1
	_, err = js.UpdateStream(cfg)
	require_NoError(t, err)

	// Wait for the stream to not be assigned on the servers (except the leader).
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		for _, s := range c.servers {
			if s == sl {
				continue
			}
			if s.JetStreamIsStreamAssigned(globalAccountName, "TEST") {
				return fmt.Errorf("stream still assigned on %s", s.Name())
			}
		}
		if !mset.closed.Load() {
			return fmt.Errorf("stream not closed yet")
		}
		return nil
	})

	// Simulating the stream was catching up and is resetting after timing out.
	require_True(t, mset.resetClusteredState(nil))

	// Allow for some time here, mset.resetClusteredState might
	// spin up a goroutine if it's resetting the stream.
	// Confirm the stream is not revived.
	time.Sleep(200 * time.Millisecond)
	_, err = rs.globalAccount().lookupStream("TEST")
	require_Error(t, err, ErrJetStreamStreamNotFound)
}

func TestJetStreamClusterCreateR3StreamWithOfflineNodes(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	ml := c.leader()
	nc, js := jsClientConnect(t, ml)
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "FOO", Replicas: 3})
	require_NoError(t, err)

	// Shutdown one server, we'll still have quorum.
	rs1 := c.randomNonLeader()
	rs1.Shutdown()
	_, err = js.AddStream(&nats.StreamConfig{Name: "BAR", Replicas: 3})
	require_NoError(t, err)

	// While we still have quorum, publish a few messages to the streams.
	// The offline server will need to both assign the stream and catchup when it comes back.
	for _, subj := range []string{"FOO", "BAR"} {
		_, err = js.Publish(subj, nil)
		require_NoError(t, err)
	}

	// Shutdown one more server, only one server left that can't reach quorum on its own.
	rs2 := c.randomNonLeader()
	rs2.Shutdown()
	_, err = js.AddStream(&nats.StreamConfig{Name: "BAZ", Replicas: 3})
	require_Contains(t, err.Error(), "no suitable peers for placement", "peer offline")

	// Restart offline servers and ensure all comes back up.
	c.restartServer(rs1)
	c.restartServer(rs2)
	c.waitOnLeader()
	c.waitOnStreamLeader(globalAccountName, "FOO")
	c.waitOnStreamLeader(globalAccountName, "BAR")
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		if err := checkState(t, c, globalAccountName, "FOO"); err != nil {
			return err
		}
		if err := checkState(t, c, globalAccountName, "BAR"); err != nil {
			return err
		}
		return nil
	})
}

func TestJetStreamClusterCreateEphemeralConsumerWithOfflineNodes(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	ml := c.leader()
	nc, js := jsClientConnect(t, ml)
	defer nc.Close()
	_, err := js.AddStream(&nats.StreamConfig{Name: "TEST", Replicas: 3})
	require_NoError(t, err)

	// Shutdown a random server.
	c.randomNonLeader().Shutdown()

	for range 10 {
		_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{})
		require_NoError(t, err)
	}
}

func TestJetStreamClusterSetPreferredToOnlineNode(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	// Shutdown one random server.
	ml := c.leader()
	rs := c.randomNonLeader()
	js := rs.getJetStream()
	cc := js.cluster
	require_NotNil(t, cc)
	meta := cc.meta
	require_NotNil(t, meta)
	rsid := meta.ID()
	rs.Shutdown()

	cfg := &StreamConfig{Storage: FileStorage, Replicas: 3}
	js = ml.getJetStream()
	cc = js.cluster
	require_NotNil(t, cc)
	peers, err := cc.selectPeerGroup(3, "R3S", cfg, nil, 0, nil)
	// Need explicit nil-check.
	if err != nil {
		require_NoError(t, err)
	}
	require_Len(t, len(peers), 3)

	// Should prefer online peers when selecting preferred.
	rg := &raftGroup{Name: groupNameForStream(peers, cfg.Storage), Storage: cfg.Storage, Peers: peers, Cluster: "R3S"}
	rg.setPreferred(ml)
	require_NotEqual(t, rg.Preferred, rsid)
}

func TestJetStreamClusterAsyncFlushBasics(t *testing.T) {
	test := func(t *testing.T, syncAlways bool) {
		supportsAsync := !syncAlways

		c := createJetStreamClusterExplicit(t, "R3S", 3)
		defer c.shutdown()

		for _, s := range c.servers {
			s.optsMu.Lock()
			s.opts.SyncAlways = syncAlways
			s.optsMu.Unlock()
		}

		nc, js := jsClientConnect(t, c.randomServer())
		defer nc.Close()

		var s *Server
		checkStoreIsAsync := func(expectAsync bool) {
			t.Helper()
			mset, err := s.globalAccount().lookupStream("TEST")
			require_NoError(t, err)
			fs := mset.Store().(*fileStore)
			fs.mu.RLock()
			lmb := fs.lmb
			asyncFlush := fs.fcfg.AsyncFlush
			fip := fs.fip
			fs.mu.RUnlock()
			require_Equal(t, asyncFlush, expectAsync)
			require_Equal(t, fip, !expectAsync)
			require_NotNil(t, lmb)
			lmb.mu.RLock()
			flusher := lmb.flusher
			lmb.mu.RUnlock()
			if expectAsync {
				if !flusher {
					t.Fatal("flusher not initialized")
				} else if !asyncFlush {
					t.Fatal("async flush config not set")
				}
			} else {
				if flusher {
					t.Fatal("flusher still initialized")
				} else if asyncFlush {
					t.Fatal("async flush config not reset")
				}
			}
		}

		cfg := &StreamConfig{
			Name:     "TEST",
			Subjects: []string{"foo"},
			Storage:  FileStorage,
			Replicas: 1,
		}
		// Test disabled async flush on create.
		_, err := jsStreamCreate(t, nc, cfg)
		require_NoError(t, err)
		s = c.streamLeader(globalAccountName, "TEST")
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
		checkStoreIsAsync(false)

		// Enabling async flush.
		cfg.Replicas = 3
		_, err = jsStreamUpdate(t, nc, cfg)
		require_NoError(t, err)
		checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
			return checkState(t, c, globalAccountName, "TEST")
		})
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
		checkStoreIsAsync(supportsAsync)

		// Disabling async flush.
		cfg.Replicas = 1
		_, err = jsStreamUpdate(t, nc, cfg)
		require_NoError(t, err)
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
		checkStoreIsAsync(false)

		// Test async flush on create.
		require_NoError(t, js.DeleteStream("TEST"))
		cfg.Replicas = 3
		_, err = jsStreamCreate(t, nc, cfg)
		require_NoError(t, err)
		s = c.streamLeader(globalAccountName, "TEST")
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
		checkStoreIsAsync(supportsAsync)
	}

	t.Run("Default", func(t *testing.T) { test(t, false) })
	t.Run("SyncAlways", func(t *testing.T) { test(t, true) })
}

func TestJetStreamClusterAsyncFlushFileStoreFlushOnSnapshot(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := jsStreamCreate(t, nc, &StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Storage:  FileStorage,
		Replicas: 3,
	})
	require_NoError(t, err)

	// Now shutdown flusher and wait for it to be closed,
	// we'll not flush published messages automatically anymore.
	sl := c.streamLeader(globalAccountName, "TEST")
	mset, err := sl.globalAccount().lookupStream("TEST")
	require_NoError(t, err)
	fs := mset.Store().(*fileStore)
	fs.mu.RLock()
	lmb := fs.lmb
	fs.mu.RUnlock()
	require_NotNil(t, lmb)

	lmb.mu.Lock()
	if lmb.qch != nil {
		close(lmb.qch)
		lmb.qch = nil
	}
	lmb.mu.Unlock()
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		lmb.mu.RLock()
		defer lmb.mu.RUnlock()
		if lmb.flusher {
			return errors.New("flusher still active")
		}
		return nil
	})

	// Get the highest applied count from snapshot before below publish.
	n := mset.raftNode().(*raft)
	_, _, previousApplied := n.Progress()

	// Confirm no pending writes.
	require_Equal(t, lmb.pendingWriteSize(), 0)

	// Publishing a message will still work, because writes are async.
	_, err = js.Publish("foo", nil)
	require_NoError(t, err)
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		return checkState(t, c, globalAccountName, "TEST")
	})

	// Confirm above write is pending.
	require_Equal(t, lmb.pendingWriteSize(), 33)

	// Make the upper layer snapshot by sending leader change signal.
	// It doesn't matter that we're already leader, it still gets handled.
	// Previously this used the mqch, but that now only snapshots on shutdown.
	n.leadc <- true

	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		n.Lock()
		snap, err := n.loadLastSnapshot()
		n.Unlock()
		if err != nil {
			return err
		}
		if snap.lastIndex <= previousApplied {
			return fmt.Errorf("snapshot still at lastIndex=%d, expected=%d", snap.lastIndex, previousApplied)
		}
		return nil
	})

	// Confirm the write is flushed as a result of the snapshot.
	require_Equal(t, lmb.pendingWriteSize(), 0)
}

func TestJetStreamClusterScheduledDelayedMessage(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		for _, storage := range []StorageType{FileStorage, MemoryStorage} {
			t.Run(fmt.Sprintf("R%d/%s", replicas, storage), func(t *testing.T) {
				c := createJetStreamClusterExplicit(t, "R3S", 3)
				defer c.shutdown()

				nc, js := jsClientConnect(t, c.randomServer())
				defer nc.Close()

				_, err := jsStreamCreate(t, nc, &StreamConfig{
					Name:              "SchedulesDisabled",
					Subjects:          []string{"disabled"},
					Storage:           storage,
					Replicas:          replicas,
					AllowMsgSchedules: false,
				})
				require_NoError(t, err)

				schedulePattern := "@at 1970-01-01T00:00:00Z"
				m := nats.NewMsg("disabled")
				m.Header.Set("Nats-Schedule", schedulePattern) // Needs to be valid, but is not used.
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesDisabledError())

				m = nats.NewMsg("disabled")
				m.Header.Set("Nats-Schedule", "disabled")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesDisabledError())

				_, err = jsStreamCreate(t, nc, &StreamConfig{
					Name:              "SchedulesEnabledNoTtl",
					Subjects:          []string{"disabled.ttl"},
					Storage:           storage,
					Replicas:          replicas,
					AllowMsgSchedules: true,
				})
				require_NoError(t, err)

				m = nats.NewMsg("disabled.ttl")
				m.Header.Set("Nats-Schedule", schedulePattern) // Needs to be valid, but is not used.
				m.Header.Set("Nats-Schedule-TTL", "1s")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageTTLDisabledError())

				cfg := &StreamConfig{
					Name:              "SchedulesEnabled",
					Subjects:          []string{"foo.*"},
					Storage:           storage,
					Replicas:          replicas,
					AllowMsgSchedules: true,
					AllowMsgTTL:       true,
				}
				_, err = jsStreamCreate(t, nc, cfg)
				require_NoError(t, err)

				m = nats.NewMsg("foo.invalid")
				m.Header.Set("Nats-Schedule", "invalid")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesPatternInvalidError())

				// Unknown time zone must be reported as time-zone-invalid, not pattern-invalid.
				m = nats.NewMsg("foo.invalid")
				m.Header.Set("Nats-Schedule", "0 * * * * *")
				m.Header.Set("Nats-Schedule-Time-Zone", "Not/A/Zone")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesTimeZoneInvalidError())

				// An empty time zone is also invalid.
				m.Header.Set("Nats-Schedule-Time-Zone", "")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesTimeZoneInvalidError())

				// A valid time zone with an invalid pattern still surfaces as pattern-invalid.
				m = nats.NewMsg("foo.invalid")
				m.Header.Set("Nats-Schedule", "invalid")
				m.Header.Set("Nats-Schedule-Time-Zone", "UTC")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesPatternInvalidError())

				m = nats.NewMsg("foo.invalid")
				m.Header.Set("Nats-Schedule", schedulePattern)
				m.Header.Set("Nats-Schedule-Target", "not.matching")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesTargetInvalidError())

				m = nats.NewMsg("foo.invalid")
				m.Header.Set("Nats-Schedule", schedulePattern)
				m.Header.Set("Nats-Schedule-Target", "foo.*") // Must be literal.
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesTargetInvalidError())

				m = nats.NewMsg("foo.invalid")
				m.Header.Set("Nats-Schedule", schedulePattern)
				m.Header.Set("Nats-Schedule-Target", "foo.invalid") // Can't equal the publish subject.
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesTargetInvalidError())

				m = nats.NewMsg("foo.invalid")
				m.Header.Set("Nats-Schedule", schedulePattern)
				m.Header.Set("Nats-Schedule-Target", "foo.publish")
				m.Header.Set("Nats-Schedule-TTL", "invalid")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesTTLInvalidError())

				m = nats.NewMsg("foo.invalid")
				m.Header.Set("Nats-Schedule", schedulePattern)
				m.Header.Set("Nats-Schedule-Target", "foo.publish")
				m.Header.Set("Nats-Schedule-TTL", "1s")
				m.Header.Set("Nats-Rollup", "all")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesRollupInvalidError())

				// Schedule with a delay that takes a very long time.
				schedule := time.Now().Add(time.Hour).Format(time.RFC3339Nano)
				schedulePattern = fmt.Sprintf("@at %s", schedule)
				m = nats.NewMsg("foo.schedule")
				m.Header.Set("Nats-Schedule", schedulePattern)
				m.Header.Set("Nats-Schedule-Target", "foo.publish")
				m.Header.Set("Nats-Schedule-TTL", "1s")
				m.Header.Set("Nats-Rollup", "sub")
				pubAck, err := js.PublishMsg(m)
				require_NoError(t, err)
				require_Equal(t, pubAck.Sequence, 1)

				// Schedule where the rollup is missing, automatically gets the rollup added.
				// Also, will rollup the previous message for this schedule.
				schedule = time.Now().Add(time.Second).Format(time.RFC3339Nano)
				schedulePattern = fmt.Sprintf("@at %s", schedule)
				m = nats.NewMsg("foo.schedule")
				m.Data = []byte("hello")
				m.Header.Set("Nats-Schedule", schedulePattern)
				m.Header.Set("Nats-Schedule-Target", "foo.publish")
				m.Header.Set("Nats-Schedule-TTL", "1s")
				// Add a bunch of headers that will be stripped on the scheduled message.
				m.Header.Set("Nats-Expected-Stream", "SchedulesEnabled")
				m.Header.Set("Nats-Expected-Last-Sequence", "1")
				m.Header.Set("Nats-Expected-Last-Subject-Sequence", "1")
				m.Header.Set("Nats-Msg-Id", "X")
				m.Header.Set("Nats-TTL", "60s")
				m.Header.Set("Header", "Value")
				pubAck, err = js.PublishMsg(m)
				require_NoError(t, err)
				require_Equal(t, pubAck.Sequence, 2)

				sl := c.streamLeader(globalAccountName, "SchedulesEnabled")
				mset, err := sl.globalAccount().lookupStream("SchedulesEnabled")
				require_NoError(t, err)

				// Only one schedule exists.
				state := mset.state()
				require_Equal(t, state.LastSeq, 2)
				require_Equal(t, state.Msgs, 1)

				// Waiting for the delayed message to be published.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					state = mset.state()
					if state.LastSeq != 3 {
						return fmt.Errorf("expected last seq 3, got %d", state.LastSeq)
					} else if state.Msgs != 1 {
						return fmt.Errorf("expected 1 msg, got %d", state.Msgs)
					}
					return nil
				})

				// Confirm the scheduled message has the correct data.
				rsm, err := js.GetLastMsg("SchedulesEnabled", "foo.publish")
				require_NoError(t, err)
				require_Equal(t, rsm.Sequence, 3)
				require_True(t, bytes.Equal(rsm.Data, []byte("hello")))
				require_Len(t, len(rsm.Header), 4)
				require_Equal(t, rsm.Header.Get("Nats-Scheduler"), "foo.schedule")
				require_Equal(t, rsm.Header.Get("Nats-Schedule-Next"), "purge")
				require_Equal(t, rsm.Header.Get("Nats-TTL"), "1s")
				require_Equal(t, rsm.Header.Get("Header"), "Value")

				// Waiting for the delayed message to age out due to its TTL.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					state = mset.state()
					if state.FirstSeq != 4 {
						return fmt.Errorf("expected first seq 4, got %d", state.FirstSeq)
					} else if state.LastSeq != 3 {
						return fmt.Errorf("expected last seq 3, got %d", state.LastSeq)
					} else if state.Msgs != 0 {
						return fmt.Errorf("expected no messages, got %d", state.Msgs)
					}
					return nil
				})

				// Servers should be synced.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					return checkState(t, c, globalAccountName, "SchedulesEnabled")
				})

				cfg.AllowMsgSchedules = false
				_, err = jsStreamUpdate(t, nc, cfg)
				require_Error(t, err, NewJSStreamInvalidConfigError(fmt.Errorf("message schedules can not be disabled")))
			})
		}
	}
}

func TestJetStreamClusterScheduledMessageSubjectSourcing(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		for _, storage := range []StorageType{FileStorage, MemoryStorage} {
			t.Run(fmt.Sprintf("R%d/%s", replicas, storage), func(t *testing.T) {
				c := createJetStreamClusterExplicit(t, "R3S", 3)
				defer c.shutdown()

				nc, js := jsClientConnect(t, c.randomServer())
				defer nc.Close()

				cfg := &StreamConfig{
					Name:              "SchedulesEnabled",
					Subjects:          []string{"foo.*"},
					Storage:           storage,
					Replicas:          replicas,
					AllowMsgSchedules: true,
					AllowMsgTTL:       true,
				}
				_, err := jsStreamCreate(t, nc, cfg)
				require_NoError(t, err)

				m := nats.NewMsg("foo.data")
				m.Header.Set("Header", "Value")
				m.Data = []byte("data")

				pubAck, err := js.PublishMsg(m)
				require_NoError(t, err)
				require_Equal(t, pubAck.Sequence, 1)

				m = nats.NewMsg("foo.schedule")
				m.Header.Set("Nats-Schedule", "@at 1970-01-01T00:00:00Z")
				m.Header.Set("Nats-Schedule-Target", "foo.publish")

				// Invalid sources include if the subject:
				// - matches the schedule/target subject
				// - contains wildcard/is not literal
				// - doesn't match the stream subjects
				for _, src := range []string{"foo.schedule", "foo.publish", "foo.*", "foo.>", "bar"} {
					m.Header.Set("Nats-Schedule-Source", src)
					_, err = js.PublishMsg(m)
					require_Error(t, err, NewJSMessageSchedulesSourceInvalidError())
				}

				// Now publish using a correct source subject.
				m.Header.Set("Nats-Schedule-Source", "foo.data")
				pubAck, err = js.PublishMsg(m)
				require_NoError(t, err)
				require_Equal(t, pubAck.Sequence, 2)

				sl := c.streamLeader(globalAccountName, "SchedulesEnabled")
				mset, err := sl.globalAccount().lookupStream("SchedulesEnabled")
				require_NoError(t, err)

				state := mset.state()
				require_Equal(t, state.LastSeq, 2)
				require_Equal(t, state.Msgs, 2)

				// Waiting for the delayed message to be published.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					state = mset.state()
					if state.LastSeq != 3 {
						return fmt.Errorf("expected last seq 3, got %d", state.LastSeq)
					} else if state.Msgs != 2 {
						// One is the scheduled message, one is the sourced message.
						return fmt.Errorf("expected 2 msgs, got %d", state.Msgs)
					}
					return nil
				})

				// Confirm the scheduled message has the correct data.
				rsm, err := js.GetLastMsg("SchedulesEnabled", "foo.publish")
				require_NoError(t, err)
				require_Equal(t, rsm.Sequence, 3)
				require_True(t, bytes.Equal(rsm.Data, []byte("data")))
				require_Len(t, len(rsm.Header), 3)
				require_Equal(t, rsm.Header.Get("Nats-Scheduler"), "foo.schedule")
				require_Equal(t, rsm.Header.Get("Nats-Schedule-Next"), "purge")
				require_Equal(t, rsm.Header.Get("Header"), "Value")

				// Servers should be synced.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					return checkState(t, c, globalAccountName, "SchedulesEnabled")
				})
			})
		}
	}
}

func TestJetStreamClusterScheduledMessageSubjectSourcingFallback(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		for _, storage := range []StorageType{FileStorage, MemoryStorage} {
			t.Run(fmt.Sprintf("R%d/%s", replicas, storage), func(t *testing.T) {
				c := createJetStreamClusterExplicit(t, "R3S", 3)
				defer c.shutdown()

				nc, js := jsClientConnect(t, c.randomServer())
				defer nc.Close()

				cfg := &StreamConfig{
					Name:              "SchedulesEnabled",
					Subjects:          []string{"foo.*"},
					Storage:           storage,
					Replicas:          replicas,
					AllowMsgSchedules: true,
					AllowMsgTTL:       true,
				}
				_, err := jsStreamCreate(t, nc, cfg)
				require_NoError(t, err)

				// Publish the schedule WITHOUT first publishing to the source subject.
				// The schedule should fall back to its own content when the source has no last message.
				m := nats.NewMsg("foo.schedule")
				m.Header.Set("Nats-Schedule", "@at 1970-01-01T00:00:00Z")
				m.Header.Set("Nats-Schedule-Target", "foo.publish")
				m.Header.Set("Nats-Schedule-Source", "foo.data")
				m.Header.Set("Header", "Fallback")
				m.Data = []byte("fallback")

				pubAck, err := js.PublishMsg(m)
				require_NoError(t, err)
				require_Equal(t, pubAck.Sequence, 1)

				sl := c.streamLeader(globalAccountName, "SchedulesEnabled")
				mset, err := sl.globalAccount().lookupStream("SchedulesEnabled")
				require_NoError(t, err)

				state := mset.state()
				require_Equal(t, state.LastSeq, 1)
				require_Equal(t, state.Msgs, 1)

				// Waiting for the delayed message to be published using the fallback content.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					state = mset.state()
					if state.LastSeq != 2 {
						return fmt.Errorf("expected last seq 2, got %d", state.LastSeq)
					} else if state.Msgs != 1 {
						// Only the published message remains; the schedule itself is purged.
						return fmt.Errorf("expected 1 msg, got %d", state.Msgs)
					}
					return nil
				})

				// Confirm the published message contains the scheduled message's own content.
				rsm, err := js.GetLastMsg("SchedulesEnabled", "foo.publish")
				require_NoError(t, err)
				require_Equal(t, rsm.Sequence, 2)
				require_True(t, bytes.Equal(rsm.Data, []byte("fallback")))
				require_Len(t, len(rsm.Header), 3)
				require_Equal(t, rsm.Header.Get("Nats-Scheduler"), "foo.schedule")
				require_Equal(t, rsm.Header.Get("Nats-Schedule-Next"), "purge")
				require_Equal(t, rsm.Header.Get("Header"), "Fallback")
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					return checkState(t, c, globalAccountName, "SchedulesEnabled")
				})
			})
		}
	}
}

func TestJetStreamClusterScheduledMessageSubjectSourcingRepeatingNotStuck(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		for _, storage := range []StorageType{FileStorage, MemoryStorage} {
			t.Run(fmt.Sprintf("R%d/%s", replicas, storage), func(t *testing.T) {
				c := createJetStreamClusterExplicit(t, "R3S", 3)
				defer c.shutdown()

				nc, js := jsClientConnect(t, c.randomServer())
				defer nc.Close()

				cfg := &StreamConfig{
					Name:              "SchedulesEnabled",
					Subjects:          []string{"foo.*"},
					Storage:           storage,
					Replicas:          replicas,
					AllowMsgSchedules: true,
				}
				_, err := jsStreamCreate(t, nc, cfg)
				require_NoError(t, err)

				// Seed foo.data with a message that carries a stale Nats-Scheduler header.
				m := nats.NewMsg("foo.data")
				m.Header.Set("Nats-Schedule-Next", "purge")
				m.Header.Set("Nats-Scheduler", "foo.victim")
				_, err = js.PublishMsg(m)
				require_NoError(t, err)

				// Repeating schedule sourcing from foo.data. If the Nats-Scheduler is not
				// stripped from the source, the scheduled output carries two Nats-Scheduler
				// headers and the store's schedule update path targets the wrong scheduler
				// subject. Resulting in the scheduling getting stuck.
				m = nats.NewMsg("foo.sb")
				m.Header.Set("Nats-Schedule", "@every 1s")
				m.Header.Set("Nats-Schedule-Target", "foo.publish")
				m.Header.Set("Nats-Schedule-Source", "foo.data")
				_, err = js.PublishMsg(m)
				require_NoError(t, err)

				// Wait until the schedule has fired multiple times. With the bug it fires
				// once and then get stuck, without it it keeps firing on its @every 1s cadence.
				checkFor(t, 5*time.Second, 200*time.Millisecond, func() error {
					mi, err := js.StreamInfo("SchedulesEnabled", &nats.StreamInfoRequest{SubjectsFilter: "foo.publish"})
					if err != nil {
						return err
					}
					if n := mi.State.Subjects["foo.publish"]; n < 3 {
						return fmt.Errorf("expected at least 3 messages on foo.publish, got %d", n)
					}
					return nil
				})

				// The schedule message itself must still be present.
				rsm, err := js.GetLastMsg("SchedulesEnabled", "foo.sb")
				require_NoError(t, err)
				require_Equal(t, rsm.Header.Get("Nats-Schedule"), "@every 1s")
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					return checkState(t, c, globalAccountName, "SchedulesEnabled")
				})
			})
		}
	}
}

func TestJetStreamClusterScheduledDelayedMessageRollup(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		for _, storage := range []StorageType{FileStorage, MemoryStorage} {
			t.Run(fmt.Sprintf("R%d/%s", replicas, storage), func(t *testing.T) {
				c := createJetStreamClusterExplicit(t, "R3S", 3)
				defer c.shutdown()

				nc, js := jsClientConnect(t, c.randomServer())
				defer nc.Close()

				cfg := &StreamConfig{
					Name:              "SchedulesEnabled",
					Subjects:          []string{"foo.*"},
					Storage:           storage,
					Replicas:          replicas,
					AllowMsgSchedules: true,
				}
				_, err := jsStreamCreate(t, nc, cfg)
				require_NoError(t, err)

				_, err = js.Publish("foo.one", []byte("one"))
				require_NoError(t, err)
				_, err = js.Publish("foo.publish", []byte("previous"))
				require_NoError(t, err)

				// Schedule with an invalid rollup
				schedule := time.Now().Add(time.Second).Format(time.RFC3339Nano)
				schedulePattern := fmt.Sprintf("@at %s", schedule)
				m := nats.NewMsg("foo.schedule")
				m.Header.Set("Nats-Schedule", schedulePattern)
				m.Header.Set("Nats-Schedule-Target", "foo.publish")
				m.Header.Set("Nats-Schedule-Source", "foo.one")
				m.Header.Set("Nats-Schedule-Rollup", "invalid")
				_, err = js.PublishMsg(m)
				require_Error(t, err, NewJSMessageSchedulesRollupInvalidError())

				// Now send with the correct rollup.
				m.Header.Set("Nats-Schedule-Rollup", "sub")
				pubAck, err := js.PublishMsg(m)
				require_NoError(t, err)
				require_Equal(t, pubAck.Sequence, 3)

				sl := c.streamLeader(globalAccountName, "SchedulesEnabled")
				mset, err := sl.globalAccount().lookupStream("SchedulesEnabled")
				require_NoError(t, err)

				// Both the schedule and the previous messages exist.
				state := mset.state()
				require_Equal(t, state.LastSeq, 3)
				require_Equal(t, state.Msgs, 3)

				// Waiting for the delayed message to be published, and rollup the previous message.
				sawPrevious := false
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					sm, _, err := mset.store.LoadNextMsg("foo.publish", false, 0, nil)
					if err != nil {
						return err
					}
					if bytes.Equal(sm.msg, []byte("previous")) {
						sawPrevious = true
						return errors.New("still previous")
					}
					if !sawPrevious {
						return errors.New("expected previous message to be rolled up")
					}

					state = mset.state()
					if state.LastSeq != 4 {
						return fmt.Errorf("expected last seq 4, got %d", state.LastSeq)
					} else if state.Msgs != 2 {
						return fmt.Errorf("expected 2 msgs, got %d", state.Msgs)
					}
					return nil
				})

				// Confirm the scheduled message has the correct data.
				rsm, err := js.GetLastMsg("SchedulesEnabled", "foo.publish")
				require_NoError(t, err)
				require_Equal(t, rsm.Sequence, 4)
				require_Equal(t, string(rsm.Data), "one")
				require_Len(t, len(rsm.Header), 3)
				require_Equal(t, rsm.Header.Get("Nats-Scheduler"), "foo.schedule")
				require_Equal(t, rsm.Header.Get("Nats-Schedule-Next"), "purge")
				require_Equal(t, rsm.Header.Get("Nats-Rollup"), "sub")

				// Servers should be synced.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					return checkState(t, c, globalAccountName, "SchedulesEnabled")
				})
			})
		}
	}
}

func TestJetStreamClusterPurgeScheduleWithInvalidScheduler(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		t.Run(fmt.Sprintf("R%d", replicas), func(t *testing.T) {
			c := createJetStreamClusterExplicit(t, "R3S", 3)
			defer c.shutdown()

			nc, js := jsClientConnect(t, c.randomServer())
			defer nc.Close()

			cfg := &StreamConfig{
				Name:              "SchedulesEnabled",
				Subjects:          []string{"foo.*"},
				Storage:           FileStorage,
				Replicas:          replicas,
				AllowMsgSchedules: true,
			}
			_, err := jsStreamCreate(t, nc, cfg)
			require_NoError(t, err)

			// Publish a normal non-schedule message.
			_, err = js.Publish("foo.normal", []byte("previous"))
			require_NoError(t, err)

			// Publish a schedule message.
			m := nats.NewMsg("foo.schedule")
			m.Header.Set("Nats-Schedule", "@every 1h")
			m.Header.Set("Nats-Schedule-Target", "foo.target")
			m.Data = []byte("previous")
			_, err = js.PublishMsg(m)
			require_NoError(t, err)

			m = nats.NewMsg("foo.schedule")
			m.Header.Set("Nats-Schedule-Next", "purge")
			// Missing scheduler
			_, err = js.PublishMsg(m)
			require_Error(t, err, NewJSMessageSchedulesSchedulerInvalidError())

			// Scheduler must be a literal subject
			m.Header.Set("Nats-Scheduler", "foo.*")
			_, err = js.PublishMsg(m)
			require_Error(t, err, NewJSMessageSchedulesSchedulerInvalidError())

			// Scheduler can't equal the publish subject.
			m.Header.Set("Nats-Scheduler", "foo.schedule")
			_, err = js.PublishMsg(m)
			require_Error(t, err, NewJSMessageSchedulesSchedulerInvalidError())

			// Should error if the scheduler message is not actually a schedule.
			// Otherwise, non-schedule messages could be purged this way.
			m.Header.Set("Nats-Scheduler", "foo.normal")
			m.Subject = "foo.last"
			_, err = js.PublishMsg(m)
			require_Error(t, err, NewJSMessageSchedulesSchedulerInvalidError())

			// We're allowed to purge a schedule, as long as the subject we're
			// publishing to doesn't equal and it is a schedule.
			m.Header.Set("Nats-Scheduler", "foo.schedule")
			m.Subject = "foo.last"
			_, err = js.PublishMsg(m)
			require_NoError(t, err)

			// Purge the normal message separately.
			require_NoError(t, js.PurgeStream("SchedulesEnabled", &nats.StreamPurgeRequest{Subject: "foo.normal"}))

			// Only the last message should exist.
			checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
				state, err := checkStateAndErr(t, c, globalAccountName, "SchedulesEnabled")
				if err != nil {
					return err
				}
				if state.Msgs != 1 || state.FirstSeq != 3 || state.LastSeq != 3 {
					return fmt.Errorf("expected 1 msg, got %v", state)
				}
				return nil
			})

			// If the scheduler message does not exist, we should preserve backward-compatibility
			// and allow the message to be persisted with a noop purge.
			m.Header.Set("Nats-Scheduler", "foo.none")
			m.Subject = "foo.last"
			_, err = js.PublishMsg(m)
			require_NoError(t, err)

			// Should still be able to perform an "expected at sequence" check to make sure the
			// schedule actually exists before persisting the message and performing a purge.
			m.Header.Set("Nats-Expected-Last-Subject-Sequence", "1")
			m.Header.Set("Nats-Expected-Last-Subject-Sequence-Subject", "foo.none")
			_, err = js.PublishMsg(m)
			require_Error(t, err, NewJSStreamWrongLastSequenceError(0))
		})
	}
}

func TestJetStreamClusterScheduledPurgeHeadersRequiresSchedulingEnabled(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		t.Run(fmt.Sprintf("R%d", replicas), func(t *testing.T) {
			c := createJetStreamClusterExplicit(t, "R3S", 3)
			defer c.shutdown()

			nc, js := jsClientConnect(t, c.randomServer())
			defer nc.Close()

			_, err := jsStreamCreate(t, nc, &StreamConfig{
				Name:              "TEST",
				Storage:           FileStorage,
				Subjects:          []string{"foo.*"},
				Replicas:          replicas,
				AllowMsgSchedules: false,
			})
			require_NoError(t, err)

			_, err = js.Publish("foo.victim", []byte("OK"))
			require_NoError(t, err)

			m := nats.NewMsg("foo.attacker")
			m.Header.Set(JSScheduleNext, JSScheduleNextPurge)
			m.Header.Set(JSScheduler, "foo.victim")

			_, err = js.PublishMsg(m)
			require_Error(t, err, NewJSMessageSchedulesDisabledError())
		})
	}
}

func TestJetStreamClusterScheduledRearmHeaderRejectedFromClient(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		for _, storage := range []StorageType{FileStorage, MemoryStorage} {
			t.Run(fmt.Sprintf("R%d/%s", replicas, storage), func(t *testing.T) {
				c := createJetStreamClusterExplicit(t, "R3S", 3)
				defer c.shutdown()

				nc, js := jsClientConnect(t, c.randomServer())
				defer nc.Close()

				_, err := jsStreamCreate(t, nc, &StreamConfig{
					Name:              "TEST",
					Storage:           storage,
					Subjects:          []string{"foo.>"},
					Replicas:          replicas,
					AllowMsgSchedules: true,
				})
				require_NoError(t, err)

				// Arm a schedule that fires shortly.
				fireIn := 500 * time.Millisecond
				m := nats.NewMsg("foo.schedule")
				m.Header.Set("Nats-Schedule", fmt.Sprintf("@at %s", time.Now().Add(fireIn).Format(time.RFC3339Nano)))
				m.Header.Set("Nats-Schedule-Target", "foo.msg")
				_, err = js.PublishMsg(m)
				require_NoError(t, err)

				// An attacker with publish access should not be able to shift
				// the schedule's next fire time by forging Nats-Schedule-Next.
				attack := nats.NewMsg("foo.attacker")
				attack.Header.Set(JSScheduleNext, time.Now().Add(100*365*24*time.Hour).Format(time.RFC3339Nano))
				attack.Header.Set(JSScheduler, "foo.schedule")
				_, err = js.PublishMsg(attack)
				require_Error(t, err, NewJSMessageSchedulesSchedulerInvalidError())

				attack.Header.Del(JSScheduleNext)
				_, err = js.PublishMsg(attack)
				require_Error(t, err, NewJSMessageSchedulesSchedulerInvalidError())

				// The schedule must still fire on time.
				checkFor(t, 2*time.Second, 50*time.Millisecond, func() error {
					if _, err = js.GetLastMsg("TEST", "foo.msg"); err != nil {
						return err
					}
					return nil
				})
			})
		}
	}
}

func TestJetStreamClusterScheduledDelayedMessageReversedHeaderOrder(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		for _, storage := range []StorageType{FileStorage, MemoryStorage} {
			t.Run(fmt.Sprintf("R%d/%s", replicas, storage), func(t *testing.T) {
				c := createJetStreamClusterExplicit(t, "R3S", 3)
				defer c.shutdown()

				nc, _ := jsClientConnect(t, c.randomServer())
				defer nc.Close()

				cfg := &StreamConfig{
					Name:              "TEST",
					Subjects:          []string{"foo.*"},
					Storage:           storage,
					Replicas:          replicas,
					AllowMsgSchedules: true,
					AllowMsgTTL:       true,
				}
				_, err := jsStreamCreate(t, nc, cfg)
				require_NoError(t, err)

				sl := c.streamLeader(globalAccountName, "TEST")
				gacc := sl.globalAccount()
				// The Nats-Schedule headers share a common prefix, so make sure if these are ordered differently
				// we can still properly schedule a message.
				hdr := genHeader(nil, "Nats-Schedule-Target", "foo.publish")
				hdr = genHeader(hdr, "Nats-Schedule", "@at 1970-01-01T00:00:00Z")
				require_NoError(t, sl.sendInternalAccountMsgWithReply(gacc, "foo.schedule", _EMPTY_, hdr, nil, false))

				mset, err := gacc.lookupStream("TEST")
				require_NoError(t, err)

				// Waiting for the delayed message to be published.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					state := mset.state()
					if state.LastSeq != 2 {
						return fmt.Errorf("expected last seq 2, got %d", state.LastSeq)
					} else if state.Msgs != 1 {
						return fmt.Errorf("expected 1 msg, got %d", state.Msgs)
					}
					return nil
				})
			})
		}
	}
}

func TestJetStreamClusterScheduledIntervalMessage(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		for _, storage := range []StorageType{FileStorage, MemoryStorage} {
			t.Run(fmt.Sprintf("R%d/%s", replicas, storage), func(t *testing.T) {
				c := createJetStreamClusterExplicit(t, "R3S", 3)
				defer c.shutdown()

				nc, js := jsClientConnect(t, c.randomServer())
				defer nc.Close()

				cfg := &StreamConfig{
					Name:              "SchedulesEnabled",
					Subjects:          []string{"foo.*"},
					Storage:           storage,
					Replicas:          replicas,
					AllowMsgSchedules: true,
				}
				_, err := jsStreamCreate(t, nc, cfg)
				require_NoError(t, err)

				// Schedule with an interval that triggers often.
				m := nats.NewMsg("foo.schedule")
				m.Header.Set("Nats-Schedule", "@every 1s")
				m.Header.Set("Nats-Schedule-Target", "foo.publish")
				pubAck, err := js.PublishMsg(m)
				require_NoError(t, err)
				require_Equal(t, pubAck.Sequence, 1)

				sl := c.streamLeader(globalAccountName, "SchedulesEnabled")
				mset, err := sl.globalAccount().lookupStream("SchedulesEnabled")
				require_NoError(t, err)

				// Waiting for the repeated message to be published enough times.
				checkFor(t, 4*time.Second, 200*time.Millisecond, func() error {
					total := mset.Store().SubjectsTotals("foo.publish")["foo.publish"]
					if total < 3 {
						return fmt.Errorf("expected at least 3 messages, got %d", total)
					}
					return nil
				})

				// Servers should be synced.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					return checkState(t, c, globalAccountName, "SchedulesEnabled")
				})

				// If we remove the schedule, the scheduling should stop.
				var removed bool
				total := mset.Store().SubjectsTotals("foo.publish")["foo.publish"]
				for i := 1; ; i++ {
					before := total
					time.Sleep(1200 * time.Millisecond)
					total = mset.Store().SubjectsTotals("foo.publish")["foo.publish"]
					if before == total {
						// If the scheduling stopped before we removed, that's a problem.
						require_True(t, removed)
						break
					}
					if !removed {
						removed = true
						require_NoError(t, js.DeleteMsg("SchedulesEnabled", pubAck.Sequence))
					}
					if i > 2 {
						t.Fatal("Scheduling didn't stop")
					}
				}

				// Servers should be synced.
				checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
					return checkState(t, c, globalAccountName, "SchedulesEnabled")
				})
			})
		}
	}
}

func TestJetStreamClusterOfflineStreamAndConsumerAfterAssetCreateOrUpdate(t *testing.T) {
	clusterName := "R3S"
	c := createJetStreamClusterExplicit(t, clusterName, 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	ml := c.leader()
	require_NotNil(t, ml)

	sjs := ml.getJetStream()
	require_NotNil(t, sjs)
	sjs.mu.Lock()
	cc := sjs.cluster
	if cc == nil || cc.meta == nil {
		sjs.mu.Unlock()
		t.Fatalf("Expected cluster to be initialized")
	}

	restart := func() {
		t.Helper()
		for _, s := range c.servers {
			sjs = s.getJetStream()
			snap, _, _, err := sjs.metaSnapshot()
			require_NoError(t, err)
			meta := sjs.getMetaGroup()
			meta.InstallSnapshot(snap, false)
		}

		c.stopAll()
		c.restartAllSamePorts()
		c.waitOnLeader()
		ml = c.leader()
		require_NotNil(t, ml)
		require_NoError(t, nc.ForceReconnect())

		sjs = ml.getJetStream()
		require_NotNil(t, sjs)
		sjs.mu.Lock()
		cc = sjs.cluster
		if cc == nil || cc.meta == nil {
			sjs.mu.Unlock()
			t.Fatalf("Expected cluster to be initialized")
		}
		sjs.mu.Unlock()
	}

	getValidMetaSnapshot := func() (wsas []writeableStreamAssignment) {
		t.Helper()
		snap, _, _, err := sjs.metaSnapshot()
		require_NoError(t, err)
		require_True(t, len(snap) > 0)
		dec, err := s2.Decode(nil, snap)
		require_NoError(t, err)
		require_NoError(t, json.Unmarshal(dec, &wsas))
		return wsas
	}

	// Create a stream that's unsupported.
	ci := &ClientInfo{
		Account: globalAccountName,
		Cluster: clusterName,
	}
	scfg := &StreamConfig{
		Name:     "DowngradeStreamTest",
		Storage:  FileStorage,
		Replicas: 3,
		Metadata: map[string]string{"_nats.req.level": strconv.Itoa(math.MaxInt - 1)},
	}
	rg, perr := sjs.createGroupForStream(ci, scfg)
	if perr != nil {
		sjs.mu.Unlock()
		require_NoError(t, perr)
	}
	sa := &streamAssignment{
		Config:  scfg,
		Sync:    syncSubjForStream(),
		Group:   rg,
		Created: time.Now().UTC(),
		Client:  ci,
	}
	err := cc.meta.Propose(encodeAddStreamAssignment(sa))
	sjs.mu.Unlock()
	require_NoError(t, err)
	c.waitOnAllCurrent()

	unsupported := func(requiredApiLevel int) string {
		return fmt.Sprintf("unsupported - required API level: %d, current API level: %d", requiredApiLevel, JSApiLevel)
	}
	expectStreamInfo := func(offlineReason, streamName string) {
		var msg *nats.Msg
		checkFor(t, 3*time.Second, 200*time.Millisecond, func() error {
			msg, err = nc.Request(fmt.Sprintf(JSApiStreamInfoT, streamName), nil, time.Second)
			return err
		})
		var si JSApiStreamInfoResponse
		require_NoError(t, json.Unmarshal(msg.Data, &si))
		require_NotNil(t, si.Error)
		require_Error(t, si.Error, NewJSStreamOfflineReasonError(errors.New(offlineReason)))

		var sn JSApiStreamNamesResponse
		msg, err = nc.Request(JSApiStreams, nil, time.Second)
		require_NoError(t, err)
		require_NoError(t, json.Unmarshal(msg.Data, &sn))
		require_Len(t, len(sn.Streams), 1)
		require_Equal(t, sn.Streams[0], streamName)

		var sl JSApiStreamListResponse
		msg, err = nc.Request(JSApiStreamList, nil, 2*time.Second)
		require_NoError(t, err)
		require_NoError(t, json.Unmarshal(msg.Data, &sl))
		require_Len(t, len(sl.Streams), 0)
		require_Len(t, len(sl.Missing), 1)
		require_Equal(t, sl.Missing[0], streamName)
		require_Len(t, len(sl.Offline), 1)
		require_Equal(t, sl.Offline[streamName], offlineReason)
	}

	// Stream should be reported as offline, but healthz should report healthy to not block downgrades.
	expectStreamInfo(unsupported(math.MaxInt-1), "DowngradeStreamTest")
	health := ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)
	restart()
	expectStreamInfo(unsupported(math.MaxInt-1), "DowngradeStreamTest")
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)

	wsas := getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa := &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeStreamTest")
	require_Equal(t, nsa.Config.Metadata["_nats.req.level"], strconv.Itoa(math.MaxInt-1))

	// Update a stream that's unsupported.
	sjs.mu.Lock()
	scfg.Metadata = map[string]string{"_nats.req.level": strconv.Itoa(math.MaxInt)}
	err = cc.meta.Propose(encodeUpdateStreamAssignment(sa))
	sjs.mu.Unlock()
	require_NoError(t, err)
	c.waitOnAllCurrent()

	// Stream should be reported as offline, but healthz should report healthy to not block downgrades.
	expectStreamInfo(unsupported(math.MaxInt), "DowngradeStreamTest")
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)
	restart()
	expectStreamInfo(unsupported(math.MaxInt), "DowngradeStreamTest")
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)

	wsas = getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa = &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeStreamTest")
	require_Equal(t, nsa.Config.Metadata["_nats.req.level"], strconv.Itoa(math.MaxInt))

	// Deleting a stream should always work, even if it is unsupported.
	require_NoError(t, js.DeleteStream("DowngradeStreamTest"))
	snap, _, _, err := sjs.metaSnapshot()
	require_NoError(t, err)
	require_True(t, snap == nil)

	// Create a supported stream and consumer.
	_, err = js.AddStream(&nats.StreamConfig{Name: "DowngradeConsumerTest", Replicas: 3})
	require_NoError(t, err)
	_, err = js.AddConsumer("DowngradeConsumerTest", &nats.ConsumerConfig{Name: "consumer"})
	require_NoError(t, err)

	// Create a consumer that's unsupported.
	sjs.mu.Lock()
	ccfg := &ConsumerConfig{
		Name:     "DowngradeConsumerTest",
		Replicas: 3,
		Metadata: map[string]string{"_nats.req.level": strconv.Itoa(math.MaxInt - 1)},
	}
	rg = cc.createGroupForConsumer(ccfg, sa)
	ca := &consumerAssignment{
		Config:  ccfg,
		Group:   rg,
		Stream:  "DowngradeConsumerTest",
		Name:    "DowngradeConsumerTest",
		Created: time.Now().UTC(),
		Client:  ci,
	}
	err = cc.meta.Propose(encodeAddConsumerAssignment(ca))
	sjs.mu.Unlock()
	require_NoError(t, err)
	c.waitOnAllCurrent()

	expectConsumerInfo := func(offlineReason string) {
		var msg *nats.Msg
		checkFor(t, 3*time.Second, 200*time.Millisecond, func() error {
			msg, err = nc.Request(fmt.Sprintf(JSApiConsumerInfoT, "DowngradeConsumerTest", "DowngradeConsumerTest"), nil, time.Second)
			return err
		})
		var ci JSApiConsumerInfoResponse
		require_NoError(t, json.Unmarshal(msg.Data, &ci))
		require_NotNil(t, ci.Error)
		require_Error(t, ci.Error, NewJSConsumerOfflineReasonError(errors.New(offlineReason)))

		var cn JSApiConsumerNamesResponse
		msg, err = nc.Request(fmt.Sprintf(JSApiConsumersT, "DowngradeConsumerTest"), nil, time.Second)
		require_NoError(t, err)
		require_NoError(t, json.Unmarshal(msg.Data, &cn))
		require_Len(t, len(cn.Consumers), 2)
		require_Equal(t, cn.Consumers[0], "DowngradeConsumerTest")
		for _, name := range cn.Consumers {
			require_True(t, name == "consumer" || name == "DowngradeConsumerTest")
		}

		var cl JSApiConsumerListResponse
		msg, err = nc.Request(fmt.Sprintf(JSApiConsumerListT, "DowngradeConsumerTest"), nil, 5*time.Second)
		require_NoError(t, err)
		require_NoError(t, json.Unmarshal(msg.Data, &cl))
		require_Len(t, len(cl.Consumers), 0)
		require_Len(t, len(cl.Missing), 2)
		for _, name := range cl.Missing {
			require_True(t, name == "consumer" || name == "DowngradeConsumerTest")
		}
		require_Len(t, len(cl.Offline), 1)
		require_Equal(t, cl.Offline["DowngradeConsumerTest"], offlineReason)

		// Stream should also be reported as offline.
		// Specifically, as "stopped" because it's still supported, but can't run due to the unsupported consumer.
		expectStreamInfo("stopped - unsupported consumer \"DowngradeConsumerTest\"", "DowngradeConsumerTest")
	}

	// Consumer should be reported as offline, but healthz should report healthy to not block downgrades.
	expectConsumerInfo(unsupported(math.MaxInt - 1))
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)
	restart()
	expectConsumerInfo(unsupported(math.MaxInt - 1))
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)

	wsas = getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa = &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeConsumerTest")
	require_Equal(t, nsa.Config.Metadata["_nats.req.level"], "0")
	require_Len(t, len(wsas[0].Consumers), 2)
	for _, wca := range wsas[0].Consumers {
		nca := &consumerAssignment{ConfigJSON: wca.ConfigJSON}
		require_NoError(t, decodeConsumerAssignmentConfig(nca))
		if nca.Config.Name == "DowngradeConsumerTest" {
			require_Equal(t, nca.Config.Metadata["_nats.req.level"], strconv.Itoa(math.MaxInt-1))
		} else {
			require_Equal(t, nca.Config.Name, "consumer")
		}
	}

	// Update a consumer (with compressed data) that's unsupported.
	ccfg.Metadata = map[string]string{"_nats.req.level": strconv.Itoa(math.MaxInt)}
	sjs.mu.Lock()
	err = cc.meta.Propose(encodeAddConsumerAssignmentCompressed(ca))
	sjs.mu.Unlock()
	require_NoError(t, err)
	c.waitOnAllCurrent()

	// Consumer should be reported as offline, but healthz should report healthy to not block downgrades.
	expectConsumerInfo(unsupported(math.MaxInt))
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)
	restart()
	expectConsumerInfo(unsupported(math.MaxInt))
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)

	wsas = getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa = &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeConsumerTest")
	require_Equal(t, nsa.Config.Metadata["_nats.req.level"], "0")
	require_Len(t, len(wsas[0].Consumers), 2)
	for _, wca := range wsas[0].Consumers {
		nca := &consumerAssignment{ConfigJSON: wca.ConfigJSON}
		require_NoError(t, decodeConsumerAssignmentConfig(nca))
		if nca.Config.Name == "DowngradeConsumerTest" {
			require_Equal(t, nca.Config.Metadata["_nats.req.level"], strconv.Itoa(math.MaxInt))
		} else {
			require_Equal(t, nca.Config.Name, "consumer")
		}
	}

	// Deleting a consumer should always work, even if it is unsupported.
	require_NoError(t, js.DeleteConsumer("DowngradeConsumerTest", "DowngradeConsumerTest"))
	c.waitOnAllCurrent()

	wsas = getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa = &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeConsumerTest")
	require_Equal(t, nsa.Config.Metadata["_nats.req.level"], "0")
	require_Len(t, len(wsas[0].Consumers), 1)
	nca := &consumerAssignment{ConfigJSON: wsas[0].Consumers[0].ConfigJSON}
	require_NoError(t, decodeConsumerAssignmentConfig(nca))
	require_Equal(t, nca.Config.Name, "consumer")
}

func TestJetStreamClusterOfflineStreamAndConsumerAfterDowngrade(t *testing.T) {
	clusterName := "R3S"
	c := createJetStreamClusterExplicit(t, clusterName, 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	ml := c.leader()
	require_NotNil(t, ml)

	sjs := ml.getJetStream()
	require_NotNil(t, sjs)
	sjs.mu.Lock()
	cc := sjs.cluster
	if cc == nil || cc.meta == nil {
		sjs.mu.Unlock()
		t.Fatalf("Expected cluster to be initialized")
	}

	restart := func() {
		t.Helper()
		for _, s := range c.servers {
			sjs = s.getJetStream()
			snap, _, _, err := sjs.metaSnapshot()
			require_NoError(t, err)
			meta := sjs.getMetaGroup()
			meta.InstallSnapshot(snap, false)
		}

		c.stopAll()
		c.restartAllSamePorts()
		c.waitOnLeader()
		ml = c.leader()
		require_NotNil(t, ml)
		require_NoError(t, nc.ForceReconnect())

		sjs = ml.getJetStream()
		require_NotNil(t, sjs)
		sjs.mu.Lock()
		cc = sjs.cluster
		if cc == nil || cc.meta == nil {
			sjs.mu.Unlock()
			t.Fatalf("Expected cluster to be initialized")
		}
		sjs.mu.Unlock()
	}

	getValidMetaSnapshot := func() (wsas []writeableStreamAssignment) {
		t.Helper()
		snap, _, _, err := sjs.metaSnapshot()
		require_NoError(t, err)
		require_True(t, len(snap) > 0)
		dec, err := s2.Decode(nil, snap)
		require_NoError(t, err)
		require_NoError(t, json.Unmarshal(dec, &wsas))
		return wsas
	}

	// Create a stream that's unsupported.
	ci := &ClientInfo{
		Account: globalAccountName,
		Cluster: clusterName,
	}
	scfg := &StreamConfig{
		Name:     "DowngradeStreamTest",
		Storage:  FileStorage,
		Replicas: 3,
	}
	rg, perr := sjs.createGroupForStream(ci, scfg)
	if perr != nil {
		sjs.mu.Unlock()
		require_NoError(t, perr)
	}
	sa := &streamAssignment{
		Config:  scfg,
		Sync:    syncSubjForStream(),
		Group:   rg,
		Created: time.Now().UTC(),
		Client:  ci,
	}
	err := cc.meta.Propose(encodeAddStreamAssignment(sa))
	sjs.mu.Unlock()
	require_NoError(t, err)
	c.waitOnStreamLeader(globalAccountName, "DowngradeStreamTest")

	expectStreamInfo := func(offline bool) {
		if !offline {
			c.waitOnStreamLeader(globalAccountName, "DowngradeStreamTest")
		}
		var msg *nats.Msg
		checkFor(t, 3*time.Second, 200*time.Millisecond, func() error {
			msg, err = nc.Request(fmt.Sprintf(JSApiStreamInfoT, "DowngradeStreamTest"), nil, time.Second)
			return err
		})
		var si JSApiStreamInfoResponse
		require_NoError(t, json.Unmarshal(msg.Data, &si))
		if !offline {
			require_True(t, si.Error == nil)
		} else {
			require_NotNil(t, si.Error)
			require_Contains(t, si.Error.Error(), "stream is offline", "unsupported", "required API level")
		}
	}

	// Stream is still supported, so it should be available and healthz should report healthy.
	expectStreamInfo(false)
	health := ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)
	restart()
	expectStreamInfo(false)
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)

	wsas := getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa := &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeStreamTest")

	// Update a stream to be unsupported.
	sjs.mu.Lock()
	scfg.Metadata = map[string]string{"_nats.req.level": strconv.Itoa(math.MaxInt)}
	err = cc.meta.Propose(encodeUpdateStreamAssignment(sa))
	sjs.mu.Unlock()
	require_NoError(t, err)
	c.waitOnAllCurrent()

	// Stream should be reported as offline, but healthz should report healthy to not block downgrades.
	expectStreamInfo(true)
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)
	restart()
	expectStreamInfo(true)
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)

	wsas = getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa = &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeStreamTest")

	// Deleting a stream should always work, even if it is unsupported.
	require_NoError(t, js.DeleteStream("DowngradeStreamTest"))
	snap, _, _, err := sjs.metaSnapshot()
	require_NoError(t, err)
	require_True(t, snap == nil)

	// Create a supported stream and consumer.
	_, err = js.AddStream(&nats.StreamConfig{Name: "DowngradeConsumerTest", Replicas: 3})
	require_NoError(t, err)

	sjs.mu.Lock()
	ccfg := &ConsumerConfig{
		Name:       "DowngradeConsumerTest",
		Replicas:   3,
		MaxWaiting: JSWaitQueueDefaultMax,
	}
	rg = cc.createGroupForConsumer(ccfg, sa)
	ca := &consumerAssignment{
		Config:  ccfg,
		Group:   rg,
		Stream:  "DowngradeConsumerTest",
		Name:    "DowngradeConsumerTest",
		Created: time.Now().UTC(),
		Client:  ci,
	}
	err = cc.meta.Propose(encodeAddConsumerAssignment(ca))
	sjs.mu.Unlock()
	require_NoError(t, err)
	c.waitOnConsumerLeader(globalAccountName, "DowngradeConsumerTest", "DowngradeConsumerTest")

	expectConsumerInfo := func(offline bool) {
		if !offline {
			c.waitOnConsumerLeader(globalAccountName, "DowngradeConsumerTest", "DowngradeConsumerTest")
		}
		var msg *nats.Msg
		checkFor(t, 3*time.Second, 200*time.Millisecond, func() error {
			msg, err = nc.Request(fmt.Sprintf(JSApiConsumerInfoT, "DowngradeConsumerTest", "DowngradeConsumerTest"), nil, 2*time.Second)
			return err
		})
		var ci JSApiConsumerInfoResponse
		require_NoError(t, json.Unmarshal(msg.Data, &ci))
		if !offline {
			require_True(t, ci.Error == nil)
		} else {
			require_NotNil(t, ci.Error)
			require_Contains(t, ci.Error.Error(), "consumer is offline", "unsupported", "required API level")
		}
	}

	// Consumer is still supported, so it should be available and healthz should report healthy.
	expectConsumerInfo(false)
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)
	restart()
	expectConsumerInfo(false)
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)

	wsas = getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa = &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeConsumerTest")
	require_Len(t, len(wsas[0].Consumers), 1)

	// Update a consumer to be unsupported.
	ccfg.Metadata = map[string]string{"_nats.req.level": strconv.Itoa(math.MaxInt)}
	sjs.mu.Lock()
	err = cc.meta.Propose(encodeAddConsumerAssignment(ca))
	sjs.mu.Unlock()
	require_NoError(t, err)
	c.waitOnAllCurrent()

	// Consumer should be reported as offline, but healthz should report healthy to not block downgrades.
	expectConsumerInfo(true)
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)
	restart()
	expectConsumerInfo(true)
	health = ml.healthz(&HealthzOptions{})
	require_Equal(t, health.StatusCode, 200)

	wsas = getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa = &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeConsumerTest")
	require_Equal(t, nsa.Config.Metadata["_nats.req.level"], "0")
	require_Len(t, len(wsas[0].Consumers), 1)
	nca := &consumerAssignment{ConfigJSON: wsas[0].Consumers[0].ConfigJSON}
	require_NoError(t, decodeConsumerAssignmentConfig(nca))
	require_Equal(t, nca.Config.Metadata["_nats.req.level"], strconv.Itoa(math.MaxInt))
}

func TestJetStreamClusterOfflineStreamAndConsumerUpdate(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{Name: "DowngradeTest", Replicas: 3})
	require_NoError(t, err)
	_, err = js.AddConsumer("DowngradeTest", &nats.ConsumerConfig{Durable: "D", Replicas: 3})
	require_NoError(t, err)
	c.waitOnAllCurrent()

	ml := c.leader()
	require_NotNil(t, ml)
	sjs := ml.getJetStream()
	require_NotNil(t, sjs)

	var sa *streamAssignment
	var ca *consumerAssignment
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		sjs.mu.Lock()
		defer sjs.mu.Unlock()
		sa = sjs.streamAssignment(globalAccountName, "DowngradeTest")
		if sa == nil {
			return errors.New("stream assignment missing")
		}
		ca = sjs.consumerAssignment(globalAccountName, "DowngradeTest", "D")
		if ca == nil {
			return errors.New("consumer assignment missing")
		}
		return nil
	})

	getValidMetaSnapshot := func() (wsas []writeableStreamAssignment) {
		t.Helper()
		snap, _, _, err := sjs.metaSnapshot()
		require_NoError(t, err)
		require_True(t, len(snap) > 0)
		dec, err := s2.Decode(nil, snap)
		require_NoError(t, err)
		require_NoError(t, json.Unmarshal(dec, &wsas))
		return wsas
	}

	wsas := getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa := &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeTest")
	require_Equal(t, nsa.Config.Metadata["_nats.req.level"], "0")
	require_Len(t, len(wsas[0].Consumers), 1)
	nca := &consumerAssignment{ConfigJSON: wsas[0].Consumers[0].ConfigJSON}
	require_NoError(t, decodeConsumerAssignmentConfig(nca))
	require_Equal(t, wsas[0].Consumers[0].Name, "D")
	require_Equal(t, nca.Config.Metadata["_nats.req.level"], "0")

	// Update a consumer to be unsupported.
	sjs.mu.Lock()
	ca.Config.Metadata = map[string]string{"_nats.req.level": strconv.Itoa(math.MaxInt)}
	err = sjs.cluster.meta.Propose(encodeAddConsumerAssignment(ca))
	sjs.mu.Unlock()
	require_NoError(t, err)

	// Update the stream to be unsupported.
	sjs.mu.Lock()
	sa.Config.Metadata = map[string]string{"_nats.req.level": strconv.Itoa(math.MaxInt)}
	err = sjs.cluster.meta.Propose(encodeUpdateStreamAssignment(sa))
	sjs.mu.Unlock()
	require_NoError(t, err)
	c.waitOnAllCurrent()

	wsas = getValidMetaSnapshot()
	require_Len(t, len(wsas), 1)
	nsa = &streamAssignment{ConfigJSON: wsas[0].ConfigJSON}
	require_NoError(t, decodeStreamAssignmentConfig(ml, nsa))
	require_Equal(t, nsa.Config.Name, "DowngradeTest")
	require_Equal(t, nsa.Config.Metadata["_nats.req.level"], strconv.Itoa(math.MaxInt))
	require_Len(t, len(wsas[0].Consumers), 1)
	nca = &consumerAssignment{ConfigJSON: wsas[0].Consumers[0].ConfigJSON}
	require_NoError(t, decodeConsumerAssignmentConfig(nca))
	require_Equal(t, wsas[0].Consumers[0].Name, "D")
	require_Equal(t, nca.Config.Metadata["_nats.req.level"], strconv.Itoa(math.MaxInt))
}

func TestJetStreamClusterOfflineStreamAndConsumerStrictDecoding(t *testing.T) {
	s := RunBasicJetStreamServer(t)
	defer s.Shutdown()

	unsupportedJson := []byte("{\"unknown\": true}")
	unsupportedStreamJson := []byte(fmt.Sprintf("{\"stream\":%s}", unsupportedJson))
	unsupportedConsumerJson := []byte(fmt.Sprintf("{\"consumer\":%s}", unsupportedJson))

	sa, err := decodeStreamAssignment(s, unsupportedStreamJson)
	require_NoError(t, err)
	require_True(t, bytes.Equal(sa.ConfigJSON, unsupportedJson))
	require_True(t, sa.unsupported != nil)

	ca, err := decodeConsumerAssignment(unsupportedConsumerJson)
	require_NoError(t, err)
	require_True(t, bytes.Equal(ca.ConfigJSON, unsupportedJson))
	require_True(t, ca.unsupported != nil)

	var bb bytes.Buffer
	s2e := s2.NewWriter(&bb)
	_, err = s2e.Write(unsupportedConsumerJson)
	require_NoError(t, err)
	require_NoError(t, s2e.Close())
	ca, err = decodeConsumerAssignmentCompressed(bb.Bytes())
	require_NoError(t, err)
	require_True(t, bytes.Equal(ca.ConfigJSON, unsupportedJson))
	require_True(t, ca.unsupported != nil)
}

func TestJetStreamClusterStreamMonitorShutdownWithoutRaftNode(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		for _, s := range c.servers {
			if !s.JetStreamIsStreamAssigned(globalAccountName, "TEST") {
				return fmt.Errorf("stream not assigned on %s", s.Name())
			}
		}
		return nil
	})

	var nodes []RaftNode
	for _, s := range c.servers {
		mset, err := s.globalAccount().lookupStream("TEST")
		require_NoError(t, err)
		// Manually nil-out the node. This shouldn't happen normally,
		// but tests we can shut down purely with the monitor goroutine quit channel.
		mset.mu.Lock()
		n := mset.node
		mset.node = nil
		mset.mu.Unlock()
		require_NotNil(t, n)
		nodes = append(nodes, n)
	}
	for _, n := range nodes {
		require_NotEqual(t, n.State(), Closed)
	}

	require_NoError(t, js.DeleteStream("TEST"))
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		for _, n := range nodes {
			if state := n.State(); state != Closed {
				return fmt.Errorf("node not closed on %s: %s", n.ID(), state.String())
			}
		}
		return nil
	})
}

func TestJetStreamClusterConsumerMonitorShutdownWithoutRaftNode(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)
	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:  "DURABLE",
		Replicas: 3,
	})
	require_NoError(t, err)
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		for _, s := range c.servers {
			_, cc := s.getJetStreamCluster()
			if !cc.isConsumerAssigned(s.globalAccount(), "TEST", "DURABLE") {
				return fmt.Errorf("consumer not assigned on %s", s.Name())
			}
		}
		return nil
	})

	var nodes []RaftNode
	for _, s := range c.servers {
		mset, err := s.globalAccount().lookupStream("TEST")
		require_NoError(t, err)
		o := mset.lookupConsumer("DURABLE")
		require_NotNil(t, o)
		// Manually nil-out the node. This shouldn't happen normally,
		// but tests we can shut down purely with the monitor goroutine quit channel.
		o.mu.Lock()
		n := o.node
		o.node = nil
		o.mu.Unlock()
		require_NotNil(t, n)
		nodes = append(nodes, n)
	}
	for _, n := range nodes {
		require_NotEqual(t, n.State(), Closed)
	}

	require_NoError(t, js.DeleteStream("TEST"))
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		for _, n := range nodes {
			if state := n.State(); state != Closed {
				return fmt.Errorf("node not closed on %s: %s", n.ID(), state.String())
			}
		}
		return nil
	})
}

func TestJetStreamClusterUnsetEmptyPlacement(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	cfg := &nats.StreamConfig{
		Name:      "TEST",
		Subjects:  []string{"foo"},
		Placement: &nats.Placement{},
	}
	si, err := js.AddStream(cfg)
	require_NoError(t, err)
	require_True(t, si.Config.Placement == nil)

	si, err = js.UpdateStream(cfg)
	require_NoError(t, err)
	require_True(t, si.Config.Placement == nil)

	// Set a placement level
	cfg.Placement = &nats.Placement{Cluster: "R3S"}
	si, err = js.UpdateStream(cfg)
	require_NoError(t, err)
	require_True(t, si.Config.Placement != nil)
	require_Equal(t, si.Config.Placement.Cluster, "R3S")

	// And ensure it can be reset.
	cfg.Placement = &nats.Placement{}
	si, err = js.UpdateStream(cfg)
	require_NoError(t, err)
	require_True(t, si.Config.Placement == nil)
}

func TestJetStreamClusterPersistModeAsync(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc := clientConnectToServer(t, c.randomServer())
	defer nc.Close()

	cfg := &StreamConfig{
		Name:        "TEST",
		Subjects:    []string{"foo"},
		Storage:     FileStorage,
		Replicas:    3,
		PersistMode: AsyncPersistMode,
	}
	_, err := jsStreamCreate(t, nc, cfg)
	require_Error(t, err, NewJSStreamInvalidConfigError(fmt.Errorf("async persist mode is not supported on replicated streams")))
}

func TestJetStreamClusterDeleteMsgEOF(t *testing.T) {
	for _, replicas := range []int{1, 3} {
		t.Run(fmt.Sprintf("R%d", replicas), func(t *testing.T) {
			c := createJetStreamClusterExplicit(t, "R3S", 3)
			defer c.shutdown()

			nc, js := jsClientConnect(t, c.randomServer())
			defer nc.Close()

			_, err := js.AddStream(&nats.StreamConfig{
				Name:     "TEST",
				Subjects: []string{"foo"},
				Replicas: replicas,
			})
			require_NoError(t, err)

			_, err = js.Publish("foo", nil)
			require_NoError(t, err)

			require_Error(t, js.DeleteMsg("TEST", 0), NewJSNoMessageFoundError())
			require_NoError(t, js.DeleteMsg("TEST", 1))
			require_Error(t, js.DeleteMsg("TEST", 1), NewJSNoMessageFoundError())
			require_Error(t, js.DeleteMsg("TEST", 2), NewJSStreamMsgDeleteFailedError(ErrStoreEOF))
		})
	}
}

func TestJetStreamClusterCatchupSkipMsgDesync(t *testing.T) {
	for _, storage := range []nats.StorageType{nats.FileStorage, nats.MemoryStorage} {
		t.Run(storage.String(), func(t *testing.T) {
			c := createJetStreamClusterExplicit(t, "R3S", 3)
			defer c.shutdown()

			nc, js := jsClientConnect(t, c.randomServer())
			defer nc.Close()

			_, err := js.AddStream(&nats.StreamConfig{
				Name:     "TEST",
				Subjects: []string{"foo"},
				Storage:  storage,
				Replicas: 3,
			})
			require_NoError(t, err)
			checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
				return checkState(t, c, globalAccountName, "TEST")
			})

			rs := c.randomNonStreamLeader(globalAccountName, "TEST")
			mset, err := rs.globalAccount().lookupStream("TEST")
			require_NoError(t, err)
			sa := mset.streamAssignment()

			// Make sure this server can't become the leader.
			n := mset.raftNode().(*raft)
			n.SetObserver(true)

			sysNc, err := nats.Connect(rs.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
			require_NoError(t, err)
			defer sysNc.Close()

			sjs := rs.getJetStream()
			sjs.mu.RLock()
			syncSubj := sa.Sync
			sjs.mu.RUnlock()

			// Respond to the catchup with an out-of-order SkipMsg.
			var eof bool
			sub, err := sysNc.Subscribe(syncSubj, func(msg *nats.Msg) {
				if !eof {
					msg.Respond(encodeStreamMsg(_EMPTY_, _EMPTY_, nil, nil, 10, 0, false))
					eof = true
				}
				msg.Respond(nil)
			})
			require_NoError(t, err)
			defer sub.Drain()
			require_NoError(t, sysNc.Flush()) // Must flush, otherwise our subscription could be too late.

			err = mset.processSnapshot(&StreamReplicatedState{FirstSeq: 1, LastSeq: 1}, 1)
			require_Error(t, err, errCatchupTooManyRetries)
			c.waitOnStreamLeader(globalAccountName, "TEST")

			pubAck, err := js.Publish("foo", nil)
			require_NoError(t, err)
			require_Equal(t, pubAck.Sequence, 1)
			checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
				return checkState(t, c, globalAccountName, "TEST")
			})
		})
	}
}

func TestJetStreamClusterJszRaftLeaderReporting(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{
		Durable:  "DURABLE",
		Replicas: 3,
	})
	require_NoError(t, err)

	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		for _, s := range c.servers {
			_, _, jsa := s.globalAccount().getJetStreamFromAccount()
			if !jsa.streamAssigned("TEST") {
				return fmt.Errorf("stream not assigned on %s", s.Name())
			}
			if !jsa.consumerAssigned("TEST", "DURABLE") {
				return fmt.Errorf("consumer not assigned on %s", s.Name())
			}
		}
		return nil
	})

	sl := c.streamLeader(globalAccountName, "TEST")
	cl := c.consumerLeader(globalAccountName, "TEST", "DURABLE")

	for _, s := range c.servers {
		jsi, err := s.Jsz(&JSzOptions{RaftGroups: true})
		require_NoError(t, err)
		if s == sl {
			require_Equal(t, jsi.StreamsLeader, 1)
		} else {
			require_Equal(t, jsi.StreamsLeader, 0)
		}
		if s == cl {
			require_Equal(t, jsi.ConsumersLeader, 1)
		} else {
			require_Equal(t, jsi.ConsumersLeader, 0)
		}
	}
}

func TestJetStreamClusterNoInterestDesyncOnConsumerCreate(t *testing.T) {
	test := func(t *testing.T, twoConsumers bool) {
		c := createJetStreamClusterExplicit(t, "R3S", 3)
		defer c.shutdown()

		nc, js := jsClientConnect(t, c.randomServer())
		defer nc.Close()

		_, err := js.AddStream(&nats.StreamConfig{
			Name:      "TEST",
			Subjects:  []string{"foo", "bar"},
			Replicas:  3,
			Retention: nats.InterestPolicy,
		})
		require_NoError(t, err)

		// Pick a random server that will not know about the new consumer being created.
		// If servers determine "no interest" individually, these servers will desync.
		rs := c.randomNonLeader()
		sjs := rs.getJetStream()
		meta := sjs.getMetaGroup()
		require_NoError(t, meta.PauseApply())

		sub, err := js.PullSubscribe("foo", "DURABLE", nats.BindStream("TEST"))
		require_NoError(t, err)
		defer sub.Drain()

		checkConsumersAssigned := func(expected int) {
			t.Helper()
			checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
				var count int
				for _, s := range c.servers {
					mset, err := s.globalAccount().lookupStream("TEST")
					if err != nil {
						return err
					}
					count += mset.numConsumers()
				}
				if count != expected {
					return fmt.Errorf("expected %d, got %d", expected, count)
				}
				return nil
			})
		}
		// Confirm only two servers know about the consumer.
		checkConsumersAssigned(2)
		c.waitOnConsumerLeader(globalAccountName, "TEST", "DURABLE")

		// Publish a single message. All servers will receive this, but only two will store it.
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
		checkLastSeq := func(lseq uint64) {
			t.Helper()
			checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
				for _, s := range c.servers {
					mset, err := s.globalAccount().lookupStream("TEST")
					if err != nil {
						return err
					}
					if seq := mset.lastSeq(); seq != lseq {
						return fmt.Errorf("expected %d, got %d", lseq, seq)
					}
				}
				return nil
			})
		}
		checkLastSeq(1)

		// Resume the meta layer such that the consumer gets created on the remaining server.
		meta.ResumeApply()
		checkConsumersAssigned(3)

		if twoConsumers {
			_, err = js.AddConsumer("TEST", &nats.ConsumerConfig{Durable: "DURABLE2", FilterSubject: "bar"})
			require_NoError(t, err)
			checkConsumersAssigned(6)
		}

		// All servers will now store another published message.
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)
		checkLastSeq(2)

		// Make sure the consumer leader is on the same server that didn't store the first message.
		cl := c.consumerLeader(globalAccountName, "TEST", "DURABLE")
		if cl != rs {
			mset, err := cl.globalAccount().lookupStream("TEST")
			require_NoError(t, err)
			o := mset.lookupConsumer("DURABLE")
			require_NotNil(t, o)
			n := o.raftNode()
			require_NoError(t, n.StepDown(rs.NodeName()))
			c.waitOnConsumerLeader(globalAccountName, "TEST", "DURABLE")
			cl = c.consumerLeader(globalAccountName, "TEST", "DURABLE")
			require_Equal(t, cl, rs)
		}

		// Since the consumer leader is the same as the server that didn't store the first message,
		// it can only receive and ack the second message.
		msgs, err := sub.Fetch(1, nats.MaxWait(time.Second))
		require_NoError(t, err)
		require_Len(t, len(msgs), 1)
		metadata, err := msgs[0].Metadata()
		require_NoError(t, err)
		require_Equal(t, metadata.Sequence.Stream, 2)
		require_Equal(t, metadata.NumPending, 0)
		require_NoError(t, msgs[0].AckSync())

		if twoConsumers {
			require_NoError(t, js.DeleteConsumer("TEST", "DURABLE"))
		}

		checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
			// The servers will eventually be synced up again, but this relies on the interest state being checked.
			for _, s := range c.servers {
				if s == rs {
					continue
				}
				mset, err := s.globalAccount().lookupStream("TEST")
				if err != nil {
					return err
				}
				mset.checkInterestState()
			}
			return checkState(t, c, globalAccountName, "TEST")
		})
	}
	t.Run("OneConsumer", func(t *testing.T) { test(t, false) })
	t.Run("TwoConsumers", func(t *testing.T) { test(t, true) })
}

func TestJetStreamClusterRaftCatchupSignalsMetaRecovery(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	rs := c.randomNonLeader()
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		if !rs.JetStreamIsStreamAssigned(globalAccountName, "TEST") {
			return errors.New("not assigned")
		}
		return nil
	})

	sjs := rs.getJetStream()
	sjs.mu.Lock()
	sa := sjs.streamAssignment(globalAccountName, "TEST")
	encodedDelete := encodeDeleteStreamAssignment(sa)
	sjs.mu.Unlock()
	meta := sjs.getMetaGroup().(*raft)
	apply := meta.ApplyQ()

	// Should not panic if we receive a nil entry "randomly".
	_, err = apply.push(nil)
	require_NoError(t, err)

	// Should put us in upper-layer recovery mode.
	// For this test using two large values so we remain in catchup.
	meta.Lock()
	meta.createCatchup(&appendEntry{pterm: 100, pindex: 100})
	meta.sendCatchupSignal()
	meta.Unlock()

	// Deleting a stream should be staged, not immediately performed.
	_, err = apply.push(newCommittedEntry(1, []*Entry{{EntryNormal, encodedDelete}}))
	require_NoError(t, err)
	time.Sleep(200 * time.Millisecond)
	require_True(t, rs.JetStreamIsStreamAssigned(globalAccountName, "TEST"))

	// Canceling the catchup, because it's completed, should result in the staged changes to be applied.
	meta.Lock()
	meta.cancelCatchup()
	meta.Unlock()
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		if rs.JetStreamIsStreamAssigned(globalAccountName, "TEST") {
			return errors.New("still assigned")
		}
		return nil
	})
}

func TestJetStreamClusterRaftCatchupSignalsMetaRecoveryRecreateStream(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
		Storage:  nats.FileStorage,
	})
	require_NoError(t, err)

	rs := c.randomNonLeader()
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		if !rs.JetStreamIsStreamAssigned(globalAccountName, "TEST") {
			return errors.New("not assigned")
		}
		return nil
	})

	sjs := rs.getJetStream()
	sjs.mu.Lock()
	sa := sjs.streamAssignment(globalAccountName, "TEST")
	encodedDelete := encodeDeleteStreamAssignment(sa)
	sa.Config.Storage = MemoryStorage
	encodedAdd := encodeAddStreamAssignment(sa)
	encodedUpdate := encodeUpdateStreamAssignment(sa)
	sjs.mu.Unlock()
	meta := sjs.getMetaGroup().(*raft)
	apply := meta.ApplyQ()

	// Should not panic if we receive a nil entry "randomly".
	_, err = apply.push(nil)
	require_NoError(t, err)

	// Should put us in upper-layer recovery mode.
	// For this test using two large values so we remain in catchup.
	meta.Lock()
	meta.createCatchup(&appendEntry{pterm: 100, pindex: 100})
	meta.Unlock()

	// Deleting a stream should be staged, not immediately performed.
	_, err = apply.push(newCommittedEntry(1, []*Entry{
		{EntryNormal, encodedDelete},
		{EntryNormal, encodedAdd},
		{EntryNormal, encodedUpdate},
	}))
	require_NoError(t, err)

	// Canceling the catchup, because it's completed, should result in the staged changes to be applied.
	meta.Lock()
	meta.cancelCatchup()
	meta.Unlock()
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		mset, err := rs.globalAccount().lookupStream("TEST")
		if err != nil {
			return err
		}
		cfg := mset.config()
		if cfg.Storage != MemoryStorage {
			return errors.New("still the old stream config")
		}
		return nil
	})
}

func TestJetStreamClusterRaftCatchupSignalsMetaRecoveryRecreateConsumer(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R3S", 3)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	sub, err := js.PullSubscribe(_EMPTY_, "CONSUMER", nats.ConsumerReplicas(3), nats.BindStream("TEST"))
	require_NoError(t, err)
	defer sub.Drain()

	_, err = js.Publish("foo", nil)
	require_NoError(t, err)

	msgs, err := sub.Fetch(1)
	require_NoError(t, err)
	require_Len(t, len(msgs), 1)
	require_NoError(t, msgs[0].AckSync())

	rs := c.randomNonLeader()
	sjs := rs.getJetStream()
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		mset, err := rs.globalAccount().lookupStream("TEST")
		if err != nil {
			return err
		}
		o := mset.lookupConsumer("CONSUMER")
		if o == nil {
			return errors.New("not found")
		}
		state, err := o.store.State()
		if err != nil {
			return err
		}
		if state.Delivered.Stream != 1 || state.AckFloor.Stream != 1 {
			return errors.New("incorrect delivered/ack state")
		}
		return nil
	})

	sjs.mu.Lock()
	ca := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
	encodedDelete := encodeDeleteConsumerAssignment(ca)
	cca := ca.copyGroup()
	cca.Group.Peers = []string{rs.NodeName()}
	cca.Group.Name = groupNameForConsumer(cca.Group.Peers, FileStorage)
	encodedAdd := encodeAddConsumerAssignment(cca)
	sjs.mu.Unlock()
	meta := sjs.getMetaGroup().(*raft)
	apply := meta.ApplyQ()

	// Should not panic if we receive a nil entry "randomly".
	_, err = apply.push(nil)
	require_NoError(t, err)

	// Should put us in upper-layer recovery mode.
	// For this test using two large values so we remain in catchup.
	meta.Lock()
	meta.createCatchup(&appendEntry{pterm: 100, pindex: 100})
	meta.Unlock()

	// Deleting a consumer should be staged, not immediately performed.
	_, err = apply.push(newCommittedEntry(1, []*Entry{
		{EntryNormal, encodedDelete},
		{EntryNormal, encodedAdd},
	}))
	require_NoError(t, err)

	// Canceling the catchup, because it's completed, should result in the staged changes to be applied.
	meta.Lock()
	meta.cancelCatchup()
	meta.Unlock()
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		mset, err := rs.globalAccount().lookupStream("TEST")
		if err != nil {
			return err
		}
		o := mset.lookupConsumer("CONSUMER")
		if o == nil {
			return errors.New("not found")
		}
		state, err := o.store.State()
		if err != nil {
			return err
		}
		if state.Delivered.Stream != 0 || state.AckFloor.Stream != 0 {
			return errors.New("old delivered/ack state")
		}
		return nil
	})
}

func TestJetStreamClusterMetaRecoveryRecreateStream(t *testing.T) {
	test := func(t *testing.T, newStream bool) {
		c := createJetStreamClusterExplicit(t, "R3S", 3)
		defer c.shutdown()

		nc, js := jsClientConnect(t, c.randomServer())
		defer nc.Close()

		// Create a stream containing 5 messages.
		cfg := &nats.StreamConfig{
			Name:     "TEST",
			Subjects: []string{"foo"},
			Replicas: 3,
		}
		_, err := js.AddStream(cfg)
		require_NoError(t, err)
		for range 5 {
			_, err = js.Publish("foo", nil)
			require_NoError(t, err)
		}

		// Select a server that's neither meta nor stream leader.
		var rs *Server
		for _, s := range c.servers {
			if s.JetStreamIsLeader() || s.JetStreamIsStreamLeader(globalAccountName, "TEST") {
				continue
			}
			rs = s
		}

		// When testing old stream state is cleaned up, pause the meta layer now.
		// Otherwise, we'll pause after creating the new stream.
		meta := rs.getJetStream().getMetaGroup()
		if !newStream {
			require_NoError(t, meta.PauseApply())
		}

		// Delete the stream.
		require_NoError(t, js.DeleteStream("TEST"))

		if newStream {
			// Need to wait for the stream to be deleted on our selected server.
			checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
				if rs.JetStreamIsStreamAssigned(globalAccountName, "TEST") {
					return errors.New("still assigned")
				}
				return nil
			})
		}

		// Recreate the stream with a single message.
		_, err = js.AddStream(cfg)
		require_NoError(t, err)
		_, err = js.Publish("foo", nil)
		require_NoError(t, err)

		if newStream {
			// Wait for the state to be the new state, then pause the meta layer.
			checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
				return checkState(t, c, globalAccountName, "TEST")
			})
			require_NoError(t, meta.PauseApply())

			// Also create a snapshot so that the stream can't "cheat" by replaying Raft entries.
			mset, err := rs.globalAccount().lookupStream("TEST")
			require_NoError(t, err)
			streamNode := mset.raftNode()
			require_NoError(t, streamNode.InstallSnapshot(mset.stateSnapshot(), false))
		}

		// Although the meta node is paused, we add one more meta entry to move the commit up one more.
		_, err = js.AddStream(&nats.StreamConfig{Name: "RANDOM", Replicas: 3})
		require_NoError(t, err)

		// We'll now restart this server.
		meta.Stop()
		meta.WaitForStop()
		rs.Shutdown()

		if newStream {
			// If we're testing new state, make sure we can't recover from our peers.
			for _, s := range c.servers {
				if s == rs {
					continue
				}
				mset, err := s.globalAccount().lookupStream("TEST")
				require_NoError(t, err)
				n := mset.raftNode()
				n.SetObserver(true)
				n.StepDown()
			}
		}
		rs = c.restartServer(rs)
		c.waitOnServerCurrent(rs)

		// We should arrive at the new state.
		checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
			mset, err := rs.globalAccount().lookupStream("TEST")
			if err != nil {
				return err
			}
			state := mset.state()
			if state.Msgs == 1 {
				return nil
			}
			return fmt.Errorf("incorrect amount of messages: %d", state.Msgs)
		})
	}

	t.Run("OldStream", func(t *testing.T) { test(t, false) })
	t.Run("NewStream", func(t *testing.T) { test(t, true) })
}

func TestJetStreamClusterMetaRecoveryRecreateConsumer(t *testing.T) {
	test := func(t *testing.T, newConsumer bool) {
		c := createJetStreamClusterExplicit(t, "R3S", 3)
		defer c.shutdown()

		nc, js := jsClientConnect(t, c.randomServer())
		defer nc.Close()

		// Create a stream containing 5 messages.
		_, err := js.AddStream(&nats.StreamConfig{
			Name:     "TEST",
			Subjects: []string{"foo"},
			Replicas: 3,
		})
		require_NoError(t, err)
		for range 5 {
			_, err = js.Publish("foo", nil)
			require_NoError(t, err)
		}

		// Create a consumer and ack all 5 messages.
		cfg := &nats.ConsumerConfig{
			Durable:   "CONSUMER",
			Replicas:  3,
			AckPolicy: nats.AckExplicitPolicy,
		}
		_, err = js.AddConsumer("TEST", cfg)
		require_NoError(t, err)

		sub, err := js.PullSubscribe(_EMPTY_, "CONSUMER", nats.Bind("TEST", "CONSUMER"))
		require_NoError(t, err)
		defer sub.Drain()

		msgs, err := sub.Fetch(5)
		require_NoError(t, err)
		require_Len(t, len(msgs), 5)
		for _, msg := range msgs {
			require_NoError(t, msg.AckSync())
		}

		// Select a server that's neither meta nor consumer leader.
		var rs *Server
		for _, s := range c.servers {
			if s.JetStreamIsLeader() || s.JetStreamIsConsumerLeader(globalAccountName, "TEST", "CONSUMER") {
				continue
			}
			rs = s
		}

		// When testing old consumer state is cleaned up, pause the meta layer now.
		// Otherwise, we'll pause after creating the new consumer.
		meta := rs.getJetStream().getMetaGroup()
		if !newConsumer {
			require_NoError(t, meta.PauseApply())
		}

		// Delete the consumer.
		sub.Drain()
		require_NoError(t, js.DeleteConsumer("TEST", "CONSUMER"))

		if newConsumer {
			// Need to wait for the consumer to be deleted on our selected server.
			checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
				sjs := rs.getJetStream()
				sjs.mu.RLock()
				ca := sjs.consumerAssignment(globalAccountName, "TEST", "CONSUMER")
				sjs.mu.RUnlock()
				if ca != nil {
					return errors.New("still assigned")
				}
				return nil
			})
		}

		// Recreate the consumer and ack a single message.
		_, err = js.AddConsumer("TEST", cfg)
		require_NoError(t, err)

		sub, err = js.PullSubscribe(_EMPTY_, "CONSUMER", nats.Bind("TEST", "CONSUMER"))
		require_NoError(t, err)
		defer sub.Drain()

		msgs, err = sub.Fetch(1)
		require_NoError(t, err)
		require_Len(t, len(msgs), 1)
		require_NoError(t, msgs[0].AckSync())

		if newConsumer {
			// Wait for the state to be the new state, then pause the meta layer.
			checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
				for _, s := range c.servers {
					mset, err := s.globalAccount().lookupStream("TEST")
					if err != nil {
						return err
					}
					o := mset.lookupConsumer("CONSUMER")
					if o == nil {
						return errors.New("consumer not found")
					}
					state, err := o.store.State()
					if err != nil {
						return err
					}
					if state.Delivered.Stream != 1 || state.AckFloor.Stream != 1 {
						return errors.New("incorrect delivered/ack state")
					}
				}
				return nil
			})
			require_NoError(t, meta.PauseApply())
		}

		// Although the meta node is paused, we add one more meta entry to move the commit up one more.
		_, err = js.AddStream(&nats.StreamConfig{Name: "RANDOM", Replicas: 3})
		require_NoError(t, err)

		// We'll now restart this server.
		sub.Drain()
		meta.Stop()
		meta.WaitForStop()
		rs.Shutdown()

		if newConsumer {
			// If we're testing new state, make sure we can't recover from our peers.
			for _, s := range c.servers {
				if s == rs {
					continue
				}
				mset, err := s.globalAccount().lookupStream("TEST")
				require_NoError(t, err)
				o := mset.lookupConsumer("CONSUMER")
				require_NotNil(t, o)
				n := o.raftNode()
				n.SetObserver(true)
				n.StepDown()
			}
		}
		rs = c.restartServer(rs)
		c.waitOnServerCurrent(rs)

		// We should arrive at the new state.
		checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
			mset, err := rs.globalAccount().lookupStream("TEST")
			if err != nil {
				return err
			}
			o := mset.lookupConsumer("CONSUMER")
			if o == nil {
				return errors.New("consumer not found")
			}
			state, err := o.store.State()
			if err != nil {
				return err
			}
			if state.Delivered.Stream != 1 || state.AckFloor.Stream != 1 {
				return errors.New("incorrect delivered/ack state")
			}
			return nil
		})
	}

	t.Run("OldConsumer", func(t *testing.T) { test(t, false) })
	t.Run("NewConsumer", func(t *testing.T) { test(t, true) })
}

func TestJetStreamClusterMetaPeerRemoveResponseAfterQuorum(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	ml := c.leader()
	nc, err := nats.Connect(ml.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
	require_NoError(t, err)
	defer nc.Close()

	var (
		s1 *Server
		s2 *Server
		s3 *Server
	)
	for _, s := range c.servers {
		if s == ml {
			continue
		} else if s1 == nil {
			s1 = s
		} else if s2 == nil {
			s2 = s
		} else {
			s3 = s
			break
		}
	}

	// Shutdown a majority.
	remove1 := s3.Name()
	remove2 := s2.Name()
	s1.Shutdown()
	s2.Shutdown()
	s3.Shutdown()

	reply := nats.NewInbox()
	sub, err := nc.SubscribeSync(reply)
	require_NoError(t, err)
	defer sub.Drain()

	auditSub, err := nc.SubscribeSync(JSAuditAdvisory)
	require_NoError(t, err)
	defer auditSub.Drain()

	// Since a majority is down, we'll expect to time out since there's no quorum.
	req := &JSApiMetaServerRemoveRequest{Server: remove1}
	jsreq, err := json.Marshal(req)
	require_NoError(t, err)
	require_NoError(t, nc.PublishRequest(JSApiRemoveServer, reply, jsreq))
	_, err = sub.NextMsg(time.Second)
	require_Error(t, err, nats.ErrTimeout)

	// Retrying the request should fail, as the leader has already removed it as its peer.
	rmsg, err := nc.Request(JSApiRemoveServer, jsreq, time.Second)
	require_NoError(t, err)
	var resp JSApiMetaServerRemoveResponse
	require_NoError(t, json.Unmarshal(rmsg.Data, &resp))
	if resp.Error == nil {
		t.Fatalf("Expected an error, got none")
	}
	require_Error(t, resp.Error, NewJSClusterServerMemberChangeInflightError())

	// Audit should reflect the same.
	rmsg, err = auditSub.NextMsg(time.Second)
	require_NoError(t, err)
	var auditResp JSAPIAudit
	require_NoError(t, json.Unmarshal(rmsg.Data, &auditResp))
	resp = JSApiMetaServerRemoveResponse{}
	require_NoError(t, json.Unmarshal([]byte(auditResp.Response), &resp))
	if resp.Error == nil {
		t.Fatalf("Expected an error, got none")
	}
	require_Error(t, resp.Error, NewJSClusterServerMemberChangeInflightError())

	// Don't allow concurrent meta membership changes.
	req = &JSApiMetaServerRemoveRequest{Server: remove2}
	jsreq, err = json.Marshal(req)
	require_NoError(t, err)
	rmsg, err = nc.Request(JSApiRemoveServer, jsreq, time.Second)
	require_NoError(t, err)
	resp = JSApiMetaServerRemoveResponse{}
	require_NoError(t, json.Unmarshal(rmsg.Data, &resp))
	if resp.Error == nil {
		t.Fatalf("Expected an error, got none")
	}
	require_Error(t, resp.Error, NewJSClusterServerMemberChangeInflightError())

	// Audit should reflect the same.
	rmsg, err = auditSub.NextMsg(time.Second)
	require_NoError(t, err)
	auditResp = JSAPIAudit{}
	require_NoError(t, json.Unmarshal(rmsg.Data, &auditResp))
	resp = JSApiMetaServerRemoveResponse{}
	require_NoError(t, json.Unmarshal([]byte(auditResp.Response), &resp))
	if resp.Error == nil {
		t.Fatalf("Expected an error, got none")
	}
	require_Error(t, resp.Error, NewJSClusterServerMemberChangeInflightError())

	// Bring back one server so that the peer-remove can get quorum now.
	// The response should come in shortly after.
	c.restartServer(s1)

	c.waitOnLeader()
	require_Equal(t, c.leader().Name(), ml.Name())

	rmsg, err = sub.NextMsg(5 * time.Second)
	require_NoError(t, err)
	resp = JSApiMetaServerRemoveResponse{}
	require_NoError(t, json.Unmarshal(rmsg.Data, &resp))
	if resp.Error != nil {
		require_NoError(t, resp.Error)
	}
	require_True(t, resp.Success)

	// Audit should reflect the same.
	rmsg, err = auditSub.NextMsg(time.Second)
	require_NoError(t, err)
	auditResp = JSAPIAudit{}
	require_NoError(t, json.Unmarshal(rmsg.Data, &auditResp))
	resp = JSApiMetaServerRemoveResponse{}
	require_NoError(t, json.Unmarshal([]byte(auditResp.Response), &resp))
	if resp.Error != nil {
		require_NoError(t, resp.Error)
	}
	require_True(t, resp.Success)

	// A retry of the first peer-remove should return an error that the peer is already removed.
	// Both a "success" response and this error should be used to know the peer-remove was successful.
	req = &JSApiMetaServerRemoveRequest{Server: remove1}
	jsreq, err = json.Marshal(req)
	require_NoError(t, err)
	rmsg, err = nc.Request(JSApiRemoveServer, jsreq, time.Second)
	require_NoError(t, err)
	resp = JSApiMetaServerRemoveResponse{}
	require_NoError(t, json.Unmarshal(rmsg.Data, &resp))
	if resp.Error == nil {
		t.Fatalf("Expected an error, got none")
	}
	require_Error(t, resp.Error, NewJSClusterServerNotMemberError())

	// Audit should reflect the same.
	rmsg, err = auditSub.NextMsg(time.Second)
	require_NoError(t, err)
	auditResp = JSAPIAudit{}
	require_NoError(t, json.Unmarshal(rmsg.Data, &auditResp))
	resp = JSApiMetaServerRemoveResponse{}
	require_NoError(t, json.Unmarshal([]byte(auditResp.Response), &resp))
	if resp.Error == nil {
		t.Fatalf("Expected an error, got none")
	}
	require_Error(t, resp.Error, NewJSClusterServerNotMemberError())
}

func TestJetStreamClusterStreamPeerRemovePeerSetDesync(t *testing.T) {
	c := createJetStreamClusterExplicit(t, "R5S", 5)
	defer c.shutdown()

	nc, js := jsClientConnect(t, c.randomServer())
	defer nc.Close()

	_, err := js.AddStream(&nats.StreamConfig{
		Name:     "TEST",
		Subjects: []string{"foo"},
		Replicas: 3,
	})
	require_NoError(t, err)

	sl := c.streamLeader(globalAccountName, "TEST")
	ml := c.leader()

	// Map peer IDs to servers.
	peerSrv := make(map[string]*Server, len(c.servers))
	for _, s := range c.servers {
		peerSrv[s.Node()] = s
	}

	// Get the stream's current peer set from the meta leader.
	mjs := ml.getJetStream()
	mjs.mu.RLock()
	sa := mjs.streamAssignment(globalAccountName, "TEST")
	var origPeers []string
	if sa != nil {
		origPeers = copyStrings(sa.Group.Peers)
	}
	mjs.mu.RUnlock()
	require_Len(t, len(origPeers), 3)

	// Pick a stream member to peer-remove that is neither the stream leader nor
	// the meta leader. The stream leader must stay in place so it is the one
	// processing the updated assignment, and not picking the meta leader means
	// stalling this server's meta apply queue doesn't stall the proposal itself.
	var rs *Server
	for _, p := range origPeers {
		if s := peerSrv[p]; s != sl && s != ml {
			rs = s
			break
		}
	}
	require_NotNil(t, rs)
	removedID := rs.Node()

	// The stream leader's raft node for the stream group.
	mset, err := sl.globalAccount().lookupStream("TEST")
	require_NoError(t, err)
	srn := mset.raftNode().(*raft)

	// Stall the to-be-removed server's meta apply queue. Its stream raft node
	// keeps running and acking append entries while being unaware of its own
	// removal. This simulates the natural lag between different servers
	// applying the same meta entry.
	rsMeta := rs.getJetStream().getMetaGroup()
	require_NoError(t, rsMeta.PauseApply())
	defer rsMeta.ResumeApply()

	// Peer-remove the selected stream member.
	req := &JSApiStreamRemovePeerRequest{Peer: rs.Name()}
	jsreq, err := json.Marshal(req)
	require_NoError(t, err)
	resp, err := nc.Request(fmt.Sprintf(JSApiStreamRemovePeerT, "TEST"), jsreq, time.Second)
	require_NoError(t, err)
	var rpResp JSApiStreamRemovePeerResponse
	require_NoError(t, json.Unmarshal(resp.Data, &rpResp))
	require_True(t, rpResp.Error == nil)

	// Wait for the stream leader to apply the updated assignment: a replacement
	// peer that was not part of the original peer set must show up in its raft
	// peer set.
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		srn.RLock()
		raftPeers := srn.peerNames()
		srn.RUnlock()
		for _, p := range raftPeers {
			if !slices.Contains(origPeers, p) {
				return nil
			}
		}
		return fmt.Errorf("stream leader has not applied the new assignment yet, raft peers: %v", raftPeers)
	})

	// Observe the reproduction: the removed peer acks the leader's peer state
	// append entry (or a heartbeat), the leader treats it as an unknown peer
	// and re-adds it via EntryAddPeer. Give it a few heartbeat intervals; on a
	// correctly behaving server this never happens.
	deadline := time.Now().Add(3 * time.Second)
	for time.Now().Before(deadline) {
		srn.RLock()
		raftPeers := srn.peerNames()
		srn.RUnlock()
		if len(raftPeers) > 3 {
			t.Logf("Removed peer %q was re-added to the raft group, peer set is now: %v", removedID, raftPeers)
			break
		}
		time.Sleep(50 * time.Millisecond)
	}

	// Let the removed server process its removal and delete its stream raft node.
	rsMeta.ResumeApply()
	checkFor(t, 2*time.Second, 200*time.Millisecond, func() error {
		if _, err = rs.globalAccount().lookupStream("TEST"); err == nil {
			return fmt.Errorf("removed server %s still has the stream", rs.Name())
		}
		return nil
	})

	// The raft layer peer set on all remaining stream members must converge
	// to the meta layer peer set: 3 peers, not including the removed peer.
	checkFor(t, 5*time.Second, 200*time.Millisecond, func() error {
		// Meta layer peer set, according to the meta leader.
		mjs.mu.RLock()
		sa = mjs.streamAssignment(globalAccountName, "TEST")
		var metaPeers []string
		if sa != nil {
			metaPeers = copyStrings(sa.Group.Peers)
		}
		mjs.mu.RUnlock()
		if len(metaPeers) != 3 {
			return fmt.Errorf("expected 3 peers in meta assignment, got: %v", metaPeers)
		}
		slices.Sort(metaPeers)

		// Each member's raft-layer peer set must match it.
		for _, p := range metaPeers {
			s := peerSrv[p]
			mset, err = s.globalAccount().lookupStream("TEST")
			if err != nil {
				return fmt.Errorf("stream not found on %s: %v", s.Name(), err)
			}
			node := mset.raftNode()
			if node == nil {
				return fmt.Errorf("no raft node for stream on %s", s.Name())
			}
			rn := node.(*raft)
			rn.RLock()
			raftPeers := rn.peerNames()
			rn.RUnlock()
			slices.Sort(raftPeers)
			if !slices.Equal(metaPeers, raftPeers) {
				return fmt.Errorf("%s: raft peer set %v desynced from meta peer set %v, contains removed peer %q: %v",
					s.Name(), raftPeers, metaPeers, removedID, slices.Contains(raftPeers, removedID))
			}
		}
		return nil
	})
}

//
// DO NOT ADD NEW TESTS IN THIS FILE (unless to balance test times)
// Add at the end of jetstream_cluster_<n>_test.go, with <n> being the highest value.
//
