mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-11 19:47:00 +08:00
Merge branch 'master' into fix/bytesconv-empty-cases
This commit is contained in:
commit
ddb67d1f2f
246
ginS/gins_test.go
Normal file
246
ginS/gins_test.go
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
// Copyright 2025 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ginS
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGET(t *testing.T) {
|
||||||
|
GET("/test", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "test")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "test", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPOST(t *testing.T) {
|
||||||
|
POST("/post", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusCreated, "created")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/post", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
|
assert.Equal(t, "created", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPUT(t *testing.T) {
|
||||||
|
PUT("/put", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "updated")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPut, "/put", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "updated", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDELETE(t *testing.T) {
|
||||||
|
DELETE("/delete", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "deleted")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodDelete, "/delete", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "deleted", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPATCH(t *testing.T) {
|
||||||
|
PATCH("/patch", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "patched")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPatch, "/patch", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "patched", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOPTIONS(t *testing.T) {
|
||||||
|
OPTIONS("/options", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "options")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodOptions, "/options", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "options", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHEAD(t *testing.T) {
|
||||||
|
HEAD("/head", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "head")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodHead, "/head", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAny(t *testing.T) {
|
||||||
|
Any("/any", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "any")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/any", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "any", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandle(t *testing.T) {
|
||||||
|
Handle(http.MethodGet, "/handle", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "handle")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/handle", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "handle", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroup(t *testing.T) {
|
||||||
|
group := Group("/group")
|
||||||
|
group.GET("/test", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "group test")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/group/test", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "group test", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUse(t *testing.T) {
|
||||||
|
var middlewareExecuted bool
|
||||||
|
Use(func(c *gin.Context) {
|
||||||
|
middlewareExecuted = true
|
||||||
|
c.Next()
|
||||||
|
})
|
||||||
|
|
||||||
|
GET("/middleware-test", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "ok")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/middleware-test", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.True(t, middlewareExecuted)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoRoute(t *testing.T) {
|
||||||
|
NoRoute(func(c *gin.Context) {
|
||||||
|
c.String(http.StatusNotFound, "custom 404")
|
||||||
|
})
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/nonexistent", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
assert.Equal(t, "custom 404", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoMethod(t *testing.T) {
|
||||||
|
NoMethod(func(c *gin.Context) {
|
||||||
|
c.String(http.StatusMethodNotAllowed, "method not allowed")
|
||||||
|
})
|
||||||
|
|
||||||
|
// This just verifies that NoMethod is callable
|
||||||
|
// Testing the actual behavior would require a separate engine instance
|
||||||
|
assert.NotNil(t, engine())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoutes(t *testing.T) {
|
||||||
|
GET("/routes-test", func(c *gin.Context) {})
|
||||||
|
|
||||||
|
routes := Routes()
|
||||||
|
assert.NotEmpty(t, routes)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, route := range routes {
|
||||||
|
if route.Path == "/routes-test" && route.Method == http.MethodGet {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.True(t, found)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetHTMLTemplate(t *testing.T) {
|
||||||
|
tmpl := template.Must(template.New("test").Parse("Hello {{.}}"))
|
||||||
|
SetHTMLTemplate(tmpl)
|
||||||
|
|
||||||
|
// Verify engine has template set
|
||||||
|
assert.NotNil(t, engine())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticFile(t *testing.T) {
|
||||||
|
StaticFile("/static-file", "../testdata/test_file.txt")
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/static-file", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatic(t *testing.T) {
|
||||||
|
Static("/static-dir", "../testdata")
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/static-dir/test_file.txt", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticFS(t *testing.T) {
|
||||||
|
fs := http.Dir("../testdata")
|
||||||
|
StaticFS("/static-fs", fs)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/static-fs/test_file.txt", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
engine().ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
4
go.mod
4
go.mod
@ -12,7 +12,7 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/modern-go/reflect2 v1.0.2
|
github.com/modern-go/reflect2 v1.0.2
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4
|
github.com/pelletier/go-toml/v2 v2.2.4
|
||||||
github.com/quic-go/quic-go v0.56.0
|
github.com/quic-go/quic-go v0.57.1
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/ugorji/go/codec v1.3.1
|
github.com/ugorji/go/codec v1.3.1
|
||||||
golang.org/x/net v0.47.0
|
golang.org/x/net v0.47.0
|
||||||
@ -32,7 +32,7 @@ require (
|
|||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
golang.org/x/arch v0.20.0 // indirect
|
golang.org/x/arch v0.20.0 // indirect
|
||||||
golang.org/x/crypto v0.45.0 // indirect
|
golang.org/x/crypto v0.45.0 // indirect
|
||||||
|
|||||||
8
go.sum
8
go.sum
@ -49,10 +49,10 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0
|
|||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||||
github.com/quic-go/quic-go v0.56.0 h1:q/TW+OLismmXAehgFLczhCDTYB3bFmua4D9lsNBWxvY=
|
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
|
||||||
github.com/quic-go/quic-go v0.56.0/go.mod h1:9gx5KsFQtw2oZ6GZTyh+7YEvOxWCL9WZAepnHxgAo6c=
|
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
|||||||
@ -68,6 +68,9 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if e, ok := err.(error); ok && errors.Is(e, http.ErrAbortHandler) {
|
||||||
|
brokenPipe = true
|
||||||
|
}
|
||||||
if logger != nil {
|
if logger != nil {
|
||||||
const stackSkip = 3
|
const stackSkip = 3
|
||||||
if brokenPipe {
|
if brokenPipe {
|
||||||
|
|||||||
@ -142,6 +142,30 @@ func TestPanicWithBrokenPipe(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPanicWithAbortHandler asserts that recovery handles http.ErrAbortHandler as broken pipe
|
||||||
|
func TestPanicWithAbortHandler(t *testing.T) {
|
||||||
|
const expectCode = 204
|
||||||
|
|
||||||
|
var buf strings.Builder
|
||||||
|
router := New()
|
||||||
|
router.Use(RecoveryWithWriter(&buf))
|
||||||
|
router.GET("/recovery", func(c *Context) {
|
||||||
|
// Start writing response
|
||||||
|
c.Header("X-Test", "Value")
|
||||||
|
c.Status(expectCode)
|
||||||
|
|
||||||
|
// Panic with ErrAbortHandler which should be treated as broken pipe
|
||||||
|
panic(http.ErrAbortHandler)
|
||||||
|
})
|
||||||
|
// RUN
|
||||||
|
w := PerformRequest(router, http.MethodGet, "/recovery")
|
||||||
|
// TEST
|
||||||
|
assert.Equal(t, expectCode, w.Code)
|
||||||
|
out := buf.String()
|
||||||
|
assert.Contains(t, out, "net/http: abort Handler")
|
||||||
|
assert.NotContains(t, out, "panic recovered")
|
||||||
|
}
|
||||||
|
|
||||||
func TestCustomRecoveryWithWriter(t *testing.T) {
|
func TestCustomRecoveryWithWriter(t *testing.T) {
|
||||||
errBuffer := new(strings.Builder)
|
errBuffer := new(strings.Builder)
|
||||||
buffer := new(strings.Builder)
|
buffer := new(strings.Builder)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user