// Copyright 2012-2024 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.

package test

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/nats-io/nats-server/v2/server"
	"github.com/nats-io/nats.go"
)

const CLIENT_PORT = 11422
const MONITOR_PORT = 11522

func runMonitorServer() *server.Server {
	resetPreviousHTTPConnections()
	opts := DefaultTestOptions
	opts.Port = CLIENT_PORT
	opts.HTTPPort = MONITOR_PORT
	opts.HTTPHost = "127.0.0.1"
	opts.NoSystemAccount = true
	return RunServer(&opts)
}

// Runs a clustered pair of monitor servers for testing the /routez endpoint
func runMonitorServerClusteredPair(t *testing.T) (*server.Server, *server.Server) {
	resetPreviousHTTPConnections()
	opts := DefaultTestOptions
	opts.Port = CLIENT_PORT
	opts.HTTPPort = MONITOR_PORT
	opts.HTTPHost = "127.0.0.1"
	opts.Cluster = server.ClusterOpts{Name: "M", Host: "127.0.0.1", Port: 10223}
	opts.Routes = server.RoutesFromStr("nats-route://127.0.0.1:10222")
	opts.NoSystemAccount = true

	s1 := RunServer(&opts)

	opts2 := DefaultTestOptions
	opts2.Port = CLIENT_PORT + 1
	opts2.HTTPPort = MONITOR_PORT + 1
	opts2.HTTPHost = "127.0.0.1"
	opts2.Cluster = server.ClusterOpts{Name: "M", Host: "127.0.0.1", Port: 10222}
	opts2.Routes = server.RoutesFromStr("nats-route://127.0.0.1:10223")
	opts2.NoSystemAccount = true

	s2 := RunServer(&opts2)

	checkClusterFormed(t, s1, s2)

	return s1, s2
}

func runMonitorServerNoHTTPPort() *server.Server {
	resetPreviousHTTPConnections()
	opts := DefaultTestOptions
	opts.Port = CLIENT_PORT
	opts.HTTPPort = 0
	opts.NoSystemAccount = true

	return RunServer(&opts)
}

func resetPreviousHTTPConnections() {
	http.DefaultTransport.(*http.Transport).CloseIdleConnections()
}

// Make sure that we do not run the http server for monitoring unless asked.
func TestNoMonitorPort(t *testing.T) {
	s := runMonitorServerNoHTTPPort()
	defer s.Shutdown()

	url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT)
	if resp, err := http.Get(url + "varz"); err == nil {
		t.Fatalf("Expected error: Got %+v\n", resp)
	}
	if resp, err := http.Get(url + "healthz"); err == nil {
		t.Fatalf("Expected error: Got %+v\n", resp)
	}
	if resp, err := http.Get(url + "connz"); err == nil {
		t.Fatalf("Expected error: Got %+v\n", resp)
	}
}

// testEndpointDataRace tests a monitoring endpoint for data races by polling
// while client code acts to ensure statistics are updated. It is designed to
// run under the -race flag to catch violations. The caller must start the
// NATS server.
func testEndpointDataRace(endpoint string, t *testing.T) {
	var doneWg sync.WaitGroup

	url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT)

	// Poll as fast as we can, while creating connections, publishing,
	// and subscribing.
	clientDone := int64(0)
	doneWg.Add(1)
	go func() {
		for atomic.LoadInt64(&clientDone) == 0 {
			resp, err := http.Get(url + endpoint)
			if err != nil {
				t.Errorf("Expected no error: Got %v\n", err)
			} else {
				resp.Body.Close()
			}
		}
		doneWg.Done()
	}()

	// create connections, subscriptions, and publish messages to
	// update the monitor variables.
	var conns []net.Conn
	for i := 0; i < 50; i++ {
		cl := createClientConnSubscribeAndPublish(t)
		// keep a few connections around to test monitor variables.
		if i%10 == 0 {
			conns = append(conns, cl)
		} else {
			cl.Close()
		}
	}
	atomic.AddInt64(&clientDone, 1)

	// wait for the endpoint polling goroutine to exit
	doneWg.Wait()

	// cleanup the conns
	for _, cl := range conns {
		cl.Close()
	}
}

func TestEndpointDataRaces(t *testing.T) {
	// setup a small cluster to test /routez
	s1, s2 := runMonitorServerClusteredPair(t)
	defer s1.Shutdown()
	defer s2.Shutdown()

	// test all of our endpoints
	testEndpointDataRace("varz", t)
	testEndpointDataRace("connz", t)
	testEndpointDataRace("routez", t)
	testEndpointDataRace("subsz", t)
	testEndpointDataRace("stacksz", t)
}

func TestVarz(t *testing.T) {
	s := runMonitorServer()
	defer s.Shutdown()

	url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT)
	resp, err := http.Get(url + "varz")
	if err != nil {
		t.Fatalf("Expected no error: Got %v\n", err)
	}
	if resp.StatusCode != 200 {
		t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Got an error reading the body: %v\n", err)
	}

	v := server.Varz{}
	if err := json.Unmarshal(body, &v); err != nil {
		t.Fatalf("Got an error unmarshalling the body: %v\n", err)
	}

	// Do some sanity checks on values
	if time.Since(v.Start) > 10*time.Second {
		t.Fatal("Expected start time to be within 10 seconds.")
	}

	cl := createClientConnSubscribeAndPublish(t)
	defer cl.Close()

	resp, err = http.Get(url + "varz")
	if err != nil {
		t.Fatalf("Expected no error: Got %v\n", err)
	}
	if resp.StatusCode != 200 {
		t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err = io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Got an error reading the body: %v\n", err)
	}

	if strings.Contains(string(body), "cluster_port") {
		t.Fatal("Varz body contains cluster information when no cluster is defined.")
	}

	v = server.Varz{}
	if err := json.Unmarshal(body, &v); err != nil {
		t.Fatalf("Got an error unmarshalling the body: %v\n", err)
	}

	if v.Connections != 1 {
		t.Fatalf("Expected Connections of 1, got %v\n", v.Connections)
	}
	if v.InMsgs != 1 {
		t.Fatalf("Expected InMsgs of 1, got %v\n", v.InMsgs)
	}
	if v.OutMsgs != 1 {
		t.Fatalf("Expected OutMsgs of 1, got %v\n", v.OutMsgs)
	}
	if v.InBytes != 5 {
		t.Fatalf("Expected InBytes of 5, got %v\n", v.InBytes)
	}
	if v.OutBytes != 5 {
		t.Fatalf("Expected OutBytes of 5, got %v\n", v.OutBytes)
	}
	if v.MaxPending != server.MAX_PENDING_SIZE {
		t.Fatalf("Expected MaxPending of %d, got %v\n",
			server.MAX_PENDING_SIZE, v.MaxPending)
	}
	if v.WriteDeadline != server.DEFAULT_FLUSH_DEADLINE {
		t.Fatalf("Expected WriteDeadline of %d, got %v\n",
			server.DEFAULT_FLUSH_DEADLINE, v.WriteDeadline)
	}
}

func TestConnz(t *testing.T) {
	s := runMonitorServer()
	defer s.Shutdown()

	url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT)
	resp, err := http.Get(url + "connz")
	if err != nil {
		t.Fatalf("Expected no error: Got %v\n", err)
	}
	if resp.StatusCode != 200 {
		t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Got an error reading the body: %v\n", err)
	}

	c := server.Connz{}
	if err := json.Unmarshal(body, &c); err != nil {
		t.Fatalf("Got an error unmarshalling the body: %v\n", err)
	}

	// Test contents..
	if c.NumConns != 0 {
		t.Fatalf("Expected 0 connections, got %d\n", c.NumConns)
	}
	if c.Total != 0 {
		t.Fatalf("Expected 0 live connections, got %d\n", c.Total)
	}
	if c.Conns == nil || len(c.Conns) != 0 {
		t.Fatalf("Expected 0 connections in array, got %p\n", c.Conns)
	}

	cl := createClientConnSubscribeAndPublish(t)
	defer cl.Close()

	resp, err = http.Get(url + "connz")
	if err != nil {
		t.Fatalf("Expected no error: Got %v\n", err)
	}
	if resp.StatusCode != 200 {
		t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err = io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Got an error reading the body: %v\n", err)
	}
	if err := json.Unmarshal(body, &c); err != nil {
		t.Fatalf("Got an error unmarshalling the body: %v\n", err)
	}

	if c.NumConns != 1 {
		t.Fatalf("Expected 1 connection, got %d\n", c.NumConns)
	}
	if c.Total != 1 {
		t.Fatalf("Expected 1 live connection, got %d\n", c.Total)
	}
	if c.Conns == nil || len(c.Conns) != 1 {
		t.Fatalf("Expected 1 connection in array, got %p\n", c.Conns)
	}

	if c.Limit != server.DefaultConnListSize {
		t.Fatalf("Expected limit of %d, got %v\n", server.DefaultConnListSize, c.Limit)
	}

	if c.Offset != 0 {
		t.Fatalf("Expected offset of 0, got %v\n", c.Offset)
	}

	// Test inside details of each connection
	ci := c.Conns[0]

	if ci.Cid == 0 {
		t.Fatalf("Expected non-zero cid, got %v\n", ci.Cid)
	}
	if ci.IP != "127.0.0.1" {
		t.Fatalf("Expected \"127.0.0.1\" for IP, got %v\n", ci.IP)
	}
	if ci.Port == 0 {
		t.Fatalf("Expected non-zero port, got %v\n", ci.Port)
	}
	if ci.NumSubs != 1 {
		t.Fatalf("Expected num_subs of 1, got %v\n", ci.NumSubs)
	}
	if len(ci.Subs) != 0 {
		t.Fatalf("Expected subs of 0, got %v\n", ci.Subs)
	}
	if ci.InMsgs != 1 {
		t.Fatalf("Expected InMsgs of 1, got %v\n", ci.InMsgs)
	}
	if ci.OutMsgs != 1 {
		t.Fatalf("Expected OutMsgs of 1, got %v\n", ci.OutMsgs)
	}
	if ci.InBytes != 5 {
		t.Fatalf("Expected InBytes of 1, got %v\n", ci.InBytes)
	}
	if ci.OutBytes != 5 {
		t.Fatalf("Expected OutBytes of 1, got %v\n", ci.OutBytes)
	}
}

func TestTLSConnz(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/tls.conf")
	defer srv.Shutdown()
	rootCAFile := "./configs/certs/ca.pem"
	clientCertFile := "./configs/certs/client-cert.pem"
	clientKeyFile := "./configs/certs/client-key.pem"

	// Test with secure connection
	endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
	nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint)
	nc, err := nats.Connect(nurl, nats.RootCAs(rootCAFile))
	if err != nil {
		t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err)
	}
	defer nc.Close()
	ch := make(chan struct{})
	nc.Subscribe("foo", func(m *nats.Msg) { ch <- struct{}{} })
	nc.Publish("foo", []byte("Hello"))

	// Wait for message
	<-ch

	url := fmt.Sprintf("https://127.0.0.1:%d/", opts.HTTPSPort)
	tlsConfig := &tls.Config{}
	caCert, err := os.ReadFile(rootCAFile)
	if err != nil {
		t.Fatalf("Got error reading RootCA file: %s", err)
	}
	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)
	tlsConfig.RootCAs = caCertPool

	cert, err := tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
	if err != nil {
		t.Fatalf("Got error reading client certificates: %s", err)
	}
	tlsConfig.Certificates = []tls.Certificate{cert}
	transport := &http.Transport{TLSClientConfig: tlsConfig}
	httpClient := &http.Client{Transport: transport}

	resp, err := httpClient.Get(url + "connz")
	if err != nil {
		t.Fatalf("Expected no error: Got %v\n", err)
	}
	if resp.StatusCode != 200 {
		t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)

	if err != nil {
		t.Fatalf("Got an error reading the body: %v\n", err)
	}
	c := server.Connz{}
	if err := json.Unmarshal(body, &c); err != nil {
		t.Fatalf("Got an error unmarshalling the body: %v\n", err)
	}

	if c.NumConns != 1 {
		t.Fatalf("Expected 1 connection, got %d\n", c.NumConns)
	}
	if c.Total != 1 {
		t.Fatalf("Expected 1 live connection, got %d\n", c.Total)
	}
	if c.Conns == nil || len(c.Conns) != 1 {
		t.Fatalf("Expected 1 connection in array, got %d\n", len(c.Conns))
	}

	// Test inside details of each connection
	ci := c.Conns[0]

	if ci.Cid == 0 {
		t.Fatalf("Expected non-zero cid, got %v\n", ci.Cid)
	}
	if ci.IP != "127.0.0.1" {
		t.Fatalf("Expected \"127.0.0.1\" for IP, got %v\n", ci.IP)
	}
	if ci.Port == 0 {
		t.Fatalf("Expected non-zero port, got %v\n", ci.Port)
	}
	if ci.NumSubs != 1 {
		t.Fatalf("Expected num_subs of 1, got %v\n", ci.NumSubs)
	}
	if len(ci.Subs) != 0 {
		t.Fatalf("Expected subs of 0, got %v\n", ci.Subs)
	}
	if ci.InMsgs != 1 {
		t.Fatalf("Expected InMsgs of 1, got %v\n", ci.InMsgs)
	}
	if ci.OutMsgs != 1 {
		t.Fatalf("Expected OutMsgs of 1, got %v\n", ci.OutMsgs)
	}
	if ci.InBytes != 5 {
		t.Fatalf("Expected InBytes of 1, got %v\n", ci.InBytes)
	}
	if ci.OutBytes != 5 {
		t.Fatalf("Expected OutBytes of 1, got %v\n", ci.OutBytes)
	}
	if ci.Start.IsZero() {
		t.Fatalf("Expected Start to be valid\n")
	}
	if ci.Uptime == "" {
		t.Fatalf("Expected Uptime to be valid\n")
	}
	if ci.LastActivity.IsZero() {
		t.Fatalf("Expected LastActivity to be valid\n")
	}
	if ci.LastActivity.UnixNano() < ci.Start.UnixNano() {
		t.Fatalf("Expected LastActivity [%v] to be > Start [%v]\n", ci.LastActivity, ci.Start)
	}
	if ci.Idle == "" {
		t.Fatalf("Expected Idle to be valid\n")
	}
}

func TestConnzWithSubs(t *testing.T) {
	s := runMonitorServer()
	defer s.Shutdown()

	cl := createClientConnSubscribeAndPublish(t)
	defer cl.Close()

	url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT)
	resp, err := http.Get(url + "connz?subs=1")
	if err != nil {
		t.Fatalf("Expected no error: Got %v\n", err)
	}
	if resp.StatusCode != 200 {
		t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Got an error reading the body: %v\n", err)
	}

	c := server.Connz{}
	if err := json.Unmarshal(body, &c); err != nil {
		t.Fatalf("Got an error unmarshalling the body: %v\n", err)
	}

	// Test inside details of each connection
	ci := c.Conns[0]
	if len(ci.Subs) != 1 || ci.Subs[0] != "foo" {
		t.Fatalf("Expected subs of 1, got %v\n", ci.Subs)
	}
}

func TestConnzWithAuth(t *testing.T) {
	srv, opts := RunServerWithConfig("./configs/multi_user.conf")
	defer srv.Shutdown()

	endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)
	curl := fmt.Sprintf("nats://%s:%s@%s/", opts.Users[0].Username, opts.Users[0].Password, endpoint)
	nc, err := nats.Connect(curl)
	if err != nil {
		t.Fatalf("Got an error on Connect: %+v\n", err)
	}
	defer nc.Close()

	ch := make(chan struct{})
	nc.Subscribe("foo", func(m *nats.Msg) { ch <- struct{}{} })
	nc.Publish("foo", []byte("Hello"))

	// Wait for message
	<-ch

	url := fmt.Sprintf("http://127.0.0.1:%d/", opts.HTTPPort)

	resp, err := http.Get(url + "connz?auth=1")
	if err != nil {
		t.Fatalf("Expected no error: Got %v\n", err)
	}
	if resp.StatusCode != 200 {
		t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Got an error reading the body: %v\n", err)
	}

	c := server.Connz{}
	if err := json.Unmarshal(body, &c); err != nil {
		t.Fatalf("Got an error unmarshalling the body: %v\n", err)
	}

	// Test that we have authorized_user and its Alice.
	ci := c.Conns[0]
	if ci.AuthorizedUser != opts.Users[0].Username {
		t.Fatalf("Expected authorized_user to be %q, got %q\n",
			opts.Users[0].Username, ci.AuthorizedUser)
	}
}

func TestConnzWithAccounts(t *testing.T) {
	resetPreviousHTTPConnections()
	s, opts := RunServerWithConfig("./configs/multi_accounts.conf")
	defer s.Shutdown()

	endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port)

	// Connect all the users. Tests depend on knowing users, accounts.
	if len(opts.Users) != 6 {
		t.Fatalf("Expected 6 total users, got %d", len(opts.Users))
	}
	if len(opts.Accounts) != 3 {
		t.Fatalf("Expected 3 total accounts, got %d", len(opts.Accounts))
	}

	// Map from user to account name.
	utoa := make(map[string]string)
	conns := make([]*nats.Conn, len(opts.Users))
	for _, u := range opts.Users {
		nc, err := nats.Connect(fmt.Sprintf("nats://%s:%s@%s/", u.Username, u.Password, endpoint))
		if err != nil {
			t.Fatalf("Got an error on Connect: %+v\n", err)
		}
		defer nc.Close()
		utoa[u.Username] = u.Account.Name
		conns = append(conns, nc)
	}

	url := fmt.Sprintf("http://127.0.0.1:%d/", opts.HTTPPort)

	grabConnz := func(args string) *server.Connz {
		t.Helper()
		if args != "" {
			args = "&" + args
		}
		resp, err := http.Get(url + "connz?auth=1" + args)
		if err != nil {
			t.Fatalf("Expected no error: Got %v\n", err)
		}
		if resp.StatusCode != 200 {
			t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
		}
		defer resp.Body.Close()
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			t.Fatalf("Got an error reading the body: %v\n", err)
		}
		c := server.Connz{}
		if err := json.Unmarshal(body, &c); err != nil {
			t.Fatalf("Got an error unmarshalling the body: %v\n", err)
		}
		return &c
	}

	c := grabConnz("")
	if c.NumConns != 6 {
		t.Fatalf("Expected 6 connection entries, got %d", c.NumConns)
	}

	checkConn := func(ci *server.ConnInfo) {
		t.Helper()
		user := ci.AuthorizedUser
		account := utoa[user]
		if user == "" || account == "" {
			t.Fatalf("Empty user or account: %q - %q", user, account)
		}
		if ci.Account != account {
			t.Fatalf("Expected account of %q, got %q", account, ci.Account)
		}
	}

	for i, ci := range c.Conns {
		if ci.Cid != uint64(i+1) {
			t.Fatalf("Expected CID of %d, got %d", i+1, ci.Cid)
		}
		checkConn(ci)
	}

	// Now make sure we can pull connections by account and user
	pullByAccount := func(accName, state string) {
		t.Helper()
		c = grabConnz("acc=" + accName + "&state=" + state)
		if c.NumConns != 2 {
			t.Fatalf("Expected 2 connection entries, got %d", c.NumConns)
		}
		for _, ci := range c.Conns {
			if ci.Account != accName {
				t.Fatalf("Expected %q account, go %q", accName, ci.Account)
			}
		}
	}

	pullByUser := func(user, state string) {
		t.Helper()
		c = grabConnz("user=" + user + "&state=" + state)
		if c.NumConns != 1 {
			t.Fatalf("Expected 1 connection, got %d", c.NumConns)
		}
		if c.Conns[0].AuthorizedUser != user {
			t.Fatalf("Expected user %q, got %q", user, c.Conns[0].AuthorizedUser)
		}
	}

	pullByAccount("engineering", "open")
	pullByAccount("finance", "open")
	pullByAccount("legal", "open")

	pullByUser("alice", "open")
	pullByUser("bob", "open")

	pullByUser("john", "open")
	pullByUser("mary", "open")

	pullByUser("peter", "open")
	pullByUser("paul", "open")

	// Now closed and make sure these work on closed as well.
	for _, nc := range conns {
		nc.Close()
	}

	checkFor(t, time.Second, 10*time.Millisecond, func() error {
		if numClients := s.NumClients(); numClients != 0 {
			return fmt.Errorf("Number of client is %d", numClients)
		}
		return nil
	})

	pullByAccount("engineering", "closed")
	pullByAccount("finance", "closed")
	pullByAccount("legal", "closed")

	pullByUser("alice", "closed")
	pullByUser("bob", "closed")

	pullByUser("john", "closed")
	pullByUser("mary", "closed")

	pullByUser("peter", "closed")
	pullByUser("paul", "closed")
}

func TestConnzWithOffsetAndLimit(t *testing.T) {
	s := runMonitorServer()
	defer s.Shutdown()

	cl1 := createClientConnSubscribeAndPublish(t)
	defer cl1.Close()

	cl2 := createClientConnSubscribeAndPublish(t)
	defer cl2.Close()

	url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT)
	resp, err := http.Get(url + "connz?offset=1&limit=1")
	if err != nil {
		t.Fatalf("Expected no error: Got %v\n", err)
	}
	if resp.StatusCode != 200 {
		t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Got an error reading the body: %v\n", err)
	}

	c := server.Connz{}
	if err := json.Unmarshal(body, &c); err != nil {
		t.Fatalf("Got an error unmarshalling the body: %v\n", err)
	}

	if c.Limit != 1 {
		t.Fatalf("Expected limit of 1, got %v\n", c.Limit)
	}

	if c.Offset != 1 {
		t.Fatalf("Expected offset of 1, got %v\n", c.Offset)
	}

	if len(c.Conns) != 1 {
		t.Fatalf("Expected conns of 1, got %v\n", len(c.Conns))
	}
}

func TestSubsz(t *testing.T) {
	s := runMonitorServer()
	defer s.Shutdown()

	cl := createClientConnSubscribeAndPublish(t)
	defer cl.Close()

	url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT)
	resp, err := http.Get(url + "subscriptionsz")
	if err != nil {
		t.Fatalf("Expected no error: Got %v\n", err)
	}
	if resp.StatusCode != 200 {
		t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Got an error reading the body: %v\n", err)
	}

	su := server.Subsz{}
	if err := json.Unmarshal(body, &su); err != nil {
		t.Fatalf("Got an error unmarshalling the body: %v\n", err)
	}

	// Do some sanity checks on values
	if su.NumSubs != 1 {
		t.Fatalf("Expected num_subs of 1, got %v\n", su.NumSubs)
	}
}

func TestHTTPHost(t *testing.T) {
	s := runMonitorServer()
	defer s.Shutdown()

	// Grab non-127.0.0.1 address and try to use that to connect.
	// Should fail.
	var ip net.IP
	ifaces, _ := net.Interfaces()
	for _, i := range ifaces {
		addrs, _ := i.Addrs()
		for _, addr := range addrs {
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			// Skip loopback/127.0.0.1 or any ipv6 for now.
			if ip.IsLoopback() || ip.To4() == nil {
				ip = nil
				continue
			}
			break
		}
		if ip != nil {
			break
		}
	}
	if ip == nil {
		t.Fatalf("Could not find non-loopback IPV4 address")
	}
	url := fmt.Sprintf("http://%v:%d/", ip, MONITOR_PORT)
	if resp, err := http.Get(url + "varz"); err == nil {
		t.Fatalf("Expected error: Got %+v\n", resp)
	}
}

// Create a connection to test ConnInfo
func createClientConnSubscribeAndPublish(t *testing.T) net.Conn {
	cl := createClientConn(t, "127.0.0.1", CLIENT_PORT)
	send, expect := setupConn(t, cl)
	expectMsgs := expectMsgsCommand(t, expect)

	send("SUB foo 1\r\nPUB foo 5\r\nhello\r\n")
	expectMsgs(1)

	return cl
}

func TestMonitorNoTLSConfig(t *testing.T) {
	opts := DefaultTestOptions
	opts.Port = CLIENT_PORT
	opts.HTTPHost = "127.0.0.1"
	opts.HTTPSPort = MONITOR_PORT
	s := server.New(&opts)
	defer s.Shutdown()
	// Check with manually starting the monitoring, which should return an error
	if err := s.StartMonitoring(); err == nil || !strings.Contains(err.Error(), "TLS") {
		t.Fatalf("Expected error about missing TLS config, got %v", err)
	}
	// Also check by calling Start(), which should produce a fatal error
	dl := &dummyLogger{}
	s.SetLogger(dl, false, false)
	defer s.SetLogger(nil, false, false)
	s.Start()
	if !strings.Contains(dl.msg, "TLS") {
		t.Fatalf("Expected error about missing TLS config, got %v", dl.msg)
	}
}

func TestMonitorErrorOnListen(t *testing.T) {
	s := runMonitorServer()
	defer s.Shutdown()

	opts := DefaultTestOptions
	opts.Port = CLIENT_PORT + 1
	opts.HTTPHost = "127.0.0.1"
	opts.HTTPPort = MONITOR_PORT
	s2 := server.New(&opts)
	defer s2.Shutdown()
	if err := s2.StartMonitoring(); err == nil || !strings.Contains(err.Error(), "listen") {
		t.Fatalf("Expected error about not able to start listener, got %v", err)
	}
}

func TestMonitorBothPortsConfigured(t *testing.T) {
	opts := DefaultTestOptions
	opts.Port = CLIENT_PORT
	opts.HTTPHost = "127.0.0.1"
	opts.HTTPPort = MONITOR_PORT
	opts.HTTPSPort = MONITOR_PORT + 1
	s := server.New(&opts)
	defer s.Shutdown()
	if err := s.StartMonitoring(); err == nil || !strings.Contains(err.Error(), "specify both") {
		t.Fatalf("Expected error about ports configured, got %v", err)
	}
}

func TestMonitorStop(t *testing.T) {
	resetPreviousHTTPConnections()
	opts := DefaultTestOptions
	opts.Port = CLIENT_PORT
	opts.HTTPHost = "127.0.0.1"
	opts.HTTPPort = MONITOR_PORT
	url := fmt.Sprintf("http://%v:%d/", opts.HTTPHost, MONITOR_PORT)
	// Create a server instance and start only the monitoring http server.
	s := server.New(&opts)
	if err := s.StartMonitoring(); err != nil {
		t.Fatalf("Error starting monitoring: %v", err)
	}
	// Make sure http server is started
	resp, err := http.Get(url + "varz")
	if err != nil {
		t.Fatalf("Error on http request: %v", err)
	}
	resp.Body.Close()
	// Although the server itself was not started (we did not call s.Start()),
	// Shutdown() should stop the http server.
	s.Shutdown()
	// HTTP request should now fail
	if resp, err := http.Get(url + "varz"); err == nil {
		t.Fatalf("Expected error: Got %+v\n", resp)
	}
}
