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

Improve tests by adding tiny go test cases #90

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions imports/wasi_http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@ The specification is in active development/flux as is the [`wit-bindgen`](https:

You should expect a degree of instability in these interfaces for the foreseeable future.

## Versioning & Clients
WASI-http doesn't have a great versioning story currently, so explicitly tying to git commit SHA and
`wit-bindgen` tooling is the best we can do.

Currently, the `wasi-go` codebase has the following versions:
* `v1` corresponds to [244e068c2d](https://github.com/WebAssembly/wasi-http/tree/244e068c2de43088bda308fcdf51ed2479d885f5) and [`wasmtime` 9.0.0](https://github.com/bytecodealliance/wasmtime/tree/release-9.0.0)

If you want to generate guest code for your particular language, you will need to use [`wit-bindgen` 0.4.0](https://github.com/bytecodealliance/wit-bindgen/releases/tag/wit-bindgen-cli-0.4.0)

Here is an example for generating the Golang guest bindings:

```sh
# Setup
git clone https://github.com/WebAssembly/wasi-http
cd wasi-http
# Sync to the definitions for the v1
git checkout 244e068
cd ..

# Generate
wit-bindgen tiny-go wasi-http/wit -w proxy
```

Comment on lines +8 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for documenting this!

## Example guest code
There are existing examples of working guest code in the following languages
* [Golang](https://github.com/dev-wasm/dev-wasm-go/tree/main/http)
Expand Down
57 changes: 41 additions & 16 deletions imports/wasi_http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package wasi_http
import (
"context"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"os"
Expand All @@ -19,21 +19,23 @@ import (
)

type handler struct {
urls []string
bodies []string
urls []string
bodies []string
methods []string
}

func (h *handler) reset() {
h.bodies = []string{}
h.urls = []string{}
h.methods = []string{}
Comment on lines 28 to +30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly curious, are the empty slices semantically different than using nil here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, I can use nil if you prefer. It's probably my own aversion to having null pointers in other languages where they are different.

}

func (h *handler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
body := ""

if req.Body != nil {
defer req.Body.Close()
data, err := ioutil.ReadAll(req.Body)
data, err := io.ReadAll(req.Body)
if err != nil {
panic(err.Error())
}
Expand All @@ -45,15 +47,13 @@ func (h *handler) ServeHTTP(res http.ResponseWriter, req *http.Request) {

h.urls = append(h.urls, req.URL.String())
h.bodies = append(h.bodies, body)
h.methods = append(h.methods, req.Method)
}

func TestHttpClient(t *testing.T) {
filePaths, _ := filepath.Glob("../../testdata/c/http/http*.wasm")
for _, file := range filePaths {
fmt.Printf("%v\n", file)
}
filePaths, _ := filepath.Glob("../../testdata/*/http/http*.wasm")
if len(filePaths) == 0 {
t.Log("nothing to test")
t.Error("nothing to test")
}

h := handler{}
Expand All @@ -64,13 +64,38 @@ func TestHttpClient(t *testing.T) {
{
"/get?some=arg&goes=here",
"/post",
"/put",
},
{
"/get?some=arg&goes=here",
"/post",
"/put",
},
}

expectedBodies := [][]string{
{
"",
"{\"foo\": \"bar\"}",
"{\"baz\": \"blah\"}",
},
{
"",
"{\"foo\": \"bar\"}",
"{\"baz\": \"blah\"}",
},
}

expectedMethods := [][]string{
{
"GET",
"POST",
"PUT",
},
{
"GET",
"POST",
"PUT",
},
}

Expand Down Expand Up @@ -128,19 +153,19 @@ func TestHttpClient(t *testing.T) {
if !reflect.DeepEqual(expectedBodies[testIx], h.bodies) {
t.Errorf("Unexpected paths: %v vs %v", h.bodies, expectedBodies[testIx])
}
if !reflect.DeepEqual(expectedMethods[testIx], h.methods) {
t.Errorf("Unexpected paths: %v vs %v", h.methods, expectedMethods[testIx])
}

h.reset()
})
}
}

func TestServer(t *testing.T) {
filePaths, _ := filepath.Glob("../../testdata/c/http/server*.wasm")
for _, file := range filePaths {
fmt.Printf("%v\n", file)
}
filePaths, _ := filepath.Glob("../../testdata/*/http/server*.wasm")
if len(filePaths) == 0 {
t.Log("nothing to test")
t.Error("nothing to test")
}

for _, test := range filePaths {
Expand Down Expand Up @@ -192,12 +217,12 @@ func TestServer(t *testing.T) {
for i := 0; i < 3; i++ {
res, err := http.Get(s.URL)
if err != nil {
t.Error("Failed to read from server.")
t.Errorf("Failed to read from server: %s", err.Error())
continue
}
defer res.Body.Close()

data, err := ioutil.ReadAll(res.Body)
data, err := io.ReadAll(res.Body)
if err != nil {
t.Error("Failed to read body.")
continue
Expand Down
6 changes: 5 additions & 1 deletion imports/wasi_http/types/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ type Request struct {
}

func (r Request) Url() string {
return fmt.Sprintf("%s://%s%s%s", r.Scheme, r.Authority, r.Path, r.Query)
u := fmt.Sprintf("%s://%s%s", r.Scheme, r.Authority, r.Path)
if len(r.Query) > 0 {
u = u + "?" + r.Query
}
return u
}

type Requests struct {
Expand Down
4 changes: 4 additions & 0 deletions testdata/c/http/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ default: http.wasm

http.wasm: http.c
${cc} proxy.c proxy_component_type.o http.c -o http.wasm

server.wasm: server.c
${cc} proxy.c proxy_component_type.o server.c -o server.wasm

6 changes: 5 additions & 1 deletion testdata/c/http/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,17 @@ int request(uint8_t method_tag, uint8_t scheme_tag, const char * authority_str,

int main() {
const char *authority = getenv("SERVER");
int r = request(TYPES_METHOD_GET, TYPES_SCHEME_HTTP, authority, "/get", "?some=arg&goes=here", NULL);
int r = request(TYPES_METHOD_GET, TYPES_SCHEME_HTTP, authority, "/get", "some=arg&goes=here", NULL);
if (r != 0) {
return r;
}
r = request(TYPES_METHOD_POST, TYPES_SCHEME_HTTP, authority, "/post", "", "{\"foo\": \"bar\"}");
if (r != 0) {
return r;
}
r = request(TYPES_METHOD_PUT, TYPES_SCHEME_HTTP, authority, "/put", "", "{\"baz\": \"blah\"}");
if (r != 0) {
return r;
}
return 0;
}
Binary file modified testdata/c/http/http.wasm
Binary file not shown.
5 changes: 5 additions & 0 deletions testdata/tinygo/http/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/stealthrocket/wasi-go/testdata/tinygo/http

go 1.20

require github.com/dev-wasm/dev-wasm-go/http v0.0.0-20230720212318-cb04411741fd // indirect
2 changes: 2 additions & 0 deletions testdata/tinygo/http/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/dev-wasm/dev-wasm-go/http v0.0.0-20230720212318-cb04411741fd h1:fLCnhqCMaCHXhA6Ozb7sfTyqt/g5mp1dHOjbHo/PacM=
github.com/dev-wasm/dev-wasm-go/http v0.0.0-20230720212318-cb04411741fd/go.mod h1:K7HKbanDfLH3UL/hvExI1hVPetqnn9OZ4IOFcD5Pzw0=
55 changes: 55 additions & 0 deletions testdata/tinygo/http/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

import (
"fmt"
"io/ioutil"
"net/http"
"os"

wasiclient "github.com/dev-wasm/dev-wasm-go/http/client"
)

func printResponse(r *http.Response) {
fmt.Printf("Status: %d\n", r.StatusCode)
for k, v := range r.Header {
fmt.Printf("%s: %s\n", k, v[0])
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err.Error())
os.Exit(4)
}
fmt.Printf("Body: \n%s\n", body)
}

func main() {
server := os.Getenv("SERVER")
client := http.Client{
Transport: wasiclient.WasiRoundTripper{},
}
res, err := client.Get("http://" + server + "/get?some=arg&goes=here")
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
printResponse(res)
res.Body.Close()

res, err = client.Post("http://"+server+"/post", "application/json", wasiclient.BodyReaderCloser([]byte("{\"foo\": \"bar\"}")))
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
}
printResponse(res)
res.Body.Close()

res, err = wasiclient.Put(&client, "http://"+server+"/put", "application/json", wasiclient.BodyReaderCloser([]byte("{\"baz\": \"blah\"}")))
if err != nil {
fmt.Println(err.Error())
os.Exit(3)
}
printResponse(res)
res.Body.Close()

os.Exit(0)
}
Binary file added testdata/tinygo/http/http.wasm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit concerned about introducing such large files in the source tree, do you think we could have them regenerated by the Makefile to avoid checking them in?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to make this driven by Makefile, though it will mean that tinygo needs to be installed in order to run these tests.

wdyt?

I can edit the tests to skip the tests if tinygo is missing, and update the github action on this repo so that it is installed for the GH action CI/CD.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can have TinyGo installed in CI without having to recompile it I think it would be the right trade off 👍

Binary file not shown.
21 changes: 21 additions & 0 deletions testdata/tinygo/http/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"
"net/http"

"github.com/dev-wasm/dev-wasm-go/http/server"
)

var count int = 0

func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Server", "Wasirun 0.0.1")
w.WriteHeader(200)
body := fmt.Sprintf("Hello from WASM! (%d)", count)
count = count + 1
w.Write([]byte(body))
})
server.ListenAndServe(nil)
}
Binary file added testdata/tinygo/http/server.wasm
Binary file not shown.