github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/rpc/lib/client/ws/client_test.go (about) 1 package ws 2 3 import ( 4 "context" 5 "encoding/json" 6 "net/http" 7 "net/http/httptest" 8 "strings" 9 "testing" 10 "time" 11 12 types "github.com/gnolang/gno/tm2/pkg/bft/rpc/lib/types" 13 "github.com/gorilla/websocket" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 // createTestServer creates a test WS server 19 func createTestServer( 20 t *testing.T, 21 handler http.Handler, 22 ) *httptest.Server { 23 t.Helper() 24 25 s := httptest.NewServer(handler) 26 t.Cleanup(s.Close) 27 28 return s 29 } 30 31 func TestClient_SendRequest(t *testing.T) { 32 t.Parallel() 33 34 t.Run("request timed out", func(t *testing.T) { 35 t.Parallel() 36 37 var ( 38 upgrader = websocket.Upgrader{} 39 40 request = types.RPCRequest{ 41 JSONRPC: "2.0", 42 ID: types.JSONRPCStringID("id"), 43 } 44 ) 45 46 ctx, cancelFn := context.WithCancel(context.Background()) 47 defer cancelFn() 48 49 // Create the server 50 handler := func(w http.ResponseWriter, r *http.Request) { 51 c, err := upgrader.Upgrade(w, r, nil) 52 require.NoError(t, err) 53 54 defer c.Close() 55 56 for { 57 _, message, err := c.ReadMessage() 58 if websocket.IsUnexpectedCloseError(err) { 59 return 60 } 61 62 require.NoError(t, err) 63 64 // Parse the message 65 var req types.RPCRequest 66 require.NoError(t, json.Unmarshal(message, &req)) 67 require.Equal(t, request.ID.String(), req.ID.String()) 68 69 // Simulate context cancellation mid-request parsing 70 cancelFn() 71 } 72 } 73 74 s := createTestServer(t, http.HandlerFunc(handler)) 75 url := "ws" + strings.TrimPrefix(s.URL, "http") 76 77 // Create the client 78 c, err := NewClient(url) 79 require.NoError(t, err) 80 81 defer func() { 82 assert.NoError(t, c.Close()) 83 }() 84 85 // Try to send the request, but wait for 86 // the context to be cancelled 87 response, err := c.SendRequest(ctx, request) 88 require.Nil(t, response) 89 90 assert.ErrorIs(t, err, ErrTimedOut) 91 }) 92 93 t.Run("valid request sent", func(t *testing.T) { 94 t.Parallel() 95 96 var ( 97 upgrader = websocket.Upgrader{} 98 99 request = types.RPCRequest{ 100 JSONRPC: "2.0", 101 ID: types.JSONRPCStringID("id"), 102 } 103 104 response = types.RPCResponse{ 105 JSONRPC: "2.0", 106 ID: request.ID, 107 } 108 ) 109 110 // Create the server 111 handler := func(w http.ResponseWriter, r *http.Request) { 112 c, err := upgrader.Upgrade(w, r, nil) 113 require.NoError(t, err) 114 115 defer c.Close() 116 117 for { 118 mt, message, err := c.ReadMessage() 119 if websocket.IsUnexpectedCloseError(err) { 120 return 121 } 122 123 require.NoError(t, err) 124 125 // Parse the message 126 var req types.RPCRequest 127 require.NoError(t, json.Unmarshal(message, &req)) 128 require.Equal(t, request.ID.String(), req.ID.String()) 129 130 marshalledResponse, err := json.Marshal(response) 131 require.NoError(t, err) 132 133 require.NoError(t, c.WriteMessage(mt, marshalledResponse)) 134 } 135 } 136 137 s := createTestServer(t, http.HandlerFunc(handler)) 138 url := "ws" + strings.TrimPrefix(s.URL, "http") 139 140 // Create the client 141 c, err := NewClient(url) 142 require.NoError(t, err) 143 144 defer func() { 145 assert.NoError(t, c.Close()) 146 }() 147 148 // Try to send the valid request 149 ctx, cancelFn := context.WithTimeout(context.Background(), time.Second*5) 150 defer cancelFn() 151 152 resp, err := c.SendRequest(ctx, request) 153 require.NoError(t, err) 154 155 assert.Equal(t, response.ID, resp.ID) 156 assert.Equal(t, response.JSONRPC, resp.JSONRPC) 157 assert.Equal(t, response.Result, resp.Result) 158 assert.Equal(t, response.Error, resp.Error) 159 }) 160 } 161 162 func TestClient_SendBatch(t *testing.T) { 163 t.Parallel() 164 165 t.Run("batch timed out", func(t *testing.T) { 166 t.Parallel() 167 168 var ( 169 upgrader = websocket.Upgrader{} 170 171 request = types.RPCRequest{ 172 JSONRPC: "2.0", 173 ID: types.JSONRPCStringID("id"), 174 } 175 176 batch = types.RPCRequests{request} 177 ) 178 179 ctx, cancelFn := context.WithCancel(context.Background()) 180 defer cancelFn() 181 182 // Create the server 183 handler := func(w http.ResponseWriter, r *http.Request) { 184 c, err := upgrader.Upgrade(w, r, nil) 185 require.NoError(t, err) 186 187 defer c.Close() 188 189 for { 190 _, message, err := c.ReadMessage() 191 if websocket.IsUnexpectedCloseError(err) { 192 return 193 } 194 195 require.NoError(t, err) 196 197 // Parse the message 198 var req types.RPCRequests 199 require.NoError(t, json.Unmarshal(message, &req)) 200 201 require.Len(t, req, 1) 202 require.Equal(t, request.ID.String(), req[0].ID.String()) 203 204 // Simulate context cancellation mid-request parsing 205 cancelFn() 206 } 207 } 208 209 s := createTestServer(t, http.HandlerFunc(handler)) 210 url := "ws" + strings.TrimPrefix(s.URL, "http") 211 212 // Create the client 213 c, err := NewClient(url) 214 require.NoError(t, err) 215 216 defer func() { 217 assert.NoError(t, c.Close()) 218 }() 219 220 // Try to send the request, but wait for 221 // the context to be cancelled 222 response, err := c.SendBatch(ctx, batch) 223 require.Nil(t, response) 224 225 assert.ErrorIs(t, err, ErrTimedOut) 226 }) 227 228 t.Run("valid batch sent", func(t *testing.T) { 229 t.Parallel() 230 231 var ( 232 upgrader = websocket.Upgrader{} 233 234 request = types.RPCRequest{ 235 JSONRPC: "2.0", 236 ID: types.JSONRPCStringID("id"), 237 } 238 239 response = types.RPCResponse{ 240 JSONRPC: "2.0", 241 ID: request.ID, 242 } 243 244 batch = types.RPCRequests{request} 245 batchResponse = types.RPCResponses{response} 246 ) 247 248 // Create the server 249 handler := func(w http.ResponseWriter, r *http.Request) { 250 c, err := upgrader.Upgrade(w, r, nil) 251 require.NoError(t, err) 252 253 defer c.Close() 254 255 for { 256 mt, message, err := c.ReadMessage() 257 if websocket.IsUnexpectedCloseError(err) { 258 return 259 } 260 261 require.NoError(t, err) 262 263 // Parse the message 264 var req types.RPCRequests 265 require.NoError(t, json.Unmarshal(message, &req)) 266 267 require.Len(t, req, 1) 268 require.Equal(t, request.ID.String(), req[0].ID.String()) 269 270 marshalledResponse, err := json.Marshal(batchResponse) 271 require.NoError(t, err) 272 273 require.NoError(t, c.WriteMessage(mt, marshalledResponse)) 274 } 275 } 276 277 s := createTestServer(t, http.HandlerFunc(handler)) 278 url := "ws" + strings.TrimPrefix(s.URL, "http") 279 280 // Create the client 281 c, err := NewClient(url) 282 require.NoError(t, err) 283 284 defer func() { 285 assert.NoError(t, c.Close()) 286 }() 287 288 // Try to send the valid request 289 ctx, cancelFn := context.WithTimeout(context.Background(), time.Second*5) 290 defer cancelFn() 291 292 resp, err := c.SendBatch(ctx, batch) 293 require.NoError(t, err) 294 295 require.Len(t, resp, 1) 296 297 assert.Equal(t, response.ID, resp[0].ID) 298 assert.Equal(t, response.JSONRPC, resp[0].JSONRPC) 299 assert.Equal(t, response.Result, resp[0].Result) 300 assert.Equal(t, response.Error, resp[0].Error) 301 }) 302 }