github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/test_test.go (about)

     1  // Copyright 2019-2023 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package server
    15  
    16  import (
    17  	"fmt"
    18  	"math/rand"
    19  	"net/url"
    20  	"os"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  )
    25  
    26  // DefaultTestOptions are default options for the unit tests.
    27  var DefaultTestOptions = Options{
    28  	Host:                  "127.0.0.1",
    29  	Port:                  4222,
    30  	NoLog:                 true,
    31  	NoSigs:                true,
    32  	MaxControlLine:        4096,
    33  	DisableShortFirstPing: true,
    34  }
    35  
    36  func testDefaultClusterOptionsForLeafNodes() *Options {
    37  	o := DefaultTestOptions
    38  	o.Port = -1
    39  	o.Cluster.Host = o.Host
    40  	o.Cluster.Port = -1
    41  	o.Gateway.Host = o.Host
    42  	o.Gateway.Port = -1
    43  	o.LeafNode.Host = o.Host
    44  	o.LeafNode.Port = -1
    45  	return &o
    46  }
    47  
    48  func RunRandClientPortServer(t *testing.T) *Server {
    49  	opts := DefaultTestOptions
    50  	opts.Port = -1
    51  	opts.StoreDir = t.TempDir()
    52  	return RunServer(&opts)
    53  }
    54  
    55  func require_True(t *testing.T, b bool) {
    56  	t.Helper()
    57  	if !b {
    58  		t.Fatalf("require true, but got false")
    59  	}
    60  }
    61  
    62  func require_False(t *testing.T, b bool) {
    63  	t.Helper()
    64  	if b {
    65  		t.Fatalf("require false, but got true")
    66  	}
    67  }
    68  
    69  func require_NoError(t testing.TB, err error) {
    70  	t.Helper()
    71  	if err != nil {
    72  		t.Fatalf("require no error, but got: %v", err)
    73  	}
    74  }
    75  
    76  func require_NotNil(t testing.TB, v any) {
    77  	t.Helper()
    78  	if v == nil {
    79  		t.Fatalf("require not nil, but got: %v", v)
    80  	}
    81  }
    82  
    83  func require_Contains(t *testing.T, s string, subStrs ...string) {
    84  	t.Helper()
    85  	for _, subStr := range subStrs {
    86  		if !strings.Contains(s, subStr) {
    87  			t.Fatalf("require %q to be contained in %q", subStr, s)
    88  		}
    89  	}
    90  }
    91  
    92  func require_Error(t *testing.T, err error, expected ...error) {
    93  	t.Helper()
    94  	if err == nil {
    95  		t.Fatalf("require error, but got none")
    96  	}
    97  	if len(expected) == 0 {
    98  		return
    99  	}
   100  	// Try to strip nats prefix from Go library if present.
   101  	const natsErrPre = "nats: "
   102  	eStr := err.Error()
   103  	if strings.HasPrefix(eStr, natsErrPre) {
   104  		eStr = strings.Replace(eStr, natsErrPre, _EMPTY_, 1)
   105  	}
   106  
   107  	for _, e := range expected {
   108  		if err == e || strings.Contains(eStr, e.Error()) || strings.Contains(e.Error(), eStr) {
   109  			return
   110  		}
   111  	}
   112  	t.Fatalf("Expected one of %v, got '%v'", expected, err)
   113  }
   114  
   115  func require_Equal[T comparable](t *testing.T, a, b T) {
   116  	t.Helper()
   117  	if a != b {
   118  		t.Fatalf("require %T equal, but got: %v != %v", a, a, b)
   119  	}
   120  }
   121  
   122  func require_NotEqual[T comparable](t *testing.T, a, b T) {
   123  	t.Helper()
   124  	if a == b {
   125  		t.Fatalf("require %T not equal, but got: %v == %v", a, a, b)
   126  	}
   127  }
   128  
   129  func require_Len(t *testing.T, a, b int) {
   130  	t.Helper()
   131  	if a != b {
   132  		t.Fatalf("require len, but got: %v != %v", a, b)
   133  	}
   134  }
   135  
   136  func require_LessThan[T ordered](t *testing.T, a, b T) {
   137  	t.Helper()
   138  	if a >= b {
   139  		t.Fatalf("require %v to be less than %v", a, b)
   140  	}
   141  }
   142  
   143  func require_ChanRead[T any](t *testing.T, ch chan T, timeout time.Duration) T {
   144  	t.Helper()
   145  	select {
   146  	case v := <-ch:
   147  		return v
   148  	case <-time.After(timeout):
   149  		t.Fatalf("require read from channel within %v but didn't get anything", timeout)
   150  	}
   151  	panic("this shouldn't be possible")
   152  }
   153  
   154  func require_NoChanRead[T any](t *testing.T, ch chan T, timeout time.Duration) {
   155  	t.Helper()
   156  	select {
   157  	case <-ch:
   158  		t.Fatalf("require no read from channel within %v but got something", timeout)
   159  	case <-time.After(timeout):
   160  	}
   161  }
   162  
   163  func checkNatsError(t *testing.T, e *ApiError, id ErrorIdentifier) {
   164  	t.Helper()
   165  	ae, ok := ApiErrors[id]
   166  	if !ok {
   167  		t.Fatalf("Unknown error ID identifier: %d", id)
   168  	}
   169  
   170  	if e.ErrCode != ae.ErrCode {
   171  		t.Fatalf("Did not get NATS Error %d: %+v", e.ErrCode, e)
   172  	}
   173  }
   174  
   175  // Creates a full cluster with numServers and given name and makes sure its well formed.
   176  // Will have Gateways and Leaf Node connections active.
   177  func createClusterWithName(t *testing.T, clusterName string, numServers int, connectTo ...*cluster) *cluster {
   178  	t.Helper()
   179  	return createClusterEx(t, false, 5*time.Millisecond, true, clusterName, numServers, connectTo...)
   180  }
   181  
   182  // Creates a cluster and optionally additional accounts and users.
   183  // Will have Gateways and Leaf Node connections active.
   184  func createClusterEx(t *testing.T, doAccounts bool, gwSolicit time.Duration, waitOnGWs bool, clusterName string, numServers int, connectTo ...*cluster) *cluster {
   185  	t.Helper()
   186  
   187  	if clusterName == "" || numServers < 1 {
   188  		t.Fatalf("Bad params")
   189  	}
   190  
   191  	// Setup some accounts and users.
   192  	// $SYS is always the system account. And we have default FOO and BAR accounts, as well
   193  	// as DLC and NGS which do a service import.
   194  	createAccountsAndUsers := func() ([]*Account, []*User) {
   195  		if !doAccounts {
   196  			return []*Account{NewAccount("$SYS")}, nil
   197  		}
   198  
   199  		sys := NewAccount("$SYS")
   200  		ngs := NewAccount("NGS")
   201  		dlc := NewAccount("DLC")
   202  		foo := NewAccount("FOO")
   203  		bar := NewAccount("BAR")
   204  
   205  		accounts := []*Account{sys, foo, bar, ngs, dlc}
   206  
   207  		ngs.AddServiceExport("ngs.usage.*", nil)
   208  		dlc.AddServiceImport(ngs, "ngs.usage", "ngs.usage.dlc")
   209  
   210  		// Setup users
   211  		users := []*User{
   212  			{Username: "dlc", Password: "pass", Permissions: nil, Account: dlc},
   213  			{Username: "ngs", Password: "pass", Permissions: nil, Account: ngs},
   214  			{Username: "foo", Password: "pass", Permissions: nil, Account: foo},
   215  			{Username: "bar", Password: "pass", Permissions: nil, Account: bar},
   216  			{Username: "sys", Password: "pass", Permissions: nil, Account: sys},
   217  		}
   218  		return accounts, users
   219  	}
   220  
   221  	bindGlobal := func(s *Server) {
   222  		ngs, err := s.LookupAccount("NGS")
   223  		if err != nil {
   224  			return
   225  		}
   226  		// Bind global to service import
   227  		gacc, _ := s.LookupAccount("$G")
   228  		gacc.AddServiceImport(ngs, "ngs.usage", "ngs.usage.$G")
   229  	}
   230  
   231  	// If we are going to connect to another cluster set that up now for options.
   232  	var gws []*RemoteGatewayOpts
   233  	for _, c := range connectTo {
   234  		// Gateways autodiscover here too, so just need one address from the set.
   235  		gwAddr := fmt.Sprintf("nats-gw://%s:%d", c.opts[0].Gateway.Host, c.opts[0].Gateway.Port)
   236  		gwurl, _ := url.Parse(gwAddr)
   237  		gws = append(gws, &RemoteGatewayOpts{Name: c.name, URLs: []*url.URL{gwurl}})
   238  	}
   239  
   240  	// Make the GWs form faster for the tests.
   241  	SetGatewaysSolicitDelay(gwSolicit)
   242  	defer ResetGatewaysSolicitDelay()
   243  
   244  	// Create seed first.
   245  	o := testDefaultClusterOptionsForLeafNodes()
   246  	o.Gateway.Name = clusterName
   247  	o.Gateway.Gateways = gws
   248  	// All of these need system accounts.
   249  	o.Accounts, o.Users = createAccountsAndUsers()
   250  	o.SystemAccount = "$SYS"
   251  	o.ServerName = fmt.Sprintf("%s1", clusterName)
   252  	// Run the server
   253  	s := RunServer(o)
   254  	bindGlobal(s)
   255  
   256  	c := &cluster{servers: make([]*Server, 0, numServers), opts: make([]*Options, 0, numServers), name: clusterName}
   257  	c.servers = append(c.servers, s)
   258  	c.opts = append(c.opts, o)
   259  
   260  	// For connecting to seed server above.
   261  	routeAddr := fmt.Sprintf("nats-route://%s:%d", o.Cluster.Host, o.Cluster.Port)
   262  	rurl, _ := url.Parse(routeAddr)
   263  	routes := []*url.URL{rurl}
   264  
   265  	for i := 1; i < numServers; i++ {
   266  		o := testDefaultClusterOptionsForLeafNodes()
   267  		o.Gateway.Name = clusterName
   268  		o.Gateway.Gateways = gws
   269  		o.Routes = routes
   270  		// All of these need system accounts.
   271  		o.Accounts, o.Users = createAccountsAndUsers()
   272  		o.SystemAccount = "$SYS"
   273  		o.ServerName = fmt.Sprintf("%s%d", clusterName, i+1)
   274  		s := RunServer(o)
   275  		bindGlobal(s)
   276  
   277  		c.servers = append(c.servers, s)
   278  		c.opts = append(c.opts, o)
   279  	}
   280  	checkClusterFormed(t, c.servers...)
   281  
   282  	if waitOnGWs {
   283  		// Wait on gateway connections if we were asked to connect to other gateways.
   284  		if numGWs := len(connectTo); numGWs > 0 {
   285  			for _, s := range c.servers {
   286  				waitForOutboundGateways(t, s, numGWs, 2*time.Second)
   287  			}
   288  		}
   289  	}
   290  	c.t = t
   291  	return c
   292  }
   293  
   294  func (c *cluster) shutdown() {
   295  	if c == nil {
   296  		return
   297  	}
   298  	// Stop any proxies.
   299  	for _, np := range c.nproxies {
   300  		np.stop()
   301  	}
   302  	// Shutdown and cleanup servers.
   303  	for i, s := range c.servers {
   304  		sd := s.StoreDir()
   305  		s.Shutdown()
   306  		s.WaitForShutdown()
   307  		if cf := c.opts[i].ConfigFile; cf != _EMPTY_ {
   308  			os.Remove(cf)
   309  		}
   310  		if sd != _EMPTY_ {
   311  			sd = strings.TrimSuffix(sd, JetStreamStoreDir)
   312  			os.RemoveAll(sd)
   313  		}
   314  	}
   315  }
   316  
   317  func shutdownCluster(c *cluster) {
   318  	c.shutdown()
   319  }
   320  
   321  func (c *cluster) randomServer() *Server {
   322  	return c.randomServerFromCluster(c.name)
   323  }
   324  
   325  func (c *cluster) randomServerFromCluster(cname string) *Server {
   326  	// Since these can be randomly shutdown in certain tests make sure they are running first.
   327  	// Copy our servers list and shuffle then walk looking for first running server.
   328  	cs := append(c.servers[:0:0], c.servers...)
   329  	rand.Shuffle(len(cs), func(i, j int) { cs[i], cs[j] = cs[j], cs[i] })
   330  
   331  	for _, s := range cs {
   332  		if s.Running() && s.ClusterName() == cname {
   333  			return s
   334  		}
   335  	}
   336  	return nil
   337  }
   338  
   339  func runSolicitLeafServer(lso *Options) (*Server, *Options) {
   340  	return runSolicitLeafServerToURL(fmt.Sprintf("nats-leaf://%s:%d", lso.LeafNode.Host, lso.LeafNode.Port))
   341  }
   342  
   343  func runSolicitLeafServerToURL(surl string) (*Server, *Options) {
   344  	o := DefaultTestOptions
   345  	o.Port = -1
   346  	o.NoSystemAccount = true
   347  	rurl, _ := url.Parse(surl)
   348  	o.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{rurl}}}
   349  	o.LeafNode.ReconnectInterval = 100 * time.Millisecond
   350  	return RunServer(&o), &o
   351  }
   352  
   353  // ordered is a constraint that permits any ordered type.
   354  //
   355  // To avoid a dependency on go1.21+ or golang.org/x/exp, we copy the ordered
   356  // interface def from go 1.21.3:src/cmp/cmp.go (https://pkg.go.dev/cmp#Ordered).
   357  //
   358  // When this repo is updated to go 1.21+, this should be deleted and references
   359  // replaced by cmp.Ordered.
   360  type ordered interface {
   361  	~int | ~int8 | ~int16 | ~int32 | ~int64 |
   362  		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
   363  		~float32 | ~float64 |
   364  		~string
   365  }