github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/rpc/test/helpers.go (about)

     1  package rpctest
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"time"
    10  
    11  	abci "github.com/line/ostracon/abci/types"
    12  	cfg "github.com/line/ostracon/config"
    13  	"github.com/line/ostracon/libs/log"
    14  	tmnet "github.com/line/ostracon/libs/net"
    15  	nm "github.com/line/ostracon/node"
    16  	"github.com/line/ostracon/p2p"
    17  	"github.com/line/ostracon/privval"
    18  	"github.com/line/ostracon/proxy"
    19  	ctypes "github.com/line/ostracon/rpc/core/types"
    20  	core_grpc "github.com/line/ostracon/rpc/grpc"
    21  	rpcclient "github.com/line/ostracon/rpc/jsonrpc/client"
    22  )
    23  
    24  // Options helps with specifying some parameters for our RPC testing for greater
    25  // control.
    26  type Options struct {
    27  	suppressStdout bool
    28  	recreateConfig bool
    29  }
    30  
    31  var globalConfig *cfg.Config
    32  var defaultOptions = Options{
    33  	suppressStdout: false,
    34  	recreateConfig: false,
    35  }
    36  
    37  func waitForRPC() {
    38  	laddr := GetConfig().RPC.ListenAddress
    39  	client, err := rpcclient.New(laddr)
    40  	if err != nil {
    41  		panic(err)
    42  	}
    43  	result := new(ctypes.ResultStatus)
    44  	for {
    45  		_, err := client.Call(context.Background(), "status", map[string]interface{}{}, result)
    46  		if err == nil {
    47  			return
    48  		}
    49  
    50  		fmt.Println("error", err)
    51  		time.Sleep(time.Millisecond)
    52  	}
    53  }
    54  
    55  func waitForGRPC() {
    56  	client := GetGRPCClient()
    57  	for {
    58  		_, err := client.Ping(context.Background(), &core_grpc.RequestPing{})
    59  		if err == nil {
    60  			return
    61  		}
    62  	}
    63  }
    64  
    65  // f**ing long, but unique for each test
    66  func makePathname() string {
    67  	// get path
    68  	p, err := os.Getwd()
    69  	if err != nil {
    70  		panic(err)
    71  	}
    72  	// fmt.Println(p)
    73  	sep := string(filepath.Separator)
    74  	return strings.ReplaceAll(p, sep, "_")
    75  }
    76  
    77  func randPort() int {
    78  	port, err := tmnet.GetFreePort()
    79  	if err != nil {
    80  		panic(err)
    81  	}
    82  	return port
    83  }
    84  
    85  func makeAddrs() (string, string, string) {
    86  	return fmt.Sprintf("tcp://127.0.0.1:%d", randPort()),
    87  		fmt.Sprintf("tcp://127.0.0.1:%d", randPort()),
    88  		fmt.Sprintf("tcp://127.0.0.1:%d", randPort())
    89  }
    90  
    91  func createConfig() *cfg.Config {
    92  	pathname := makePathname()
    93  	c := cfg.ResetTestRoot(pathname)
    94  
    95  	// and we use random ports to run in parallel
    96  	tm, rpc, grpc := makeAddrs()
    97  	c.P2P.ListenAddress = tm
    98  	c.RPC.ListenAddress = rpc
    99  	c.RPC.CORSAllowedOrigins = []string{"https://ostracon.com/"}
   100  	c.RPC.GRPCListenAddress = grpc
   101  	return c
   102  }
   103  
   104  // GetConfig returns a config for the test cases as a singleton
   105  func GetConfig(forceCreate ...bool) *cfg.Config {
   106  	if globalConfig == nil || (len(forceCreate) > 0 && forceCreate[0]) {
   107  		globalConfig = createConfig()
   108  	}
   109  	return globalConfig
   110  }
   111  
   112  func GetGRPCClient() core_grpc.BroadcastAPIClient {
   113  	grpcAddr := globalConfig.RPC.GRPCListenAddress
   114  	return core_grpc.StartGRPCClient(grpcAddr)
   115  }
   116  
   117  // StartOstracon starts a test ostracon server in a go routine and returns when it is initialized
   118  func StartOstracon(app abci.Application, opts ...func(*Options)) *nm.Node {
   119  	nodeOpts := defaultOptions
   120  	for _, opt := range opts {
   121  		opt(&nodeOpts)
   122  	}
   123  	node := NewOstracon(app, &nodeOpts)
   124  	err := node.Start()
   125  	if err != nil {
   126  		panic(err)
   127  	}
   128  
   129  	// wait for rpc
   130  	waitForRPC()
   131  	waitForGRPC()
   132  
   133  	if !nodeOpts.suppressStdout {
   134  		fmt.Println("Ostracon running!")
   135  	}
   136  
   137  	return node
   138  }
   139  
   140  // StopOstracon stops a test ostracon server, waits until it's stopped and
   141  // cleans up test/config files.
   142  func StopOstracon(node *nm.Node) {
   143  	if err := node.Stop(); err != nil {
   144  		node.Logger.Error("Error when trying to stop node", "err", err)
   145  	}
   146  	node.Wait()
   147  	os.RemoveAll(node.Config().RootDir)
   148  }
   149  
   150  // NewOstracon creates a new ostracon server and sleeps forever
   151  func NewOstracon(app abci.Application, opts *Options) *nm.Node {
   152  	// Create & start node
   153  	config := GetConfig(opts.recreateConfig)
   154  	var logger log.Logger
   155  	if opts.suppressStdout {
   156  		logger = log.NewNopLogger()
   157  	} else {
   158  		logger = log.NewOCLogger(log.NewSyncWriter(os.Stdout))
   159  		logger = log.NewFilter(logger, log.AllowError())
   160  	}
   161  	pvKeyFile := config.PrivValidatorKeyFile()
   162  	pvKeyStateFile := config.PrivValidatorStateFile()
   163  	pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile)
   164  	papp := proxy.NewLocalClientCreator(app)
   165  	nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
   166  	if err != nil {
   167  		panic(err)
   168  	}
   169  	node, err := nm.NewNode(config, pv, nodeKey, papp,
   170  		nm.DefaultGenesisDocProviderFunc(config),
   171  		nm.DefaultDBProvider,
   172  		nm.DefaultMetricsProvider(config.Instrumentation),
   173  		logger)
   174  	if err != nil {
   175  		panic(err)
   176  	}
   177  	return node
   178  }
   179  
   180  // SuppressStdout is an option that tries to make sure the RPC test Ostracon
   181  // node doesn't log anything to stdout.
   182  func SuppressStdout(o *Options) {
   183  	o.suppressStdout = true
   184  }
   185  
   186  // RecreateConfig instructs the RPC test to recreate the configuration each
   187  // time, instead of treating it as a global singleton.
   188  func RecreateConfig(o *Options) {
   189  	o.recreateConfig = true
   190  }