Repo: reorganize the repo structure

This commit is contained in:
Muyao CHEN
2024-09-30 13:42:37 +02:00
parent c3fc59b8f1
commit 17b19e3a10
14 changed files with 203 additions and 58 deletions

View File

@ -0,0 +1,20 @@
package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Recovery is a middleware that recovers from the panic
func Recovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
c.JSON(http.StatusInternalServerError, err)
}
}()
c.Next()
}
}

View File

@ -0,0 +1,31 @@
package middleware
import (
"log"
"github.com/gin-gonic/gin"
)
func Test1() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("middleware test1 pre")
c.Next()
log.Println("middleware test1 post")
}
}
func Test2() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("middleware test2 pre")
c.Next()
log.Println("middleware test2 post")
}
}
func Test3() gin.HandlerFunc {
return func(c *gin.Context) {
log.Println("middleware test3 pre")
c.Next()
log.Println("middleware test3 post")
}
}

View File

@ -0,0 +1,46 @@
package middleware
import (
"context"
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func Timeout(d time.Duration) gin.HandlerFunc {
return func(c *gin.Context) {
finish := make(chan struct{}, 1)
panicChan := make(chan interface{}, 1)
durationCtx, cancel := context.WithTimeout(c.Request.Context(), d)
defer cancel()
go func() {
// Handle panic
defer func() {
if p := recover(); p != nil {
panicChan <- p
}
}()
// Run the next middleware or the business logic
c.Next()
finish <- struct{}{}
}()
select {
case p := <-panicChan:
// panic
log.Println(p)
c.Status(http.StatusInternalServerError)
case <-finish:
// finish normally
log.Println("finish")
case <-durationCtx.Done():
c.JSON(http.StatusRequestTimeout, "time out")
}
}
}

View File

@ -0,0 +1,98 @@
package middleware
import (
"bytes"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gin-gonic/gin"
)
func TestTimeout(t *testing.T) {
t.Run("Test timeout handler", func(t *testing.T) {
timeoutHandler := Timeout(1 * time.Millisecond)
longHandler := func(c *gin.Context) {
time.Sleep(2 * time.Millisecond)
}
res := prepareMiddlewareTest(t, timeoutHandler, longHandler)
assertCode(t, res.StatusCode, http.StatusRequestTimeout)
assertBody(t, res.Body, "\"time out\"")
})
t.Run("Test no timeout", func(t *testing.T) {
timeoutHandler := Timeout(2 * time.Millisecond)
quickHandler := func(c *gin.Context) {
// time.Sleep(1 * time.Millisecond)
c.JSON(http.StatusOK, "ok")
}
res := prepareMiddlewareTest(t, timeoutHandler, quickHandler)
assertCode(t, res.StatusCode, http.StatusOK)
})
}
func TestRecover(t *testing.T) {
t.Run("Test panic", func(t *testing.T) {
recoverer := Recovery()
panicHandler := func(c *gin.Context) {
panic("panic")
}
res := prepareMiddlewareTest(t, recoverer, panicHandler)
assertCode(t, res.StatusCode, http.StatusInternalServerError)
})
t.Run("Test no panic", func(t *testing.T) {
recoverer := Recovery()
normalHandler := func(c *gin.Context) {
c.JSON(http.StatusOK, "ok")
}
res := prepareMiddlewareTest(t, recoverer, normalHandler)
assertCode(t, res.StatusCode, http.StatusOK)
})
}
func prepareMiddlewareTest(
t testing.TB,
mid gin.HandlerFunc,
in gin.HandlerFunc,
) *http.Response {
t.Helper()
request := httptest.NewRequest(http.MethodGet, "/", nil)
response := httptest.NewRecorder()
_, r := gin.CreateTestContext(response)
r.Use(mid)
r.GET("/", in)
r.ServeHTTP(response, request)
res := response.Result()
return res
}
func assertCode(t testing.TB, got int, want int) {
t.Helper()
if got != want {
t.Errorf("status code got %d, want %d", got, want)
}
}
func assertBody(t testing.TB, got io.Reader, want string) {
t.Helper()
buf, _ := io.ReadAll(got)
if cmp := bytes.Compare(buf, []byte(want)); cmp != 0 {
t.Errorf("got %q, want %q", string(buf), want)
}
}