components-contrib/middleware/http/wasm/internal/e2e_test.go

149 lines
4.0 KiB
Go

package internal_test
import (
"bytes"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path"
"testing"
"github.com/dapr/components-contrib/middleware/http/wasm"
"github.com/dapr/kit/logger"
"github.com/dapr/components-contrib/metadata"
"github.com/stretchr/testify/require"
"github.com/dapr/components-contrib/middleware"
)
var guestWasm map[string][]byte
const (
guestWasmOutput = "output"
guestWasmRewrite = "rewrite"
)
// TestMain ensures we can read the test wasm prior to running e2e tests.
func TestMain(m *testing.M) {
wasms := []string{guestWasmOutput, guestWasmRewrite}
guestWasm = make(map[string][]byte, len(wasms))
for _, name := range wasms {
if wasm, err := os.ReadFile(path.Join("e2e-guests", name, "main.wasm")); err != nil {
log.Panicln(err)
} else {
guestWasm[name] = wasm
}
}
os.Exit(m.Run())
}
func Test_EndToEnd(t *testing.T) {
l := logger.NewLogger(t.Name())
var buf bytes.Buffer
l.SetOutputLevel(logger.DebugLevel)
l.SetOutput(&buf)
type testCase struct {
name string
guest []byte
test func(t *testing.T, handler http.Handler, log *bytes.Buffer)
}
tests := []testCase{
{
name: "consoleLog stdout and stderr",
guest: guestWasm[guestWasmOutput],
test: func(t *testing.T, handler http.Handler, log *bytes.Buffer) {
var w http.ResponseWriter
var r http.Request
handler.ServeHTTP(w, &r)
// First, we expect any console logging written inline from
// init (main) and the request[0-9] funcs to info level.
//
// Then, we expect to see stdout and stderr from both scopes
// at debug level.
for _, s := range []string{
`level=info msg="main ConsoleLog"`,
`level=info msg="request[0] ConsoleLog"`,
`level=debug msg="wasm stdout: main Stdout\nrequest[0] Stdout\n"`,
`level=debug msg="wasm stderr: main Stderr\nrequest[0] Stderr\n"`,
} {
require.Contains(t, log.String(), s)
}
},
},
{
name: "consoleLog multiple requests",
guest: guestWasm[guestWasmOutput],
test: func(t *testing.T, handler http.Handler, log *bytes.Buffer) {
// Service more requests than one to ensure pooling works properly.
for i := 0; i < 3; i++ {
r := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
}
// We expect to see initialization (main) twice, once for each
// module in the pool. We expect to see request[1] which shows
// round-robin back to the first module in the pool.
for _, s := range []string{
`level=info msg="main ConsoleLog"`,
`level=info msg="request[0] ConsoleLog"`,
`level=debug msg="wasm stdout: main Stdout\nrequest[0] Stdout\n"`,
`level=debug msg="wasm stderr: main Stderr\nrequest[0] Stderr\n"`,
`level=info msg="request[1] ConsoleLog"`,
`level=debug msg="wasm stdout: request[1] Stdout\n"`,
`level=debug msg="wasm stderr: request[1] Stderr\n"`,
} {
require.Contains(t, log.String(), s)
}
},
},
{
name: "rewrite",
guest: guestWasm[guestWasmRewrite],
test: func(t *testing.T, handler http.Handler, log *bytes.Buffer) {
var w http.ResponseWriter
u, err := url.Parse("https://test.io/v1.0/hi?name=panda")
if err != nil {
panic(err)
}
r := &http.Request{
Method: http.MethodGet,
URL: u,
Host: "test.io",
Header: map[string][]string{
"User-Agent": {"curl/7.79.1"},
"Accept": {"*/*"},
},
}
handler.ServeHTTP(w, r)
require.Equal(t, "/v1.0/hello?name=teddy", r.URL.RequestURI())
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
defer buf.Reset()
wasmPath := path.Join(t.TempDir(), "guest.wasm")
require.NoError(t, os.WriteFile(wasmPath, tc.guest, 0o600))
meta := metadata.Base{Properties: map[string]string{"path": wasmPath}}
handlerFn, err := wasm.NewMiddleware(l).GetHandler(middleware.Metadata{Base: meta})
require.NoError(t, err)
handler := handlerFn(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
tc.test(t, handler, &buf)
})
}
}