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 }