1 Star 0 Fork 0

jnliok/fiber

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
app_test.go 54.38 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
// 🤖 Github Repository: https://github.com/gofiber/fiber
// 📌 API Documentation: https://docs.gofiber.io
//nolint:bodyclose // Much easier to just ignore memory leaks in tests
package fiber
import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"mime/multipart"
"net"
"net/http"
"net/http/httptest"
"reflect"
"regexp"
"runtime"
"strings"
"testing"
"time"
"github.com/gofiber/utils/v2"
"github.com/stretchr/testify/require"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttputil"
)
func testEmptyHandler(_ Ctx) error {
return nil
}
func testStatus200(t *testing.T, app *App, url, method string) {
t.Helper()
req := httptest.NewRequest(method, url, nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
}
func testErrorResponse(t *testing.T, err error, resp *http.Response, expectedBodyError string) {
t.Helper()
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 500, resp.StatusCode, "Status code")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, expectedBodyError, string(body), "Response body")
}
func Test_App_MethodNotAllowed(t *testing.T) {
t.Parallel()
app := New()
app.Use(func(c Ctx) error {
return c.Next()
})
app.Post("/", testEmptyHandler)
app.Options("/", testEmptyHandler)
resp, err := app.Test(httptest.NewRequest(MethodPost, "/", nil))
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
require.Equal(t, "", resp.Header.Get(HeaderAllow))
resp, err = app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.NoError(t, err)
require.Equal(t, 405, resp.StatusCode)
require.Equal(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow))
resp, err = app.Test(httptest.NewRequest(MethodPatch, "/", nil))
require.NoError(t, err)
require.Equal(t, 405, resp.StatusCode)
require.Equal(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow))
resp, err = app.Test(httptest.NewRequest(MethodPut, "/", nil))
require.NoError(t, err)
require.Equal(t, 405, resp.StatusCode)
require.Equal(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow))
app.Get("/", testEmptyHandler)
resp, err = app.Test(httptest.NewRequest(MethodTrace, "/", nil))
require.NoError(t, err)
require.Equal(t, 405, resp.StatusCode)
require.Equal(t, "GET, POST, OPTIONS", resp.Header.Get(HeaderAllow))
resp, err = app.Test(httptest.NewRequest(MethodPatch, "/", nil))
require.NoError(t, err)
require.Equal(t, 405, resp.StatusCode)
require.Equal(t, "GET, POST, OPTIONS", resp.Header.Get(HeaderAllow))
app.Head("/", testEmptyHandler)
resp, err = app.Test(httptest.NewRequest(MethodPut, "/", nil))
require.NoError(t, err)
require.Equal(t, 405, resp.StatusCode)
require.Equal(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow))
}
func Test_App_Custom_Middleware_404_Should_Not_SetMethodNotAllowed(t *testing.T) {
t.Parallel()
app := New()
app.Use(func(c Ctx) error {
return c.SendStatus(404)
})
app.Post("/", testEmptyHandler)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.NoError(t, err)
require.Equal(t, 404, resp.StatusCode)
g := app.Group("/with-next", func(c Ctx) error {
return c.Status(404).Next()
})
g.Post("/", testEmptyHandler)
resp, err = app.Test(httptest.NewRequest(MethodGet, "/with-next", nil))
require.NoError(t, err)
require.Equal(t, 404, resp.StatusCode)
}
func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) {
t.Parallel()
expectedError := regexp.MustCompile(
`error when reading request headers: small read buffer\. Increase ReadBufferSize\. Buffer size=4096, contents: "GET / HTTP/1.1\\r\\nHost: example\.com\\r\\nVery-Long-Header: -+`,
)
app := New()
app.Get("/", func(c Ctx) error {
panic(errors.New("should never called"))
})
request := httptest.NewRequest(MethodGet, "/", nil)
logHeaderSlice := make([]string, 5000)
request.Header.Set("Very-Long-Header", strings.Join(logHeaderSlice, "-"))
_, err := app.Test(request)
if err == nil {
t.Error("Expect an error at app.Test(request)")
}
require.Regexp(t, expectedError, err.Error())
}
func Test_App_Errors(t *testing.T) {
t.Parallel()
app := New(Config{
BodyLimit: 4,
})
app.Get("/", func(c Ctx) error {
return errors.New("hi, i'm an error")
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 500, resp.StatusCode, "Status code")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "hi, i'm an error", string(body))
_, err = app.Test(httptest.NewRequest(MethodGet, "/", strings.NewReader("big body")))
if err != nil {
require.Equal(t, "body size exceeds the given limit", err.Error(), "app.Test(req)")
}
}
func Test_App_ErrorHandler_Custom(t *testing.T) {
t.Parallel()
app := New(Config{
ErrorHandler: func(c Ctx, err error) error {
return c.Status(200).SendString("hi, i'm an custom error")
},
})
app.Get("/", func(c Ctx) error {
return errors.New("hi, i'm an error")
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "hi, i'm an custom error", string(body))
}
func Test_App_ErrorHandler_HandlerStack(t *testing.T) {
t.Parallel()
app := New(Config{
ErrorHandler: func(c Ctx, err error) error {
require.Equal(t, "1: USE error", err.Error())
return DefaultErrorHandler(c, err)
},
})
app.Use("/", func(c Ctx) error {
err := c.Next() // call next USE
require.Equal(t, "2: USE error", err.Error())
return errors.New("1: USE error")
}, func(c Ctx) error {
err := c.Next() // call [0] GET
require.Equal(t, "0: GET error", err.Error())
return errors.New("2: USE error")
})
app.Get("/", func(c Ctx) error {
return errors.New("0: GET error")
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 500, resp.StatusCode, "Status code")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "1: USE error", string(body))
}
func Test_App_ErrorHandler_RouteStack(t *testing.T) {
t.Parallel()
app := New(Config{
ErrorHandler: func(c Ctx, err error) error {
require.Equal(t, "1: USE error", err.Error())
return DefaultErrorHandler(c, err)
},
})
app.Use("/", func(c Ctx) error {
err := c.Next()
require.Equal(t, "0: GET error", err.Error())
return errors.New("1: USE error") // [2] call ErrorHandler
})
app.Get("/test", func(c Ctx) error {
return errors.New("0: GET error") // [1] return to USE
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 500, resp.StatusCode, "Status code")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "1: USE error", string(body))
}
func Test_App_serverErrorHandler_Internal_Error(t *testing.T) {
t.Parallel()
app := New()
msg := "test err"
c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed
app.serverErrorHandler(c.fasthttp, errors.New(msg))
require.Equal(t, string(c.fasthttp.Response.Body()), msg)
require.Equal(t, StatusBadRequest, c.fasthttp.Response.StatusCode())
}
func Test_App_serverErrorHandler_Network_Error(t *testing.T) {
t.Parallel()
app := New()
c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed
app.serverErrorHandler(c.fasthttp, &net.DNSError{
Err: "test error",
Name: "test host",
IsTimeout: false,
})
require.Equal(t, string(c.fasthttp.Response.Body()), utils.StatusMessage(StatusBadGateway))
require.Equal(t, StatusBadGateway, c.fasthttp.Response.StatusCode())
}
func Test_App_Nested_Params(t *testing.T) {
t.Parallel()
app := New()
app.Get("/test", func(c Ctx) error {
return c.Status(400).Send([]byte("Should move on"))
})
app.Get("/test/:param", func(c Ctx) error {
return c.Status(400).Send([]byte("Should move on"))
})
app.Get("/test/:param/test", func(c Ctx) error {
return c.Status(400).Send([]byte("Should move on"))
})
app.Get("/test/:param/test/:param2", func(c Ctx) error {
return c.Status(200).Send([]byte("Good job"))
})
req := httptest.NewRequest(MethodGet, "/test/john/test/doe", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
}
func Test_App_Use_Params(t *testing.T) {
t.Parallel()
app := New()
app.Use("/prefix/:param", func(c Ctx) error {
require.Equal(t, "john", c.Params("param"))
return nil
})
app.Use("/foo/:bar?", func(c Ctx) error {
require.Equal(t, "foobar", c.Params("bar", "foobar"))
return nil
})
app.Use("/:param/*", func(c Ctx) error {
require.Equal(t, "john", c.Params("param"))
require.Equal(t, "doe", c.Params("*"))
return nil
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/prefix/john", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
resp, err = app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
defer func() {
if err := recover(); err != nil {
require.Equal(t, "use: invalid handler func()\n", fmt.Sprintf("%v", err))
}
}()
app.Use("/:param/*", func() {
// this should panic
})
}
func Test_App_Use_UnescapedPath(t *testing.T) {
t.Parallel()
app := New(Config{UnescapePath: true, CaseSensitive: true})
app.Use("/cRéeR/:param", func(c Ctx) error {
require.Equal(t, "/cRéeR/اختبار", c.Path())
return c.SendString(c.Params("param"))
})
app.Use("/abc", func(c Ctx) error {
require.Equal(t, "/AbC", c.Path())
return nil
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/cR%C3%A9eR/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err, "app.Test(req)")
// check the param result
require.Equal(t, "اختبار", app.getString(body))
// with lowercase letters
resp, err = app.Test(httptest.NewRequest(MethodGet, "/cr%C3%A9er/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusNotFound, resp.StatusCode, "Status code")
}
func Test_App_Use_CaseSensitive(t *testing.T) {
t.Parallel()
app := New(Config{CaseSensitive: true})
app.Use("/abc", func(c Ctx) error {
return c.SendString(c.Path())
})
// wrong letters in the requested route -> 404
resp, err := app.Test(httptest.NewRequest(MethodGet, "/AbC", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusNotFound, resp.StatusCode, "Status code")
// right letters in the requrested route -> 200
resp, err = app.Test(httptest.NewRequest(MethodGet, "/abc", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
// check the detected path when the case insensitive recognition is activated
app.config.CaseSensitive = false
// check the case sensitive feature
resp, err = app.Test(httptest.NewRequest(MethodGet, "/AbC", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err, "app.Test(req)")
// check the detected path result
require.Equal(t, "/AbC", app.getString(body))
}
func Test_App_Not_Use_StrictRouting(t *testing.T) {
t.Parallel()
app := New()
app.Use("/abc", func(c Ctx) error {
return c.SendString(c.Path())
})
g := app.Group("/foo")
g.Use("/", func(c Ctx) error {
return c.SendString(c.Path())
})
// wrong path in the requested route -> 404
resp, err := app.Test(httptest.NewRequest(MethodGet, "/abc/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
// right path in the requrested route -> 200
resp, err = app.Test(httptest.NewRequest(MethodGet, "/abc", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
// wrong path with group in the requested route -> 404
resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
// right path with group in the requrested route -> 200
resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
}
func Test_App_Use_MultiplePrefix(t *testing.T) {
t.Parallel()
app := New()
app.Use([]string{"/john", "/doe"}, func(c Ctx) error {
return c.SendString(c.Path())
})
g := app.Group("/test")
g.Use([]string{"/john", "/doe"}, func(c Ctx) error {
return c.SendString(c.Path())
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/john", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "/john", string(body))
resp, err = app.Test(httptest.NewRequest(MethodGet, "/doe", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "/doe", string(body))
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/john", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "/test/john", string(body))
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/doe", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "/test/doe", string(body))
}
func Test_App_Use_StrictRouting(t *testing.T) {
t.Parallel()
app := New(Config{StrictRouting: true})
app.Get("/abc", func(c Ctx) error {
return c.SendString(c.Path())
})
g := app.Group("/foo")
g.Get("/", func(c Ctx) error {
return c.SendString(c.Path())
})
// wrong path in the requested route -> 404
resp, err := app.Test(httptest.NewRequest(MethodGet, "/abc/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusNotFound, resp.StatusCode, "Status code")
// right path in the requrested route -> 200
resp, err = app.Test(httptest.NewRequest(MethodGet, "/abc", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
// wrong path with group in the requested route -> 404
resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusNotFound, resp.StatusCode, "Status code")
// right path with group in the requrested route -> 200
resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
}
func Test_App_Add_Method_Test(t *testing.T) {
t.Parallel()
defer func() {
if err := recover(); err != nil {
require.Equal(t, "add: invalid http method JANE\n", fmt.Sprintf("%v", err))
}
}()
methods := append(DefaultMethods, "JOHN") //nolint:gocritic // We want a new slice here
app := New(Config{
RequestMethods: methods,
})
app.Add([]string{"JOHN"}, "/doe", testEmptyHandler)
resp, err := app.Test(httptest.NewRequest("JOHN", "/doe", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
resp, err = app.Test(httptest.NewRequest(MethodGet, "/doe", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusMethodNotAllowed, resp.StatusCode, "Status code")
resp, err = app.Test(httptest.NewRequest("UNKNOWN", "/doe", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusNotImplemented, resp.StatusCode, "Status code")
app.Add([]string{"JANE"}, "/doe", testEmptyHandler)
}
// go test -run Test_App_GETOnly
func Test_App_GETOnly(t *testing.T) {
t.Parallel()
app := New(Config{
GETOnly: true,
})
app.Post("/", func(c Ctx) error {
return c.SendString("Hello 👋!")
})
req := httptest.NewRequest(MethodPost, "/", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusMethodNotAllowed, resp.StatusCode, "Status code")
}
func Test_App_Use_Params_Group(t *testing.T) {
t.Parallel()
app := New()
group := app.Group("/prefix/:param/*")
group.Use("/", func(c Ctx) error {
return c.Next()
})
group.Get("/test", func(c Ctx) error {
require.Equal(t, "john", c.Params("param"))
require.Equal(t, "doe", c.Params("*"))
return nil
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/prefix/john/doe/test", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
}
func Test_App_Chaining(t *testing.T) {
t.Parallel()
n := func(c Ctx) error {
return c.Next()
}
app := New()
app.Use("/john", n, n, n, n, func(c Ctx) error {
return c.SendStatus(202)
})
// check handler count for registered HEAD route
require.Len(t, app.stack[app.methodInt(MethodHead)][0].Handlers, 5, "app.Test(req)")
req := httptest.NewRequest(MethodPost, "/john", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 202, resp.StatusCode, "Status code")
app.Get("/test", n, n, n, n, func(c Ctx) error {
return c.SendStatus(203)
})
req = httptest.NewRequest(MethodGet, "/test", nil)
resp, err = app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 203, resp.StatusCode, "Status code")
}
func Test_App_Order(t *testing.T) {
t.Parallel()
app := New()
app.Get("/test", func(c Ctx) error {
_, err := c.Write([]byte("1"))
require.NoError(t, err)
return c.Next()
})
app.All("/test", func(c Ctx) error {
_, err := c.Write([]byte("2"))
require.NoError(t, err)
return c.Next()
})
app.Use(func(c Ctx) error {
_, err := c.Write([]byte("3"))
require.NoError(t, err)
return nil
})
req := httptest.NewRequest(MethodGet, "/test", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "123", string(body))
}
func Test_App_Methods(t *testing.T) {
t.Parallel()
dummyHandler := testEmptyHandler
app := New()
app.Connect("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", "CONNECT")
app.Put("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodPut)
app.Post("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodPost)
app.Delete("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodDelete)
app.Head("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodHead)
app.Patch("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodPatch)
app.Options("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodOptions)
app.Trace("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodTrace)
app.Get("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodGet)
app.All("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodPost)
app.Use("/:john?/:doe?", dummyHandler)
testStatus200(t, app, "/john/doe", MethodGet)
}
func Test_App_Route_Naming(t *testing.T) {
t.Parallel()
app := New()
handler := func(c Ctx) error {
return c.SendStatus(StatusOK)
}
app.Get("/john", handler).Name("john")
app.Delete("/doe", handler)
app.Name("doe")
jane := app.Group("/jane").Name("jane.")
group := app.Group("/group")
subGroup := jane.Group("/sub-group").Name("sub.")
jane.Get("/test", handler).Name("test")
jane.Trace("/trace", handler).Name("trace")
group.Get("/test", handler).Name("test")
app.Post("/post", handler).Name("post")
subGroup.Get("/done", handler).Name("done")
require.Equal(t, "post", app.GetRoute("post").Name)
require.Equal(t, "john", app.GetRoute("john").Name)
require.Equal(t, "jane.test", app.GetRoute("jane.test").Name)
require.Equal(t, "jane.trace", app.GetRoute("jane.trace").Name)
require.Equal(t, "jane.sub.done", app.GetRoute("jane.sub.done").Name)
require.Equal(t, "test", app.GetRoute("test").Name)
}
func Test_App_New(t *testing.T) {
t.Parallel()
app := New()
app.Get("/", testEmptyHandler)
appConfig := New(Config{
Immutable: true,
})
appConfig.Get("/", testEmptyHandler)
}
func Test_App_Config(t *testing.T) {
t.Parallel()
app := New(Config{
StrictRouting: true,
})
require.True(t, app.Config().StrictRouting)
}
func Test_App_Shutdown(t *testing.T) {
t.Parallel()
t.Run("success", func(t *testing.T) {
t.Parallel()
app := New()
require.NoError(t, app.Shutdown())
})
t.Run("no server", func(t *testing.T) {
t.Parallel()
app := &App{}
if err := app.Shutdown(); err != nil {
require.ErrorContains(t, err, "shutdown: server is not running")
}
})
}
func Test_App_ShutdownWithTimeout(t *testing.T) {
t.Parallel()
app := New()
app.Get("/", func(c Ctx) error {
time.Sleep(5 * time.Second)
return c.SendString("body")
})
ln := fasthttputil.NewInmemoryListener()
go func() {
require.NoError(t, app.Listener(ln))
}()
time.Sleep(1 * time.Second)
go func() {
conn, err := ln.Dial()
if err != nil {
t.Errorf("unexepcted error: %v", err)
}
if _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")); err != nil {
t.Errorf("unexpected error: %v", err)
}
}()
time.Sleep(1 * time.Second)
shutdownErr := make(chan error)
go func() {
shutdownErr <- app.ShutdownWithTimeout(1 * time.Second)
}()
timer := time.NewTimer(time.Second * 5)
select {
case <-timer.C:
t.Fatal("idle connections not closed on shutdown")
case err := <-shutdownErr:
if err == nil || !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("unexpected err %v. Expecting %v", err, context.DeadlineExceeded)
}
}
}
func Test_App_ShutdownWithContext(t *testing.T) {
t.Parallel()
app := New()
app.Get("/", func(ctx Ctx) error {
time.Sleep(5 * time.Second)
return ctx.SendString("body")
})
ln := fasthttputil.NewInmemoryListener()
go func() {
require.NoError(t, app.Listener(ln))
}()
time.Sleep(1 * time.Second)
go func() {
conn, err := ln.Dial()
if err != nil {
t.Errorf("unexepcted error: %v", err)
}
if _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")); err != nil {
t.Errorf("unexpected error: %v", err)
}
}()
time.Sleep(1 * time.Second)
shutdownErr := make(chan error)
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
shutdownErr <- app.ShutdownWithContext(ctx)
}()
select {
case <-time.After(5 * time.Second):
t.Fatal("idle connections not closed on shutdown")
case err := <-shutdownErr:
if err == nil || !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("unexpected err %v. Expecting %v", err, context.DeadlineExceeded)
}
}
}
// go test -run Test_App_Static_Index_Default
func Test_App_Static_Index_Default(t *testing.T) {
app := New()
app.Static("/prefix", "./.github/workflows")
app.Static("", "./.github/")
app.Static("test", "", Static{Index: "index.html"})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(body), "Hello, World!")
resp, err = app.Test(httptest.NewRequest(MethodGet, "/not-found", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 404, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType))
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "Cannot GET /not-found", string(body))
}
// go test -run Test_App_Static_Index
func Test_App_Static_Direct(t *testing.T) {
app := New()
app.Static("/", "./.github")
resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(body), "Hello, World!")
resp, err = app.Test(httptest.NewRequest(MethodGet, "/testdata/testRoutes.json", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMEApplicationJSON, resp.Header.Get("Content-Type"))
require.Equal(t, "", resp.Header.Get(HeaderCacheControl), "CacheControl Control")
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(body), "test_routes")
}
// go test -run Test_App_Static_MaxAge
func Test_App_Static_MaxAge(t *testing.T) {
app := New()
app.Static("/", "./.github", Static{MaxAge: 100})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, "text/html; charset=utf-8", resp.Header.Get(HeaderContentType))
require.Equal(t, "public, max-age=100", resp.Header.Get(HeaderCacheControl), "CacheControl Control")
}
// go test -run Test_App_Static_Custom_CacheControl
func Test_App_Static_Custom_CacheControl(t *testing.T) {
app := New()
app.Static("/", "./.github", Static{ModifyResponse: func(c Ctx) error {
if strings.Contains(c.GetRespHeader("Content-Type"), "text/html") {
c.Response().Header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
}
return nil
}})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, "no-cache, no-store, must-revalidate", resp.Header.Get(HeaderCacheControl), "CacheControl Control")
normalResp, normalErr := app.Test(httptest.NewRequest(MethodGet, "/config.yml", nil))
require.NoError(t, normalErr, "app.Test(req)")
require.Equal(t, "", normalResp.Header.Get(HeaderCacheControl), "CacheControl Control")
}
// go test -run Test_App_Static_Download
func Test_App_Static_Download(t *testing.T) {
app := New()
app.Static("/fiber.png", "./.github/testdata/fs/img/fiber.png", Static{Download: true})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/fiber.png", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, "image/png", resp.Header.Get(HeaderContentType))
require.Equal(t, `attachment`, resp.Header.Get(HeaderContentDisposition))
}
// go test -run Test_App_Static_Group
func Test_App_Static_Group(t *testing.T) {
app := New()
grp := app.Group("/v1", func(c Ctx) error {
c.Set("Test-Header", "123")
return c.Next()
})
grp.Static("/v2", "./.github/index.html")
req := httptest.NewRequest(MethodGet, "/v1/v2", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
require.Equal(t, "123", resp.Header.Get("Test-Header"))
grp = app.Group("/v2")
grp.Static("/v3*", "./.github/index.html")
req = httptest.NewRequest(MethodGet, "/v2/v3/john/doe", nil)
resp, err = app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
}
func Test_App_Static_Wildcard(t *testing.T) {
app := New()
app.Static("*", "./.github/index.html")
req := httptest.NewRequest(MethodGet, "/yesyes/john/doe", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(body), "Test file")
}
func Test_App_Static_Prefix_Wildcard(t *testing.T) {
app := New()
app.Static("/test/*", "./.github/index.html")
req := httptest.NewRequest(MethodGet, "/test/john/doe", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
app.Static("/my/nameisjohn*", "./.github/index.html")
resp, err = app.Test(httptest.NewRequest(MethodGet, "/my/nameisjohn/no/its/not", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(body), "Test file")
}
func Test_App_Static_Prefix(t *testing.T) {
app := New()
app.Static("/john", "./.github")
req := httptest.NewRequest(MethodGet, "/john/index.html", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
app.Static("/prefix", "./.github/testdata")
req = httptest.NewRequest(MethodGet, "/prefix/index.html", nil)
resp, err = app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
app.Static("/single", "./.github/testdata/testRoutes.json")
req = httptest.NewRequest(MethodGet, "/single", nil)
resp, err = app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMEApplicationJSON, resp.Header.Get(HeaderContentType))
}
func Test_App_Static_Trailing_Slash(t *testing.T) {
app := New()
app.Static("/john", "./.github")
req := httptest.NewRequest(MethodGet, "/john/", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
app.Static("/john_without_index", "./.github/testdata/fs/css")
req = httptest.NewRequest(MethodGet, "/john_without_index/", nil)
resp, err = app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 404, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType))
app.Static("/john/", "./.github")
req = httptest.NewRequest(MethodGet, "/john/", nil)
resp, err = app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
req = httptest.NewRequest(MethodGet, "/john", nil)
resp, err = app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
app.Static("/john_without_index/", "./.github/testdata/fs/css")
req = httptest.NewRequest(MethodGet, "/john_without_index/", nil)
resp, err = app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 404, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType))
}
func Test_App_Static_Next(t *testing.T) {
app := New()
app.Static("/", ".github", Static{
Next: func(c Ctx) bool {
// If value of the header is any other from "skip"
// c.Next() will be invoked
return c.Get("X-Custom-Header") == "skip"
},
})
app.Get("/", func(c Ctx) error {
return c.SendString("You've skipped app.Static")
})
t.Run("app.Static is skipped: invoking Get handler", func(t *testing.T) {
req := httptest.NewRequest(MethodGet, "/", nil)
req.Header.Set("X-Custom-Header", "skip")
resp, err := app.Test(req)
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType))
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(body), "You've skipped app.Static")
})
t.Run("app.Static is not skipped: serving index.html", func(t *testing.T) {
req := httptest.NewRequest(MethodGet, "/", nil)
req.Header.Set("X-Custom-Header", "don't skip")
resp, err := app.Test(req)
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType))
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(body), "Hello, World!")
})
}
// go test -run Test_App_Mixed_Routes_WithSameLen
func Test_App_Mixed_Routes_WithSameLen(t *testing.T) {
app := New()
// middleware
app.Use(func(c Ctx) error {
c.Set("TestHeader", "TestValue")
return c.Next()
})
// routes with the same length
app.Static("/tesbar", "./.github")
app.Get("/foobar", func(c Ctx) error {
c.Type("html")
return c.Send([]byte("FOO_BAR"))
})
// match get route
req := httptest.NewRequest(MethodGet, "/foobar", nil)
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, "TestValue", resp.Header.Get("TestHeader"))
require.Equal(t, "text/html", resp.Header.Get(HeaderContentType))
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "FOO_BAR", string(body))
// match static route
req = httptest.NewRequest(MethodGet, "/tesbar", nil)
resp, err = app.Test(req)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.NotEmpty(t, resp.Header.Get(HeaderContentLength))
require.Equal(t, "TestValue", resp.Header.Get("TestHeader"))
require.Equal(t, "text/html; charset=utf-8", resp.Header.Get(HeaderContentType))
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Contains(t, string(body), "Hello, World!")
require.True(t, strings.HasPrefix(string(body), "<!DOCTYPE html>"), "Response: "+string(body))
}
func Test_App_Group_Invalid(t *testing.T) {
t.Parallel()
defer func() {
if err := recover(); err != nil {
require.Equal(t, "use: invalid handler int\n", fmt.Sprintf("%v", err))
}
}()
New().Group("/").Use(1)
}
func Test_App_Group(t *testing.T) {
t.Parallel()
dummyHandler := testEmptyHandler
app := New()
grp := app.Group("/test")
grp.Get("/", dummyHandler)
testStatus200(t, app, "/test", MethodGet)
grp.Get("/:demo?", dummyHandler)
testStatus200(t, app, "/test/john", MethodGet)
grp.Connect("/CONNECT", dummyHandler)
testStatus200(t, app, "/test/CONNECT", MethodConnect)
grp.Put("/PUT", dummyHandler)
testStatus200(t, app, "/test/PUT", MethodPut)
grp.Post("/POST", dummyHandler)
testStatus200(t, app, "/test/POST", MethodPost)
grp.Delete("/DELETE", dummyHandler)
testStatus200(t, app, "/test/DELETE", MethodDelete)
grp.Head("/HEAD", dummyHandler)
testStatus200(t, app, "/test/HEAD", MethodHead)
grp.Patch("/PATCH", dummyHandler)
testStatus200(t, app, "/test/PATCH", MethodPatch)
grp.Options("/OPTIONS", dummyHandler)
testStatus200(t, app, "/test/OPTIONS", MethodOptions)
grp.Trace("/TRACE", dummyHandler)
testStatus200(t, app, "/test/TRACE", MethodTrace)
grp.All("/ALL", dummyHandler)
testStatus200(t, app, "/test/ALL", MethodPost)
grp.Use(dummyHandler)
testStatus200(t, app, "/test/oke", MethodGet)
grp.Use("/USE", dummyHandler)
testStatus200(t, app, "/test/USE/oke", MethodGet)
api := grp.Group("/v1")
api.Post("/", dummyHandler)
resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
// require.Equal(t, "/test/v1", resp.Header.Get("Location"), "Location")
api.Get("/users", dummyHandler)
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/UsErS", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
// require.Equal(t, "/test/v1/users", resp.Header.Get("Location"), "Location")
}
func Test_App_Route(t *testing.T) {
t.Parallel()
dummyHandler := testEmptyHandler
app := New()
register := app.Route("/test").
Get(dummyHandler).
Head(dummyHandler).
Post(dummyHandler).
Put(dummyHandler).
Delete(dummyHandler).
Connect(dummyHandler).
Options(dummyHandler).
Trace(dummyHandler).
Patch(dummyHandler)
testStatus200(t, app, "/test", MethodGet)
testStatus200(t, app, "/test", MethodHead)
testStatus200(t, app, "/test", MethodPost)
testStatus200(t, app, "/test", MethodPut)
testStatus200(t, app, "/test", MethodDelete)
testStatus200(t, app, "/test", MethodConnect)
testStatus200(t, app, "/test", MethodOptions)
testStatus200(t, app, "/test", MethodTrace)
testStatus200(t, app, "/test", MethodPatch)
register.Route("/v1").Get(dummyHandler).Post(dummyHandler)
resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
register.Route("/v1").Route("/v2").Route("/v3").Get(dummyHandler).Trace(dummyHandler)
resp, err = app.Test(httptest.NewRequest(MethodTrace, "/test/v1/v2/v3", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/v2/v3", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
}
func Test_App_Deep_Group(t *testing.T) {
t.Parallel()
runThroughCount := 0
dummyHandler := func(c Ctx) error {
runThroughCount++
return c.Next()
}
app := New()
gAPI := app.Group("/api", dummyHandler)
gV1 := gAPI.Group("/v1", dummyHandler)
gUser := gV1.Group("/user", dummyHandler)
gUser.Get("/authenticate", func(c Ctx) error {
runThroughCount++
return c.SendStatus(200)
})
testStatus200(t, app, "/api/v1/user/authenticate", MethodGet)
require.Equal(t, 4, runThroughCount, "Loop count")
}
// go test -run Test_App_Next_Method
func Test_App_Next_Method(t *testing.T) {
t.Parallel()
app := New()
app.Use(func(c Ctx) error {
require.Equal(t, MethodGet, c.Method())
err := c.Next()
require.Equal(t, MethodGet, c.Method())
return err
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 404, resp.StatusCode, "Status code")
}
// go test -v -run=^$ -bench=Benchmark_AcquireCtx -benchmem -count=4
func Benchmark_AcquireCtx(b *testing.B) {
app := New()
for n := 0; n < b.N; n++ {
c := app.AcquireCtx()
c.Reset(&fasthttp.RequestCtx{})
app.ReleaseCtx(c)
}
}
// go test -v -run=^$ -bench=Benchmark_NewError -benchmem -count=4
func Benchmark_NewError(b *testing.B) {
for n := 0; n < b.N; n++ {
NewError(200, "test") //nolint:errcheck // not needed
}
}
// go test -run Test_NewError
func Test_NewError(t *testing.T) {
t.Parallel()
e := NewError(StatusForbidden, "permission denied")
require.Equal(t, StatusForbidden, e.Code)
require.Equal(t, "permission denied", e.Message)
}
// go test -run Test_Test_Timeout
func Test_Test_Timeout(t *testing.T) {
t.Parallel()
app := New()
app.Get("/", testEmptyHandler)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil), -1)
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
app.Get("timeout", func(c Ctx) error {
time.Sleep(200 * time.Millisecond)
return nil
})
_, err = app.Test(httptest.NewRequest(MethodGet, "/timeout", nil), 20)
require.Error(t, err, "app.Test(req)")
}
type errorReader int
var errErrorReader = errors.New("errorReader")
func (errorReader) Read([]byte) (int, error) {
return 0, errErrorReader
}
// go test -run Test_Test_DumpError
func Test_Test_DumpError(t *testing.T) {
t.Parallel()
app := New()
app.Get("/", testEmptyHandler)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", errorReader(0)))
require.Nil(t, resp)
require.ErrorIs(t, err, errErrorReader)
}
// go test -run Test_App_Handler
func Test_App_Handler(t *testing.T) {
t.Parallel()
h := New().Handler()
require.Equal(t, "fasthttp.RequestHandler", reflect.TypeOf(h).String())
}
type invalidView struct{}
func (invalidView) Load() error { return errors.New("invalid view") }
func (invalidView) Render(io.Writer, string, any, ...string) error { panic("implement me") }
// go test -run Test_App_Init_Error_View
func Test_App_Init_Error_View(t *testing.T) {
app := New(Config{Views: invalidView{}})
defer func() {
if err := recover(); err != nil {
require.Equal(t, "implement me", fmt.Sprintf("%v", err))
}
}()
err := app.config.Views.Render(nil, "", nil)
require.NoError(t, err)
}
// go test -run Test_App_Stack
func Test_App_Stack(t *testing.T) {
t.Parallel()
app := New()
app.Use("/path0", testEmptyHandler)
app.Get("/path1", testEmptyHandler)
app.Get("/path2", testEmptyHandler)
app.Post("/path3", testEmptyHandler)
stack := app.Stack()
methodList := app.config.RequestMethods
require.Equal(t, len(methodList), len(stack))
require.Len(t, stack[app.methodInt(MethodGet)], 3)
require.Len(t, stack[app.methodInt(MethodHead)], 1)
require.Len(t, stack[app.methodInt(MethodPost)], 2)
require.Len(t, stack[app.methodInt(MethodPut)], 1)
require.Len(t, stack[app.methodInt(MethodPatch)], 1)
require.Len(t, stack[app.methodInt(MethodDelete)], 1)
require.Len(t, stack[app.methodInt(MethodConnect)], 1)
require.Len(t, stack[app.methodInt(MethodOptions)], 1)
require.Len(t, stack[app.methodInt(MethodTrace)], 1)
}
// go test -run Test_App_HandlersCount
func Test_App_HandlersCount(t *testing.T) {
t.Parallel()
app := New()
app.Use("/path0", testEmptyHandler)
app.Get("/path2", testEmptyHandler)
app.Post("/path3", testEmptyHandler)
count := app.HandlersCount()
require.Equal(t, uint32(3), count)
}
// go test -run Test_App_ReadTimeout
func Test_App_ReadTimeout(t *testing.T) {
t.Parallel()
app := New(Config{
ReadTimeout: time.Nanosecond,
IdleTimeout: time.Minute,
DisableKeepalive: true,
})
app.Get("/read-timeout", func(c Ctx) error {
return c.SendString("I should not be sent")
})
go func() {
time.Sleep(500 * time.Millisecond)
conn, err := net.Dial(NetworkTCP4, "127.0.0.1:4004")
require.NoError(t, err)
defer func(conn net.Conn) {
err := conn.Close()
require.NoError(t, err)
}(conn)
_, err = conn.Write([]byte("HEAD /read-timeout HTTP/1.1\r\n"))
require.NoError(t, err)
buf := make([]byte, 1024)
var n int
n, err = conn.Read(buf)
require.NoError(t, err)
require.True(t, bytes.Contains(buf[:n], []byte("408 Request Timeout")))
require.NoError(t, app.Shutdown())
}()
require.NoError(t, app.Listen(":4004", ListenConfig{DisableStartupMessage: true}))
}
// go test -run Test_App_BadRequest
func Test_App_BadRequest(t *testing.T) {
t.Parallel()
app := New()
app.Get("/bad-request", func(c Ctx) error {
return c.SendString("I should not be sent")
})
go func() {
time.Sleep(500 * time.Millisecond)
conn, err := net.Dial(NetworkTCP4, "127.0.0.1:4005")
require.NoError(t, err)
defer func(conn net.Conn) {
err := conn.Close()
require.NoError(t, err)
}(conn)
_, err = conn.Write([]byte("BadRequest\r\n"))
require.NoError(t, err)
buf := make([]byte, 1024)
var n int
n, err = conn.Read(buf)
require.NoError(t, err)
require.True(t, bytes.Contains(buf[:n], []byte("400 Bad Request")))
require.NoError(t, app.Shutdown())
}()
require.NoError(t, app.Listen(":4005", ListenConfig{DisableStartupMessage: true}))
}
// go test -run Test_App_SmallReadBuffer
func Test_App_SmallReadBuffer(t *testing.T) {
t.Parallel()
app := New(Config{
ReadBufferSize: 1,
})
app.Get("/small-read-buffer", func(c Ctx) error {
return c.SendString("I should not be sent")
})
go func() {
time.Sleep(500 * time.Millisecond)
req, err := http.NewRequestWithContext(context.Background(), MethodGet, "http://127.0.0.1:4006/small-read-buffer", http.NoBody)
require.NoError(t, err)
var client http.Client
resp, err := client.Do(req)
require.NoError(t, err)
require.Equal(t, 431, resp.StatusCode)
require.NoError(t, app.Shutdown())
}()
require.NoError(t, app.Listen(":4006", ListenConfig{DisableStartupMessage: true}))
}
func Test_App_Server(t *testing.T) {
t.Parallel()
app := New()
require.NotNil(t, app.Server())
}
func Test_App_Error_In_Fasthttp_Server(t *testing.T) {
app := New()
app.config.ErrorHandler = func(c Ctx, err error) error {
return errors.New("fake error")
}
app.server.GetOnly = true
resp, err := app.Test(httptest.NewRequest(MethodPost, "/", nil))
require.NoError(t, err)
require.Equal(t, 500, resp.StatusCode)
}
// go test -race -run Test_App_New_Test_Parallel
func Test_App_New_Test_Parallel(t *testing.T) {
t.Parallel()
t.Run("Test_App_New_Test_Parallel_1", func(t *testing.T) {
t.Parallel()
app := New(Config{Immutable: true})
_, err := app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.NoError(t, err)
})
t.Run("Test_App_New_Test_Parallel_2", func(t *testing.T) {
t.Parallel()
app := New(Config{Immutable: true})
_, err := app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.NoError(t, err)
})
}
func Test_App_ReadBodyStream(t *testing.T) {
t.Parallel()
app := New(Config{StreamRequestBody: true})
app.Post("/", func(c Ctx) error {
// Calling c.Body() automatically reads the entire stream.
return c.SendString(fmt.Sprintf("%v %s", c.Request().IsBodyStream(), c.Body()))
})
testString := "this is a test"
resp, err := app.Test(httptest.NewRequest(MethodPost, "/", bytes.NewBufferString(testString)))
require.NoError(t, err, "app.Test(req)")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err, "io.ReadAll(resp.Body)")
require.Equal(t, fmt.Sprintf("true %s", testString), string(body))
}
func Test_App_DisablePreParseMultipartForm(t *testing.T) {
t.Parallel()
// Must be used with both otherwise there is no point.
testString := "this is a test"
app := New(Config{DisablePreParseMultipartForm: true, StreamRequestBody: true})
app.Post("/", func(c Ctx) error {
req := c.Request()
mpf, err := req.MultipartForm()
if err != nil {
return err
}
if !req.IsBodyStream() {
return fmt.Errorf("not a body stream")
}
file, err := mpf.File["test"][0].Open()
if err != nil {
return fmt.Errorf("failed to open: %w", err)
}
buffer := make([]byte, len(testString))
n, err := file.Read(buffer)
if err != nil {
return fmt.Errorf("failed to read: %w", err)
}
if n != len(testString) {
return fmt.Errorf("bad read length")
}
return c.Send(buffer)
})
b := &bytes.Buffer{}
w := multipart.NewWriter(b)
writer, err := w.CreateFormFile("test", "test")
require.NoError(t, err, "w.CreateFormFile")
n, err := writer.Write([]byte(testString))
require.NoError(t, err, "writer.Write")
require.Len(t, testString, n, "writer n")
require.NoError(t, w.Close(), "w.Close()")
req := httptest.NewRequest(MethodPost, "/", b)
req.Header.Set("Content-Type", w.FormDataContentType())
resp, err := app.Test(req)
require.NoError(t, err, "app.Test(req)")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err, "io.ReadAll(resp.Body)")
require.Equal(t, testString, string(body))
}
func Test_App_Test_no_timeout_infinitely(t *testing.T) {
t.Parallel()
var err error
c := make(chan int)
go func() {
defer func() { c <- 0 }()
app := New()
app.Get("/", func(c Ctx) error {
runtime.Goexit()
return nil
})
req := httptest.NewRequest(MethodGet, "/", http.NoBody)
_, err = app.Test(req, -1)
}()
tk := time.NewTimer(5 * time.Second)
defer tk.Stop()
select {
case <-tk.C:
t.Error("hanging test")
t.FailNow()
case <-c:
}
if err == nil {
t.Error("unexpected success request")
t.FailNow()
}
}
func Test_App_SetTLSHandler(t *testing.T) {
t.Parallel()
tlsHandler := &TLSHandler{clientHelloInfo: &tls.ClientHelloInfo{
ServerName: "example.golang",
}}
app := New()
app.SetTLSHandler(tlsHandler)
c := app.NewCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
require.Equal(t, "example.golang", c.ClientHelloInfo().ServerName)
}
func Test_App_AddCustomRequestMethod(t *testing.T) {
t.Parallel()
methods := append(DefaultMethods, "TEST") //nolint:gocritic // We want a new slice here
app := New(Config{
RequestMethods: methods,
})
appMethods := app.config.RequestMethods
// method name is always uppercase - https://datatracker.ietf.org/doc/html/rfc7231#section-4.1
require.Equal(t, len(app.stack), len(appMethods))
require.Equal(t, len(app.stack), len(appMethods))
require.Equal(t, "TEST", appMethods[len(appMethods)-1])
}
func TestApp_GetRoutes(t *testing.T) {
t.Parallel()
app := New()
app.Use(func(c Ctx) error {
return c.Next()
})
handler := func(c Ctx) error {
return c.SendStatus(StatusOK)
}
app.Delete("/delete", handler).Name("delete")
app.Post("/post", handler).Name("post")
routes := app.GetRoutes(false)
require.Len(t, routes, 2+len(app.config.RequestMethods))
methodMap := map[string]string{"/delete": "delete", "/post": "post"}
for _, route := range routes {
name, ok := methodMap[route.Path]
if ok {
require.Equal(t, name, route.Name)
}
}
routes = app.GetRoutes(true)
require.Len(t, routes, 2)
for _, route := range routes {
name, ok := methodMap[route.Path]
require.True(t, ok)
require.Equal(t, name, route.Name)
}
}
func Test_Middleware_Route_Naming_With_Use(t *testing.T) {
named := "named"
app := New()
app.Get("/unnamed", func(c Ctx) error {
return c.Next()
})
app.Post("/named", func(c Ctx) error {
return c.Next()
}).Name(named)
app.Use(func(c Ctx) error {
return c.Next()
}) // no name - logging MW
app.Use(func(c Ctx) error {
return c.Next()
}).Name("corsMW")
app.Use(func(c Ctx) error {
return c.Next()
}).Name("compressMW")
app.Use(func(c Ctx) error {
return c.Next()
}) // no name - cache MW
grp := app.Group("/pages").Name("pages.")
grp.Use(func(c Ctx) error {
return c.Next()
}).Name("csrfMW")
grp.Get("/home", func(c Ctx) error {
return c.Next()
}).Name("home")
grp.Get("/unnamed", func(c Ctx) error {
return c.Next()
})
for _, route := range app.GetRoutes() {
switch route.Path {
case "/":
require.Equal(t, "compressMW", route.Name)
case "/unnamed":
require.Equal(t, "", route.Name)
case "named":
require.Equal(t, named, route.Name)
case "/pages":
require.Equal(t, "pages.csrfMW", route.Name)
case "/pages/home":
require.Equal(t, "pages.home", route.Name)
case "/pages/unnamed":
require.Equal(t, "", route.Name)
}
}
}
func Test_Route_Naming_Issue_2671_2685(t *testing.T) {
app := New()
app.Get("/", emptyHandler).Name("index")
require.Equal(t, "/", app.GetRoute("index").Path)
app.Get("/a/:a_id", emptyHandler).Name("a")
require.Equal(t, "/a/:a_id", app.GetRoute("a").Path)
app.Post("/b/:bId", emptyHandler).Name("b")
require.Equal(t, "/b/:bId", app.GetRoute("b").Path)
c := app.Group("/c")
c.Get("", emptyHandler).Name("c.get")
require.Equal(t, "/c", app.GetRoute("c.get").Path)
c.Post("", emptyHandler).Name("c.post")
require.Equal(t, "/c", app.GetRoute("c.post").Path)
c.Get("/d", emptyHandler).Name("c.get.d")
require.Equal(t, "/c/d", app.GetRoute("c.get.d").Path)
d := app.Group("/d/:d_id")
d.Get("", emptyHandler).Name("d.get")
require.Equal(t, "/d/:d_id", app.GetRoute("d.get").Path)
d.Post("", emptyHandler).Name("d.post")
require.Equal(t, "/d/:d_id", app.GetRoute("d.post").Path)
e := app.Group("/e/:eId")
e.Get("", emptyHandler).Name("e.get")
require.Equal(t, "/e/:eId", app.GetRoute("e.get").Path)
e.Post("", emptyHandler).Name("e.post")
require.Equal(t, "/e/:eId", app.GetRoute("e.post").Path)
e.Get("f", emptyHandler).Name("e.get.f")
require.Equal(t, "/e/:eId/f", app.GetRoute("e.get.f").Path)
postGroup := app.Group("/post/:postId")
postGroup.Get("", emptyHandler).Name("post.get")
require.Equal(t, "/post/:postId", app.GetRoute("post.get").Path)
postGroup.Post("", emptyHandler).Name("post.update")
require.Equal(t, "/post/:postId", app.GetRoute("post.update").Path)
// Add testcase for routes use the same PATH on different methods
app.Get("/users", emptyHandler).Name("get-users")
app.Post("/users", emptyHandler).Name("add-user")
getUsers := app.GetRoute("get-users")
require.Equal(t, "/users", getUsers.Path)
addUser := app.GetRoute("add-user")
require.Equal(t, "/users", addUser.Path)
// Add testcase for routes use the same PATH on different methods (for groups)
newGrp := app.Group("/name-test")
newGrp.Get("/users", emptyHandler).Name("grp-get-users")
newGrp.Post("/users", emptyHandler).Name("grp-add-user")
getUsers = app.GetRoute("grp-get-users")
require.Equal(t, "/name-test/users", getUsers.Path)
addUser = app.GetRoute("grp-add-user")
require.Equal(t, "/name-test/users", addUser.Path)
// Add testcase for HEAD route naming
app.Get("/simple-route", emptyHandler).Name("simple-route")
app.Head("/simple-route", emptyHandler).Name("simple-route2")
sRoute := app.GetRoute("simple-route")
require.Equal(t, "/simple-route", sRoute.Path)
sRoute2 := app.GetRoute("simple-route2")
require.Equal(t, "/simple-route", sRoute2.Path)
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/jnliok/fiber.git
[email protected]:jnliok/fiber.git
jnliok
fiber
fiber
main

搜索帮助