github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/network/node/node_test.go (about) 1 package node 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 "reflect" 8 "testing" 9 "time" 10 11 "github.com/neatlab/neatio/network/p2p" 12 "github.com/neatlab/neatio/network/rpc" 13 "github.com/neatlab/neatio/utilities/crypto" 14 ) 15 16 var ( 17 testNodeKey, _ = crypto.GenerateKey() 18 ) 19 20 func testNodeConfig() *Config { 21 return &Config{ 22 Name: "test node", 23 P2P: p2p.Config{PrivateKey: testNodeKey}, 24 } 25 } 26 27 func TestNodeLifeCycle(t *testing.T) { 28 stack, err := New(testNodeConfig()) 29 if err != nil { 30 t.Fatalf("failed to create protocol stack: %v", err) 31 } 32 33 for i := 0; i < 3; i++ { 34 if err := stack.Stop(); err != ErrNodeStopped { 35 t.Fatalf("iter %d: stop failure mismatch: have %v, want %v", i, err, ErrNodeStopped) 36 } 37 } 38 39 if err := stack.Start(); err != nil { 40 t.Fatalf("failed to start node: %v", err) 41 } 42 if err := stack.Start(); err != ErrNodeRunning { 43 t.Fatalf("start failure mismatch: have %v, want %v ", err, ErrNodeRunning) 44 } 45 46 for i := 0; i < 3; i++ { 47 if err := stack.Restart(); err != nil { 48 t.Fatalf("iter %d: failed to restart node: %v", i, err) 49 } 50 } 51 52 if err := stack.Stop(); err != nil { 53 t.Fatalf("failed to stop node: %v", err) 54 } 55 if err := stack.Stop(); err != ErrNodeStopped { 56 t.Fatalf("stop failure mismatch: have %v, want %v ", err, ErrNodeStopped) 57 } 58 } 59 60 func TestNodeUsedDataDir(t *testing.T) { 61 62 dir, err := ioutil.TempDir("", "") 63 if err != nil { 64 t.Fatalf("failed to create temporary data directory: %v", err) 65 } 66 defer os.RemoveAll(dir) 67 68 original, err := New(&Config{DataDir: dir}) 69 if err != nil { 70 t.Fatalf("failed to create original protocol stack: %v", err) 71 } 72 if err := original.Start(); err != nil { 73 t.Fatalf("failed to start original protocol stack: %v", err) 74 } 75 defer original.Stop() 76 77 duplicate, err := New(&Config{DataDir: dir}) 78 if err != nil { 79 t.Fatalf("failed to create duplicate protocol stack: %v", err) 80 } 81 if err := duplicate.Start(); err != ErrDatadirUsed { 82 t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed) 83 } 84 } 85 86 func TestServiceRegistry(t *testing.T) { 87 stack, err := New(testNodeConfig()) 88 if err != nil { 89 t.Fatalf("failed to create protocol stack: %v", err) 90 } 91 92 services := []ServiceConstructor{NewNoopServiceA, NewNoopServiceB, NewNoopServiceC} 93 for i, constructor := range services { 94 if err := stack.Register(constructor); err != nil { 95 t.Fatalf("service #%d: registration failed: %v", i, err) 96 } 97 } 98 if err := stack.Start(); err != nil { 99 t.Fatalf("failed to start original service stack: %v", err) 100 } 101 if err := stack.Stop(); err != nil { 102 t.Fatalf("failed to stop original service stack: %v", err) 103 } 104 105 if err := stack.Register(NewNoopServiceB); err != nil { 106 t.Fatalf("duplicate registration failed: %v", err) 107 } 108 if err := stack.Start(); err == nil { 109 t.Fatalf("duplicate service started") 110 } else { 111 if _, ok := err.(*DuplicateServiceError); !ok { 112 t.Fatalf("duplicate error mismatch: have %v, want %v", err, DuplicateServiceError{}) 113 } 114 } 115 } 116 117 func TestServiceLifeCycle(t *testing.T) { 118 stack, err := New(testNodeConfig()) 119 if err != nil { 120 t.Fatalf("failed to create protocol stack: %v", err) 121 } 122 123 services := map[string]InstrumentingWrapper{ 124 "A": InstrumentedServiceMakerA, 125 "B": InstrumentedServiceMakerB, 126 "C": InstrumentedServiceMakerC, 127 } 128 started := make(map[string]bool) 129 stopped := make(map[string]bool) 130 131 for id, maker := range services { 132 id := id 133 constructor := func(*ServiceContext) (Service, error) { 134 return &InstrumentedService{ 135 startHook: func(*p2p.Server) { started[id] = true }, 136 stopHook: func() { stopped[id] = true }, 137 }, nil 138 } 139 if err := stack.Register(maker(constructor)); err != nil { 140 t.Fatalf("service %s: registration failed: %v", id, err) 141 } 142 } 143 144 if err := stack.Start(); err != nil { 145 t.Fatalf("failed to start protocol stack: %v", err) 146 } 147 for id := range services { 148 if !started[id] { 149 t.Fatalf("service %s: freshly started service not running", id) 150 } 151 if stopped[id] { 152 t.Fatalf("service %s: freshly started service already stopped", id) 153 } 154 } 155 156 if err := stack.Stop(); err != nil { 157 t.Fatalf("failed to stop protocol stack: %v", err) 158 } 159 for id := range services { 160 if !stopped[id] { 161 t.Fatalf("service %s: freshly terminated service still running", id) 162 } 163 } 164 } 165 166 func TestServiceRestarts(t *testing.T) { 167 stack, err := New(testNodeConfig()) 168 if err != nil { 169 t.Fatalf("failed to create protocol stack: %v", err) 170 } 171 172 var ( 173 running bool 174 started int 175 ) 176 constructor := func(*ServiceContext) (Service, error) { 177 running = false 178 179 return &InstrumentedService{ 180 startHook: func(*p2p.Server) { 181 if running { 182 panic("already running") 183 } 184 running = true 185 started++ 186 }, 187 }, nil 188 } 189 190 if err := stack.Register(constructor); err != nil { 191 t.Fatalf("failed to register the service: %v", err) 192 } 193 if err := stack.Start(); err != nil { 194 t.Fatalf("failed to start protocol stack: %v", err) 195 } 196 defer stack.Stop() 197 198 if !running || started != 1 { 199 t.Fatalf("running/started mismatch: have %v/%d, want true/1", running, started) 200 } 201 202 for i := 0; i < 3; i++ { 203 if err := stack.Restart(); err != nil { 204 t.Fatalf("iter %d: failed to restart stack: %v", i, err) 205 } 206 } 207 if !running || started != 4 { 208 t.Fatalf("running/started mismatch: have %v/%d, want true/4", running, started) 209 } 210 } 211 212 func TestServiceConstructionAbortion(t *testing.T) { 213 stack, err := New(testNodeConfig()) 214 if err != nil { 215 t.Fatalf("failed to create protocol stack: %v", err) 216 } 217 218 services := map[string]InstrumentingWrapper{ 219 "A": InstrumentedServiceMakerA, 220 "B": InstrumentedServiceMakerB, 221 "C": InstrumentedServiceMakerC, 222 } 223 started := make(map[string]bool) 224 for id, maker := range services { 225 id := id 226 constructor := func(*ServiceContext) (Service, error) { 227 return &InstrumentedService{ 228 startHook: func(*p2p.Server) { started[id] = true }, 229 }, nil 230 } 231 if err := stack.Register(maker(constructor)); err != nil { 232 t.Fatalf("service %s: registration failed: %v", id, err) 233 } 234 } 235 236 failure := errors.New("fail") 237 failer := func(*ServiceContext) (Service, error) { 238 return nil, failure 239 } 240 if err := stack.Register(failer); err != nil { 241 t.Fatalf("failer registration failed: %v", err) 242 } 243 244 for i := 0; i < 100; i++ { 245 if err := stack.Start(); err != failure { 246 t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure) 247 } 248 for id := range services { 249 if started[id] { 250 t.Fatalf("service %s: started should not have", id) 251 } 252 delete(started, id) 253 } 254 } 255 } 256 257 func TestServiceStartupAbortion(t *testing.T) { 258 stack, err := New(testNodeConfig()) 259 if err != nil { 260 t.Fatalf("failed to create protocol stack: %v", err) 261 } 262 263 services := map[string]InstrumentingWrapper{ 264 "A": InstrumentedServiceMakerA, 265 "B": InstrumentedServiceMakerB, 266 "C": InstrumentedServiceMakerC, 267 } 268 started := make(map[string]bool) 269 stopped := make(map[string]bool) 270 271 for id, maker := range services { 272 id := id 273 constructor := func(*ServiceContext) (Service, error) { 274 return &InstrumentedService{ 275 startHook: func(*p2p.Server) { started[id] = true }, 276 stopHook: func() { stopped[id] = true }, 277 }, nil 278 } 279 if err := stack.Register(maker(constructor)); err != nil { 280 t.Fatalf("service %s: registration failed: %v", id, err) 281 } 282 } 283 284 failure := errors.New("fail") 285 failer := func(*ServiceContext) (Service, error) { 286 return &InstrumentedService{ 287 start: failure, 288 }, nil 289 } 290 if err := stack.Register(failer); err != nil { 291 t.Fatalf("failer registration failed: %v", err) 292 } 293 294 for i := 0; i < 100; i++ { 295 if err := stack.Start(); err != failure { 296 t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure) 297 } 298 for id := range services { 299 if started[id] && !stopped[id] { 300 t.Fatalf("service %s: started but not stopped", id) 301 } 302 delete(started, id) 303 delete(stopped, id) 304 } 305 } 306 } 307 308 func TestServiceTerminationGuarantee(t *testing.T) { 309 stack, err := New(testNodeConfig()) 310 if err != nil { 311 t.Fatalf("failed to create protocol stack: %v", err) 312 } 313 314 services := map[string]InstrumentingWrapper{ 315 "A": InstrumentedServiceMakerA, 316 "B": InstrumentedServiceMakerB, 317 "C": InstrumentedServiceMakerC, 318 } 319 started := make(map[string]bool) 320 stopped := make(map[string]bool) 321 322 for id, maker := range services { 323 id := id 324 constructor := func(*ServiceContext) (Service, error) { 325 return &InstrumentedService{ 326 startHook: func(*p2p.Server) { started[id] = true }, 327 stopHook: func() { stopped[id] = true }, 328 }, nil 329 } 330 if err := stack.Register(maker(constructor)); err != nil { 331 t.Fatalf("service %s: registration failed: %v", id, err) 332 } 333 } 334 335 failure := errors.New("fail") 336 failer := func(*ServiceContext) (Service, error) { 337 return &InstrumentedService{ 338 stop: failure, 339 }, nil 340 } 341 if err := stack.Register(failer); err != nil { 342 t.Fatalf("failer registration failed: %v", err) 343 } 344 345 for i := 0; i < 100; i++ { 346 347 if err := stack.Start(); err != nil { 348 t.Fatalf("iter %d: failed to start protocol stack: %v", i, err) 349 } 350 for id := range services { 351 if !started[id] { 352 t.Fatalf("iter %d, service %s: service not running", i, id) 353 } 354 if stopped[id] { 355 t.Fatalf("iter %d, service %s: service already stopped", i, id) 356 } 357 } 358 359 err := stack.Stop() 360 if err, ok := err.(*StopError); !ok { 361 t.Fatalf("iter %d: termination failure mismatch: have %v, want StopError", i, err) 362 } else { 363 failer := reflect.TypeOf(&InstrumentedService{}) 364 if err.Services[failer] != failure { 365 t.Fatalf("iter %d: failer termination failure mismatch: have %v, want %v", i, err.Services[failer], failure) 366 } 367 if len(err.Services) != 1 { 368 t.Fatalf("iter %d: failure count mismatch: have %d, want %d", i, len(err.Services), 1) 369 } 370 } 371 for id := range services { 372 if !stopped[id] { 373 t.Fatalf("iter %d, service %s: service not terminated", i, id) 374 } 375 delete(started, id) 376 delete(stopped, id) 377 } 378 } 379 } 380 381 func TestServiceRetrieval(t *testing.T) { 382 383 stack, err := New(testNodeConfig()) 384 if err != nil { 385 t.Fatalf("failed to create protocol stack: %v", err) 386 } 387 if err := stack.Register(NewNoopService); err != nil { 388 t.Fatalf("noop service registration failed: %v", err) 389 } 390 if err := stack.Register(NewInstrumentedService); err != nil { 391 t.Fatalf("instrumented service registration failed: %v", err) 392 } 393 394 var noopServ *NoopService 395 if err := stack.Service(&noopServ); err != ErrNodeStopped { 396 t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, ErrNodeStopped) 397 } 398 var instServ *InstrumentedService 399 if err := stack.Service(&instServ); err != ErrNodeStopped { 400 t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, ErrNodeStopped) 401 } 402 403 if err := stack.Start(); err != nil { 404 t.Fatalf("failed to start stack: %v", err) 405 } 406 defer stack.Stop() 407 408 if err := stack.Service(&noopServ); err != nil { 409 t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, nil) 410 } 411 if err := stack.Service(&instServ); err != nil { 412 t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, nil) 413 } 414 } 415 416 func TestProtocolGather(t *testing.T) { 417 stack, err := New(testNodeConfig()) 418 if err != nil { 419 t.Fatalf("failed to create protocol stack: %v", err) 420 } 421 422 services := map[string]struct { 423 Count int 424 Maker InstrumentingWrapper 425 }{ 426 "Zero Protocols": {0, InstrumentedServiceMakerA}, 427 "Single Protocol": {1, InstrumentedServiceMakerB}, 428 "Many Protocols": {25, InstrumentedServiceMakerC}, 429 } 430 for id, config := range services { 431 protocols := make([]p2p.Protocol, config.Count) 432 for i := 0; i < len(protocols); i++ { 433 protocols[i].Name = id 434 protocols[i].Version = uint(i) 435 } 436 constructor := func(*ServiceContext) (Service, error) { 437 return &InstrumentedService{ 438 protocols: protocols, 439 }, nil 440 } 441 if err := stack.Register(config.Maker(constructor)); err != nil { 442 t.Fatalf("service %s: registration failed: %v", id, err) 443 } 444 } 445 446 if err := stack.Start(); err != nil { 447 t.Fatalf("failed to start protocol stack: %v", err) 448 } 449 defer stack.Stop() 450 451 protocols := stack.Server().Protocols 452 if len(protocols) != 26 { 453 t.Fatalf("mismatching number of protocols launched: have %d, want %d", len(protocols), 26) 454 } 455 for id, config := range services { 456 for ver := 0; ver < config.Count; ver++ { 457 launched := false 458 for i := 0; i < len(protocols); i++ { 459 if protocols[i].Name == id && protocols[i].Version == uint(ver) { 460 launched = true 461 break 462 } 463 } 464 if !launched { 465 t.Errorf("configured protocol not launched: %s v%d", id, ver) 466 } 467 } 468 } 469 } 470 471 func TestAPIGather(t *testing.T) { 472 stack, err := New(testNodeConfig()) 473 if err != nil { 474 t.Fatalf("failed to create protocol stack: %v", err) 475 } 476 477 calls := make(chan string, 1) 478 makeAPI := func(result string) *OneMethodApi { 479 return &OneMethodApi{fun: func() { calls <- result }} 480 } 481 services := map[string]struct { 482 APIs []rpc.API 483 Maker InstrumentingWrapper 484 }{ 485 "Zero APIs": { 486 []rpc.API{}, InstrumentedServiceMakerA}, 487 "Single API": { 488 []rpc.API{ 489 {Namespace: "single", Version: "1", Service: makeAPI("single.v1"), Public: true}, 490 }, InstrumentedServiceMakerB}, 491 "Many APIs": { 492 []rpc.API{ 493 {Namespace: "multi", Version: "1", Service: makeAPI("multi.v1"), Public: true}, 494 {Namespace: "multi.v2", Version: "2", Service: makeAPI("multi.v2"), Public: true}, 495 {Namespace: "multi.v2.nested", Version: "2", Service: makeAPI("multi.v2.nested"), Public: true}, 496 }, InstrumentedServiceMakerC}, 497 } 498 499 for id, config := range services { 500 config := config 501 constructor := func(*ServiceContext) (Service, error) { 502 return &InstrumentedService{apis: config.APIs}, nil 503 } 504 if err := stack.Register(config.Maker(constructor)); err != nil { 505 t.Fatalf("service %s: registration failed: %v", id, err) 506 } 507 } 508 509 if err := stack.Start(); err != nil { 510 t.Fatalf("failed to start protocol stack: %v", err) 511 } 512 defer stack.Stop() 513 514 client, err := stack.Attach() 515 if err != nil { 516 t.Fatalf("failed to connect to the inproc API server: %v", err) 517 } 518 defer client.Close() 519 520 tests := []struct { 521 Method string 522 Result string 523 }{ 524 {"single_theOneMethod", "single.v1"}, 525 {"multi_theOneMethod", "multi.v1"}, 526 {"multi.v2_theOneMethod", "multi.v2"}, 527 {"multi.v2.nested_theOneMethod", "multi.v2.nested"}, 528 } 529 for i, test := range tests { 530 if err := client.Call(nil, test.Method); err != nil { 531 t.Errorf("test %d: API request failed: %v", i, err) 532 } 533 select { 534 case result := <-calls: 535 if result != test.Result { 536 t.Errorf("test %d: result mismatch: have %s, want %s", i, result, test.Result) 537 } 538 case <-time.After(time.Second): 539 t.Fatalf("test %d: rpc execution timeout", i) 540 } 541 } 542 }