Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests for slice/array returned from jrpc calls as well as a cleaner shortcut: jrpc.GetArray() #634

Merged
merged 4 commits into from Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Expand Up @@ -52,13 +52,13 @@ You can install from source:
The [releases](https://github.com/fortio/fortio/releases) page has binaries for many OS/architecture combinations (see assets).

```shell
curl -L https://github.com/fortio/fortio/releases/download/v1.38.1/fortio-linux_amd64-1.38.1.tgz \
curl -L https://github.com/fortio/fortio/releases/download/v1.38.2/fortio-linux_amd64-1.38.2.tgz \
| sudo tar -C / -xvzpf -
# or the debian package
wget https://github.com/fortio/fortio/releases/download/v1.38.1/fortio_1.38.1_amd64.deb
dpkg -i fortio_1.38.1_amd64.deb
wget https://github.com/fortio/fortio/releases/download/v1.38.2/fortio_1.38.2_amd64.deb
dpkg -i fortio_1.38.2_amd64.deb
# or the rpm
rpm -i https://github.com/fortio/fortio/releases/download/v1.38.1/fortio-1.38.1-1.x86_64.rpm
rpm -i https://github.com/fortio/fortio/releases/download/v1.38.2/fortio-1.38.2-1.x86_64.rpm
# and more, see assets in release page
```

Expand All @@ -68,7 +68,7 @@ On a MacOS you can also install Fortio using [Homebrew](https://brew.sh/):
brew install fortio
```

On Windows, download https://github.com/fortio/fortio/releases/download/v1.38.1/fortio_win_1.38.1.zip and extract `fortio.exe` to any location, then using the Windows Command Prompt:
On Windows, download https://github.com/fortio/fortio/releases/download/v1.38.2/fortio_win_1.38.2.zip and extract `fortio.exe` to any location, then using the Windows Command Prompt:
```
fortio.exe server
```
Expand Down Expand Up @@ -116,7 +116,7 @@ Full list of command line flags (`fortio help`):
<details>
<!-- use release/updateFlags.sh to update this section -->
<pre>
Φορτίο 1.38.1 usage:
Φορτίο 1.38.2 usage:
fortio command [flags] target
where command is one of: load (load testing), server (starts ui, rest api,
http-echo, redirect, proxies, tcp-echo and grpc ping servers), tcp-echo (only
Expand Down
12 changes: 11 additions & 1 deletion jrpc/jrpcClient.go
Expand Up @@ -120,6 +120,16 @@ func Get[Q any](url *Destination) (*Q, error) {
return Fetch[Q](url, []byte{})
}

// GetArray fetches and deseializes the JSON returned by the Destination into a slice of
// Q struct (ie the response is a json array).
func GetArray[Q any](url *Destination) ([]Q, error) {
slicePtr, err := Fetch[[]Q](url, []byte{})
if slicePtr == nil {
return nil, err
}
return *slicePtr, err
}

// GetURL is Get without additional options (default timeout and headers).
func GetURL[Q any](url string) (*Q, error) {
return Get[Q](NewDestination(url))
Expand Down Expand Up @@ -162,7 +172,7 @@ func Fetch[Q any](url *Destination, bytes []byte) (*Q, error) {
if ok {
return nil, err
}
return nil, &FetchError{"deserialization error", code, err, bytes}
return nil, &FetchError{"non ok http result and deserialization error", code, err, bytes}
}
if !ok {
// can still be "ok" for some callers, they can use the result object as it deserialized as expected.
Expand Down
95 changes: 94 additions & 1 deletion jrpc/jrpc_test.go
Expand Up @@ -270,7 +270,7 @@ func TestJPRC(t *testing.T) {
if unwrap.Error() != expected {
t.Errorf("unwrapped error expected to be %q, got %v", expected, unwrap.Error())
}
expected = "deserialization error, code 747: " + expected + " (raw reply: {bad})"
expected = "non ok http result and deserialization error, code 747: " + expected + " (raw reply: {bad})"
ldemailly marked this conversation as resolved.
Show resolved Hide resolved
if err.Error() != expected {
t.Errorf("error string expected %q, got %q", expected, err.Error())
}
Expand Down Expand Up @@ -396,3 +396,96 @@ func TestSerializeServerReply(t *testing.T) {
t.Errorf("expected %s, got %s", expected, str)
}
}

// Testing slices

type SliceRequest struct {
HowMany int
}

type SliceOneResponse struct {
Index int
Data string
}

func TestJPRCSlices(t *testing.T) {
mux, addr := fhttp.HTTPServer("test3", "0")
port := addr.(*net.TCPAddr).Port
mux.HandleFunc("/test-api-array", func(w http.ResponseWriter, r *http.Request) {
req, err := jrpc.HandleCall[SliceRequest](w, r)
if err != nil {
err = jrpc.ReplyError(w, "request error", err)
if err != nil {
t.Errorf("Error in replying error: %v", err)
}
return
}
n := req.HowMany
if n < 0 {
jrpc.ReplyError(w, "invalid negative count", nil)
return
}
if r.FormValue("errror") != "" {
jrpc.ReplyError(w, "error requested", nil)
return
}
if n == 0 {
n = 42 // for testing of GetArray
}
resp := make([]SliceOneResponse, n)
for i := 0; i < n; i++ {
resp[i] = SliceOneResponse{
Index: i,
Data: fmt.Sprintf("data %d", i),
}
}
jrpc.ReplyOk(w, &resp)
})
url := fmt.Sprintf("http://localhost:%d/test-api-array", port)
req := SliceRequest{10}
res, err := jrpc.CallURL[[]SliceOneResponse](url, &req)
if err != nil {
t.Errorf("failed Call: %v", err)
}
if res == nil {
t.Errorf("nil response")
return
}
slice := *res
if len(slice) != 10 {
t.Errorf("expected 10 results, got %d", len(slice))
}
for i := 0; i < len(slice); i++ {
el := slice[i]
if el.Index != i {
t.Errorf("expected index %d, got %d", i, el.Index)
}
if el.Data != fmt.Sprintf("data %d", i) {
t.Errorf("expected data %d, got %s", i, el.Data)
}
}
slice, err = jrpc.GetArray[SliceOneResponse](jrpc.NewDestination(url))
if err != nil {
t.Errorf("failed GetArray: %v", err)
}
if len(slice) != 42 {
t.Errorf("expected 42 results, got %d", len(slice))
}
for i := 0; i < len(slice); i++ {
el := slice[i]
if el.Index != i {
t.Errorf("expected index %d, got %d", i, el.Index)
}
if el.Data != fmt.Sprintf("data %d", i) {
t.Errorf("expected data %d, got %s", i, el.Data)
}
}
// Empty slice/error
slice, err = jrpc.GetArray[SliceOneResponse](jrpc.NewDestination(url + "?errror=true"))
if err == nil {
t.Errorf("expected error, got nil")
}
if slice != nil {
t.Errorf("expected nil slice, got %v", slice)
}
}