initial commit
This commit is contained in:
356
inject_test.go
Normal file
356
inject_test.go
Normal file
@ -0,0 +1,356 @@
|
||||
package lazyhandler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.jeffthecoder.xyz/public/lazyhandler/magic"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type StatusPathValues struct {
|
||||
Status int `pathvalue:"status"`
|
||||
}
|
||||
|
||||
type HelloWorldRequest struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
type HelloWorldResponse struct {
|
||||
Greeting string `json:"greeting"`
|
||||
}
|
||||
|
||||
type HelloWorldParams struct {
|
||||
Name *string `pathvalue:"name,match=" query:"name"`
|
||||
}
|
||||
|
||||
type MultipleParams struct {
|
||||
Numbers []int
|
||||
Strings []string
|
||||
OptionalNumber *int
|
||||
OptionalString *string
|
||||
}
|
||||
|
||||
func TestNotAFunc(t *testing.T) {
|
||||
v, err := MagicHandler(1)
|
||||
if err == nil {
|
||||
t.Fatalf("expect not ok, get %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyFunc(t *testing.T) {
|
||||
MagicHandler(func() {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
type SomeType int
|
||||
|
||||
func (obj SomeType) Func() {
|
||||
|
||||
}
|
||||
|
||||
func TestMethod(t *testing.T) {
|
||||
obj := SomeType(0)
|
||||
MagicHandler(obj.Func)
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
Path string
|
||||
Pattern string
|
||||
|
||||
CreateHandler func() http.Handler
|
||||
ExpectPanicOnCreateHandler bool
|
||||
|
||||
NoCheck bool
|
||||
MakeRequest func(string) *http.Request
|
||||
CheckResponse func(*http.Response) error
|
||||
}
|
||||
|
||||
var (
|
||||
cases = []testCase{
|
||||
{
|
||||
Path: "/",
|
||||
Pattern: "/{$}",
|
||||
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func() (int, string, error) {
|
||||
return 200, "hi", nil
|
||||
})
|
||||
},
|
||||
MakeRequest: func(base string) *http.Request {
|
||||
r, err := http.NewRequest("GET", base+"/", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/error-checked-before-other-return-values",
|
||||
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func() (int, string, error) {
|
||||
// it checks errors first
|
||||
return 200, "hi", fmt.Errorf("it is handled before other return values")
|
||||
})
|
||||
},
|
||||
MakeRequest: func(base string) *http.Request {
|
||||
r, err := http.NewRequest("GET", base+"/return-error", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/return-nil",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func() (magic.RespondWriter, error) {
|
||||
// no response body, 200
|
||||
return nil, nil
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/no-return",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func() {
|
||||
// return nothing at all
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/no-error",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func() int {
|
||||
// well, error is not needed
|
||||
return http.StatusNoContent
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/respond-json",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func() magic.Json[map[string]any] {
|
||||
return magic.Json[map[string]any]{
|
||||
Data: map[string]any{
|
||||
"message": "hello, world",
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/extract-json",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(body magic.Json[HelloWorldRequest]) magic.Json[HelloWorldResponse] {
|
||||
return magic.Json[HelloWorldResponse]{
|
||||
Data: HelloWorldResponse{
|
||||
Greeting: "hello, " + body.Data.Name,
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/extract-optional",
|
||||
CreateHandler: func() http.Handler {
|
||||
// the parameter accepts a pointer to extractor, which indicates that it is optional
|
||||
return MustMagicHandler(func(body *magic.Json[HelloWorldRequest]) magic.Json[HelloWorldResponse] {
|
||||
name := "world"
|
||||
if body != nil {
|
||||
name = body.Data.Name
|
||||
}
|
||||
return magic.Json[HelloWorldResponse]{
|
||||
Data: HelloWorldResponse{
|
||||
Greeting: "hello, " + name,
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/inject-request-respond-status-from-http-request/418", // yeah...i'm a teapot, lol
|
||||
Pattern: "/inject-request-respond-status-from-http-request/{status}",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(r *http.Request) (int, error) {
|
||||
// inject *http.Request, respond with status only
|
||||
return strconv.Atoi(r.PathValue("status"))
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/inject-path-value-respond-status/418", // yeah...i'm a teapot, lol
|
||||
Pattern: "/inject-path-value-respond-status/{status}",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(pathValues magic.PathValue[StatusPathValues]) int {
|
||||
return pathValues.Data.Status
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/inject-optional-path-value/rose", // name extracted
|
||||
Pattern: "/inject-optional-path-value/{name}",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(pathValues magic.PathValue[HelloWorldParams]) magic.Json[HelloWorldResponse] {
|
||||
name := "world"
|
||||
if pathValues.Data.Name != nil {
|
||||
name = *pathValues.Data.Name
|
||||
}
|
||||
return magic.Json[HelloWorldResponse]{
|
||||
Data: HelloWorldResponse{
|
||||
Greeting: "hello, " + name,
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/inject-optional-path-value", // name fallback to world
|
||||
Pattern: "/inject-optional-path-value",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(pathValues magic.PathValue[HelloWorldParams]) magic.Json[HelloWorldResponse] {
|
||||
name := "world"
|
||||
if pathValues.Data.Name != nil {
|
||||
name = *pathValues.Data.Name
|
||||
}
|
||||
return magic.Json[HelloWorldResponse]{
|
||||
Data: HelloWorldResponse{
|
||||
Greeting: "hello, " + name,
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/direct-response-writer-extractor",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(w http.ResponseWriter) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/hahaha-it-act-just-like-http-handlerfunc",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "/websocket",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(ws *websocket.Conn) {
|
||||
|
||||
})
|
||||
},
|
||||
NoCheck: true,
|
||||
},
|
||||
{
|
||||
Pattern: "/multiple-respond-extractor-should-panic-on-create",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(conn *websocket.Conn) magic.Json[HelloWorldResponse] {
|
||||
return magic.Json[HelloWorldResponse]{}
|
||||
})
|
||||
},
|
||||
ExpectPanicOnCreateHandler: true,
|
||||
},
|
||||
// {
|
||||
// Pattern: "/hello-query",
|
||||
// CreateHandler: func() http.Handler {
|
||||
// return MustMagicHandler(func(query magic.Query[HelloWorldParams]) magic.Json[HelloWorldResponse] {
|
||||
// name := "world"
|
||||
// if query.Data.Name != nil {
|
||||
// name = *query.Data.Name
|
||||
// }
|
||||
// return magic.Json[HelloWorldResponse]{
|
||||
// Data: HelloWorldResponse{
|
||||
// Greeting: "hello, " + name,
|
||||
// },
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
// },
|
||||
{
|
||||
Pattern: "/test-query",
|
||||
CreateHandler: func() http.Handler {
|
||||
return MustMagicHandler(func(query magic.Query[MultipleParams]) {
|
||||
log.Println(query.Data)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestActualFunctions(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
t.Run("register-routes", func(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
path := c.Path
|
||||
if path == "" {
|
||||
path = c.Pattern
|
||||
}
|
||||
t.Run(strings.TrimPrefix(path, "/"), func(t *testing.T) {
|
||||
defer func() {
|
||||
if o := recover(); (o != nil) != c.ExpectPanicOnCreateHandler {
|
||||
if c.ExpectPanicOnCreateHandler {
|
||||
t.Fatal("expect panic but panic did not occur")
|
||||
} else {
|
||||
t.Fatalf("panic not expected: %v", o)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if handler := c.CreateHandler(); handler != nil {
|
||||
mux.Handle(c.Pattern, handler)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("check-actual-result", func(t *testing.T) {
|
||||
server := httptest.NewTLSServer(mux)
|
||||
client := server.Client()
|
||||
for _, c := range cases {
|
||||
if c.ExpectPanicOnCreateHandler || c.NoCheck {
|
||||
continue
|
||||
}
|
||||
|
||||
path := c.Path
|
||||
if path == "" {
|
||||
path = c.Pattern
|
||||
}
|
||||
t.Run(strings.TrimPrefix(path, "/"), func(t *testing.T) {
|
||||
var req *http.Request
|
||||
|
||||
if c.MakeRequest != nil {
|
||||
req = c.MakeRequest(server.URL)
|
||||
} else {
|
||||
request, err := http.NewRequest("GET", server.URL+path, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req = request
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
log.Println(resp)
|
||||
if bytes, err := io.ReadAll(resp.Body); err == nil {
|
||||
log.Println(string(bytes))
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user