github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/node/node_test.go (about) 1 // Copyright 2015 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The Spectrum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package node 18 19 import ( 20 "errors" 21 "io/ioutil" 22 "os" 23 "reflect" 24 "testing" 25 "time" 26 27 "github.com/SmartMeshFoundation/Spectrum/crypto" 28 "github.com/SmartMeshFoundation/Spectrum/p2p" 29 "github.com/SmartMeshFoundation/Spectrum/rpc" 30 ) 31 32 var ( 33 testNodeKey, _ = crypto.GenerateKey() 34 ) 35 36 func testNodeConfig() *Config { 37 return &Config{ 38 Name: "test node", 39 P2P: p2p.Config{PrivateKey: testNodeKey}, 40 } 41 } 42 43 // Tests that an empty protocol stack can be started, restarted and stopped. 44 func TestNodeLifeCycle(t *testing.T) { 45 stack, err := New(testNodeConfig()) 46 if err != nil { 47 t.Fatalf("failed to create protocol stack: %v", err) 48 } 49 // Ensure that a stopped node can be stopped again 50 for i := 0; i < 3; i++ { 51 if err := stack.Stop(); err != ErrNodeStopped { 52 t.Fatalf("iter %d: stop failure mismatch: have %v, want %v", i, err, ErrNodeStopped) 53 } 54 } 55 // Ensure that a node can be successfully started, but only once 56 if err := stack.Start(); err != nil { 57 t.Fatalf("failed to start node: %v", err) 58 } 59 if err := stack.Start(); err != ErrNodeRunning { 60 t.Fatalf("start failure mismatch: have %v, want %v ", err, ErrNodeRunning) 61 } 62 // Ensure that a node can be restarted arbitrarily many times 63 for i := 0; i < 3; i++ { 64 if err := stack.Restart(); err != nil { 65 t.Fatalf("iter %d: failed to restart node: %v", i, err) 66 } 67 } 68 // Ensure that a node can be stopped, but only once 69 if err := stack.Stop(); err != nil { 70 t.Fatalf("failed to stop node: %v", err) 71 } 72 if err := stack.Stop(); err != ErrNodeStopped { 73 t.Fatalf("stop failure mismatch: have %v, want %v ", err, ErrNodeStopped) 74 } 75 } 76 77 // Tests that if the data dir is already in use, an appropriate error is returned. 78 func TestNodeUsedDataDir(t *testing.T) { 79 // Create a temporary folder to use as the data directory 80 dir, err := ioutil.TempDir("", "") 81 if err != nil { 82 t.Fatalf("failed to create temporary data directory: %v", err) 83 } 84 defer os.RemoveAll(dir) 85 86 // Create a new node based on the data directory 87 original, err := New(&Config{DataDir: dir}) 88 if err != nil { 89 t.Fatalf("failed to create original protocol stack: %v", err) 90 } 91 if err := original.Start(); err != nil { 92 t.Fatalf("failed to start original protocol stack: %v", err) 93 } 94 defer original.Stop() 95 96 // Create a second node based on the same data directory and ensure failure 97 duplicate, err := New(&Config{DataDir: dir}) 98 if err != nil { 99 t.Fatalf("failed to create duplicate protocol stack: %v", err) 100 } 101 if err := duplicate.Start(); err != ErrDatadirUsed { 102 t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed) 103 } 104 } 105 106 // Tests whether services can be registered and duplicates caught. 107 func TestServiceRegistry(t *testing.T) { 108 stack, err := New(testNodeConfig()) 109 if err != nil { 110 t.Fatalf("failed to create protocol stack: %v", err) 111 } 112 // Register a batch of unique services and ensure they start successfully 113 services := []ServiceConstructor{NewNoopServiceA, NewNoopServiceB, NewNoopServiceC} 114 for i, constructor := range services { 115 if err := stack.Register(constructor); err != nil { 116 t.Fatalf("service #%d: registration failed: %v", i, err) 117 } 118 } 119 if err := stack.Start(); err != nil { 120 t.Fatalf("failed to start original service stack: %v", err) 121 } 122 if err := stack.Stop(); err != nil { 123 t.Fatalf("failed to stop original service stack: %v", err) 124 } 125 // Duplicate one of the services and retry starting the node 126 if err := stack.Register(NewNoopServiceB); err != nil { 127 t.Fatalf("duplicate registration failed: %v", err) 128 } 129 if err := stack.Start(); err == nil { 130 t.Fatalf("duplicate service started") 131 } else { 132 if _, ok := err.(*DuplicateServiceError); !ok { 133 t.Fatalf("duplicate error mismatch: have %v, want %v", err, DuplicateServiceError{}) 134 } 135 } 136 } 137 138 // Tests that registered services get started and stopped correctly. 139 func TestServiceLifeCycle(t *testing.T) { 140 stack, err := New(testNodeConfig()) 141 if err != nil { 142 t.Fatalf("failed to create protocol stack: %v", err) 143 } 144 // Register a batch of life-cycle instrumented services 145 services := map[string]InstrumentingWrapper{ 146 "A": InstrumentedServiceMakerA, 147 "B": InstrumentedServiceMakerB, 148 "C": InstrumentedServiceMakerC, 149 } 150 started := make(map[string]bool) 151 stopped := make(map[string]bool) 152 153 for id, maker := range services { 154 id := id // Closure for the constructor 155 constructor := func(*ServiceContext) (Service, error) { 156 return &InstrumentedService{ 157 startHook: func(*p2p.Server) { started[id] = true }, 158 stopHook: func() { stopped[id] = true }, 159 }, nil 160 } 161 if err := stack.Register(maker(constructor)); err != nil { 162 t.Fatalf("service %s: registration failed: %v", id, err) 163 } 164 } 165 // Start the node and check that all services are running 166 if err := stack.Start(); err != nil { 167 t.Fatalf("failed to start protocol stack: %v", err) 168 } 169 for id := range services { 170 if !started[id] { 171 t.Fatalf("service %s: freshly started service not running", id) 172 } 173 if stopped[id] { 174 t.Fatalf("service %s: freshly started service already stopped", id) 175 } 176 } 177 // Stop the node and check that all services have been stopped 178 if err := stack.Stop(); err != nil { 179 t.Fatalf("failed to stop protocol stack: %v", err) 180 } 181 for id := range services { 182 if !stopped[id] { 183 t.Fatalf("service %s: freshly terminated service still running", id) 184 } 185 } 186 } 187 188 // Tests that services are restarted cleanly as new instances. 189 func TestServiceRestarts(t *testing.T) { 190 stack, err := New(testNodeConfig()) 191 if err != nil { 192 t.Fatalf("failed to create protocol stack: %v", err) 193 } 194 // Define a service that does not support restarts 195 var ( 196 running bool 197 started int 198 ) 199 constructor := func(*ServiceContext) (Service, error) { 200 running = false 201 202 return &InstrumentedService{ 203 startHook: func(*p2p.Server) { 204 if running { 205 panic("already running") 206 } 207 running = true 208 started++ 209 }, 210 }, nil 211 } 212 // Register the service and start the protocol stack 213 if err := stack.Register(constructor); err != nil { 214 t.Fatalf("failed to register the service: %v", err) 215 } 216 if err := stack.Start(); err != nil { 217 t.Fatalf("failed to start protocol stack: %v", err) 218 } 219 defer stack.Stop() 220 221 if !running || started != 1 { 222 t.Fatalf("running/started mismatch: have %v/%d, want true/1", running, started) 223 } 224 // Restart the stack a few times and check successful service restarts 225 for i := 0; i < 3; i++ { 226 if err := stack.Restart(); err != nil { 227 t.Fatalf("iter %d: failed to restart stack: %v", i, err) 228 } 229 } 230 if !running || started != 4 { 231 t.Fatalf("running/started mismatch: have %v/%d, want true/4", running, started) 232 } 233 } 234 235 // Tests that if a service fails to initialize itself, none of the other services 236 // will be allowed to even start. 237 func TestServiceConstructionAbortion(t *testing.T) { 238 stack, err := New(testNodeConfig()) 239 if err != nil { 240 t.Fatalf("failed to create protocol stack: %v", err) 241 } 242 // Define a batch of good services 243 services := map[string]InstrumentingWrapper{ 244 "A": InstrumentedServiceMakerA, 245 "B": InstrumentedServiceMakerB, 246 "C": InstrumentedServiceMakerC, 247 } 248 started := make(map[string]bool) 249 for id, maker := range services { 250 id := id // Closure for the constructor 251 constructor := func(*ServiceContext) (Service, error) { 252 return &InstrumentedService{ 253 startHook: func(*p2p.Server) { started[id] = true }, 254 }, nil 255 } 256 if err := stack.Register(maker(constructor)); err != nil { 257 t.Fatalf("service %s: registration failed: %v", id, err) 258 } 259 } 260 // Register a service that fails to construct itself 261 failure := errors.New("fail") 262 failer := func(*ServiceContext) (Service, error) { 263 return nil, failure 264 } 265 if err := stack.Register(failer); err != nil { 266 t.Fatalf("failer registration failed: %v", err) 267 } 268 // Start the protocol stack and ensure none of the services get started 269 for i := 0; i < 100; i++ { 270 if err := stack.Start(); err != failure { 271 t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure) 272 } 273 for id := range services { 274 if started[id] { 275 t.Fatalf("service %s: started should not have", id) 276 } 277 delete(started, id) 278 } 279 } 280 } 281 282 // Tests that if a service fails to start, all others started before it will be 283 // shut down. 284 func TestServiceStartupAbortion(t *testing.T) { 285 stack, err := New(testNodeConfig()) 286 if err != nil { 287 t.Fatalf("failed to create protocol stack: %v", err) 288 } 289 // Register a batch of good services 290 services := map[string]InstrumentingWrapper{ 291 "A": InstrumentedServiceMakerA, 292 "B": InstrumentedServiceMakerB, 293 "C": InstrumentedServiceMakerC, 294 } 295 started := make(map[string]bool) 296 stopped := make(map[string]bool) 297 298 for id, maker := range services { 299 id := id // Closure for the constructor 300 constructor := func(*ServiceContext) (Service, error) { 301 return &InstrumentedService{ 302 startHook: func(*p2p.Server) { started[id] = true }, 303 stopHook: func() { stopped[id] = true }, 304 }, nil 305 } 306 if err := stack.Register(maker(constructor)); err != nil { 307 t.Fatalf("service %s: registration failed: %v", id, err) 308 } 309 } 310 // Register a service that fails to start 311 failure := errors.New("fail") 312 failer := func(*ServiceContext) (Service, error) { 313 return &InstrumentedService{ 314 start: failure, 315 }, nil 316 } 317 if err := stack.Register(failer); err != nil { 318 t.Fatalf("failer registration failed: %v", err) 319 } 320 // Start the protocol stack and ensure all started services stop 321 for i := 0; i < 100; i++ { 322 if err := stack.Start(); err != failure { 323 t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure) 324 } 325 for id := range services { 326 if started[id] && !stopped[id] { 327 t.Fatalf("service %s: started but not stopped", id) 328 } 329 delete(started, id) 330 delete(stopped, id) 331 } 332 } 333 } 334 335 // Tests that even if a registered service fails to shut down cleanly, it does 336 // not influece the rest of the shutdown invocations. 337 func TestServiceTerminationGuarantee(t *testing.T) { 338 stack, err := New(testNodeConfig()) 339 if err != nil { 340 t.Fatalf("failed to create protocol stack: %v", err) 341 } 342 // Register a batch of good services 343 services := map[string]InstrumentingWrapper{ 344 "A": InstrumentedServiceMakerA, 345 "B": InstrumentedServiceMakerB, 346 "C": InstrumentedServiceMakerC, 347 } 348 started := make(map[string]bool) 349 stopped := make(map[string]bool) 350 351 for id, maker := range services { 352 id := id // Closure for the constructor 353 constructor := func(*ServiceContext) (Service, error) { 354 return &InstrumentedService{ 355 startHook: func(*p2p.Server) { started[id] = true }, 356 stopHook: func() { stopped[id] = true }, 357 }, nil 358 } 359 if err := stack.Register(maker(constructor)); err != nil { 360 t.Fatalf("service %s: registration failed: %v", id, err) 361 } 362 } 363 // Register a service that fails to shot down cleanly 364 failure := errors.New("fail") 365 failer := func(*ServiceContext) (Service, error) { 366 return &InstrumentedService{ 367 stop: failure, 368 }, nil 369 } 370 if err := stack.Register(failer); err != nil { 371 t.Fatalf("failer registration failed: %v", err) 372 } 373 // Start the protocol stack, and ensure that a failing shut down terminates all 374 for i := 0; i < 100; i++ { 375 // Start the stack and make sure all is online 376 if err := stack.Start(); err != nil { 377 t.Fatalf("iter %d: failed to start protocol stack: %v", i, err) 378 } 379 for id := range services { 380 if !started[id] { 381 t.Fatalf("iter %d, service %s: service not running", i, id) 382 } 383 if stopped[id] { 384 t.Fatalf("iter %d, service %s: service already stopped", i, id) 385 } 386 } 387 // Stop the stack, verify failure and check all terminations 388 err := stack.Stop() 389 if err, ok := err.(*StopError); !ok { 390 t.Fatalf("iter %d: termination failure mismatch: have %v, want StopError", i, err) 391 } else { 392 failer := reflect.TypeOf(&InstrumentedService{}) 393 if err.Services[failer] != failure { 394 t.Fatalf("iter %d: failer termination failure mismatch: have %v, want %v", i, err.Services[failer], failure) 395 } 396 if len(err.Services) != 1 { 397 t.Fatalf("iter %d: failure count mismatch: have %d, want %d", i, len(err.Services), 1) 398 } 399 } 400 for id := range services { 401 if !stopped[id] { 402 t.Fatalf("iter %d, service %s: service not terminated", i, id) 403 } 404 delete(started, id) 405 delete(stopped, id) 406 } 407 } 408 } 409 410 // TestServiceRetrieval tests that individual services can be retrieved. 411 func TestServiceRetrieval(t *testing.T) { 412 // Create a simple stack and register two service types 413 stack, err := New(testNodeConfig()) 414 if err != nil { 415 t.Fatalf("failed to create protocol stack: %v", err) 416 } 417 if err := stack.Register(NewNoopService); err != nil { 418 t.Fatalf("noop service registration failed: %v", err) 419 } 420 if err := stack.Register(NewInstrumentedService); err != nil { 421 t.Fatalf("instrumented service registration failed: %v", err) 422 } 423 // Make sure none of the services can be retrieved until started 424 var noopServ *NoopService 425 if err := stack.Service(&noopServ); err != ErrNodeStopped { 426 t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, ErrNodeStopped) 427 } 428 var instServ *InstrumentedService 429 if err := stack.Service(&instServ); err != ErrNodeStopped { 430 t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, ErrNodeStopped) 431 } 432 // Start the stack and ensure everything is retrievable now 433 if err := stack.Start(); err != nil { 434 t.Fatalf("failed to start stack: %v", err) 435 } 436 defer stack.Stop() 437 438 if err := stack.Service(&noopServ); err != nil { 439 t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, nil) 440 } 441 if err := stack.Service(&instServ); err != nil { 442 t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, nil) 443 } 444 } 445 446 // Tests that all protocols defined by individual services get launched. 447 func TestProtocolGather(t *testing.T) { 448 stack, err := New(testNodeConfig()) 449 if err != nil { 450 t.Fatalf("failed to create protocol stack: %v", err) 451 } 452 // Register a batch of services with some configured number of protocols 453 services := map[string]struct { 454 Count int 455 Maker InstrumentingWrapper 456 }{ 457 "Zero Protocols": {0, InstrumentedServiceMakerA}, 458 "Single Protocol": {1, InstrumentedServiceMakerB}, 459 "Many Protocols": {25, InstrumentedServiceMakerC}, 460 } 461 for id, config := range services { 462 protocols := make([]p2p.Protocol, config.Count) 463 for i := 0; i < len(protocols); i++ { 464 protocols[i].Name = id 465 protocols[i].Version = uint(i) 466 } 467 constructor := func(*ServiceContext) (Service, error) { 468 return &InstrumentedService{ 469 protocols: protocols, 470 }, nil 471 } 472 if err := stack.Register(config.Maker(constructor)); err != nil { 473 t.Fatalf("service %s: registration failed: %v", id, err) 474 } 475 } 476 // Start the services and ensure all protocols start successfully 477 if err := stack.Start(); err != nil { 478 t.Fatalf("failed to start protocol stack: %v", err) 479 } 480 defer stack.Stop() 481 482 protocols := stack.Server().Protocols 483 if len(protocols) != 26 { 484 t.Fatalf("mismatching number of protocols launched: have %d, want %d", len(protocols), 26) 485 } 486 for id, config := range services { 487 for ver := 0; ver < config.Count; ver++ { 488 launched := false 489 for i := 0; i < len(protocols); i++ { 490 if protocols[i].Name == id && protocols[i].Version == uint(ver) { 491 launched = true 492 break 493 } 494 } 495 if !launched { 496 t.Errorf("configured protocol not launched: %s v%d", id, ver) 497 } 498 } 499 } 500 } 501 502 // Tests that all APIs defined by individual services get exposed. 503 func TestAPIGather(t *testing.T) { 504 stack, err := New(testNodeConfig()) 505 if err != nil { 506 t.Fatalf("failed to create protocol stack: %v", err) 507 } 508 // Register a batch of services with some configured APIs 509 calls := make(chan string, 1) 510 makeAPI := func(result string) *OneMethodApi { 511 return &OneMethodApi{fun: func() { calls <- result }} 512 } 513 services := map[string]struct { 514 APIs []rpc.API 515 Maker InstrumentingWrapper 516 }{ 517 "Zero APIs": { 518 []rpc.API{}, InstrumentedServiceMakerA}, 519 "Single API": { 520 []rpc.API{ 521 {Namespace: "single", Version: "1", Service: makeAPI("single.v1"), Public: true}, 522 }, InstrumentedServiceMakerB}, 523 "Many APIs": { 524 []rpc.API{ 525 {Namespace: "multi", Version: "1", Service: makeAPI("multi.v1"), Public: true}, 526 {Namespace: "multi.v2", Version: "2", Service: makeAPI("multi.v2"), Public: true}, 527 {Namespace: "multi.v2.nested", Version: "2", Service: makeAPI("multi.v2.nested"), Public: true}, 528 }, InstrumentedServiceMakerC}, 529 } 530 531 for id, config := range services { 532 config := config 533 constructor := func(*ServiceContext) (Service, error) { 534 return &InstrumentedService{apis: config.APIs}, nil 535 } 536 if err := stack.Register(config.Maker(constructor)); err != nil { 537 t.Fatalf("service %s: registration failed: %v", id, err) 538 } 539 } 540 // Start the services and ensure all API start successfully 541 if err := stack.Start(); err != nil { 542 t.Fatalf("failed to start protocol stack: %v", err) 543 } 544 defer stack.Stop() 545 546 // Connect to the RPC server and verify the various registered endpoints 547 client, err := stack.Attach() 548 if err != nil { 549 t.Fatalf("failed to connect to the inproc API server: %v", err) 550 } 551 defer client.Close() 552 553 tests := []struct { 554 Method string 555 Result string 556 }{ 557 {"single_theOneMethod", "single.v1"}, 558 {"multi_theOneMethod", "multi.v1"}, 559 {"multi.v2_theOneMethod", "multi.v2"}, 560 {"multi.v2.nested_theOneMethod", "multi.v2.nested"}, 561 } 562 for i, test := range tests { 563 if err := client.Call(nil, test.Method); err != nil { 564 t.Errorf("test %d: API request failed: %v", i, err) 565 } 566 select { 567 case result := <-calls: 568 if result != test.Result { 569 t.Errorf("test %d: result mismatch: have %s, want %s", i, result, test.Result) 570 } 571 case <-time.After(time.Second): 572 t.Fatalf("test %d: rpc execution timeout", i) 573 } 574 } 575 }