Skip to content

Commit

Permalink
Merge pull request #180 from danielgtaylor/fix-gmux-register
Browse files Browse the repository at this point in the history
fix(gorilla/mux): properly register operations with the router
  • Loading branch information
danielgtaylor authored Dec 10, 2023
2 parents f54b62a + 363bbff commit f875445
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 7 deletions.
103 changes: 103 additions & 0 deletions adapters/adapters_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Package adapters_test runs basic verification tests on all adapters.
package adapters_test

import (
"context"
"fmt"
"net/http"
"strings"
"testing"

"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humachi"
"github.com/danielgtaylor/huma/v2/adapters/humafiber"
"github.com/danielgtaylor/huma/v2/adapters/humagin"
"github.com/danielgtaylor/huma/v2/adapters/humahttprouter"
"github.com/danielgtaylor/huma/v2/adapters/humamux"
"github.com/danielgtaylor/huma/v2/humatest"
"github.com/gin-gonic/gin"
chi4 "github.com/go-chi/chi"
"github.com/go-chi/chi/v5"
"github.com/gofiber/fiber/v2"
"github.com/gorilla/mux"
"github.com/julienschmidt/httprouter"
"github.com/stretchr/testify/assert"
)

// Test the various input types (path, query, header, body).
type TestInput struct {
Group string `path:"group"`
Verbose bool `query:"verbose"`
Auth string `header:"Authorization"`
Body struct {
Name string `json:"name"`
Email string `json:"email"`
}
}

// Test outputs (headers, body).
type TestOutput struct {
MyHeader string `header:"MyHeader"`
Body struct {
Message string `json:"message"`
}
}

func testHandler(ctx context.Context, input *TestInput) (*TestOutput, error) {
resp := &TestOutput{}
resp.MyHeader = "my-value"
resp.Body.Message = fmt.Sprintf("Hello, %s <%s>! (%s, %v, %s)", input.Body.Name, input.Body.Email, input.Group, input.Verbose, input.Auth)
return resp, nil
}

func testAdapter(t *testing.T, api huma.API) {
t.Helper()

methods := []string{http.MethodPut, http.MethodPost}

// Test two operations with the same path but different methods
for _, method := range methods {
huma.Register(api, huma.Operation{
OperationID: method + "-test",
Method: method,
Path: "/{group}",
}, testHandler)
}

// Make test calls
for _, method := range methods {
testAPI := humatest.Wrap(t, api)
resp := testAPI.Do(method, "/foo",
"Host: localhost",
"Authorization: Bearer abc123",
strings.NewReader(`{"name": "Daniel", "email": "[email protected]"}`),
)

assert.Equal(t, http.StatusOK, resp.Code)
assert.Equal(t, "my-value", resp.Header().Get("MyHeader"))
assert.JSONEq(t, `{
"$schema": "http://localhost/schemas/TestOutputBody.json",
"message": "Hello, Daniel <[email protected]>! (foo, false, Bearer abc123)"
}`, resp.Body.String())
}
}

func TestAdapters(t *testing.T) {
config := huma.DefaultConfig("Test", "1.0.0")

for _, adapter := range []struct {
name string
new func() huma.API
}{
{"chi", func() huma.API { return humachi.New(chi.NewMux(), config) }},
{"chi4", func() huma.API { return humachi.NewV4(chi4.NewMux(), config) }},
{"fiber", func() huma.API { return humafiber.New(fiber.New(), config) }},
{"gin", func() huma.API { return humagin.New(gin.New(), config) }},
{"httprouter", func() huma.API { return humahttprouter.New(httprouter.New(), config) }},
{"mux", func() huma.API { return humamux.New(mux.NewRouter(), config) }},
} {
t.Run(adapter.name, func(t *testing.T) {
testAdapter(t, adapter.new())
})
}
}
2 changes: 1 addition & 1 deletion adapters/humamux/humagmux_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package humagmux
package humamux

import (
"context"
Expand Down
13 changes: 7 additions & 6 deletions adapters/humamux/humamux.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package humagmux
package humamux

import (
"context"
Expand Down Expand Up @@ -93,12 +93,13 @@ type gMux struct {
}

func (a *gMux) Handle(op *huma.Operation, handler func(huma.Context)) {
m := op.Method
a.router.HandleFunc(op.Path, func(w http.ResponseWriter, r *http.Request) {
if r.Method == m {
a.router.
NewRoute().
Path(op.Path).
Methods(op.Method).
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler(&gmuxContext{op: op, r: r, w: w})
}
})
})
}

func (a *gMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Expand Down

0 comments on commit f875445

Please sign in to comment.