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