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