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