github.com/julesgoullee/go-ethereum@v1.9.7/p2p/simulations/network_test.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package simulations 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "fmt" 24 "reflect" 25 "strconv" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/ethereum/go-ethereum/node" 32 "github.com/ethereum/go-ethereum/p2p/enode" 33 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 34 ) 35 36 // Tests that a created snapshot with a minimal service only contains the expected connections 37 // and that a network when loaded with this snapshot only contains those same connections 38 func TestSnapshot(t *testing.T) { 39 40 // PART I 41 // create snapshot from ring network 42 43 // this is a minimal service, whose protocol will take exactly one message OR close of connection before quitting 44 adapter := adapters.NewSimAdapter(adapters.Services{ 45 "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) { 46 return NewNoopService(nil), nil 47 }, 48 }) 49 50 // create network 51 network := NewNetwork(adapter, &NetworkConfig{ 52 DefaultService: "noopwoop", 53 }) 54 // \todo consider making a member of network, set to true threadsafe when shutdown 55 runningOne := true 56 defer func() { 57 if runningOne { 58 network.Shutdown() 59 } 60 }() 61 62 // create and start nodes 63 nodeCount := 20 64 ids := make([]enode.ID, nodeCount) 65 for i := 0; i < nodeCount; i++ { 66 conf := adapters.RandomNodeConfig() 67 node, err := network.NewNodeWithConfig(conf) 68 if err != nil { 69 t.Fatalf("error creating node: %s", err) 70 } 71 if err := network.Start(node.ID()); err != nil { 72 t.Fatalf("error starting node: %s", err) 73 } 74 ids[i] = node.ID() 75 } 76 77 // subscribe to peer events 78 evC := make(chan *Event) 79 sub := network.Events().Subscribe(evC) 80 defer sub.Unsubscribe() 81 82 // connect nodes in a ring 83 // spawn separate thread to avoid deadlock in the event listeners 84 go func() { 85 for i, id := range ids { 86 peerID := ids[(i+1)%len(ids)] 87 if err := network.Connect(id, peerID); err != nil { 88 t.Fatal(err) 89 } 90 } 91 }() 92 93 // collect connection events up to expected number 94 ctx, cancel := context.WithTimeout(context.TODO(), time.Second) 95 defer cancel() 96 checkIds := make(map[enode.ID][]enode.ID) 97 connEventCount := nodeCount 98 OUTER: 99 for { 100 select { 101 case <-ctx.Done(): 102 t.Fatal(ctx.Err()) 103 case ev := <-evC: 104 if ev.Type == EventTypeConn && !ev.Control { 105 106 // fail on any disconnect 107 if !ev.Conn.Up { 108 t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other) 109 } 110 checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other) 111 checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One) 112 connEventCount-- 113 log.Debug("ev", "count", connEventCount) 114 if connEventCount == 0 { 115 break OUTER 116 } 117 } 118 } 119 } 120 121 // create snapshot of current network 122 snap, err := network.Snapshot() 123 if err != nil { 124 t.Fatal(err) 125 } 126 j, err := json.Marshal(snap) 127 if err != nil { 128 t.Fatal(err) 129 } 130 log.Debug("snapshot taken", "nodes", len(snap.Nodes), "conns", len(snap.Conns), "json", string(j)) 131 132 // verify that the snap element numbers check out 133 if len(checkIds) != len(snap.Conns) || len(checkIds) != len(snap.Nodes) { 134 t.Fatalf("snapshot wrong node,conn counts %d,%d != %d", len(snap.Nodes), len(snap.Conns), len(checkIds)) 135 } 136 137 // shut down sim network 138 runningOne = false 139 sub.Unsubscribe() 140 network.Shutdown() 141 142 // check that we have all the expected connections in the snapshot 143 for nodid, nodConns := range checkIds { 144 for _, nodConn := range nodConns { 145 var match bool 146 for _, snapConn := range snap.Conns { 147 if snapConn.One == nodid && snapConn.Other == nodConn { 148 match = true 149 break 150 } else if snapConn.Other == nodid && snapConn.One == nodConn { 151 match = true 152 break 153 } 154 } 155 if !match { 156 t.Fatalf("snapshot missing conn %v -> %v", nodid, nodConn) 157 } 158 } 159 } 160 log.Info("snapshot checked") 161 162 // PART II 163 // load snapshot and verify that exactly same connections are formed 164 165 adapter = adapters.NewSimAdapter(adapters.Services{ 166 "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) { 167 return NewNoopService(nil), nil 168 }, 169 }) 170 network = NewNetwork(adapter, &NetworkConfig{ 171 DefaultService: "noopwoop", 172 }) 173 defer func() { 174 network.Shutdown() 175 }() 176 177 // subscribe to peer events 178 // every node up and conn up event will generate one additional control event 179 // therefore multiply the count by two 180 evC = make(chan *Event, (len(snap.Conns)*2)+(len(snap.Nodes)*2)) 181 sub = network.Events().Subscribe(evC) 182 defer sub.Unsubscribe() 183 184 // load the snapshot 185 // spawn separate thread to avoid deadlock in the event listeners 186 err = network.Load(snap) 187 if err != nil { 188 t.Fatal(err) 189 } 190 191 // collect connection events up to expected number 192 ctx, cancel = context.WithTimeout(context.TODO(), time.Second*3) 193 defer cancel() 194 195 connEventCount = nodeCount 196 197 OuterTwo: 198 for { 199 select { 200 case <-ctx.Done(): 201 t.Fatal(ctx.Err()) 202 case ev := <-evC: 203 if ev.Type == EventTypeConn && !ev.Control { 204 205 // fail on any disconnect 206 if !ev.Conn.Up { 207 t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other) 208 } 209 log.Debug("conn", "on", ev.Conn.One, "other", ev.Conn.Other) 210 checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other) 211 checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One) 212 connEventCount-- 213 log.Debug("ev", "count", connEventCount) 214 if connEventCount == 0 { 215 break OuterTwo 216 } 217 } 218 } 219 } 220 221 // check that we have all expected connections in the network 222 for _, snapConn := range snap.Conns { 223 var match bool 224 for nodid, nodConns := range checkIds { 225 for _, nodConn := range nodConns { 226 if snapConn.One == nodid && snapConn.Other == nodConn { 227 match = true 228 break 229 } else if snapConn.Other == nodid && snapConn.One == nodConn { 230 match = true 231 break 232 } 233 } 234 } 235 if !match { 236 t.Fatalf("network missing conn %v -> %v", snapConn.One, snapConn.Other) 237 } 238 } 239 240 // verify that network didn't generate any other additional connection events after the ones we have collected within a reasonable period of time 241 ctx, cancel = context.WithTimeout(context.TODO(), time.Second) 242 defer cancel() 243 select { 244 case <-ctx.Done(): 245 case ev := <-evC: 246 if ev.Type == EventTypeConn { 247 t.Fatalf("Superfluous conn found %v -> %v", ev.Conn.One, ev.Conn.Other) 248 } 249 } 250 251 // This test validates if all connections from the snapshot 252 // are created in the network. 253 t.Run("conns after load", func(t *testing.T) { 254 // Create new network. 255 n := NewNetwork( 256 adapters.NewSimAdapter(adapters.Services{ 257 "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) { 258 return NewNoopService(nil), nil 259 }, 260 }), 261 &NetworkConfig{ 262 DefaultService: "noopwoop", 263 }, 264 ) 265 defer n.Shutdown() 266 267 // Load the same snapshot. 268 err := n.Load(snap) 269 if err != nil { 270 t.Fatal(err) 271 } 272 273 // Check every connection from the snapshot 274 // if it is in the network, too. 275 for _, c := range snap.Conns { 276 if n.GetConn(c.One, c.Other) == nil { 277 t.Errorf("missing connection: %s -> %s", c.One, c.Other) 278 } 279 } 280 }) 281 } 282 283 // TestNetworkSimulation creates a multi-node simulation network with each node 284 // connected in a ring topology, checks that all nodes successfully handshake 285 // with each other and that a snapshot fully represents the desired topology 286 func TestNetworkSimulation(t *testing.T) { 287 // create simulation network with 20 testService nodes 288 adapter := adapters.NewSimAdapter(adapters.Services{ 289 "test": newTestService, 290 }) 291 network := NewNetwork(adapter, &NetworkConfig{ 292 DefaultService: "test", 293 }) 294 defer network.Shutdown() 295 nodeCount := 20 296 ids := make([]enode.ID, nodeCount) 297 for i := 0; i < nodeCount; i++ { 298 conf := adapters.RandomNodeConfig() 299 node, err := network.NewNodeWithConfig(conf) 300 if err != nil { 301 t.Fatalf("error creating node: %s", err) 302 } 303 if err := network.Start(node.ID()); err != nil { 304 t.Fatalf("error starting node: %s", err) 305 } 306 ids[i] = node.ID() 307 } 308 309 // perform a check which connects the nodes in a ring (so each node is 310 // connected to exactly two peers) and then checks that all nodes 311 // performed two handshakes by checking their peerCount 312 action := func(_ context.Context) error { 313 for i, id := range ids { 314 peerID := ids[(i+1)%len(ids)] 315 if err := network.Connect(id, peerID); err != nil { 316 return err 317 } 318 } 319 return nil 320 } 321 check := func(ctx context.Context, id enode.ID) (bool, error) { 322 // check we haven't run out of time 323 select { 324 case <-ctx.Done(): 325 return false, ctx.Err() 326 default: 327 } 328 329 // get the node 330 node := network.GetNode(id) 331 if node == nil { 332 return false, fmt.Errorf("unknown node: %s", id) 333 } 334 335 // check it has exactly two peers 336 client, err := node.Client() 337 if err != nil { 338 return false, err 339 } 340 var peerCount int64 341 if err := client.CallContext(ctx, &peerCount, "test_peerCount"); err != nil { 342 return false, err 343 } 344 switch { 345 case peerCount < 2: 346 return false, nil 347 case peerCount == 2: 348 return true, nil 349 default: 350 return false, fmt.Errorf("unexpected peerCount: %d", peerCount) 351 } 352 } 353 354 timeout := 30 * time.Second 355 ctx, cancel := context.WithTimeout(context.Background(), timeout) 356 defer cancel() 357 358 // trigger a check every 100ms 359 trigger := make(chan enode.ID) 360 go triggerChecks(ctx, ids, trigger, 100*time.Millisecond) 361 362 result := NewSimulation(network).Run(ctx, &Step{ 363 Action: action, 364 Trigger: trigger, 365 Expect: &Expectation{ 366 Nodes: ids, 367 Check: check, 368 }, 369 }) 370 if result.Error != nil { 371 t.Fatalf("simulation failed: %s", result.Error) 372 } 373 374 // take a network snapshot and check it contains the correct topology 375 snap, err := network.Snapshot() 376 if err != nil { 377 t.Fatal(err) 378 } 379 if len(snap.Nodes) != nodeCount { 380 t.Fatalf("expected snapshot to contain %d nodes, got %d", nodeCount, len(snap.Nodes)) 381 } 382 if len(snap.Conns) != nodeCount { 383 t.Fatalf("expected snapshot to contain %d connections, got %d", nodeCount, len(snap.Conns)) 384 } 385 for i, id := range ids { 386 conn := snap.Conns[i] 387 if conn.One != id { 388 t.Fatalf("expected conn[%d].One to be %s, got %s", i, id, conn.One) 389 } 390 peerID := ids[(i+1)%len(ids)] 391 if conn.Other != peerID { 392 t.Fatalf("expected conn[%d].Other to be %s, got %s", i, peerID, conn.Other) 393 } 394 } 395 } 396 397 func createTestNodes(count int, network *Network) (nodes []*Node, err error) { 398 for i := 0; i < count; i++ { 399 nodeConf := adapters.RandomNodeConfig() 400 node, err := network.NewNodeWithConfig(nodeConf) 401 if err != nil { 402 return nil, err 403 } 404 if err := network.Start(node.ID()); err != nil { 405 return nil, err 406 } 407 408 nodes = append(nodes, node) 409 } 410 411 return nodes, nil 412 } 413 414 func createTestNodesWithProperty(property string, count int, network *Network) (propertyNodes []*Node, err error) { 415 for i := 0; i < count; i++ { 416 nodeConf := adapters.RandomNodeConfig() 417 nodeConf.Properties = append(nodeConf.Properties, property) 418 419 node, err := network.NewNodeWithConfig(nodeConf) 420 if err != nil { 421 return nil, err 422 } 423 if err := network.Start(node.ID()); err != nil { 424 return nil, err 425 } 426 427 propertyNodes = append(propertyNodes, node) 428 } 429 430 return propertyNodes, nil 431 } 432 433 // TestGetNodeIDs creates a set of nodes and attempts to retrieve their IDs,. 434 // It then tests again whilst excluding a node ID from being returned. 435 // If a node ID is not returned, or more node IDs than expected are returned, the test fails. 436 func TestGetNodeIDs(t *testing.T) { 437 adapter := adapters.NewSimAdapter(adapters.Services{ 438 "test": newTestService, 439 }) 440 network := NewNetwork(adapter, &NetworkConfig{ 441 DefaultService: "test", 442 }) 443 defer network.Shutdown() 444 445 numNodes := 5 446 nodes, err := createTestNodes(numNodes, network) 447 if err != nil { 448 t.Fatalf("Could not creat test nodes %v", err) 449 } 450 451 gotNodeIDs := network.GetNodeIDs() 452 if len(gotNodeIDs) != numNodes { 453 t.Fatalf("Expected %d nodes, got %d", numNodes, len(gotNodeIDs)) 454 } 455 456 for _, node1 := range nodes { 457 match := false 458 for _, node2ID := range gotNodeIDs { 459 if bytes.Equal(node1.ID().Bytes(), node2ID.Bytes()) { 460 match = true 461 break 462 } 463 } 464 465 if !match { 466 t.Fatalf("A created node was not returned by GetNodes(), ID: %s", node1.ID().String()) 467 } 468 } 469 470 excludeNodeID := nodes[3].ID() 471 gotNodeIDsExcl := network.GetNodeIDs(excludeNodeID) 472 if len(gotNodeIDsExcl) != numNodes-1 { 473 t.Fatalf("Expected one less node ID to be returned") 474 } 475 for _, nodeID := range gotNodeIDsExcl { 476 if bytes.Equal(excludeNodeID.Bytes(), nodeID.Bytes()) { 477 t.Fatalf("GetNodeIDs returned the node ID we excluded, ID: %s", nodeID.String()) 478 } 479 } 480 } 481 482 // TestGetNodes creates a set of nodes and attempts to retrieve them again. 483 // It then tests again whilst excluding a node from being returned. 484 // If a node is not returned, or more nodes than expected are returned, the test fails. 485 func TestGetNodes(t *testing.T) { 486 adapter := adapters.NewSimAdapter(adapters.Services{ 487 "test": newTestService, 488 }) 489 network := NewNetwork(adapter, &NetworkConfig{ 490 DefaultService: "test", 491 }) 492 defer network.Shutdown() 493 494 numNodes := 5 495 nodes, err := createTestNodes(numNodes, network) 496 if err != nil { 497 t.Fatalf("Could not creat test nodes %v", err) 498 } 499 500 gotNodes := network.GetNodes() 501 if len(gotNodes) != numNodes { 502 t.Fatalf("Expected %d nodes, got %d", numNodes, len(gotNodes)) 503 } 504 505 for _, node1 := range nodes { 506 match := false 507 for _, node2 := range gotNodes { 508 if bytes.Equal(node1.ID().Bytes(), node2.ID().Bytes()) { 509 match = true 510 break 511 } 512 } 513 514 if !match { 515 t.Fatalf("A created node was not returned by GetNodes(), ID: %s", node1.ID().String()) 516 } 517 } 518 519 excludeNodeID := nodes[3].ID() 520 gotNodesExcl := network.GetNodes(excludeNodeID) 521 if len(gotNodesExcl) != numNodes-1 { 522 t.Fatalf("Expected one less node to be returned") 523 } 524 for _, node := range gotNodesExcl { 525 if bytes.Equal(excludeNodeID.Bytes(), node.ID().Bytes()) { 526 t.Fatalf("GetNodes returned the node we excluded, ID: %s", node.ID().String()) 527 } 528 } 529 } 530 531 // TestGetNodesByID creates a set of nodes and attempts to retrieve a subset of them by ID 532 // If a node is not returned, or more nodes than expected are returned, the test fails. 533 func TestGetNodesByID(t *testing.T) { 534 adapter := adapters.NewSimAdapter(adapters.Services{ 535 "test": newTestService, 536 }) 537 network := NewNetwork(adapter, &NetworkConfig{ 538 DefaultService: "test", 539 }) 540 defer network.Shutdown() 541 542 numNodes := 5 543 nodes, err := createTestNodes(numNodes, network) 544 if err != nil { 545 t.Fatalf("Could not create test nodes: %v", err) 546 } 547 548 numSubsetNodes := 2 549 subsetNodes := nodes[0:numSubsetNodes] 550 var subsetNodeIDs []enode.ID 551 for _, node := range subsetNodes { 552 subsetNodeIDs = append(subsetNodeIDs, node.ID()) 553 } 554 555 gotNodesByID := network.GetNodesByID(subsetNodeIDs) 556 if len(gotNodesByID) != numSubsetNodes { 557 t.Fatalf("Expected %d nodes, got %d", numSubsetNodes, len(gotNodesByID)) 558 } 559 560 for _, node1 := range subsetNodes { 561 match := false 562 for _, node2 := range gotNodesByID { 563 if bytes.Equal(node1.ID().Bytes(), node2.ID().Bytes()) { 564 match = true 565 break 566 } 567 } 568 569 if !match { 570 t.Fatalf("A created node was not returned by GetNodesByID(), ID: %s", node1.ID().String()) 571 } 572 } 573 } 574 575 // TestGetNodesByProperty creates a subset of nodes with a property assigned. 576 // GetNodesByProperty is then checked for correctness by comparing the nodes returned to those initially created. 577 // If a node with a property is not found, or more nodes than expected are returned, the test fails. 578 func TestGetNodesByProperty(t *testing.T) { 579 adapter := adapters.NewSimAdapter(adapters.Services{ 580 "test": newTestService, 581 }) 582 network := NewNetwork(adapter, &NetworkConfig{ 583 DefaultService: "test", 584 }) 585 defer network.Shutdown() 586 587 numNodes := 3 588 _, err := createTestNodes(numNodes, network) 589 if err != nil { 590 t.Fatalf("Failed to create nodes: %v", err) 591 } 592 593 numPropertyNodes := 3 594 propertyTest := "test" 595 propertyNodes, err := createTestNodesWithProperty(propertyTest, numPropertyNodes, network) 596 if err != nil { 597 t.Fatalf("Failed to create nodes with property: %v", err) 598 } 599 600 gotNodesByProperty := network.GetNodesByProperty(propertyTest) 601 if len(gotNodesByProperty) != numPropertyNodes { 602 t.Fatalf("Expected %d nodes with a property, got %d", numPropertyNodes, len(gotNodesByProperty)) 603 } 604 605 for _, node1 := range propertyNodes { 606 match := false 607 for _, node2 := range gotNodesByProperty { 608 if bytes.Equal(node1.ID().Bytes(), node2.ID().Bytes()) { 609 match = true 610 break 611 } 612 } 613 614 if !match { 615 t.Fatalf("A created node with property was not returned by GetNodesByProperty(), ID: %s", node1.ID().String()) 616 } 617 } 618 } 619 620 // TestGetNodeIDsByProperty creates a subset of nodes with a property assigned. 621 // GetNodeIDsByProperty is then checked for correctness by comparing the node IDs returned to those initially created. 622 // If a node ID with a property is not found, or more nodes IDs than expected are returned, the test fails. 623 func TestGetNodeIDsByProperty(t *testing.T) { 624 adapter := adapters.NewSimAdapter(adapters.Services{ 625 "test": newTestService, 626 }) 627 network := NewNetwork(adapter, &NetworkConfig{ 628 DefaultService: "test", 629 }) 630 defer network.Shutdown() 631 632 numNodes := 3 633 _, err := createTestNodes(numNodes, network) 634 if err != nil { 635 t.Fatalf("Failed to create nodes: %v", err) 636 } 637 638 numPropertyNodes := 3 639 propertyTest := "test" 640 propertyNodes, err := createTestNodesWithProperty(propertyTest, numPropertyNodes, network) 641 if err != nil { 642 t.Fatalf("Failed to created nodes with property: %v", err) 643 } 644 645 gotNodeIDsByProperty := network.GetNodeIDsByProperty(propertyTest) 646 if len(gotNodeIDsByProperty) != numPropertyNodes { 647 t.Fatalf("Expected %d nodes with a property, got %d", numPropertyNodes, len(gotNodeIDsByProperty)) 648 } 649 650 for _, node1 := range propertyNodes { 651 match := false 652 id1 := node1.ID() 653 for _, id2 := range gotNodeIDsByProperty { 654 if bytes.Equal(id1.Bytes(), id2.Bytes()) { 655 match = true 656 break 657 } 658 } 659 660 if !match { 661 t.Fatalf("Not all nodes IDs were returned by GetNodeIDsByProperty(), ID: %s", id1.String()) 662 } 663 } 664 } 665 666 func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, interval time.Duration) { 667 tick := time.NewTicker(interval) 668 defer tick.Stop() 669 for { 670 select { 671 case <-tick.C: 672 for _, id := range ids { 673 select { 674 case trigger <- id: 675 case <-ctx.Done(): 676 return 677 } 678 } 679 case <-ctx.Done(): 680 return 681 } 682 } 683 } 684 685 // \todo: refactor to implement shapshots 686 // and connect configuration methods once these are moved from 687 // swarm/network/simulations/connect.go 688 func BenchmarkMinimalService(b *testing.B) { 689 b.Run("ring/32", benchmarkMinimalServiceTmp) 690 } 691 692 func benchmarkMinimalServiceTmp(b *testing.B) { 693 694 // stop timer to discard setup time pollution 695 args := strings.Split(b.Name(), "/") 696 nodeCount, err := strconv.ParseInt(args[2], 10, 16) 697 if err != nil { 698 b.Fatal(err) 699 } 700 701 for i := 0; i < b.N; i++ { 702 // this is a minimal service, whose protocol will close a channel upon run of protocol 703 // making it possible to bench the time it takes for the service to start and protocol actually to be run 704 protoCMap := make(map[enode.ID]map[enode.ID]chan struct{}) 705 adapter := adapters.NewSimAdapter(adapters.Services{ 706 "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) { 707 protoCMap[ctx.Config.ID] = make(map[enode.ID]chan struct{}) 708 svc := NewNoopService(protoCMap[ctx.Config.ID]) 709 return svc, nil 710 }, 711 }) 712 713 // create network 714 network := NewNetwork(adapter, &NetworkConfig{ 715 DefaultService: "noopwoop", 716 }) 717 defer network.Shutdown() 718 719 // create and start nodes 720 ids := make([]enode.ID, nodeCount) 721 for i := 0; i < int(nodeCount); i++ { 722 conf := adapters.RandomNodeConfig() 723 node, err := network.NewNodeWithConfig(conf) 724 if err != nil { 725 b.Fatalf("error creating node: %s", err) 726 } 727 if err := network.Start(node.ID()); err != nil { 728 b.Fatalf("error starting node: %s", err) 729 } 730 ids[i] = node.ID() 731 } 732 733 // ready, set, go 734 b.ResetTimer() 735 736 // connect nodes in a ring 737 for i, id := range ids { 738 peerID := ids[(i+1)%len(ids)] 739 if err := network.Connect(id, peerID); err != nil { 740 b.Fatal(err) 741 } 742 } 743 744 // wait for all protocols to signal to close down 745 ctx, cancel := context.WithTimeout(context.TODO(), time.Second) 746 defer cancel() 747 for nodid, peers := range protoCMap { 748 for peerid, peerC := range peers { 749 log.Debug("getting ", "node", nodid, "peer", peerid) 750 select { 751 case <-ctx.Done(): 752 b.Fatal(ctx.Err()) 753 case <-peerC: 754 } 755 } 756 } 757 } 758 } 759 760 func TestNode_UnmarshalJSON(t *testing.T) { 761 t.Run( 762 "test unmarshal of Node up field", 763 func(t *testing.T) { 764 runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONUpField()) 765 }, 766 ) 767 t.Run( 768 "test unmarshal of Node Config field", 769 func(t *testing.T) { 770 runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONConfigField()) 771 }, 772 ) 773 } 774 775 func runNodeUnmarshalJSON(t *testing.T, tests []nodeUnmarshalTestCase) { 776 t.Helper() 777 for _, tt := range tests { 778 t.Run(tt.name, func(t *testing.T) { 779 var got Node 780 if err := got.UnmarshalJSON([]byte(tt.marshaled)); err != nil { 781 expectErrorMessageToContain(t, err, tt.wantErr) 782 } 783 expectNodeEquality(t, got, tt.want) 784 }) 785 } 786 } 787 788 type nodeUnmarshalTestCase struct { 789 name string 790 marshaled string 791 want Node 792 wantErr string 793 } 794 795 func expectErrorMessageToContain(t *testing.T, got error, want string) { 796 t.Helper() 797 if got == nil && want == "" { 798 return 799 } 800 801 if got == nil && want != "" { 802 t.Errorf("error was expected, got: nil, want: %v", want) 803 return 804 } 805 806 if !strings.Contains(got.Error(), want) { 807 t.Errorf( 808 "unexpected error message, got %v, want: %v", 809 want, 810 got, 811 ) 812 } 813 } 814 815 func expectNodeEquality(t *testing.T, got Node, want Node) { 816 t.Helper() 817 if !reflect.DeepEqual(got, want) { 818 t.Errorf("Node.UnmarshalJSON() = %v, want %v", got, want) 819 } 820 } 821 822 func casesNodeUnmarshalJSONUpField() []nodeUnmarshalTestCase { 823 return []nodeUnmarshalTestCase{ 824 { 825 name: "empty json", 826 marshaled: "{}", 827 want: Node{ 828 up: false, 829 }, 830 }, 831 { 832 name: "a stopped node", 833 marshaled: "{\"up\": false}", 834 want: Node{ 835 up: false, 836 }, 837 }, 838 { 839 name: "a running node", 840 marshaled: "{\"up\": true}", 841 want: Node{ 842 up: true, 843 }, 844 }, 845 { 846 name: "invalid JSON value on valid key", 847 marshaled: "{\"up\": foo}", 848 wantErr: "invalid character", 849 }, 850 { 851 name: "invalid JSON key and value", 852 marshaled: "{foo: bar}", 853 wantErr: "invalid character", 854 }, 855 { 856 name: "bool value expected but got something else (string)", 857 marshaled: "{\"up\": \"true\"}", 858 wantErr: "cannot unmarshal string into Go struct", 859 }, 860 } 861 } 862 863 func casesNodeUnmarshalJSONConfigField() []nodeUnmarshalTestCase { 864 // Don't do a big fuss around testing, as adapters.NodeConfig should 865 // handle it's own serialization. Just do a sanity check. 866 return []nodeUnmarshalTestCase{ 867 { 868 name: "Config field is omitted", 869 marshaled: "{}", 870 want: Node{ 871 Config: nil, 872 }, 873 }, 874 { 875 name: "Config field is nil", 876 marshaled: "{\"config\": nil}", 877 want: Node{ 878 Config: nil, 879 }, 880 }, 881 { 882 name: "a non default Config field", 883 marshaled: "{\"config\":{\"name\":\"node_ecdd0\",\"port\":44665}}", 884 want: Node{ 885 Config: &adapters.NodeConfig{ 886 Name: "node_ecdd0", 887 Port: 44665, 888 }, 889 }, 890 }, 891 } 892 }