get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/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 if cf := c.opts[i].ConfigFile; cf != _EMPTY_ { 307 os.Remove(cf) 308 } 309 if sd != _EMPTY_ { 310 sd = strings.TrimSuffix(sd, JetStreamStoreDir) 311 os.RemoveAll(sd) 312 } 313 } 314 } 315 316 func shutdownCluster(c *cluster) { 317 c.shutdown() 318 } 319 320 func (c *cluster) randomServer() *Server { 321 return c.randomServerFromCluster(c.name) 322 } 323 324 func (c *cluster) randomServerFromCluster(cname string) *Server { 325 // Since these can be randomly shutdown in certain tests make sure they are running first. 326 // Copy our servers list and shuffle then walk looking for first running server. 327 cs := append(c.servers[:0:0], c.servers...) 328 rand.Shuffle(len(cs), func(i, j int) { cs[i], cs[j] = cs[j], cs[i] }) 329 330 for _, s := range cs { 331 if s.Running() && s.ClusterName() == cname { 332 return s 333 } 334 } 335 return nil 336 } 337 338 func runSolicitLeafServer(lso *Options) (*Server, *Options) { 339 return runSolicitLeafServerToURL(fmt.Sprintf("nats-leaf://%s:%d", lso.LeafNode.Host, lso.LeafNode.Port)) 340 } 341 342 func runSolicitLeafServerToURL(surl string) (*Server, *Options) { 343 o := DefaultTestOptions 344 o.Port = -1 345 o.NoSystemAccount = true 346 rurl, _ := url.Parse(surl) 347 o.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{rurl}}} 348 o.LeafNode.ReconnectInterval = 100 * time.Millisecond 349 return RunServer(&o), &o 350 } 351 352 // ordered is a constraint that permits any ordered type. 353 // 354 // To avoid a dependency on go1.21+ or golang.org/x/exp, we copy the ordered 355 // interface def from go 1.21.3:src/cmp/cmp.go (https://pkg.go.dev/cmp#Ordered). 356 // 357 // When this repo is updated to go 1.21+, this should be deleted and references 358 // replaced by cmp.Ordered. 359 type ordered interface { 360 ~int | ~int8 | ~int16 | ~int32 | ~int64 | 361 ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | 362 ~float32 | ~float64 | 363 ~string 364 }