2021-12-04 16:42:11 +00:00

1156 lines
36 KiB
Go

package jsonrpc
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"testing"
. "github.com/onsi/gomega"
)
// needed to retrieve requests that arrived at httpServer for further investigation
var requestChan = make(chan *RequestData, 1)
// the request datastructure that can be retrieved for test assertions
type RequestData struct {
request *http.Request
body string
}
// set the response body the httpServer should return for the next request
var responseBody = ""
var httpServer *httptest.Server
// start the testhttp server and stop it when tests are finished
func TestMain(m *testing.M) {
httpServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body)
defer r.Body.Close()
// put request and body to channel for the client to investigate them
requestChan <- &RequestData{r, string(data)}
fmt.Fprintf(w, responseBody)
}))
defer httpServer.Close()
os.Exit(m.Run())
}
func TestSimpleRpcCallHeaderCorrect(t *testing.T) {
RegisterTestingT(t)
rpcClient := NewClient(httpServer.URL)
rpcClient.Call("add", 1, 2)
req := (<-requestChan).request
Expect(req.Method).To(Equal("POST"))
Expect(req.Header.Get("Content-Type")).To(Equal("application/json"))
Expect(req.Header.Get("Accept")).To(Equal("application/json"))
}
// test if the structure of an rpc request is built correctly by validating the data that arrived on the test server
func TestRpcClient_Call(t *testing.T) {
RegisterTestingT(t)
rpcClient := NewClient(httpServer.URL)
person := Person{
Name: "Alex",
Age: 35,
Country: "Germany",
}
drink := Drink{
Name: "Cuba Libre",
Ingredients: []string{"rum", "cola"},
}
rpcClient.Call("missingParam")
Expect((<-requestChan).body).To(Equal(`{"method":"missingParam","id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("nullParam", nil)
Expect((<-requestChan).body).To(Equal(`{"method":"nullParam","params":[null],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("nullParams", nil, nil)
Expect((<-requestChan).body).To(Equal(`{"method":"nullParams","params":[null,null],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("emptyParams", []interface{}{})
Expect((<-requestChan).body).To(Equal(`{"method":"emptyParams","params":[],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("emptyAnyParams", []string{})
Expect((<-requestChan).body).To(Equal(`{"method":"emptyAnyParams","params":[],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("emptyObject", struct{}{})
Expect((<-requestChan).body).To(Equal(`{"method":"emptyObject","params":{},"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("emptyObjectList", []struct{}{{}, {}})
Expect((<-requestChan).body).To(Equal(`{"method":"emptyObjectList","params":[{},{}],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("boolParam", true)
Expect((<-requestChan).body).To(Equal(`{"method":"boolParam","params":[true],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("boolParams", true, false, true)
Expect((<-requestChan).body).To(Equal(`{"method":"boolParams","params":[true,false,true],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("stringParam", "Alex")
Expect((<-requestChan).body).To(Equal(`{"method":"stringParam","params":["Alex"],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("stringParams", "JSON", "RPC")
Expect((<-requestChan).body).To(Equal(`{"method":"stringParams","params":["JSON","RPC"],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("numberParam", 123)
Expect((<-requestChan).body).To(Equal(`{"method":"numberParam","params":[123],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("numberParams", 123, 321)
Expect((<-requestChan).body).To(Equal(`{"method":"numberParams","params":[123,321],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("floatParam", 1.23)
Expect((<-requestChan).body).To(Equal(`{"method":"floatParam","params":[1.23],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("floatParams", 1.23, 3.21)
Expect((<-requestChan).body).To(Equal(`{"method":"floatParams","params":[1.23,3.21],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("manyParams", "Alex", 35, true, nil, 2.34)
Expect((<-requestChan).body).To(Equal(`{"method":"manyParams","params":["Alex",35,true,null,2.34],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("emptyMissingPublicFieldObject", struct{ name string }{name: "Alex"})
Expect((<-requestChan).body).To(Equal(`{"method":"emptyMissingPublicFieldObject","params":{},"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("singleStruct", person)
Expect((<-requestChan).body).To(Equal(`{"method":"singleStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("singlePointerToStruct", &person)
Expect((<-requestChan).body).To(Equal(`{"method":"singlePointerToStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":0,"jsonrpc":"2.0"}`))
pp := &person
rpcClient.Call("doublePointerStruct", &pp)
Expect((<-requestChan).body).To(Equal(`{"method":"doublePointerStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("multipleStructs", person, &drink)
Expect((<-requestChan).body).To(Equal(`{"method":"multipleStructs","params":[{"name":"Alex","age":35,"country":"Germany"},{"name":"Cuba Libre","ingredients":["rum","cola"]}],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("singleStructInArray", []interface{}{person})
Expect((<-requestChan).body).To(Equal(`{"method":"singleStructInArray","params":[{"name":"Alex","age":35,"country":"Germany"}],"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("namedParameters", map[string]interface{}{
"name": "Alex",
"age": 35,
})
Expect((<-requestChan).body).To(Equal(`{"method":"namedParameters","params":{"age":35,"name":"Alex"},"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("anonymousStructNoTags", struct {
Name string
Age int
}{"Alex", 33})
Expect((<-requestChan).body).To(Equal(`{"method":"anonymousStructNoTags","params":{"Name":"Alex","Age":33},"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("anonymousStructWithTags", struct {
Name string `json:"name"`
Age int `json:"age"`
}{"Alex", 33})
Expect((<-requestChan).body).To(Equal(`{"method":"anonymousStructWithTags","params":{"name":"Alex","age":33},"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("structWithNullField", struct {
Name string `json:"name"`
Address *string `json:"address"`
}{"Alex", nil})
Expect((<-requestChan).body).To(Equal(`{"method":"structWithNullField","params":{"name":"Alex","address":null},"id":0,"jsonrpc":"2.0"}`))
rpcClient.Call("nestedStruct",
Planet{
Name: "Mars",
Properties: Properties{
Distance: 54600000,
Color: "red",
},
})
Expect((<-requestChan).body).To(Equal(`{"method":"nestedStruct","params":{"name":"Mars","properties":{"distance":54600000,"color":"red"}},"id":0,"jsonrpc":"2.0"}`))
}
func TestRpcClient_CallBatch(t *testing.T) {
RegisterTestingT(t)
rpcClient := NewClient(httpServer.URL)
person := Person{
Name: "Alex",
Age: 35,
Country: "Germany",
}
drink := Drink{
Name: "Cuba Libre",
Ingredients: []string{"rum", "cola"},
}
// invalid parameters are possible by manually defining *RPCRequest
rpcClient.CallBatch(RPCRequests{
{
Method: "singleRequest",
Params: 3, // invalid, should be []int{3}
},
})
Expect((<-requestChan).body).To(Equal(`[{"method":"singleRequest","params":3,"id":0,"jsonrpc":"2.0"}]`))
// better use Params() unless you know what you are doing
rpcClient.CallBatch(RPCRequests{
{
Method: "singleRequest",
Params: Params(3), // always valid json rpc
},
})
Expect((<-requestChan).body).To(Equal(`[{"method":"singleRequest","params":[3],"id":0,"jsonrpc":"2.0"}]`))
// even better, use NewRequest()
rpcClient.CallBatch(RPCRequests{
NewRequest("multipleRequests1", 1),
NewRequest("multipleRequests2", 2),
NewRequest("multipleRequests3", 3),
})
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"}]`))
// test a huge batch request
requests := RPCRequests{
NewRequest("nullParam", nil),
NewRequest("nullParams", nil, nil),
NewRequest("emptyParams", []interface{}{}),
NewRequest("emptyAnyParams", []string{}),
NewRequest("emptyObject", struct{}{}),
NewRequest("emptyObjectList", []struct{}{{}, {}}),
NewRequest("boolParam", true),
NewRequest("boolParams", true, false, true),
NewRequest("stringParam", "Alex"),
NewRequest("stringParams", "JSON", "RPC"),
NewRequest("numberParam", 123),
NewRequest("numberParams", 123, 321),
NewRequest("floatParam", 1.23),
NewRequest("floatParams", 1.23, 3.21),
NewRequest("manyParams", "Alex", 35, true, nil, 2.34),
NewRequest("emptyMissingPublicFieldObject", struct{ name string }{name: "Alex"}),
NewRequest("singleStruct", person),
NewRequest("singlePointerToStruct", &person),
NewRequest("multipleStructs", person, &drink),
NewRequest("singleStructInArray", []interface{}{person}),
NewRequest("namedParameters", map[string]interface{}{
"name": "Alex",
"age": 35,
}),
NewRequest("anonymousStructNoTags", struct {
Name string
Age int
}{"Alex", 33}),
NewRequest("anonymousStructWithTags", struct {
Name string `json:"name"`
Age int `json:"age"`
}{"Alex", 33}),
NewRequest("structWithNullField", struct {
Name string `json:"name"`
Address *string `json:"address"`
}{"Alex", nil}),
}
rpcClient.CallBatch(requests)
Expect((<-requestChan).body).To(Equal(`[{"method":"nullParam","params":[null],"id":0,"jsonrpc":"2.0"},` +
`{"method":"nullParams","params":[null,null],"id":1,"jsonrpc":"2.0"},` +
`{"method":"emptyParams","params":[],"id":2,"jsonrpc":"2.0"},` +
`{"method":"emptyAnyParams","params":[],"id":3,"jsonrpc":"2.0"},` +
`{"method":"emptyObject","params":{},"id":4,"jsonrpc":"2.0"},` +
`{"method":"emptyObjectList","params":[{},{}],"id":5,"jsonrpc":"2.0"},` +
`{"method":"boolParam","params":[true],"id":6,"jsonrpc":"2.0"},` +
`{"method":"boolParams","params":[true,false,true],"id":7,"jsonrpc":"2.0"},` +
`{"method":"stringParam","params":["Alex"],"id":8,"jsonrpc":"2.0"},` +
`{"method":"stringParams","params":["JSON","RPC"],"id":9,"jsonrpc":"2.0"},` +
`{"method":"numberParam","params":[123],"id":10,"jsonrpc":"2.0"},` +
`{"method":"numberParams","params":[123,321],"id":11,"jsonrpc":"2.0"},` +
`{"method":"floatParam","params":[1.23],"id":12,"jsonrpc":"2.0"},` +
`{"method":"floatParams","params":[1.23,3.21],"id":13,"jsonrpc":"2.0"},` +
`{"method":"manyParams","params":["Alex",35,true,null,2.34],"id":14,"jsonrpc":"2.0"},` +
`{"method":"emptyMissingPublicFieldObject","params":{},"id":15,"jsonrpc":"2.0"},` +
`{"method":"singleStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":16,"jsonrpc":"2.0"},` +
`{"method":"singlePointerToStruct","params":{"name":"Alex","age":35,"country":"Germany"},"id":17,"jsonrpc":"2.0"},` +
`{"method":"multipleStructs","params":[{"name":"Alex","age":35,"country":"Germany"},{"name":"Cuba Libre","ingredients":["rum","cola"]}],"id":18,"jsonrpc":"2.0"},` +
`{"method":"singleStructInArray","params":[{"name":"Alex","age":35,"country":"Germany"}],"id":19,"jsonrpc":"2.0"},` +
`{"method":"namedParameters","params":{"age":35,"name":"Alex"},"id":20,"jsonrpc":"2.0"},` +
`{"method":"anonymousStructNoTags","params":{"Name":"Alex","Age":33},"id":21,"jsonrpc":"2.0"},` +
`{"method":"anonymousStructWithTags","params":{"name":"Alex","age":33},"id":22,"jsonrpc":"2.0"},` +
`{"method":"structWithNullField","params":{"name":"Alex","address":null},"id":23,"jsonrpc":"2.0"}]`))
// create batch manually
requests = []*RPCRequest{
{
Method: "myMethod1",
Params: []int{1},
ID: 123, // will be forced to requests[i].ID == i unless you use CallBatchRaw
JSONRPC: "7.0", // will be forced to "2.0" unless you use CallBatchRaw
},
{
Method: "myMethod2",
Params: &person,
ID: 321, // will be forced to requests[i].ID == i unless you use CallBatchRaw
JSONRPC: "wrong", // will be forced to "2.0" unless you use CallBatchRaw
},
}
rpcClient.CallBatch(requests)
Expect((<-requestChan).body).To(Equal(`[{"method":"myMethod1","params":[1],"id":0,"jsonrpc":"2.0"},` +
`{"method":"myMethod2","params":{"name":"Alex","age":35,"country":"Germany"},"id":1,"jsonrpc":"2.0"}]`))
// use raw batch
requests = []*RPCRequest{
{
Method: "myMethod1",
Params: []int{1},
ID: 123,
JSONRPC: "7.0",
},
{
Method: "myMethod2",
Params: &person,
ID: 321,
JSONRPC: "wrong",
},
}
rpcClient.CallBatchRaw(requests)
Expect((<-requestChan).body).To(Equal(`[{"method":"myMethod1","params":[1],"id":123,"jsonrpc":"7.0"},` +
`{"method":"myMethod2","params":{"name":"Alex","age":35,"country":"Germany"},"id":321,"jsonrpc":"wrong"}]`))
}
// test if the result of an an rpc request is parsed correctly and if errors are thrown correctly
func TestRpcJsonResponseStruct(t *testing.T) {
RegisterTestingT(t)
rpcClient := NewClient(httpServer.URL)
// empty return body is an error
responseBody = ``
res, err := rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())
// not a json body is an error
responseBody = `{ "not": "a", "json": "object"`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())
// field "anotherField" not allowed in rpc response is an error
responseBody = `{ "anotherField": "norpc"}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())
// TODO: result must contain one of "result", "error"
// TODO: is there an efficient way to do this?
/*responseBody = `{}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())*/
// result null is ok
responseBody = `{"result": null}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(BeNil())
Expect(res.Error).To(BeNil())
// error null is ok
responseBody = `{"error": null}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(BeNil())
Expect(res.Error).To(BeNil())
// result and error null is ok
responseBody = `{"result": null, "error": null}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(BeNil())
Expect(res.Error).To(BeNil())
// TODO: result must not contain both of "result", "error" != null
// TODO: is there an efficient way to do this?
/*responseBody = `{ "result": 123, "error": {"code": 123, "message": "something wrong"}}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())*/
// result string is ok
responseBody = `{"result": "ok"}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(Equal("ok"))
// result with error null is ok
responseBody = `{"result": "ok", "error": null}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(Equal("ok"))
// error with result null is ok
responseBody = `{"error": {"code": 123, "message": "something wrong"}, "result": null}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(BeNil())
Expect(res.Error.Code).To(Equal(123))
Expect(res.Error.Message).To(Equal("something wrong"))
// TODO: empty error is not ok, must at least contain code and message
/*responseBody = `{ "error": {}}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(BeNil())
Expect(res.Error).NotTo(BeNil())*/
// TODO: only code in error is not ok, must at least contain code and message
/*responseBody = `{ "error": {"code": 123}}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(BeNil())
Expect(res.Error).NotTo(BeNil())*/
// TODO: only message in error is not ok, must at least contain code and message
/*responseBody = `{ "error": {"message": "something wrong"}}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(BeNil())
Expect(res.Error).NotTo(BeNil())*/
// error with code and message is ok
responseBody = `{ "error": {"code": 123, "message": "something wrong"}}`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Result).To(BeNil())
Expect(res.Error.Code).To(Equal(123))
Expect(res.Error.Message).To(Equal("something wrong"))
// check results
// should return int correctly
responseBody = `{ "result": 1 }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
i, err := res.GetInt()
Expect(err).To(BeNil())
Expect(i).To(Equal(int64(1)))
// error on wrong type
i = 3
responseBody = `{ "result": "notAnInt" }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
i, err = res.GetInt()
Expect(err).NotTo(BeNil())
Expect(i).To(Equal(int64(0)))
// error on result null
i = 3
responseBody = `{ "result": null }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
i, err = res.GetInt()
Expect(err).NotTo(BeNil())
Expect(i).To(Equal(int64(0)))
b := false
responseBody = `{ "result": true }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
b, err = res.GetBool()
Expect(err).To(BeNil())
Expect(b).To(Equal(true))
b = true
responseBody = `{ "result": 123 }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
b, err = res.GetBool()
Expect(err).NotTo(BeNil())
Expect(b).To(Equal(false))
var p *Person
responseBody = `{ "result": {"name": "Alex", "age": 35, "anotherField": "something"} }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p)
Expect(err).To(BeNil())
Expect(p.Name).To(Equal("Alex"))
Expect(p.Age).To(Equal(35))
Expect(p.Country).To(Equal(""))
// TODO: How to check if result could be parsed or if it is default?
p = nil
responseBody = `{ "result": {"anotherField": "something"} }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p)
Expect(err).To(BeNil())
Expect(p).NotTo(BeNil())
// TODO: HERE######
var pp *PointerFieldPerson
responseBody = `{ "result": {"anotherField": "something", "country": "Germany"} }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&pp)
Expect(err).To(BeNil())
Expect(pp.Name).To(BeNil())
Expect(pp.Age).To(BeNil())
Expect(*pp.Country).To(Equal("Germany"))
p = nil
responseBody = `{ "result": null }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p)
Expect(err).To(BeNil())
Expect(p).To(BeNil())
// passing nil is an error
p = nil
responseBody = `{ "result": null }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(p)
Expect(err).NotTo(BeNil())
Expect(p).To(BeNil())
p2 := &Person{
Name: "Alex",
}
responseBody = `{ "result": null }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p2)
Expect(err).To(BeNil())
Expect(p2).To(BeNil())
p2 = &Person{
Name: "Alex",
}
responseBody = `{ "result": {"age": 35} }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(p2)
Expect(err).To(BeNil())
Expect(p2.Name).To(Equal("Alex"))
Expect(p2.Age).To(Equal(35))
// prefilled struct is kept on no result
p3 := Person{
Name: "Alex",
}
responseBody = `{ "result": null }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p3)
Expect(err).To(BeNil())
Expect(p3.Name).To(Equal("Alex"))
// prefilled struct is extended / overwritten
p3 = Person{
Name: "Alex",
Age: 123,
}
responseBody = `{ "result": {"age": 35, "country": "Germany"} }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p3)
Expect(err).To(BeNil())
Expect(p3.Name).To(Equal("Alex"))
Expect(p3.Age).To(Equal(35))
Expect(p3.Country).To(Equal("Germany"))
// nil is an error
responseBody = `{ "result": {"age": 35} }`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(nil)
Expect(err).NotTo(BeNil())
}
func TestRpcBatchJsonResponseStruct(t *testing.T) {
RegisterTestingT(t)
rpcClient := NewClient(httpServer.URL)
// empty return body is an error
responseBody = ``
res, err := rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())
// not a json body is an error
responseBody = `{ "not": "a", "json": "object"`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())
// field "anotherField" not allowed in rpc response is an error
responseBody = `{ "anotherField": "norpc"}`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())
// TODO: result must contain one of "result", "error"
// TODO: is there an efficient way to do this?
/*responseBody = `[{}]`
res, err = rpcClient.Call("something", 1, 2, 3)
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())*/
// result must be wrapped in array on batch request
responseBody = `{"result": null}`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err.Error()).NotTo(BeNil())
// result ok since in array
responseBody = `[{"result": null}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(len(res)).To(Equal(1))
Expect(res[0].Result).To(BeNil())
// error null is ok
responseBody = `[{"error": null}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Result).To(BeNil())
Expect(res[0].Error).To(BeNil())
// result and error null is ok
responseBody = `[{"result": null, "error": null}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Result).To(BeNil())
Expect(res[0].Error).To(BeNil())
// TODO: result must not contain both of "result", "error" != null
// TODO: is there an efficient way to do this?
/*responseBody = `[{ "result": 123, "error": {"code": 123, "message": "something wrong"}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something",1, 2, 3),
})
<-requestChan
Expect(err).NotTo(BeNil())
Expect(res).To(BeNil())*/
// result string is ok
responseBody = `[{"result": "ok","id":0}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Result).To(Equal("ok"))
Expect(res[0].ID).To(Equal(0))
// result with error null is ok
responseBody = `[{"result": "ok", "error": null}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Result).To(Equal("ok"))
// error with result null is ok
responseBody = `[{"error": {"code": 123, "message": "something wrong"}, "result": null}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Result).To(BeNil())
Expect(res[0].Error.Code).To(Equal(123))
Expect(res[0].Error.Message).To(Equal("something wrong"))
// TODO: empty error is not ok, must at least contain code and message
/*responseBody = `[{ "error": {}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something",1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Result).To(BeNil())
Expect(res[0].Error).NotTo(BeNil())*/ /*
// TODO: only code in error is not ok, must at least contain code and message
*/ /*responseBody = `[{ "error": {"code": 123}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something",1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Result).To(BeNil())
Expect(res[0].Error).NotTo(BeNil())*/ /*
// TODO: only message in error is not ok, must at least contain code and message
*/ /*responseBody = `[{ "error": {"message": "something wrong"}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something",1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Result).To(BeNil())
Expect(res[0].Error).NotTo(BeNil())*/
// error with code and message is ok
responseBody = `[{ "error": {"code": 123, "message": "something wrong"}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Result).To(BeNil())
Expect(res[0].Error.Code).To(Equal(123))
Expect(res[0].Error.Message).To(Equal("something wrong"))
// check results
// should return int correctly
responseBody = `[{ "result": 1 }]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Error).To(BeNil())
i, err := res[0].GetInt()
Expect(err).To(BeNil())
Expect(i).To(Equal(int64(1)))
// error on wrong type
i = 3
responseBody = `[{ "result": "notAnInt" }]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Error).To(BeNil())
i, err = res[0].GetInt()
Expect(err).NotTo(BeNil())
Expect(i).To(Equal(int64(0)))
var p *Person
responseBody = `[{"id":0, "result": {"name": "Alex", "age": 35}}, {"id":2, "result": {"name": "Lena", "age": 2}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res[0].Error).To(BeNil())
Expect(res[0].ID).To(Equal(0))
Expect(res[1].Error).To(BeNil())
Expect(res[1].ID).To(Equal(2))
err = res[0].GetObject(&p)
Expect(p.Name).To(Equal("Alex"))
Expect(p.Age).To(Equal(35))
err = res[1].GetObject(&p)
Expect(p.Name).To(Equal("Lena"))
Expect(p.Age).To(Equal(2))
// check if error occurred
responseBody = `[{ "result": "someresult", "error": null}, { "result": null, "error": {"code": 123, "message": "something wrong"}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.HasError()).To(BeTrue())
// check if error occurred
responseBody = `[{ "result": null, "error": {"code": 123, "message": "something wrong"}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.HasError()).To(BeTrue())
// check if error occurred
responseBody = `[{ "result": null, "error": {"code": 123, "message": "something wrong"}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.HasError()).To(BeTrue())
// check if response mapping works
responseBody = `[{ "id":123,"result": 123},{ "id":1,"result": 1}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.HasError()).To(BeFalse())
resMap := res.AsMap()
int1, _ := resMap[1].GetInt()
int123, _ := resMap[123].GetInt()
Expect(int1).To(Equal(int64(1)))
Expect(int123).To(Equal(int64(123)))
// check if getByID works
int123, _ = res.GetByID(123).GetInt()
Expect(int123).To(Equal(int64(123)))
// check if error occurred
responseBody = `[{ "result": null, "error": {"code": 123, "message": "something wrong"}}]`
res, err = rpcClient.CallBatch(RPCRequests{
NewRequest("something", 1, 2, 3),
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.HasError()).To(BeTrue())
/*
// TODO: How to check if result could be parsed or if it is default?
p = nil
responseBody = `{ "result": {"anotherField": "something"} }`
res, err = rpcClient.CallBatch(RPCRequests{
{"something", Params(1, 2, 3)},
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p)
Expect(err).To(BeNil())
Expect(p).NotTo(BeNil())
// TODO: HERE######
var pp *PointerFieldPerson
responseBody = `{ "result": {"anotherField": "something", "country": "Germany"} }`
res, err = rpcClient.CallBatch(RPCRequests{
{"something", Params(1, 2, 3)},
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&pp)
Expect(err).To(BeNil())
Expect(pp.Name).To(BeNil())
Expect(pp.Age).To(BeNil())
Expect(*pp.Country).To(Equal("Germany"))
p = nil
responseBody = `{ "result": null }`
res, err = rpcClient.CallBatch(RPCRequests{
{"something", Params(1, 2, 3)},
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p)
Expect(err).To(BeNil())
Expect(p).To(BeNil())
// passing nil is an error
p = nil
responseBody = `{ "result": null }`
res, err = rpcClient.CallBatch(RPCRequests{
{"something", Params(1, 2, 3)},
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(p)
Expect(err).NotTo(BeNil())
Expect(p).To(BeNil())
p2 := &Person{
Name: "Alex",
}
responseBody = `{ "result": null }`
res, err = rpcClient.CallBatch(RPCRequests{
{"something", Params(1, 2, 3)},
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p2)
Expect(err).To(BeNil())
Expect(p2).To(BeNil())
p2 = &Person{
Name: "Alex",
}
responseBody = `{ "result": {"age": 35} }`
res, err = rpcClient.CallBatch(RPCRequests{
{"something", Params(1, 2, 3)},
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(p2)
Expect(err).To(BeNil())
Expect(p2.Name).To(Equal("Alex"))
Expect(p2.Age).To(Equal(35))
// prefilled struct is kept on no result
p3 := Person{
Name: "Alex",
}
responseBody = `{ "result": null }`
res, err = rpcClient.CallBatch(RPCRequests{
{"something", Params(1, 2, 3)},
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p3)
Expect(err).To(BeNil())
Expect(p3.Name).To(Equal("Alex"))
// prefilled struct is extended / overwritten
p3 = Person{
Name: "Alex",
Age: 123,
}
responseBody = `{ "result": {"age": 35, "country": "Germany"} }`
res, err = rpcClient.CallBatch(RPCRequests{
{"something", Params(1, 2, 3)},
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(&p3)
Expect(err).To(BeNil())
Expect(p3.Name).To(Equal("Alex"))
Expect(p3.Age).To(Equal(35))
Expect(p3.Country).To(Equal("Germany"))
// nil is an error
responseBody = `{ "result": {"age": 35} }`
res, err = rpcClient.CallBatch(RPCRequests{
{"something", Params(1, 2, 3)},
})
<-requestChan
Expect(err).To(BeNil())
Expect(res.Error).To(BeNil())
err = res.GetObject(nil)
Expect(err).NotTo(BeNil())
*/
}
func TestRpcClient_CallFor(t *testing.T) {
RegisterTestingT(t)
rpcClient := NewClient(httpServer.URL)
i := 0
responseBody = `{"result":3,"id":0,"jsonrpc":"2.0"}`
err := rpcClient.CallFor(&i, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(i).To(Equal(3))
/*
i = 3
responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(&i, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
// i is not modified when result is empty since null (nil) value cannot be stored in int
Expect(i).To(Equal(3))
var pi *int
responseBody = `{"result":4,"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(pi, "something", 1, 2, 3)
<-requestChan
Expect(err).NotTo(BeNil())
Expect(pi).To(BeNil())
responseBody = `{"result":4,"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(&pi, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
Expect(*pi).To(Equal(4))
*pi = 3
responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(&pi, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
// since pi has a value it is not overwritten by null result
Expect(pi).To(BeNil())
p := &Person{}
responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(p, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
// p is not changed since it has a value and result is null
Expect(p).NotTo(BeNil())
var p2 *Person
responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(p2, "something", 1, 2, 3)
<-requestChan
Expect(err).NotTo(BeNil())
// p is not changed since it has a value and result is null
Expect(p2).To(BeNil())
p3 := Person{}
responseBody = `{"result":null,"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(&p3, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
// p is not changed since it has a value and result is null
Expect(p).NotTo(BeNil())
p = &Person{Age: 35}
responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(p, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
// p is not changed since it has a value and result is null
Expect(p.Name).To(Equal("Alex"))
Expect(p.Age).To(Equal(35))
p2 = nil
responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(p2, "something", 1, 2, 3)
<-requestChan
Expect(err).NotTo(BeNil())
// p is not changed since it has a value and result is null
Expect(p2).To(BeNil())
p2 = nil
responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(&p2, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
// p is not changed since it has a value and result is null
Expect(p2).NotTo(BeNil())
Expect(p2.Name).To(Equal("Alex"))
p3 = Person{Age: 35}
responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(&p3, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
// p is not changed since it has a value and result is null
Expect(p.Name).To(Equal("Alex"))
Expect(p.Age).To(Equal(35))
p3 = Person{Age: 35}
responseBody = `{"result":{"name":"Alex"},"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(&p3, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
// p is not changed since it has a value and result is null
Expect(p.Name).To(Equal("Alex"))
Expect(p.Age).To(Equal(35))
var intArray []int
responseBody = `{"result":[1, 2, 3],"id":0,"jsonrpc":"2.0"}`
err = rpcClient.CallFor(&intArray, "something", 1, 2, 3)
<-requestChan
Expect(err).To(BeNil())
// p is not changed since it has a value and result is null
Expect(intArray).To(ContainElement(1))
Expect(intArray).To(ContainElement(2))
Expect(intArray).To(ContainElement(3))*/
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Country string `json:"country"`
}
type PointerFieldPerson struct {
Name *string `json:"name"`
Age *int `json:"age"`
Country *string `json:"country"`
}
type Drink struct {
Name string `json:"name"`
Ingredients []string `json:"ingredients"`
}
type Planet struct {
Name string `json:"name"`
Properties Properties `json:"properties"`
}
type Properties struct {
Distance int `json:"distance"`
Color string `json:"color"`
}