github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/network/simulations/overlay_test.go (about)

     1  // Copyleft 2018 The susy-graviton Authors
     2  // This file is part of the susy-graviton library.
     3  //
     4  // The susy-graviton 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 susy-graviton library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MSRCHANTABILITY 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 susy-graviton library. If not, see <http://www.gnu.org/licenses/>.
    16  package main
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"net/url"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/susy-go/susy-graviton/p2p/enode"
    30  	"github.com/susy-go/susy-graviton/p2p/simulations"
    31  	"github.com/susy-go/susy-graviton/swarm/log"
    32  )
    33  
    34  var (
    35  	nodeCount = 10
    36  )
    37  
    38  //This test is used to test the overlay simulation.
    39  //As the simulation is executed via a main, it is easily missed on changes
    40  //An automated test will prevent that
    41  //The test just connects to the simulations, starts the network,
    42  //starts the mocker, gets the number of nodes, and stops it again.
    43  //It also provides a documentation on the steps needed by frontends
    44  //to use the simulations
    45  func TestOverlaySim(t *testing.T) {
    46  	t.Skip("Test is flaky, see: https://github.com/sophysphere/susy-graviton/issues/592")
    47  	//start the simulation
    48  	log.Info("Start simulation backend")
    49  	//get the simulation networ; needed to subscribe for up events
    50  	net := newSimulationNetwork()
    51  	//create the overlay simulation
    52  	sim := newOverlaySim(net)
    53  	//create a http test server with it
    54  	srv := httptest.NewServer(sim)
    55  	defer srv.Close()
    56  
    57  	log.Debug("Http simulation server started. Start simulation network")
    58  	//start the simulation network (initialization of simulation)
    59  	resp, err := http.Post(srv.URL+"/start", "application/json", nil)
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  	defer resp.Body.Close()
    64  	if resp.StatusCode != http.StatusOK {
    65  		t.Fatalf("Expected Status Code %d, got %d", http.StatusOK, resp.StatusCode)
    66  	}
    67  
    68  	log.Debug("Start mocker")
    69  	//start the mocker, needs a node count and an ID
    70  	resp, err = http.PostForm(srv.URL+"/mocker/start",
    71  		url.Values{
    72  			"node-count":  {fmt.Sprintf("%d", nodeCount)},
    73  			"mocker-type": {simulations.GetMockerList()[0]},
    74  		})
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	defer resp.Body.Close()
    79  	if resp.StatusCode != http.StatusOK {
    80  		reason, err := ioutil.ReadAll(resp.Body)
    81  		if err != nil {
    82  			t.Fatal(err)
    83  		}
    84  		t.Fatalf("Expected Status Code %d, got %d, response body %s", http.StatusOK, resp.StatusCode, string(reason))
    85  	}
    86  
    87  	//variables needed to wait for nodes being up
    88  	var upCount int
    89  	trigger := make(chan enode.ID)
    90  
    91  	//wait for all nodes to be up
    92  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    93  	defer cancel()
    94  
    95  	//start watching node up events...
    96  	go watchSimEvents(net, ctx, trigger)
    97  
    98  	//...and wait until all expected up events (nodeCount) have been received
    99  LOOP:
   100  	for {
   101  		select {
   102  		case <-trigger:
   103  			//new node up event received, increase counter
   104  			upCount++
   105  			//all expected node up events received
   106  			if upCount == nodeCount {
   107  				break LOOP
   108  			}
   109  		case <-ctx.Done():
   110  			t.Fatalf("Timed out waiting for up events")
   111  		}
   112  
   113  	}
   114  
   115  	//at this point we can query the server
   116  	log.Info("Get number of nodes")
   117  	//get the number of nodes
   118  	resp, err = http.Get(srv.URL + "/nodes")
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  
   123  	defer resp.Body.Close()
   124  	if resp.StatusCode != http.StatusOK {
   125  		t.Fatalf("err %s", resp.Status)
   126  	}
   127  	b, err := ioutil.ReadAll(resp.Body)
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  
   132  	//unmarshal number of nodes from JSON response
   133  	var nodesArr []simulations.Node
   134  	err = json.Unmarshal(b, &nodesArr)
   135  	if err != nil {
   136  		t.Fatal(err)
   137  	}
   138  
   139  	//check if number of nodes received is same as sent
   140  	if len(nodesArr) != nodeCount {
   141  		t.Fatal(fmt.Errorf("Expected %d number of nodes, got %d", nodeCount, len(nodesArr)))
   142  	}
   143  
   144  	//need to let it run for a little while, otherwise stopping it immediately can crash due running nodes
   145  	//wanting to connect to already stopped nodes
   146  	time.Sleep(1 * time.Second)
   147  
   148  	log.Info("Stop the network")
   149  	//stop the network
   150  	resp, err = http.Post(srv.URL+"/stop", "application/json", nil)
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  	defer resp.Body.Close()
   155  	if resp.StatusCode != http.StatusOK {
   156  		t.Fatalf("err %s", resp.Status)
   157  	}
   158  
   159  	log.Info("Reset the network")
   160  	//reset the network (removes all nodes and connections)
   161  	resp, err = http.Post(srv.URL+"/reset", "application/json", nil)
   162  	if err != nil {
   163  		t.Fatal(err)
   164  	}
   165  	defer resp.Body.Close()
   166  	if resp.StatusCode != http.StatusOK {
   167  		t.Fatalf("err %s", resp.Status)
   168  	}
   169  }
   170  
   171  //watch for events so we know when all nodes are up
   172  func watchSimEvents(net *simulations.Network, ctx context.Context, trigger chan enode.ID) {
   173  	events := make(chan *simulations.Event)
   174  	sub := net.Events().Subscribe(events)
   175  	defer sub.Unsubscribe()
   176  
   177  	for {
   178  		select {
   179  		case ev := <-events:
   180  			//only catch node up events
   181  			if ev.Type == simulations.EventTypeNode {
   182  				if ev.Node.Up() {
   183  					log.Debug("got node up event", "event", ev, "node", ev.Node.Config.ID)
   184  					select {
   185  					case trigger <- ev.Node.Config.ID:
   186  					case <-ctx.Done():
   187  						return
   188  					}
   189  				}
   190  			}
   191  		case <-ctx.Done():
   192  			return
   193  		}
   194  	}
   195  }