github.com/gagliardetto/solana-go@v1.11.0/rpc/jsonrpc/jsonrpc_test.go (about) 1 package jsonrpc 2 3 import ( 4 "context" 5 stdjson "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "net/http/httptest" 10 "os" 11 "strconv" 12 "testing" 13 14 . "github.com/onsi/gomega" 15 "github.com/stretchr/testify/require" 16 ) 17 18 // needed to retrieve requests that arrived at httpServer for further investigation 19 var requestChan = make(chan *RequestData, 1) 20 21 // the request datastructure that can be retrieved for test assertions 22 type RequestData struct { 23 request *http.Request 24 body string 25 } 26 27 // set the response body the httpServer should return for the next request 28 var responseBody = "" 29 30 var httpServer *httptest.Server 31 32 // start the testhttp server and stop it when tests are finished 33 func TestMain(m *testing.M) { 34 httpServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 35 data, _ := ioutil.ReadAll(r.Body) 36 defer r.Body.Close() 37 // put request and body to channel for the client to investigate them 38 requestChan <- &RequestData{r, string(data)} 39 40 fmt.Fprintf(w, responseBody) 41 })) 42 defer httpServer.Close() 43 44 os.Exit(m.Run()) 45 } 46 47 func TestSimpleRpcCallHeaderCorrect(t *testing.T) { 48 RegisterTestingT(t) 49 50 rpcClient := NewClient(httpServer.URL) 51 rpcClient.Call(context.Background(), "add", 1, 2) 52 53 req := (<-requestChan).request 54 55 Expect(req.Method).To(Equal("POST")) 56 Expect(req.Header.Get("Content-Type")).To(Equal("application/json")) 57 Expect(req.Header.Get("Accept")).To(Equal("application/json")) 58 } 59 60 // test if the structure of an rpc request is built correctly by validating the data that arrived on the test server 61 func TestRpcClient_Call(t *testing.T) { 62 RegisterTestingT(t) 63 rpcClient := NewClient(httpServer.URL) 64 65 person := Person{ 66 Name: "Alex", 67 Age: 35, 68 Country: "Germany", 69 } 70 71 drink := Drink{ 72 Name: "Cuba Libre", 73 Ingredients: []string{"rum", "cola"}, 74 } 75 76 useFixedID = true 77 78 rpcClient.Call(context.Background(), "missingParam") 79 Expect((<-requestChan).body).To(Equal(`{"method":"missingParam","id":1,"jsonrpc":"2.0"}`)) 80 81 rpcClient.Call(context.Background(), "nullParam", nil) 82 Expect((<-requestChan).body).To(Equal(`{"method":"nullParam","params":[null],"id":1,"jsonrpc":"2.0"}`)) 83 84 rpcClient.Call(context.Background(), "nullParams", nil, nil) 85 Expect((<-requestChan).body).To(Equal(`{"method":"nullParams","params":[null,null],"id":1,"jsonrpc":"2.0"}`)) 86 87 rpcClient.Call(context.Background(), "emptyParams", []interface{}{}) 88 Expect((<-requestChan).body).To(Equal(`{"method":"emptyParams","params":[],"id":1,"jsonrpc":"2.0"}`)) 89 90 rpcClient.Call(context.Background(), "emptyAnyParams", []string{}) 91 Expect((<-requestChan).body).To(Equal(`{"method":"emptyAnyParams","params":[],"id":1,"jsonrpc":"2.0"}`)) 92 93 rpcClient.Call(context.Background(), "emptyObject", struct{}{}) 94 Expect((<-requestChan).body).To(Equal(`{"method":"emptyObject","params":{},"id":1,"jsonrpc":"2.0"}`)) 95 96 rpcClient.Call(context.Background(), "emptyObjectList", []struct{}{{}, {}}) 97 Expect((<-requestChan).body).To(Equal(`{"method":"emptyObjectList","params":[{},{}],"id":1,"jsonrpc":"2.0"}`)) 98 99 rpcClient.Call(context.Background(), "boolParam", true) 100 Expect((<-requestChan).body).To(Equal(`{"method":"boolParam","params":[true],"id":1,"jsonrpc":"2.0"}`)) 101 102 rpcClient.Call(context.Background(), "boolParams", true, false, true) 103 Expect((<-requestChan).body).To(Equal(`{"method":"boolParams","params":[true,false,true],"id":1,"jsonrpc":"2.0"}`)) 104 105 rpcClient.Call(context.Background(), "stringParam", "Alex") 106 Expect((<-requestChan).body).To(Equal(`{"method":"stringParam","params":["Alex"],"id":1,"jsonrpc":"2.0"}`)) 107 108 rpcClient.Call(context.Background(), "stringParams", "JSON", "RPC") 109 Expect((<-requestChan).body).To(Equal(`{"method":"stringParams","params":["JSON","RPC"],"id":1,"jsonrpc":"2.0"}`)) 110 111 rpcClient.Call(context.Background(), "numberParam", 123) 112 Expect((<-requestChan).body).To(Equal(`{"method":"numberParam","params":[123],"id":1,"jsonrpc":"2.0"}`)) 113 114 rpcClient.Call(context.Background(), "numberParams", 123, 321) 115 Expect((<-requestChan).body).To(Equal(`{"method":"numberParams","params":[123,321],"id":1,"jsonrpc":"2.0"}`)) 116 117 rpcClient.Call(context.Background(), "floatParam", 1.23) 118 Expect((<-requestChan).body).To(Equal(`{"method":"floatParam","params":[1.23],"id":1,"jsonrpc":"2.0"}`)) 119 120 rpcClient.Call(context.Background(), "floatParams", 1.23, 3.21) 121 Expect((<-requestChan).body).To(Equal(`{"method":"floatParams","params":[1.23,3.21],"id":1,"jsonrpc":"2.0"}`)) 122 123 rpcClient.Call(context.Background(), "manyParams", "Alex", 35, true, nil, 2.34) 124 Expect((<-requestChan).body).To(Equal(`{"method":"manyParams","params":["Alex",35,true,null,2.34],"id":1,"jsonrpc":"2.0"}`)) 125 126 rpcClient.Call(context.Background(), "emptyMissingPublicFieldObject", struct{ name string }{name: "Alex"}) 127 Expect((<-requestChan).body).To(Equal(`{"method":"emptyMissingPublicFieldObject","params":{},"id":1,"jsonrpc":"2.0"}`)) 128 129 rpcClient.Call(context.Background(), "singleStruct", person) 130 Expect((<-requestChan).body).To(Equal(`{"method":"singleStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":1,"jsonrpc":"2.0"}`)) 131 132 rpcClient.Call(context.Background(), "singlePointerToStruct", &person) 133 Expect((<-requestChan).body).To(Equal(`{"method":"singlePointerToStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":1,"jsonrpc":"2.0"}`)) 134 135 pp := &person 136 rpcClient.Call(context.Background(), "doublePointerStruct", &pp) 137 Expect((<-requestChan).body).To(Equal(`{"method":"doublePointerStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":1,"jsonrpc":"2.0"}`)) 138 139 rpcClient.Call(context.Background(), "multipleStructs", person, &drink) 140 Expect((<-requestChan).body).To(Equal(`{"method":"multipleStructs","params":[{"name":"Alex","age":35,"country":"Germany"},{"name":"Cuba Libre","ingredients":["rum","cola"]}],"id":1,"jsonrpc":"2.0"}`)) 141 142 rpcClient.Call(context.Background(), "singleStructInArray", []interface{}{person}) 143 Expect((<-requestChan).body).To(Equal(`{"method":"singleStructInArray","params":[{"name":"Alex","age":35,"country":"Germany"}],"id":1,"jsonrpc":"2.0"}`)) 144 145 rpcClient.Call(context.Background(), "namedParameters", map[string]interface{}{ 146 "name": "Alex", 147 "age": 35, 148 }) 149 Expect((<-requestChan).body).To(Equal(`{"method":"namedParameters","params":{"age":35,"name":"Alex"},"id":1,"jsonrpc":"2.0"}`)) 150 151 rpcClient.Call(context.Background(), "anonymousStructNoTags", struct { 152 Name string 153 Age int 154 }{"Alex", 33}) 155 Expect((<-requestChan).body).To(Equal(`{"method":"anonymousStructNoTags","params":{"Name":"Alex","Age":33},"id":1,"jsonrpc":"2.0"}`)) 156 157 rpcClient.Call(context.Background(), "anonymousStructWithTags", struct { 158 Name string `json:"name"` 159 Age int `json:"age"` 160 }{"Alex", 33}) 161 Expect((<-requestChan).body).To(Equal(`{"method":"anonymousStructWithTags","params":{"name":"Alex","age":33},"id":1,"jsonrpc":"2.0"}`)) 162 163 rpcClient.Call(context.Background(), "structWithNullField", struct { 164 Name string `json:"name"` 165 Address *string `json:"address"` 166 }{"Alex", nil}) 167 Expect((<-requestChan).body).To(Equal(`{"method":"structWithNullField","params":{"name":"Alex","address":null},"id":1,"jsonrpc":"2.0"}`)) 168 } 169 170 func TestRpcClient_CallBatch(t *testing.T) { 171 RegisterTestingT(t) 172 rpcClient := NewClient(httpServer.URL) 173 174 person := Person{ 175 Name: "Alex", 176 Age: 35, 177 Country: "Germany", 178 } 179 180 drink := Drink{ 181 Name: "Cuba Libre", 182 Ingredients: []string{"rum", "cola"}, 183 } 184 185 // invalid parameters are possible by manually defining *RPCRequest 186 rpcClient.CallBatch(context.Background(), RPCRequests{ 187 { 188 Method: "singleRequest", 189 Params: 3, // invalid, should be []int{3} 190 }, 191 }) 192 Expect((<-requestChan).body).To(Equal(`[{"method":"singleRequest","params":3,"id":0,"jsonrpc":"2.0"}]`)) 193 194 // better use Params() unless you know what you are doing 195 rpcClient.CallBatch(context.Background(), RPCRequests{ 196 { 197 Method: "singleRequest", 198 Params: Params(3), // always valid json rpc 199 }, 200 }) 201 Expect((<-requestChan).body).To(Equal(`[{"method":"singleRequest","params":[3],"id":0,"jsonrpc":"2.0"}]`)) 202 203 // even better, use NewRequest() 204 rpcClient.CallBatch(context.Background(), RPCRequests{ 205 NewRequest("multipleRequests1", 1), 206 NewRequest("multipleRequests2", 2), 207 NewRequest("multipleRequests3", 3), 208 }) 209 Expect((<-requestChan).body).To(Equal(`[{"method":"multipleRequests1","params":[1],"id":0,"jsonrpc":"2.0"},{"method":"multipleRequests2","params":[2],"id":1,"jsonrpc":"2.0"},{"method":"multipleRequests3","params":[3],"id":2,"jsonrpc":"2.0"}]`)) 210 211 // test a huge batch request 212 requests := RPCRequests{ 213 NewRequest("nullParam", nil), 214 NewRequest("nullParams", nil, nil), 215 NewRequest("emptyParams", []interface{}{}), 216 NewRequest("emptyAnyParams", []string{}), 217 NewRequest("emptyObject", struct{}{}), 218 NewRequest("emptyObjectList", []struct{}{{}, {}}), 219 NewRequest("boolParam", true), 220 NewRequest("boolParams", true, false, true), 221 NewRequest("stringParam", "Alex"), 222 NewRequest("stringParams", "JSON", "RPC"), 223 NewRequest("numberParam", 123), 224 NewRequest("numberParams", 123, 321), 225 NewRequest("floatParam", 1.23), 226 NewRequest("floatParams", 1.23, 3.21), 227 NewRequest("manyParams", "Alex", 35, true, nil, 2.34), 228 NewRequest("emptyMissingPublicFieldObject", struct{ name string }{name: "Alex"}), 229 NewRequest("singleStruct", person), 230 NewRequest("singlePointerToStruct", &person), 231 NewRequest("multipleStructs", person, &drink), 232 NewRequest("singleStructInArray", []interface{}{person}), 233 NewRequest("namedParameters", map[string]interface{}{ 234 "name": "Alex", 235 "age": 35, 236 }), 237 NewRequest("anonymousStructNoTags", struct { 238 Name string 239 Age int 240 }{"Alex", 33}), 241 NewRequest("anonymousStructWithTags", struct { 242 Name string `json:"name"` 243 Age int `json:"age"` 244 }{"Alex", 33}), 245 NewRequest("structWithNullField", struct { 246 Name string `json:"name"` 247 Address *string `json:"address"` 248 }{"Alex", nil}), 249 } 250 rpcClient.CallBatch(context.Background(), requests) 251 252 Expect((<-requestChan).body).To(Equal(`[{"method":"nullParam","params":[null],"id":0,"jsonrpc":"2.0"},` + 253 `{"method":"nullParams","params":[null,null],"id":1,"jsonrpc":"2.0"},` + 254 `{"method":"emptyParams","params":[],"id":2,"jsonrpc":"2.0"},` + 255 `{"method":"emptyAnyParams","params":[],"id":3,"jsonrpc":"2.0"},` + 256 `{"method":"emptyObject","params":{},"id":4,"jsonrpc":"2.0"},` + 257 `{"method":"emptyObjectList","params":[{},{}],"id":5,"jsonrpc":"2.0"},` + 258 `{"method":"boolParam","params":[true],"id":6,"jsonrpc":"2.0"},` + 259 `{"method":"boolParams","params":[true,false,true],"id":7,"jsonrpc":"2.0"},` + 260 `{"method":"stringParam","params":["Alex"],"id":8,"jsonrpc":"2.0"},` + 261 `{"method":"stringParams","params":["JSON","RPC"],"id":9,"jsonrpc":"2.0"},` + 262 `{"method":"numberParam","params":[123],"id":10,"jsonrpc":"2.0"},` + 263 `{"method":"numberParams","params":[123,321],"id":11,"jsonrpc":"2.0"},` + 264 `{"method":"floatParam","params":[1.23],"id":12,"jsonrpc":"2.0"},` + 265 `{"method":"floatParams","params":[1.23,3.21],"id":13,"jsonrpc":"2.0"},` + 266 `{"method":"manyParams","params":["Alex",35,true,null,2.34],"id":14,"jsonrpc":"2.0"},` + 267 `{"method":"emptyMissingPublicFieldObject","params":{},"id":15,"jsonrpc":"2.0"},` + 268 `{"method":"singleStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":16,"jsonrpc":"2.0"},` + 269 `{"method":"singlePointerToStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":17,"jsonrpc":"2.0"},` + 270 `{"method":"multipleStructs","params":[{"name":"Alex","age":35,"country":"Germany"},{"name":"Cuba Libre","ingredients":["rum","cola"]}],"id":18,"jsonrpc":"2.0"},` + 271 `{"method":"singleStructInArray","params":[{"name":"Alex","age":35,"country":"Germany"}],"id":19,"jsonrpc":"2.0"},` + 272 `{"method":"namedParameters","params":{"age":35,"name":"Alex"},"id":20,"jsonrpc":"2.0"},` + 273 `{"method":"anonymousStructNoTags","params":{"Name":"Alex","Age":33},"id":21,"jsonrpc":"2.0"},` + 274 `{"method":"anonymousStructWithTags","params":{"name":"Alex","age":33},"id":22,"jsonrpc":"2.0"},` + 275 `{"method":"structWithNullField","params":{"name":"Alex","address":null},"id":23,"jsonrpc":"2.0"}]`)) 276 277 // create batch manually 278 requests = []*RPCRequest{ 279 { 280 Method: "myMethod1", 281 Params: []int{1}, 282 ID: 123, // will be forced to requests[i].ID == i unless you use CallBatchRaw 283 JSONRPC: "7.0", // will be forced to "2.0" unless you use CallBatchRaw 284 }, 285 { 286 Method: "myMethod2", 287 Params: &person, 288 ID: 321, // will be forced to requests[i].ID == i unless you use CallBatchRaw 289 JSONRPC: "wrong", // will be forced to "2.0" unless you use CallBatchRaw 290 }, 291 } 292 rpcClient.CallBatch(context.Background(), requests) 293 294 Expect((<-requestChan).body).To(Equal(`[{"method":"myMethod1","params":[1],"id":0,"jsonrpc":"2.0"},` + 295 `{"method":"myMethod2","params":{"name":"Alex","age":35,"country":"Germany"},"id":1,"jsonrpc":"2.0"}]`)) 296 297 // use raw batch 298 requests = []*RPCRequest{ 299 { 300 Method: "myMethod1", 301 Params: []int{1}, 302 ID: 123, 303 JSONRPC: "7.0", 304 }, 305 { 306 Method: "myMethod2", 307 Params: &person, 308 ID: 321, 309 JSONRPC: "wrong", 310 }, 311 } 312 rpcClient.CallBatchRaw(context.Background(), requests) 313 314 Expect((<-requestChan).body).To(Equal(`[{"method":"myMethod1","params":[1],"id":123,"jsonrpc":"7.0"},` + 315 `{"method":"myMethod2","params":{"name":"Alex","age":35,"country":"Germany"},"id":321,"jsonrpc":"wrong"}]`)) 316 } 317 318 // test if the result of an an rpc request is parsed correctly and if errors are thrown correctly 319 func TestRpcJsonResponseStruct(t *testing.T) { 320 RegisterTestingT(t) 321 rpcClient := NewClient(httpServer.URL) 322 323 // empty return body is an error 324 responseBody = `` 325 res, err := rpcClient.Call(context.Background(), "something", 1, 2, 3) 326 <-requestChan 327 Expect(err).NotTo(BeNil()) 328 Expect(res).To(BeNil()) 329 330 // not a json body is an error 331 responseBody = `{ "not": "a", "json": "object"` 332 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 333 <-requestChan 334 Expect(err).NotTo(BeNil()) 335 Expect(res).To(BeNil()) 336 337 // field "anotherField" not allowed in rpc response is an error 338 responseBody = `{ "anotherField": "norpc"}` 339 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 340 <-requestChan 341 Expect(err).NotTo(BeNil()) 342 Expect(res).To(BeNil()) 343 344 // TODO: result must contain one of "result", "error" 345 // TODO: is there an efficient way to do this? 346 /*responseBody = `{}` 347 res, err = rpcClient.Call("something", 1, 2, 3) 348 <-requestChan 349 Expect(err).NotTo(BeNil()) 350 Expect(res).To(BeNil())*/ 351 352 // result null is ok 353 responseBody = `{"result": null}` 354 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 355 <-requestChan 356 Expect(err).To(BeNil()) 357 Expect(res.Result).To(BeNil()) 358 Expect(res.Error).To(BeNil()) 359 360 // error null is ok 361 responseBody = `{"error": null}` 362 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 363 <-requestChan 364 Expect(err).To(BeNil()) 365 Expect(res.Result).To(BeNil()) 366 Expect(res.Error).To(BeNil()) 367 368 // result and error null is ok 369 responseBody = `{"result": null, "error": null}` 370 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 371 <-requestChan 372 Expect(err).To(BeNil()) 373 Expect(res.Result).To(BeNil()) 374 Expect(res.Error).To(BeNil()) 375 376 // TODO: result must not contain both of "result", "error" != null 377 // TODO: is there an efficient way to do this? 378 /*responseBody = `{ "result": 123, "error": {"code": 123, "message": "something wrong"}}` 379 res, err = rpcClient.Call("something", 1, 2, 3) 380 <-requestChan 381 Expect(err).NotTo(BeNil()) 382 Expect(res).To(BeNil())*/ 383 384 // result string is ok 385 responseBody = `{"result": "ok"}` 386 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 387 <-requestChan 388 Expect(err).To(BeNil()) 389 Expect(res.Result).To(Equal(stdjson.RawMessage([]byte(strconv.Quote("ok"))))) 390 391 // result with error null is ok 392 responseBody = `{"result": "ok", "error": null}` 393 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 394 <-requestChan 395 Expect(err).To(BeNil()) 396 Expect(res.Result).To(Equal(stdjson.RawMessage([]byte(strconv.Quote("ok"))))) 397 398 // error with result null is ok 399 responseBody = `{"error": {"code": 123, "message": "something wrong"}, "result": null}` 400 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 401 <-requestChan 402 Expect(err).To(BeNil()) 403 Expect(res.Result).To(BeNil()) 404 Expect(res.Error.Code).To(Equal(123)) 405 Expect(res.Error.Message).To(Equal("something wrong")) 406 407 // TODO: empty error is not ok, must at least contain code and message 408 /*responseBody = `{ "error": {}}` 409 res, err = rpcClient.Call("something", 1, 2, 3) 410 <-requestChan 411 Expect(err).To(BeNil()) 412 Expect(res.Result).To(BeNil()) 413 Expect(res.Error).NotTo(BeNil())*/ 414 415 // TODO: only code in error is not ok, must at least contain code and message 416 /*responseBody = `{ "error": {"code": 123}}` 417 res, err = rpcClient.Call("something", 1, 2, 3) 418 <-requestChan 419 Expect(err).To(BeNil()) 420 Expect(res.Result).To(BeNil()) 421 Expect(res.Error).NotTo(BeNil())*/ 422 423 // TODO: only message in error is not ok, must at least contain code and message 424 /*responseBody = `{ "error": {"message": "something wrong"}}` 425 res, err = rpcClient.Call("something", 1, 2, 3) 426 <-requestChan 427 Expect(err).To(BeNil()) 428 Expect(res.Result).To(BeNil()) 429 Expect(res.Error).NotTo(BeNil())*/ 430 431 // error with code and message is ok 432 responseBody = `{ "error": {"code": 123, "message": "something wrong"}}` 433 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 434 <-requestChan 435 Expect(err).To(BeNil()) 436 Expect(res.Result).To(BeNil()) 437 Expect(res.Error.Code).To(Equal(123)) 438 Expect(res.Error.Message).To(Equal("something wrong")) 439 440 // check results 441 442 var p *Person 443 responseBody = `{ "result": {"name": "Alex", "age": 35, "anotherField": "something"} }` 444 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 445 <-requestChan 446 Expect(err).To(BeNil()) 447 Expect(res.Error).To(BeNil()) 448 err = res.GetObject(&p) 449 Expect(err).To(BeNil()) 450 Expect(p.Name).To(Equal("Alex")) 451 Expect(p.Age).To(Equal(35)) 452 Expect(p.Country).To(Equal("")) 453 454 // TODO: How to check if result could be parsed or if it is default? 455 p = nil 456 responseBody = `{ "result": {"anotherField": "something"} }` 457 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 458 <-requestChan 459 Expect(err).To(BeNil()) 460 Expect(res.Error).To(BeNil()) 461 err = res.GetObject(&p) 462 Expect(err).To(BeNil()) 463 Expect(p).NotTo(BeNil()) 464 465 // TODO: HERE###### 466 var pp *PointerFieldPerson 467 responseBody = `{ "result": {"anotherField": "something", "country": "Germany"} }` 468 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 469 <-requestChan 470 Expect(err).To(BeNil()) 471 Expect(res.Error).To(BeNil()) 472 err = res.GetObject(&pp) 473 Expect(err).To(BeNil()) 474 Expect(pp.Name).To(BeNil()) 475 Expect(pp.Age).To(BeNil()) 476 Expect(*pp.Country).To(Equal("Germany")) 477 478 p = nil 479 responseBody = `{ "result": null }` 480 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 481 <-requestChan 482 Expect(err).To(BeNil()) 483 Expect(res.Error).To(BeNil()) 484 err = res.GetObject(&p) 485 Expect(err).To(BeNil()) 486 Expect(p).To(BeNil()) 487 488 // passing nil is an error 489 // TODO 490 // p = nil 491 // responseBody = `{ "result": null }` 492 // res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 493 // <-requestChan 494 // Expect(err).To(BeNil()) 495 // Expect(res.Error).To(BeNil()) 496 // err = res.GetObject(p) 497 // Expect(err).NotTo(BeNil()) 498 // Expect(p).To(BeNil()) 499 500 p2 := &Person{ 501 Name: "Alex", 502 } 503 responseBody = `{ "result": null }` 504 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 505 <-requestChan 506 Expect(err).To(BeNil()) 507 Expect(res.Error).To(BeNil()) 508 err = res.GetObject(&p2) 509 Expect(err).To(BeNil()) 510 Expect(p2).To(BeNil()) 511 512 p2 = &Person{ 513 Name: "Alex", 514 } 515 responseBody = `{ "result": {"age": 35} }` 516 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 517 <-requestChan 518 Expect(err).To(BeNil()) 519 Expect(res.Error).To(BeNil()) 520 err = res.GetObject(p2) 521 Expect(err).To(BeNil()) 522 Expect(p2.Name).To(Equal("Alex")) 523 Expect(p2.Age).To(Equal(35)) 524 525 // prefilled struct is kept on no result 526 p3 := Person{ 527 Name: "Alex", 528 } 529 responseBody = `{ "result": null }` 530 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 531 <-requestChan 532 Expect(err).To(BeNil()) 533 Expect(res.Error).To(BeNil()) 534 err = res.GetObject(&p3) 535 Expect(err).To(BeNil()) 536 Expect(p3.Name).To(Equal("Alex")) 537 538 // prefilled struct is extended / overwritten 539 p3 = Person{ 540 Name: "Alex", 541 Age: 123, 542 } 543 responseBody = `{ "result": {"age": 35, "country": "Germany"} }` 544 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 545 <-requestChan 546 Expect(err).To(BeNil()) 547 Expect(res.Error).To(BeNil()) 548 err = res.GetObject(&p3) 549 Expect(err).To(BeNil()) 550 Expect(p3.Name).To(Equal("Alex")) 551 Expect(p3.Age).To(Equal(35)) 552 Expect(p3.Country).To(Equal("Germany")) 553 554 // nil is an error 555 responseBody = `{ "result": {"age": 35} }` 556 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 557 <-requestChan 558 Expect(err).To(BeNil()) 559 Expect(res.Error).To(BeNil()) 560 err = res.GetObject(nil) 561 Expect(err).NotTo(BeNil()) 562 } 563 564 func TestRpcBatchJsonResponseStruct(t *testing.T) { 565 RegisterTestingT(t) 566 rpcClient := NewClient(httpServer.URL) 567 568 // empty return body is an error 569 responseBody = `` 570 res, err := rpcClient.CallBatch(context.Background(), RPCRequests{ 571 NewRequest("something", 1, 2, 3), 572 }) 573 <-requestChan 574 Expect(err).NotTo(BeNil()) 575 Expect(res).To(BeNil()) 576 577 // not a json body is an error 578 responseBody = `{ "not": "a", "json": "object"` 579 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 580 NewRequest("something", 1, 2, 3), 581 }) 582 <-requestChan 583 Expect(err).NotTo(BeNil()) 584 Expect(res).To(BeNil()) 585 586 // field "anotherField" not allowed in rpc response is an error 587 responseBody = `{ "anotherField": "norpc"}` 588 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 589 NewRequest("something", 1, 2, 3), 590 }) 591 <-requestChan 592 Expect(err).NotTo(BeNil()) 593 Expect(res).To(BeNil()) 594 595 // TODO: result must contain one of "result", "error" 596 // TODO: is there an efficient way to do this? 597 /*responseBody = `[{}]` 598 res, err = rpcClient.Call(context.Background(), "something", 1, 2, 3) 599 <-requestChan 600 Expect(err).NotTo(BeNil()) 601 Expect(res).To(BeNil())*/ 602 603 // result must be wrapped in array on batch request 604 responseBody = `{"result": null}` 605 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 606 NewRequest("something", 1, 2, 3), 607 }) 608 <-requestChan 609 Expect(err.Error()).NotTo(BeNil()) 610 611 // result ok since in arrary 612 responseBody = `[{"result": null}]` 613 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 614 NewRequest("something", 1, 2, 3), 615 }) 616 <-requestChan 617 Expect(err).To(BeNil()) 618 Expect(len(res)).To(Equal(1)) 619 Expect(res[0].Result).To(BeNil()) 620 621 // error null is ok 622 responseBody = `[{"error": null}]` 623 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 624 NewRequest("something", 1, 2, 3), 625 }) 626 <-requestChan 627 Expect(err).To(BeNil()) 628 Expect(res[0].Result).To(BeNil()) 629 Expect(res[0].Error).To(BeNil()) 630 631 // result and error null is ok 632 responseBody = `[{"result": null, "error": null}]` 633 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 634 NewRequest("something", 1, 2, 3), 635 }) 636 <-requestChan 637 Expect(err).To(BeNil()) 638 Expect(res[0].Result).To(BeNil()) 639 Expect(res[0].Error).To(BeNil()) 640 641 // TODO: result must not contain both of "result", "error" != null 642 // TODO: is there an efficient way to do this? 643 /*responseBody = `[{ "result": 123, "error": {"code": 123, "message": "something wrong"}}]` 644 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 645 NewRequest("something",1, 2, 3), 646 }) 647 <-requestChan 648 Expect(err).NotTo(BeNil()) 649 Expect(res).To(BeNil())*/ 650 651 // result string is ok 652 responseBody = `[{"result": "ok","id":1}]` 653 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 654 NewRequest("something", 1, 2, 3), 655 }) 656 <-requestChan 657 Expect(err).To(BeNil()) 658 Expect(res[0].Result).To(Equal(stdjson.RawMessage([]byte(strconv.Quote("ok"))))) 659 Expect(res[0].ID).To(Equal(stdjson.Number("1"))) 660 661 // result with error null is ok 662 responseBody = `[{"result": "ok", "error": null}]` 663 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 664 NewRequest("something", 1, 2, 3), 665 }) 666 <-requestChan 667 Expect(err).To(BeNil()) 668 Expect(res[0].Result).To(Equal(stdjson.RawMessage([]byte(strconv.Quote("ok"))))) 669 670 // error with result null is ok 671 responseBody = `[{"error": {"code": 123, "message": "something wrong"}, "result": null}]` 672 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 673 NewRequest("something", 1, 2, 3), 674 }) 675 <-requestChan 676 Expect(err).To(BeNil()) 677 Expect(res[0].Result).To(BeNil()) 678 Expect(res[0].Error.Code).To(Equal(123)) 679 Expect(res[0].Error.Message).To(Equal("something wrong")) 680 681 // TODO: empty error is not ok, must at least contain code and message 682 /*responseBody = `[{ "error": {}}]` 683 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 684 NewRequest("something",1, 2, 3), 685 }) 686 <-requestChan 687 Expect(err).To(BeNil()) 688 Expect(res[0].Result).To(BeNil()) 689 Expect(res[0].Error).NotTo(BeNil())*/ /* 690 691 // TODO: only code in error is not ok, must at least contain code and message 692 */ /*responseBody = `[{ "error": {"code": 123}}]` 693 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 694 NewRequest("something",1, 2, 3), 695 }) 696 <-requestChan 697 Expect(err).To(BeNil()) 698 Expect(res[0].Result).To(BeNil()) 699 Expect(res[0].Error).NotTo(BeNil())*/ /* 700 701 // TODO: only message in error is not ok, must at least contain code and message 702 */ /*responseBody = `[{ "error": {"message": "something wrong"}}]` 703 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 704 NewRequest("something",1, 2, 3), 705 }) 706 <-requestChan 707 Expect(err).To(BeNil()) 708 Expect(res[0].Result).To(BeNil()) 709 Expect(res[0].Error).NotTo(BeNil())*/ 710 711 // error with code and message is ok 712 responseBody = `[{ "error": {"code": 123, "message": "something wrong"}}]` 713 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 714 NewRequest("something", 1, 2, 3), 715 }) 716 <-requestChan 717 Expect(err).To(BeNil()) 718 Expect(res[0].Result).To(BeNil()) 719 Expect(res[0].Error.Code).To(Equal(123)) 720 Expect(res[0].Error.Message).To(Equal("something wrong")) 721 722 // check results 723 724 var p *Person 725 responseBody = `[{"id":1, "result": {"name": "Alex", "age": 35}}, {"id":2, "result": {"name": "Lena", "age": 2}}]` 726 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 727 NewRequest("something", 1, 2, 3), 728 }) 729 730 <-requestChan 731 Expect(err).To(BeNil()) 732 733 Expect(res[0].Error).To(BeNil()) 734 Expect(res[0].ID).To(Equal(stdjson.Number("1"))) 735 736 Expect(res[1].Error).To(BeNil()) 737 Expect(res[1].ID).To(Equal(stdjson.Number("2"))) 738 739 err = res[0].GetObject(&p) 740 require.NoError(t, err) 741 Expect(p.Name).To(Equal("Alex")) 742 Expect(p.Age).To(Equal(35)) 743 744 err = res[1].GetObject(&p) 745 require.NoError(t, err) 746 Expect(p.Name).To(Equal("Lena")) 747 Expect(p.Age).To(Equal(2)) 748 749 // check if error occurred 750 responseBody = `[{ "result": "someresult", "error": null}, { "result": null, "error": {"code": 123, "message": "something wrong"}}]` 751 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 752 NewRequest("something", 1, 2, 3), 753 }) 754 <-requestChan 755 Expect(err).To(BeNil()) 756 Expect(res.HasError()).To(BeTrue()) 757 758 // check if error occurred 759 responseBody = `[{ "result": null, "error": {"code": 123, "message": "something wrong"}}]` 760 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 761 NewRequest("something", 1, 2, 3), 762 }) 763 <-requestChan 764 Expect(err).To(BeNil()) 765 Expect(res.HasError()).To(BeTrue()) 766 // check if error occurred 767 responseBody = `[{ "result": null, "error": {"code": 123, "message": "something wrong"}}]` 768 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 769 NewRequest("something", 1, 2, 3), 770 }) 771 <-requestChan 772 Expect(err).To(BeNil()) 773 Expect(res.HasError()).To(BeTrue()) 774 775 // check if response mapping works 776 responseBody = `[{ "id":123,"result": 123},{ "id":1,"result": 1}]` 777 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 778 NewRequest("something", 1, 2, 3), 779 }) 780 <-requestChan 781 Expect(err).To(BeNil()) 782 Expect(res.HasError()).To(BeFalse()) 783 resMap := res.AsMap() 784 785 var int1 int64 786 require.NotNil(t, resMap[1]) 787 resMap[1].GetObject(&int1) 788 var int123 int64 789 require.NotNil(t, resMap[123]) 790 resMap[123].GetObject(&int123) 791 Expect(int1).To(Equal(int64(1))) 792 Expect(int123).To(Equal(int64(123))) 793 794 // check if getByID works 795 res.GetByID(123).GetObject(&int123) 796 Expect(int123).To(Equal(int64(123))) 797 798 // check if error occurred 799 responseBody = `[{ "result": null, "error": {"code": 123, "message": "something wrong"}}]` 800 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 801 NewRequest("something", 1, 2, 3), 802 }) 803 <-requestChan 804 Expect(err).To(BeNil()) 805 Expect(res.HasError()).To(BeTrue()) 806 807 /* 808 // TODO: How to check if result could be parsed or if it is default? 809 p = nil 810 responseBody = `{ "result": {"anotherField": "something"} }` 811 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 812 {"something", Params(1, 2, 3)}, 813 }) 814 <-requestChan 815 Expect(err).To(BeNil()) 816 Expect(res.Error).To(BeNil()) 817 err = res.GetObject(&p) 818 Expect(err).To(BeNil()) 819 Expect(p).NotTo(BeNil()) 820 821 // TODO: HERE###### 822 var pp *PointerFieldPerson 823 responseBody = `{ "result": {"anotherField": "something", "country": "Germany"} }` 824 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 825 {"something", Params(1, 2, 3)}, 826 }) 827 <-requestChan 828 Expect(err).To(BeNil()) 829 Expect(res.Error).To(BeNil()) 830 err = res.GetObject(&pp) 831 Expect(err).To(BeNil()) 832 Expect(pp.Name).To(BeNil()) 833 Expect(pp.Age).To(BeNil()) 834 Expect(*pp.Country).To(Equal("Germany")) 835 836 p = nil 837 responseBody = `{ "result": null }` 838 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 839 {"something", Params(1, 2, 3)}, 840 }) 841 <-requestChan 842 Expect(err).To(BeNil()) 843 Expect(res.Error).To(BeNil()) 844 err = res.GetObject(&p) 845 Expect(err).To(BeNil()) 846 Expect(p).To(BeNil()) 847 848 // passing nil is an error 849 p = nil 850 responseBody = `{ "result": null }` 851 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 852 {"something", Params(1, 2, 3)}, 853 }) 854 <-requestChan 855 Expect(err).To(BeNil()) 856 Expect(res.Error).To(BeNil()) 857 err = res.GetObject(p) 858 Expect(err).NotTo(BeNil()) 859 Expect(p).To(BeNil()) 860 861 p2 := &Person{ 862 Name: "Alex", 863 } 864 responseBody = `{ "result": null }` 865 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 866 {"something", Params(1, 2, 3)}, 867 }) 868 <-requestChan 869 Expect(err).To(BeNil()) 870 Expect(res.Error).To(BeNil()) 871 err = res.GetObject(&p2) 872 Expect(err).To(BeNil()) 873 Expect(p2).To(BeNil()) 874 875 p2 = &Person{ 876 Name: "Alex", 877 } 878 responseBody = `{ "result": {"age": 35} }` 879 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 880 {"something", Params(1, 2, 3)}, 881 }) 882 <-requestChan 883 Expect(err).To(BeNil()) 884 Expect(res.Error).To(BeNil()) 885 err = res.GetObject(p2) 886 Expect(err).To(BeNil()) 887 Expect(p2.Name).To(Equal("Alex")) 888 Expect(p2.Age).To(Equal(35)) 889 890 // prefilled struct is kept on no result 891 p3 := Person{ 892 Name: "Alex", 893 } 894 responseBody = `{ "result": null }` 895 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 896 {"something", Params(1, 2, 3)}, 897 }) 898 <-requestChan 899 Expect(err).To(BeNil()) 900 Expect(res.Error).To(BeNil()) 901 err = res.GetObject(&p3) 902 Expect(err).To(BeNil()) 903 Expect(p3.Name).To(Equal("Alex")) 904 905 // prefilled struct is extended / overwritten 906 p3 = Person{ 907 Name: "Alex", 908 Age: 123, 909 } 910 responseBody = `{ "result": {"age": 35, "country": "Germany"} }` 911 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 912 {"something", Params(1, 2, 3)}, 913 }) 914 <-requestChan 915 Expect(err).To(BeNil()) 916 Expect(res.Error).To(BeNil()) 917 err = res.GetObject(&p3) 918 Expect(err).To(BeNil()) 919 Expect(p3.Name).To(Equal("Alex")) 920 Expect(p3.Age).To(Equal(35)) 921 Expect(p3.Country).To(Equal("Germany")) 922 923 // nil is an error 924 responseBody = `{ "result": {"age": 35} }` 925 res, err = rpcClient.CallBatch(context.Background(), RPCRequests{ 926 {"something", Params(1, 2, 3)}, 927 }) 928 <-requestChan 929 Expect(err).To(BeNil()) 930 Expect(res.Error).To(BeNil()) 931 err = res.GetObject(nil) 932 Expect(err).NotTo(BeNil()) 933 */ 934 } 935 936 func TestRpcClient_CallFor(t *testing.T) { 937 RegisterTestingT(t) 938 rpcClient := NewClient(httpServer.URL) 939 940 i := 0 941 responseBody = `{"result":3,"id":1,"jsonrpc":"2.0"}` 942 err := rpcClient.CallFor(context.Background(), &i, "something", 1, 2, 3) 943 <-requestChan 944 Expect(err).To(BeNil()) 945 Expect(i).To(Equal(3)) 946 947 /* 948 i = 3 949 responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` 950 err = rpcClient.CallFor(&i, "something", 1, 2, 3) 951 <-requestChan 952 Expect(err).To(BeNil()) 953 // i is not modified when result is empty since null (nil) value cannot be stored in int 954 Expect(i).To(Equal(3)) 955 956 var pi *int 957 responseBody = `{"result":4,"id":1,"jsonrpc":"2.0"}` 958 err = rpcClient.CallFor(pi, "something", 1, 2, 3) 959 <-requestChan 960 Expect(err).NotTo(BeNil()) 961 Expect(pi).To(BeNil()) 962 963 responseBody = `{"result":4,"id":1,"jsonrpc":"2.0"}` 964 err = rpcClient.CallFor(&pi, "something", 1, 2, 3) 965 <-requestChan 966 Expect(err).To(BeNil()) 967 Expect(*pi).To(Equal(4)) 968 969 *pi = 3 970 responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` 971 err = rpcClient.CallFor(&pi, "something", 1, 2, 3) 972 <-requestChan 973 Expect(err).To(BeNil()) 974 // since pi has a value it is not overwritten by null result 975 Expect(pi).To(BeNil()) 976 977 p := &Person{} 978 responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` 979 err = rpcClient.CallFor(p, "something", 1, 2, 3) 980 <-requestChan 981 Expect(err).To(BeNil()) 982 // p is not changed since it has a value and result is null 983 Expect(p).NotTo(BeNil()) 984 985 var p2 *Person 986 responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` 987 err = rpcClient.CallFor(p2, "something", 1, 2, 3) 988 <-requestChan 989 Expect(err).NotTo(BeNil()) 990 // p is not changed since it has a value and result is null 991 Expect(p2).To(BeNil()) 992 993 p3 := Person{} 994 responseBody = `{"result":null,"id":1,"jsonrpc":"2.0"}` 995 err = rpcClient.CallFor(&p3, "something", 1, 2, 3) 996 <-requestChan 997 Expect(err).To(BeNil()) 998 // p is not changed since it has a value and result is null 999 Expect(p).NotTo(BeNil()) 1000 1001 p = &Person{Age: 35} 1002 responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` 1003 err = rpcClient.CallFor(p, "something", 1, 2, 3) 1004 <-requestChan 1005 Expect(err).To(BeNil()) 1006 // p is not changed since it has a value and result is null 1007 Expect(p.Name).To(Equal("Alex")) 1008 Expect(p.Age).To(Equal(35)) 1009 1010 p2 = nil 1011 responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` 1012 err = rpcClient.CallFor(p2, "something", 1, 2, 3) 1013 <-requestChan 1014 Expect(err).NotTo(BeNil()) 1015 // p is not changed since it has a value and result is null 1016 Expect(p2).To(BeNil()) 1017 1018 p2 = nil 1019 responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` 1020 err = rpcClient.CallFor(&p2, "something", 1, 2, 3) 1021 <-requestChan 1022 Expect(err).To(BeNil()) 1023 // p is not changed since it has a value and result is null 1024 Expect(p2).NotTo(BeNil()) 1025 Expect(p2.Name).To(Equal("Alex")) 1026 1027 p3 = Person{Age: 35} 1028 responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` 1029 err = rpcClient.CallFor(&p3, "something", 1, 2, 3) 1030 <-requestChan 1031 Expect(err).To(BeNil()) 1032 // p is not changed since it has a value and result is null 1033 Expect(p.Name).To(Equal("Alex")) 1034 Expect(p.Age).To(Equal(35)) 1035 1036 p3 = Person{Age: 35} 1037 responseBody = `{"result":{"name":"Alex"},"id":1,"jsonrpc":"2.0"}` 1038 err = rpcClient.CallFor(&p3, "something", 1, 2, 3) 1039 <-requestChan 1040 Expect(err).To(BeNil()) 1041 // p is not changed since it has a value and result is null 1042 Expect(p.Name).To(Equal("Alex")) 1043 Expect(p.Age).To(Equal(35)) 1044 1045 var intArray []int 1046 responseBody = `{"result":[1, 2, 3],"id":1,"jsonrpc":"2.0"}` 1047 err = rpcClient.CallFor(&intArray, "something", 1, 2, 3) 1048 <-requestChan 1049 Expect(err).To(BeNil()) 1050 // p is not changed since it has a value and result is null 1051 Expect(intArray).To(ContainElement(1)) 1052 Expect(intArray).To(ContainElement(2)) 1053 Expect(intArray).To(ContainElement(3))*/ 1054 } 1055 1056 type Person struct { 1057 Name string `json:"name"` 1058 Age int `json:"age"` 1059 Country string `json:"country"` 1060 } 1061 1062 type PointerFieldPerson struct { 1063 Name *string `json:"name"` 1064 Age *int `json:"age"` 1065 Country *string `json:"country"` 1066 } 1067 1068 type Drink struct { 1069 Name string `json:"name"` 1070 Ingredients []string `json:"ingredients"` 1071 }