github.com/gofiber/fiber/v2@v2.47.0/app_test.go (about) 1 // ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ 2 // 🤖 Github Repository: https://github.com/gofiber/fiber 3 // 📌 API Documentation: https://docs.gofiber.io 4 5 //nolint:bodyclose // Much easier to just ignore memory leaks in tests 6 package fiber 7 8 import ( 9 "bytes" 10 "context" 11 "crypto/tls" 12 "errors" 13 "fmt" 14 "io" 15 "mime/multipart" 16 "net" 17 "net/http" 18 "net/http/httptest" 19 "reflect" 20 "regexp" 21 "runtime" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/gofiber/fiber/v2/utils" 27 28 "github.com/valyala/fasthttp" 29 "github.com/valyala/fasthttp/fasthttputil" 30 ) 31 32 func testEmptyHandler(_ *Ctx) error { 33 return nil 34 } 35 36 func testStatus200(t *testing.T, app *App, url, method string) { 37 t.Helper() 38 39 req := httptest.NewRequest(method, url, nil) 40 41 resp, err := app.Test(req) 42 utils.AssertEqual(t, nil, err, "app.Test(req)") 43 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 44 } 45 46 func testErrorResponse(t *testing.T, err error, resp *http.Response, expectedBodyError string) { 47 t.Helper() 48 49 utils.AssertEqual(t, nil, err, "app.Test(req)") 50 utils.AssertEqual(t, 500, resp.StatusCode, "Status code") 51 52 body, err := io.ReadAll(resp.Body) 53 utils.AssertEqual(t, nil, err) 54 utils.AssertEqual(t, expectedBodyError, string(body), "Response body") 55 } 56 57 func Test_App_MethodNotAllowed(t *testing.T) { 58 t.Parallel() 59 app := New() 60 61 app.Use(func(c *Ctx) error { 62 return c.Next() 63 }) 64 65 app.Post("/", testEmptyHandler) 66 67 app.Options("/", testEmptyHandler) 68 69 resp, err := app.Test(httptest.NewRequest(MethodPost, "/", nil)) 70 utils.AssertEqual(t, nil, err) 71 utils.AssertEqual(t, 200, resp.StatusCode) 72 utils.AssertEqual(t, "", resp.Header.Get(HeaderAllow)) 73 74 resp, err = app.Test(httptest.NewRequest(MethodGet, "/", nil)) 75 utils.AssertEqual(t, nil, err) 76 utils.AssertEqual(t, 405, resp.StatusCode) 77 utils.AssertEqual(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow)) 78 79 resp, err = app.Test(httptest.NewRequest(MethodPatch, "/", nil)) 80 utils.AssertEqual(t, nil, err) 81 utils.AssertEqual(t, 405, resp.StatusCode) 82 utils.AssertEqual(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow)) 83 84 resp, err = app.Test(httptest.NewRequest(MethodPut, "/", nil)) 85 utils.AssertEqual(t, nil, err) 86 utils.AssertEqual(t, 405, resp.StatusCode) 87 utils.AssertEqual(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow)) 88 89 app.Get("/", testEmptyHandler) 90 91 resp, err = app.Test(httptest.NewRequest(MethodTrace, "/", nil)) 92 utils.AssertEqual(t, nil, err) 93 utils.AssertEqual(t, 405, resp.StatusCode) 94 utils.AssertEqual(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) 95 96 resp, err = app.Test(httptest.NewRequest(MethodPatch, "/", nil)) 97 utils.AssertEqual(t, nil, err) 98 utils.AssertEqual(t, 405, resp.StatusCode) 99 utils.AssertEqual(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) 100 101 resp, err = app.Test(httptest.NewRequest(MethodPut, "/", nil)) 102 utils.AssertEqual(t, nil, err) 103 utils.AssertEqual(t, 405, resp.StatusCode) 104 utils.AssertEqual(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) 105 } 106 107 func Test_App_Custom_Middleware_404_Should_Not_SetMethodNotAllowed(t *testing.T) { 108 t.Parallel() 109 app := New() 110 111 app.Use(func(c *Ctx) error { 112 return c.SendStatus(404) 113 }) 114 115 app.Post("/", testEmptyHandler) 116 117 resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) 118 utils.AssertEqual(t, nil, err) 119 utils.AssertEqual(t, 404, resp.StatusCode) 120 121 g := app.Group("/with-next", func(c *Ctx) error { 122 return c.Status(404).Next() 123 }) 124 125 g.Post("/", testEmptyHandler) 126 127 resp, err = app.Test(httptest.NewRequest(MethodGet, "/with-next", nil)) 128 utils.AssertEqual(t, nil, err) 129 utils.AssertEqual(t, 404, resp.StatusCode) 130 } 131 132 func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) { 133 t.Parallel() 134 expectedError := regexp.MustCompile( 135 `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: -+`, 136 ) 137 app := New() 138 139 app.Get("/", func(c *Ctx) error { 140 panic(errors.New("should never called")) 141 }) 142 143 request := httptest.NewRequest(MethodGet, "/", nil) 144 logHeaderSlice := make([]string, 5000) 145 request.Header.Set("Very-Long-Header", strings.Join(logHeaderSlice, "-")) 146 _, err := app.Test(request) 147 if err == nil { 148 t.Error("Expect an error at app.Test(request)") 149 } 150 151 utils.AssertEqual( 152 t, 153 true, 154 expectedError.MatchString(err.Error()), 155 fmt.Sprintf("Has: %s, expected pattern: %s", err.Error(), expectedError.String()), 156 ) 157 } 158 159 func Test_App_Errors(t *testing.T) { 160 t.Parallel() 161 app := New(Config{ 162 BodyLimit: 4, 163 }) 164 165 app.Get("/", func(c *Ctx) error { 166 return errors.New("hi, i'm an error") 167 }) 168 169 resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) 170 utils.AssertEqual(t, nil, err, "app.Test(req)") 171 utils.AssertEqual(t, 500, resp.StatusCode, "Status code") 172 173 body, err := io.ReadAll(resp.Body) 174 utils.AssertEqual(t, nil, err) 175 utils.AssertEqual(t, "hi, i'm an error", string(body)) 176 177 _, err = app.Test(httptest.NewRequest(MethodGet, "/", strings.NewReader("big body"))) 178 if err != nil { 179 utils.AssertEqual(t, "body size exceeds the given limit", err.Error(), "app.Test(req)") 180 } 181 } 182 183 func Test_App_ErrorHandler_Custom(t *testing.T) { 184 t.Parallel() 185 app := New(Config{ 186 ErrorHandler: func(c *Ctx, err error) error { 187 return c.Status(200).SendString("hi, i'm an custom error") 188 }, 189 }) 190 191 app.Get("/", func(c *Ctx) error { 192 return errors.New("hi, i'm an error") 193 }) 194 195 resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) 196 utils.AssertEqual(t, nil, err, "app.Test(req)") 197 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 198 199 body, err := io.ReadAll(resp.Body) 200 utils.AssertEqual(t, nil, err) 201 utils.AssertEqual(t, "hi, i'm an custom error", string(body)) 202 } 203 204 func Test_App_ErrorHandler_HandlerStack(t *testing.T) { 205 t.Parallel() 206 app := New(Config{ 207 ErrorHandler: func(c *Ctx, err error) error { 208 utils.AssertEqual(t, "1: USE error", err.Error()) 209 return DefaultErrorHandler(c, err) 210 }, 211 }) 212 app.Use("/", func(c *Ctx) error { 213 err := c.Next() // call next USE 214 utils.AssertEqual(t, "2: USE error", err.Error()) 215 return errors.New("1: USE error") 216 }, func(c *Ctx) error { 217 err := c.Next() // call [0] GET 218 utils.AssertEqual(t, "0: GET error", err.Error()) 219 return errors.New("2: USE error") 220 }) 221 app.Get("/", func(c *Ctx) error { 222 return errors.New("0: GET error") 223 }) 224 225 resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) 226 utils.AssertEqual(t, nil, err, "app.Test(req)") 227 utils.AssertEqual(t, 500, resp.StatusCode, "Status code") 228 229 body, err := io.ReadAll(resp.Body) 230 utils.AssertEqual(t, nil, err) 231 utils.AssertEqual(t, "1: USE error", string(body)) 232 } 233 234 func Test_App_ErrorHandler_RouteStack(t *testing.T) { 235 t.Parallel() 236 app := New(Config{ 237 ErrorHandler: func(c *Ctx, err error) error { 238 utils.AssertEqual(t, "1: USE error", err.Error()) 239 return DefaultErrorHandler(c, err) 240 }, 241 }) 242 app.Use("/", func(c *Ctx) error { 243 err := c.Next() 244 utils.AssertEqual(t, "0: GET error", err.Error()) 245 return errors.New("1: USE error") // [2] call ErrorHandler 246 }) 247 app.Get("/test", func(c *Ctx) error { 248 return errors.New("0: GET error") // [1] return to USE 249 }) 250 251 resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil)) 252 utils.AssertEqual(t, nil, err, "app.Test(req)") 253 utils.AssertEqual(t, 500, resp.StatusCode, "Status code") 254 255 body, err := io.ReadAll(resp.Body) 256 utils.AssertEqual(t, nil, err) 257 utils.AssertEqual(t, "1: USE error", string(body)) 258 } 259 260 func Test_App_serverErrorHandler_Internal_Error(t *testing.T) { 261 t.Parallel() 262 app := New() 263 msg := "test err" 264 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 265 defer app.ReleaseCtx(c) 266 app.serverErrorHandler(c.fasthttp, errors.New(msg)) 267 utils.AssertEqual(t, string(c.fasthttp.Response.Body()), msg) 268 utils.AssertEqual(t, c.fasthttp.Response.StatusCode(), StatusBadRequest) 269 } 270 271 func Test_App_serverErrorHandler_Network_Error(t *testing.T) { 272 t.Parallel() 273 app := New() 274 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 275 defer app.ReleaseCtx(c) 276 app.serverErrorHandler(c.fasthttp, &net.DNSError{ 277 Err: "test error", 278 Name: "test host", 279 IsTimeout: false, 280 }) 281 utils.AssertEqual(t, string(c.fasthttp.Response.Body()), utils.StatusMessage(StatusBadGateway)) 282 utils.AssertEqual(t, c.fasthttp.Response.StatusCode(), StatusBadGateway) 283 } 284 285 func Test_App_Nested_Params(t *testing.T) { 286 t.Parallel() 287 app := New() 288 289 app.Get("/test", func(c *Ctx) error { 290 return c.Status(400).Send([]byte("Should move on")) 291 }) 292 app.Get("/test/:param", func(c *Ctx) error { 293 return c.Status(400).Send([]byte("Should move on")) 294 }) 295 app.Get("/test/:param/test", func(c *Ctx) error { 296 return c.Status(400).Send([]byte("Should move on")) 297 }) 298 app.Get("/test/:param/test/:param2", func(c *Ctx) error { 299 return c.Status(200).Send([]byte("Good job")) 300 }) 301 302 req := httptest.NewRequest(MethodGet, "/test/john/test/doe", nil) 303 resp, err := app.Test(req) 304 305 utils.AssertEqual(t, nil, err, "app.Test(req)") 306 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 307 } 308 309 func Test_App_Use_Params(t *testing.T) { 310 t.Parallel() 311 app := New() 312 313 app.Use("/prefix/:param", func(c *Ctx) error { 314 utils.AssertEqual(t, "john", c.Params("param")) 315 return nil 316 }) 317 318 app.Use("/foo/:bar?", func(c *Ctx) error { 319 utils.AssertEqual(t, "foobar", c.Params("bar", "foobar")) 320 return nil 321 }) 322 323 app.Use("/:param/*", func(c *Ctx) error { 324 utils.AssertEqual(t, "john", c.Params("param")) 325 utils.AssertEqual(t, "doe", c.Params("*")) 326 return nil 327 }) 328 329 resp, err := app.Test(httptest.NewRequest(MethodGet, "/prefix/john", nil)) 330 utils.AssertEqual(t, nil, err, "app.Test(req)") 331 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 332 333 resp, err = app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil)) 334 utils.AssertEqual(t, nil, err, "app.Test(req)") 335 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 336 337 resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo", nil)) 338 utils.AssertEqual(t, nil, err, "app.Test(req)") 339 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 340 341 defer func() { 342 if err := recover(); err != nil { 343 utils.AssertEqual(t, "use: invalid handler func()\n", fmt.Sprintf("%v", err)) 344 } 345 }() 346 347 app.Use("/:param/*", func() { 348 // this should panic 349 }) 350 } 351 352 func Test_App_Use_UnescapedPath(t *testing.T) { 353 t.Parallel() 354 app := New(Config{UnescapePath: true, CaseSensitive: true}) 355 356 app.Use("/cRéeR/:param", func(c *Ctx) error { 357 utils.AssertEqual(t, "/cRéeR/اختبار", c.Path()) 358 return c.SendString(c.Params("param")) 359 }) 360 361 app.Use("/abc", func(c *Ctx) error { 362 utils.AssertEqual(t, "/AbC", c.Path()) 363 return nil 364 }) 365 366 resp, err := app.Test(httptest.NewRequest(MethodGet, "/cR%C3%A9eR/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1", nil)) 367 utils.AssertEqual(t, nil, err, "app.Test(req)") 368 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 369 370 body, err := io.ReadAll(resp.Body) 371 utils.AssertEqual(t, nil, err, "app.Test(req)") 372 // check the param result 373 utils.AssertEqual(t, "اختبار", app.getString(body)) 374 375 // with lowercase letters 376 resp, err = app.Test(httptest.NewRequest(MethodGet, "/cr%C3%A9er/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1", nil)) 377 utils.AssertEqual(t, nil, err, "app.Test(req)") 378 utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") 379 } 380 381 func Test_App_Use_CaseSensitive(t *testing.T) { 382 t.Parallel() 383 app := New(Config{CaseSensitive: true}) 384 385 app.Use("/abc", func(c *Ctx) error { 386 return c.SendString(c.Path()) 387 }) 388 389 // wrong letters in the requested route -> 404 390 resp, err := app.Test(httptest.NewRequest(MethodGet, "/AbC", nil)) 391 utils.AssertEqual(t, nil, err, "app.Test(req)") 392 utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") 393 394 // right letters in the requrested route -> 200 395 resp, err = app.Test(httptest.NewRequest(MethodGet, "/abc", nil)) 396 utils.AssertEqual(t, nil, err, "app.Test(req)") 397 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 398 399 // check the detected path when the case insensitive recognition is activated 400 app.config.CaseSensitive = false 401 // check the case sensitive feature 402 resp, err = app.Test(httptest.NewRequest(MethodGet, "/AbC", nil)) 403 utils.AssertEqual(t, nil, err, "app.Test(req)") 404 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 405 406 body, err := io.ReadAll(resp.Body) 407 utils.AssertEqual(t, nil, err, "app.Test(req)") 408 // check the detected path result 409 utils.AssertEqual(t, "/AbC", app.getString(body)) 410 } 411 412 func Test_App_Not_Use_StrictRouting(t *testing.T) { 413 t.Parallel() 414 app := New() 415 416 app.Use("/abc", func(c *Ctx) error { 417 return c.SendString(c.Path()) 418 }) 419 420 g := app.Group("/foo") 421 g.Use("/", func(c *Ctx) error { 422 return c.SendString(c.Path()) 423 }) 424 425 // wrong path in the requested route -> 404 426 resp, err := app.Test(httptest.NewRequest(MethodGet, "/abc/", nil)) 427 utils.AssertEqual(t, nil, err, "app.Test(req)") 428 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 429 430 // right path in the requrested route -> 200 431 resp, err = app.Test(httptest.NewRequest(MethodGet, "/abc", nil)) 432 utils.AssertEqual(t, nil, err, "app.Test(req)") 433 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 434 435 // wrong path with group in the requested route -> 404 436 resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo", nil)) 437 utils.AssertEqual(t, nil, err, "app.Test(req)") 438 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 439 440 // right path with group in the requrested route -> 200 441 resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo/", nil)) 442 utils.AssertEqual(t, nil, err, "app.Test(req)") 443 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 444 } 445 446 func Test_App_Use_MultiplePrefix(t *testing.T) { 447 t.Parallel() 448 app := New() 449 450 app.Use([]string{"/john", "/doe"}, func(c *Ctx) error { 451 return c.SendString(c.Path()) 452 }) 453 454 g := app.Group("/test") 455 g.Use([]string{"/john", "/doe"}, func(c *Ctx) error { 456 return c.SendString(c.Path()) 457 }) 458 459 resp, err := app.Test(httptest.NewRequest(MethodGet, "/john", nil)) 460 utils.AssertEqual(t, nil, err, "app.Test(req)") 461 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 462 463 body, err := io.ReadAll(resp.Body) 464 utils.AssertEqual(t, nil, err) 465 utils.AssertEqual(t, "/john", string(body)) 466 467 resp, err = app.Test(httptest.NewRequest(MethodGet, "/doe", nil)) 468 utils.AssertEqual(t, nil, err, "app.Test(req)") 469 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 470 471 body, err = io.ReadAll(resp.Body) 472 utils.AssertEqual(t, nil, err) 473 utils.AssertEqual(t, "/doe", string(body)) 474 475 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/john", nil)) 476 utils.AssertEqual(t, nil, err, "app.Test(req)") 477 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 478 479 body, err = io.ReadAll(resp.Body) 480 utils.AssertEqual(t, nil, err) 481 utils.AssertEqual(t, "/test/john", string(body)) 482 483 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/doe", nil)) 484 utils.AssertEqual(t, nil, err, "app.Test(req)") 485 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 486 487 body, err = io.ReadAll(resp.Body) 488 utils.AssertEqual(t, nil, err) 489 utils.AssertEqual(t, "/test/doe", string(body)) 490 } 491 492 func Test_App_Use_StrictRouting(t *testing.T) { 493 t.Parallel() 494 app := New(Config{StrictRouting: true}) 495 496 app.Get("/abc", func(c *Ctx) error { 497 return c.SendString(c.Path()) 498 }) 499 500 g := app.Group("/foo") 501 g.Get("/", func(c *Ctx) error { 502 return c.SendString(c.Path()) 503 }) 504 505 // wrong path in the requested route -> 404 506 resp, err := app.Test(httptest.NewRequest(MethodGet, "/abc/", nil)) 507 utils.AssertEqual(t, nil, err, "app.Test(req)") 508 utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") 509 510 // right path in the requrested route -> 200 511 resp, err = app.Test(httptest.NewRequest(MethodGet, "/abc", nil)) 512 utils.AssertEqual(t, nil, err, "app.Test(req)") 513 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 514 515 // wrong path with group in the requested route -> 404 516 resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo", nil)) 517 utils.AssertEqual(t, nil, err, "app.Test(req)") 518 utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") 519 520 // right path with group in the requrested route -> 200 521 resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo/", nil)) 522 utils.AssertEqual(t, nil, err, "app.Test(req)") 523 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 524 } 525 526 func Test_App_Add_Method_Test(t *testing.T) { 527 t.Parallel() 528 defer func() { 529 if err := recover(); err != nil { 530 utils.AssertEqual(t, "add: invalid http method JANE\n", fmt.Sprintf("%v", err)) 531 } 532 }() 533 534 methods := append(DefaultMethods, "JOHN") //nolint:gocritic // We want a new slice here 535 app := New(Config{ 536 RequestMethods: methods, 537 }) 538 539 app.Add("JOHN", "/doe", testEmptyHandler) 540 541 resp, err := app.Test(httptest.NewRequest("JOHN", "/doe", nil)) 542 utils.AssertEqual(t, nil, err, "app.Test(req)") 543 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 544 545 resp, err = app.Test(httptest.NewRequest(MethodGet, "/doe", nil)) 546 utils.AssertEqual(t, nil, err, "app.Test(req)") 547 utils.AssertEqual(t, StatusMethodNotAllowed, resp.StatusCode, "Status code") 548 549 resp, err = app.Test(httptest.NewRequest("UNKNOWN", "/doe", nil)) 550 utils.AssertEqual(t, nil, err, "app.Test(req)") 551 utils.AssertEqual(t, StatusBadRequest, resp.StatusCode, "Status code") 552 553 app.Add("JANE", "/doe", testEmptyHandler) 554 } 555 556 // go test -run Test_App_GETOnly 557 func Test_App_GETOnly(t *testing.T) { 558 t.Parallel() 559 app := New(Config{ 560 GETOnly: true, 561 }) 562 563 app.Post("/", func(c *Ctx) error { 564 return c.SendString("Hello 👋!") 565 }) 566 567 req := httptest.NewRequest(MethodPost, "/", nil) 568 resp, err := app.Test(req) 569 utils.AssertEqual(t, nil, err, "app.Test(req)") 570 utils.AssertEqual(t, StatusMethodNotAllowed, resp.StatusCode, "Status code") 571 } 572 573 func Test_App_Use_Params_Group(t *testing.T) { 574 t.Parallel() 575 app := New() 576 577 group := app.Group("/prefix/:param/*") 578 group.Use("/", func(c *Ctx) error { 579 return c.Next() 580 }) 581 group.Get("/test", func(c *Ctx) error { 582 utils.AssertEqual(t, "john", c.Params("param")) 583 utils.AssertEqual(t, "doe", c.Params("*")) 584 return nil 585 }) 586 587 resp, err := app.Test(httptest.NewRequest(MethodGet, "/prefix/john/doe/test", nil)) 588 utils.AssertEqual(t, nil, err, "app.Test(req)") 589 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 590 } 591 592 func Test_App_Chaining(t *testing.T) { 593 t.Parallel() 594 n := func(c *Ctx) error { 595 return c.Next() 596 } 597 app := New() 598 app.Use("/john", n, n, n, n, func(c *Ctx) error { 599 return c.SendStatus(202) 600 }) 601 // check handler count for registered HEAD route 602 utils.AssertEqual(t, 5, len(app.stack[app.methodInt(MethodHead)][0].Handlers), "app.Test(req)") 603 604 req := httptest.NewRequest(MethodPost, "/john", nil) 605 606 resp, err := app.Test(req) 607 utils.AssertEqual(t, nil, err, "app.Test(req)") 608 utils.AssertEqual(t, 202, resp.StatusCode, "Status code") 609 610 app.Get("/test", n, n, n, n, func(c *Ctx) error { 611 return c.SendStatus(203) 612 }) 613 614 req = httptest.NewRequest(MethodGet, "/test", nil) 615 616 resp, err = app.Test(req) 617 utils.AssertEqual(t, nil, err, "app.Test(req)") 618 utils.AssertEqual(t, 203, resp.StatusCode, "Status code") 619 } 620 621 func Test_App_Order(t *testing.T) { 622 t.Parallel() 623 app := New() 624 625 app.Get("/test", func(c *Ctx) error { 626 _, err := c.Write([]byte("1")) 627 utils.AssertEqual(t, nil, err) 628 return c.Next() 629 }) 630 631 app.All("/test", func(c *Ctx) error { 632 _, err := c.Write([]byte("2")) 633 utils.AssertEqual(t, nil, err) 634 return c.Next() 635 }) 636 637 app.Use(func(c *Ctx) error { 638 _, err := c.Write([]byte("3")) 639 utils.AssertEqual(t, nil, err) 640 return nil 641 }) 642 643 req := httptest.NewRequest(MethodGet, "/test", nil) 644 645 resp, err := app.Test(req) 646 utils.AssertEqual(t, nil, err, "app.Test(req)") 647 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 648 649 body, err := io.ReadAll(resp.Body) 650 utils.AssertEqual(t, nil, err) 651 utils.AssertEqual(t, "123", string(body)) 652 } 653 654 func Test_App_Methods(t *testing.T) { 655 t.Parallel() 656 dummyHandler := testEmptyHandler 657 658 app := New() 659 660 app.Connect("/:john?/:doe?", dummyHandler) 661 testStatus200(t, app, "/john/doe", "CONNECT") 662 663 app.Put("/:john?/:doe?", dummyHandler) 664 testStatus200(t, app, "/john/doe", MethodPut) 665 666 app.Post("/:john?/:doe?", dummyHandler) 667 testStatus200(t, app, "/john/doe", MethodPost) 668 669 app.Delete("/:john?/:doe?", dummyHandler) 670 testStatus200(t, app, "/john/doe", MethodDelete) 671 672 app.Head("/:john?/:doe?", dummyHandler) 673 testStatus200(t, app, "/john/doe", MethodHead) 674 675 app.Patch("/:john?/:doe?", dummyHandler) 676 testStatus200(t, app, "/john/doe", MethodPatch) 677 678 app.Options("/:john?/:doe?", dummyHandler) 679 testStatus200(t, app, "/john/doe", MethodOptions) 680 681 app.Trace("/:john?/:doe?", dummyHandler) 682 testStatus200(t, app, "/john/doe", MethodTrace) 683 684 app.Get("/:john?/:doe?", dummyHandler) 685 testStatus200(t, app, "/john/doe", MethodGet) 686 687 app.All("/:john?/:doe?", dummyHandler) 688 testStatus200(t, app, "/john/doe", MethodPost) 689 690 app.Use("/:john?/:doe?", dummyHandler) 691 testStatus200(t, app, "/john/doe", MethodGet) 692 } 693 694 func Test_App_Route_Naming(t *testing.T) { 695 t.Parallel() 696 app := New() 697 handler := func(c *Ctx) error { 698 return c.SendStatus(StatusOK) 699 } 700 app.Get("/john", handler).Name("john") 701 app.Delete("/doe", handler) 702 app.Name("doe") 703 704 jane := app.Group("/jane").Name("jane.") 705 group := app.Group("/group") 706 subGroup := jane.Group("/sub-group").Name("sub.") 707 708 jane.Get("/test", handler).Name("test") 709 jane.Trace("/trace", handler).Name("trace") 710 711 group.Get("/test", handler).Name("test") 712 713 app.Post("/post", handler).Name("post") 714 715 subGroup.Get("/done", handler).Name("done") 716 717 utils.AssertEqual(t, "post", app.GetRoute("post").Name) 718 utils.AssertEqual(t, "john", app.GetRoute("john").Name) 719 utils.AssertEqual(t, "jane.test", app.GetRoute("jane.test").Name) 720 utils.AssertEqual(t, "jane.trace", app.GetRoute("jane.trace").Name) 721 utils.AssertEqual(t, "jane.sub.done", app.GetRoute("jane.sub.done").Name) 722 utils.AssertEqual(t, "test", app.GetRoute("test").Name) 723 } 724 725 func Test_App_New(t *testing.T) { 726 t.Parallel() 727 app := New() 728 app.Get("/", testEmptyHandler) 729 730 appConfig := New(Config{ 731 Immutable: true, 732 }) 733 appConfig.Get("/", testEmptyHandler) 734 } 735 736 func Test_App_Config(t *testing.T) { 737 t.Parallel() 738 app := New(Config{ 739 DisableStartupMessage: true, 740 }) 741 utils.AssertEqual(t, true, app.Config().DisableStartupMessage) 742 } 743 744 func Test_App_Shutdown(t *testing.T) { 745 t.Parallel() 746 t.Run("success", func(t *testing.T) { 747 t.Parallel() 748 app := New(Config{ 749 DisableStartupMessage: true, 750 }) 751 utils.AssertEqual(t, true, app.Shutdown() == nil) 752 }) 753 754 t.Run("no server", func(t *testing.T) { 755 t.Parallel() 756 app := &App{} 757 if err := app.Shutdown(); err != nil { 758 if err.Error() != "shutdown: server is not running" { 759 t.Fatal() 760 } 761 } 762 }) 763 } 764 765 func Test_App_ShutdownWithTimeout(t *testing.T) { 766 t.Parallel() 767 app := New() 768 app.Get("/", func(ctx *Ctx) error { 769 time.Sleep(5 * time.Second) 770 return ctx.SendString("body") 771 }) 772 ln := fasthttputil.NewInmemoryListener() 773 go func() { 774 utils.AssertEqual(t, nil, app.Listener(ln)) 775 }() 776 time.Sleep(1 * time.Second) 777 go func() { 778 conn, err := ln.Dial() 779 if err != nil { 780 t.Errorf("unexepcted error: %v", err) 781 } 782 783 if _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")); err != nil { 784 t.Errorf("unexpected error: %v", err) 785 } 786 }() 787 time.Sleep(1 * time.Second) 788 789 shutdownErr := make(chan error) 790 go func() { 791 shutdownErr <- app.ShutdownWithTimeout(1 * time.Second) 792 }() 793 794 timer := time.NewTimer(time.Second * 5) 795 select { 796 case <-timer.C: 797 t.Fatal("idle connections not closed on shutdown") 798 case err := <-shutdownErr: 799 if err == nil || !errors.Is(err, context.DeadlineExceeded) { 800 t.Fatalf("unexpected err %v. Expecting %v", err, context.DeadlineExceeded) 801 } 802 } 803 } 804 805 func Test_App_ShutdownWithContext(t *testing.T) { 806 t.Parallel() 807 808 app := New() 809 app.Get("/", func(ctx *Ctx) error { 810 time.Sleep(5 * time.Second) 811 return ctx.SendString("body") 812 }) 813 814 ln := fasthttputil.NewInmemoryListener() 815 816 go func() { 817 utils.AssertEqual(t, nil, app.Listener(ln)) 818 }() 819 820 time.Sleep(1 * time.Second) 821 822 go func() { 823 conn, err := ln.Dial() 824 if err != nil { 825 t.Errorf("unexepcted error: %v", err) 826 } 827 828 if _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")); err != nil { 829 t.Errorf("unexpected error: %v", err) 830 } 831 }() 832 833 time.Sleep(1 * time.Second) 834 835 shutdownErr := make(chan error) 836 go func() { 837 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 838 defer cancel() 839 shutdownErr <- app.ShutdownWithContext(ctx) 840 }() 841 842 select { 843 case <-time.After(5 * time.Second): 844 t.Fatal("idle connections not closed on shutdown") 845 case err := <-shutdownErr: 846 if err == nil || !errors.Is(err, context.DeadlineExceeded) { 847 t.Fatalf("unexpected err %v. Expecting %v", err, context.DeadlineExceeded) 848 } 849 } 850 } 851 852 // go test -run Test_App_Static_Index_Default 853 func Test_App_Static_Index_Default(t *testing.T) { 854 t.Parallel() 855 app := New() 856 857 app.Static("/prefix", "./.github/workflows") 858 app.Static("", "./.github/") 859 app.Static("test", "", Static{Index: "index.html"}) 860 861 resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) 862 utils.AssertEqual(t, nil, err, "app.Test(req)") 863 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 864 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 865 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 866 867 body, err := io.ReadAll(resp.Body) 868 utils.AssertEqual(t, nil, err) 869 utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) 870 871 resp, err = app.Test(httptest.NewRequest(MethodGet, "/not-found", nil)) 872 utils.AssertEqual(t, nil, err, "app.Test(req)") 873 utils.AssertEqual(t, 404, resp.StatusCode, "Status code") 874 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 875 utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) 876 877 body, err = io.ReadAll(resp.Body) 878 utils.AssertEqual(t, nil, err) 879 utils.AssertEqual(t, "Cannot GET /not-found", string(body)) 880 } 881 882 // go test -run Test_App_Static_Index 883 func Test_App_Static_Direct(t *testing.T) { 884 t.Parallel() 885 app := New() 886 887 app.Static("/", "./.github") 888 889 resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil)) 890 utils.AssertEqual(t, nil, err, "app.Test(req)") 891 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 892 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 893 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 894 895 body, err := io.ReadAll(resp.Body) 896 utils.AssertEqual(t, nil, err) 897 utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) 898 899 resp, err = app.Test(httptest.NewRequest(MethodGet, "/testdata/testRoutes.json", nil)) 900 utils.AssertEqual(t, nil, err, "app.Test(req)") 901 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 902 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 903 utils.AssertEqual(t, MIMEApplicationJSON, resp.Header.Get("Content-Type")) 904 utils.AssertEqual(t, "", resp.Header.Get(HeaderCacheControl), "CacheControl Control") 905 906 body, err = io.ReadAll(resp.Body) 907 utils.AssertEqual(t, nil, err) 908 utils.AssertEqual(t, true, strings.Contains(string(body), "test_routes")) 909 } 910 911 // go test -run Test_App_Static_MaxAge 912 func Test_App_Static_MaxAge(t *testing.T) { 913 t.Parallel() 914 app := New() 915 916 app.Static("/", "./.github", Static{MaxAge: 100}) 917 918 resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil)) 919 utils.AssertEqual(t, nil, err, "app.Test(req)") 920 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 921 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 922 utils.AssertEqual(t, "text/html; charset=utf-8", resp.Header.Get(HeaderContentType)) 923 utils.AssertEqual(t, "public, max-age=100", resp.Header.Get(HeaderCacheControl), "CacheControl Control") 924 } 925 926 // go test -run Test_App_Static_Custom_CacheControl 927 func Test_App_Static_Custom_CacheControl(t *testing.T) { 928 t.Parallel() 929 app := New() 930 931 app.Static("/", "./.github", Static{ModifyResponse: func(c *Ctx) error { 932 if strings.Contains(c.GetRespHeader("Content-Type"), "text/html") { 933 c.Response().Header.Set("Cache-Control", "no-cache, no-store, must-revalidate") 934 } 935 return nil 936 }}) 937 938 resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil)) 939 utils.AssertEqual(t, nil, err, "app.Test(req)") 940 utils.AssertEqual(t, "no-cache, no-store, must-revalidate", resp.Header.Get(HeaderCacheControl), "CacheControl Control") 941 942 respNormal, errNormal := app.Test(httptest.NewRequest(MethodGet, "/config.yml", nil)) 943 utils.AssertEqual(t, nil, errNormal, "app.Test(req)") 944 utils.AssertEqual(t, "", respNormal.Header.Get(HeaderCacheControl), "CacheControl Control") 945 } 946 947 // go test -run Test_App_Static_Download 948 func Test_App_Static_Download(t *testing.T) { 949 t.Parallel() 950 app := New() 951 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 952 defer app.ReleaseCtx(c) 953 954 app.Static("/fiber.png", "./.github/testdata/fs/img/fiber.png", Static{Download: true}) 955 956 resp, err := app.Test(httptest.NewRequest(MethodGet, "/fiber.png", nil)) 957 utils.AssertEqual(t, nil, err, "app.Test(req)") 958 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 959 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 960 utils.AssertEqual(t, "image/png", resp.Header.Get(HeaderContentType)) 961 utils.AssertEqual(t, `attachment`, resp.Header.Get(HeaderContentDisposition)) 962 } 963 964 // go test -run Test_App_Static_Group 965 func Test_App_Static_Group(t *testing.T) { 966 t.Parallel() 967 app := New() 968 969 grp := app.Group("/v1", func(c *Ctx) error { 970 c.Set("Test-Header", "123") 971 return c.Next() 972 }) 973 974 grp.Static("/v2", "./.github/index.html") 975 976 req := httptest.NewRequest(MethodGet, "/v1/v2", nil) 977 resp, err := app.Test(req) 978 utils.AssertEqual(t, nil, err, "app.Test(req)") 979 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 980 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 981 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 982 utils.AssertEqual(t, "123", resp.Header.Get("Test-Header")) 983 984 grp = app.Group("/v2") 985 grp.Static("/v3*", "./.github/index.html") 986 987 req = httptest.NewRequest(MethodGet, "/v2/v3/john/doe", nil) 988 resp, err = app.Test(req) 989 utils.AssertEqual(t, nil, err, "app.Test(req)") 990 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 991 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 992 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 993 } 994 995 func Test_App_Static_Wildcard(t *testing.T) { 996 t.Parallel() 997 app := New() 998 999 app.Static("*", "./.github/index.html") 1000 1001 req := httptest.NewRequest(MethodGet, "/yesyes/john/doe", nil) 1002 resp, err := app.Test(req) 1003 utils.AssertEqual(t, nil, err, "app.Test(req)") 1004 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1005 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1006 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 1007 1008 body, err := io.ReadAll(resp.Body) 1009 utils.AssertEqual(t, nil, err) 1010 utils.AssertEqual(t, true, strings.Contains(string(body), "Test file")) 1011 } 1012 1013 func Test_App_Static_Prefix_Wildcard(t *testing.T) { 1014 t.Parallel() 1015 app := New() 1016 1017 app.Static("/test/*", "./.github/index.html") 1018 1019 req := httptest.NewRequest(MethodGet, "/test/john/doe", nil) 1020 resp, err := app.Test(req) 1021 utils.AssertEqual(t, nil, err, "app.Test(req)") 1022 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1023 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1024 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 1025 1026 app.Static("/my/nameisjohn*", "./.github/index.html") 1027 1028 resp, err = app.Test(httptest.NewRequest(MethodGet, "/my/nameisjohn/no/its/not", nil)) 1029 utils.AssertEqual(t, nil, err, "app.Test(req)") 1030 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1031 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1032 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 1033 1034 body, err := io.ReadAll(resp.Body) 1035 utils.AssertEqual(t, nil, err) 1036 utils.AssertEqual(t, true, strings.Contains(string(body), "Test file")) 1037 } 1038 1039 func Test_App_Static_Prefix(t *testing.T) { 1040 t.Parallel() 1041 app := New() 1042 app.Static("/john", "./.github") 1043 1044 req := httptest.NewRequest(MethodGet, "/john/index.html", nil) 1045 resp, err := app.Test(req) 1046 utils.AssertEqual(t, nil, err, "app.Test(req)") 1047 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1048 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1049 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 1050 1051 app.Static("/prefix", "./.github/testdata") 1052 1053 req = httptest.NewRequest(MethodGet, "/prefix/index.html", nil) 1054 resp, err = app.Test(req) 1055 utils.AssertEqual(t, nil, err, "app.Test(req)") 1056 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1057 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1058 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 1059 1060 app.Static("/single", "./.github/testdata/testRoutes.json") 1061 1062 req = httptest.NewRequest(MethodGet, "/single", nil) 1063 resp, err = app.Test(req) 1064 utils.AssertEqual(t, nil, err, "app.Test(req)") 1065 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1066 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1067 utils.AssertEqual(t, MIMEApplicationJSON, resp.Header.Get(HeaderContentType)) 1068 } 1069 1070 func Test_App_Static_Trailing_Slash(t *testing.T) { 1071 t.Parallel() 1072 app := New() 1073 app.Static("/john", "./.github") 1074 1075 req := httptest.NewRequest(MethodGet, "/john/", nil) 1076 resp, err := app.Test(req) 1077 utils.AssertEqual(t, nil, err, "app.Test(req)") 1078 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1079 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1080 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 1081 1082 app.Static("/john_without_index", "./.github/testdata/fs/css") 1083 1084 req = httptest.NewRequest(MethodGet, "/john_without_index/", nil) 1085 resp, err = app.Test(req) 1086 utils.AssertEqual(t, nil, err, "app.Test(req)") 1087 utils.AssertEqual(t, 404, resp.StatusCode, "Status code") 1088 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1089 utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) 1090 1091 app.Static("/john/", "./.github") 1092 1093 req = httptest.NewRequest(MethodGet, "/john/", nil) 1094 resp, err = app.Test(req) 1095 utils.AssertEqual(t, nil, err, "app.Test(req)") 1096 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1097 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1098 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 1099 1100 req = httptest.NewRequest(MethodGet, "/john", nil) 1101 resp, err = app.Test(req) 1102 utils.AssertEqual(t, nil, err, "app.Test(req)") 1103 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1104 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1105 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 1106 1107 app.Static("/john_without_index/", "./.github/testdata/fs/css") 1108 1109 req = httptest.NewRequest(MethodGet, "/john_without_index/", nil) 1110 resp, err = app.Test(req) 1111 utils.AssertEqual(t, nil, err, "app.Test(req)") 1112 utils.AssertEqual(t, 404, resp.StatusCode, "Status code") 1113 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1114 utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) 1115 } 1116 1117 func Test_App_Static_Next(t *testing.T) { 1118 t.Parallel() 1119 app := New() 1120 app.Static("/", ".github", Static{ 1121 Next: func(c *Ctx) bool { 1122 // If value of the header is any other from "skip" 1123 // c.Next() will be invoked 1124 return c.Get("X-Custom-Header") == "skip" 1125 }, 1126 }) 1127 app.Get("/", func(c *Ctx) error { 1128 return c.SendString("You've skipped app.Static") 1129 }) 1130 1131 t.Run("app.Static is skipped: invoking Get handler", func(t *testing.T) { 1132 t.Parallel() 1133 req := httptest.NewRequest(MethodGet, "/", nil) 1134 req.Header.Set("X-Custom-Header", "skip") 1135 resp, err := app.Test(req) 1136 utils.AssertEqual(t, nil, err) 1137 utils.AssertEqual(t, 200, resp.StatusCode) 1138 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1139 utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) 1140 1141 body, err := io.ReadAll(resp.Body) 1142 utils.AssertEqual(t, nil, err) 1143 utils.AssertEqual(t, true, strings.Contains(string(body), "You've skipped app.Static")) 1144 }) 1145 1146 t.Run("app.Static is not skipped: serving index.html", func(t *testing.T) { 1147 t.Parallel() 1148 req := httptest.NewRequest(MethodGet, "/", nil) 1149 req.Header.Set("X-Custom-Header", "don't skip") 1150 resp, err := app.Test(req) 1151 utils.AssertEqual(t, nil, err) 1152 utils.AssertEqual(t, 200, resp.StatusCode) 1153 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1154 utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) 1155 1156 body, err := io.ReadAll(resp.Body) 1157 utils.AssertEqual(t, nil, err) 1158 utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) 1159 }) 1160 } 1161 1162 // go test -run Test_App_Mixed_Routes_WithSameLen 1163 func Test_App_Mixed_Routes_WithSameLen(t *testing.T) { 1164 t.Parallel() 1165 app := New() 1166 1167 // middleware 1168 app.Use(func(c *Ctx) error { 1169 c.Set("TestHeader", "TestValue") 1170 return c.Next() 1171 }) 1172 // routes with the same length 1173 app.Static("/tesbar", "./.github") 1174 app.Get("/foobar", func(c *Ctx) error { 1175 c.Type("html") 1176 return c.Send([]byte("FOO_BAR")) 1177 }) 1178 1179 // match get route 1180 req := httptest.NewRequest(MethodGet, "/foobar", nil) 1181 resp, err := app.Test(req) 1182 utils.AssertEqual(t, nil, err, "app.Test(req)") 1183 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1184 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1185 utils.AssertEqual(t, "TestValue", resp.Header.Get("TestHeader")) 1186 utils.AssertEqual(t, "text/html", resp.Header.Get(HeaderContentType)) 1187 1188 body, err := io.ReadAll(resp.Body) 1189 utils.AssertEqual(t, nil, err) 1190 utils.AssertEqual(t, "FOO_BAR", string(body)) 1191 1192 // match static route 1193 req = httptest.NewRequest(MethodGet, "/tesbar", nil) 1194 resp, err = app.Test(req) 1195 utils.AssertEqual(t, nil, err, "app.Test(req)") 1196 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1197 utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") 1198 utils.AssertEqual(t, "TestValue", resp.Header.Get("TestHeader")) 1199 utils.AssertEqual(t, "text/html; charset=utf-8", resp.Header.Get(HeaderContentType)) 1200 1201 body, err = io.ReadAll(resp.Body) 1202 utils.AssertEqual(t, nil, err) 1203 utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!"), "Response: "+string(body)) 1204 utils.AssertEqual(t, true, strings.HasPrefix(string(body), "<!DOCTYPE html>"), "Response: "+string(body)) 1205 } 1206 1207 func Test_App_Group_Invalid(t *testing.T) { 1208 t.Parallel() 1209 defer func() { 1210 if err := recover(); err != nil { 1211 utils.AssertEqual(t, "use: invalid handler int\n", fmt.Sprintf("%v", err)) 1212 } 1213 }() 1214 New().Group("/").Use(1) 1215 } 1216 1217 func Test_App_Group(t *testing.T) { 1218 t.Parallel() 1219 dummyHandler := testEmptyHandler 1220 1221 app := New() 1222 1223 grp := app.Group("/test") 1224 grp.Get("/", dummyHandler) 1225 testStatus200(t, app, "/test", MethodGet) 1226 1227 grp.Get("/:demo?", dummyHandler) 1228 testStatus200(t, app, "/test/john", MethodGet) 1229 1230 grp.Connect("/CONNECT", dummyHandler) 1231 testStatus200(t, app, "/test/CONNECT", MethodConnect) 1232 1233 grp.Put("/PUT", dummyHandler) 1234 testStatus200(t, app, "/test/PUT", MethodPut) 1235 1236 grp.Post("/POST", dummyHandler) 1237 testStatus200(t, app, "/test/POST", MethodPost) 1238 1239 grp.Delete("/DELETE", dummyHandler) 1240 testStatus200(t, app, "/test/DELETE", MethodDelete) 1241 1242 grp.Head("/HEAD", dummyHandler) 1243 testStatus200(t, app, "/test/HEAD", MethodHead) 1244 1245 grp.Patch("/PATCH", dummyHandler) 1246 testStatus200(t, app, "/test/PATCH", MethodPatch) 1247 1248 grp.Options("/OPTIONS", dummyHandler) 1249 testStatus200(t, app, "/test/OPTIONS", MethodOptions) 1250 1251 grp.Trace("/TRACE", dummyHandler) 1252 testStatus200(t, app, "/test/TRACE", MethodTrace) 1253 1254 grp.All("/ALL", dummyHandler) 1255 testStatus200(t, app, "/test/ALL", MethodPost) 1256 1257 grp.Use(dummyHandler) 1258 testStatus200(t, app, "/test/oke", MethodGet) 1259 1260 grp.Use("/USE", dummyHandler) 1261 testStatus200(t, app, "/test/USE/oke", MethodGet) 1262 1263 api := grp.Group("/v1") 1264 api.Post("/", dummyHandler) 1265 1266 resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1/", nil)) 1267 utils.AssertEqual(t, nil, err, "app.Test(req)") 1268 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1269 // utils.AssertEqual(t, "/test/v1", resp.Header.Get("Location"), "Location") 1270 1271 api.Get("/users", dummyHandler) 1272 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/UsErS", nil)) 1273 utils.AssertEqual(t, nil, err, "app.Test(req)") 1274 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1275 // utils.AssertEqual(t, "/test/v1/users", resp.Header.Get("Location"), "Location") 1276 } 1277 1278 func Test_App_Route(t *testing.T) { 1279 t.Parallel() 1280 dummyHandler := testEmptyHandler 1281 1282 app := New() 1283 1284 grp := app.Route("/test", func(grp Router) { 1285 grp.Get("/", dummyHandler) 1286 grp.Get("/:demo?", dummyHandler) 1287 grp.Connect("/CONNECT", dummyHandler) 1288 grp.Put("/PUT", dummyHandler) 1289 grp.Post("/POST", dummyHandler) 1290 grp.Delete("/DELETE", dummyHandler) 1291 grp.Head("/HEAD", dummyHandler) 1292 grp.Patch("/PATCH", dummyHandler) 1293 grp.Options("/OPTIONS", dummyHandler) 1294 grp.Trace("/TRACE", dummyHandler) 1295 grp.All("/ALL", dummyHandler) 1296 grp.Use(dummyHandler) 1297 grp.Use("/USE", dummyHandler) 1298 }) 1299 1300 testStatus200(t, app, "/test", MethodGet) 1301 testStatus200(t, app, "/test/john", MethodGet) 1302 testStatus200(t, app, "/test/CONNECT", MethodConnect) 1303 testStatus200(t, app, "/test/PUT", MethodPut) 1304 testStatus200(t, app, "/test/POST", MethodPost) 1305 testStatus200(t, app, "/test/DELETE", MethodDelete) 1306 testStatus200(t, app, "/test/HEAD", MethodHead) 1307 testStatus200(t, app, "/test/PATCH", MethodPatch) 1308 testStatus200(t, app, "/test/OPTIONS", MethodOptions) 1309 testStatus200(t, app, "/test/TRACE", MethodTrace) 1310 testStatus200(t, app, "/test/ALL", MethodPost) 1311 testStatus200(t, app, "/test/oke", MethodGet) 1312 testStatus200(t, app, "/test/USE/oke", MethodGet) 1313 1314 grp.Route("/v1", func(grp Router) { 1315 grp.Post("/", dummyHandler) 1316 grp.Get("/users", dummyHandler) 1317 }) 1318 1319 resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1/", nil)) 1320 utils.AssertEqual(t, nil, err, "app.Test(req)") 1321 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1322 1323 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/UsErS", nil)) 1324 utils.AssertEqual(t, nil, err, "app.Test(req)") 1325 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1326 } 1327 1328 func Test_App_Deep_Group(t *testing.T) { 1329 t.Parallel() 1330 runThroughCount := 0 1331 dummyHandler := func(c *Ctx) error { 1332 runThroughCount++ 1333 return c.Next() 1334 } 1335 1336 app := New() 1337 gAPI := app.Group("/api", dummyHandler) 1338 gV1 := gAPI.Group("/v1", dummyHandler) 1339 gUser := gV1.Group("/user", dummyHandler) 1340 gUser.Get("/authenticate", func(c *Ctx) error { 1341 runThroughCount++ 1342 return c.SendStatus(200) 1343 }) 1344 testStatus200(t, app, "/api/v1/user/authenticate", MethodGet) 1345 utils.AssertEqual(t, 4, runThroughCount, "Loop count") 1346 } 1347 1348 // go test -run Test_App_Next_Method 1349 func Test_App_Next_Method(t *testing.T) { 1350 t.Parallel() 1351 app := New() 1352 app.config.DisableStartupMessage = true 1353 1354 app.Use(func(c *Ctx) error { 1355 utils.AssertEqual(t, MethodGet, c.Method()) 1356 err := c.Next() 1357 utils.AssertEqual(t, MethodGet, c.Method()) 1358 return err 1359 }) 1360 1361 resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) 1362 utils.AssertEqual(t, nil, err, "app.Test(req)") 1363 utils.AssertEqual(t, 404, resp.StatusCode, "Status code") 1364 } 1365 1366 // go test -v -run=^$ -bench=Benchmark_AcquireCtx -benchmem -count=4 1367 func Benchmark_AcquireCtx(b *testing.B) { 1368 app := New() 1369 for n := 0; n < b.N; n++ { 1370 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1371 app.ReleaseCtx(c) 1372 } 1373 } 1374 1375 // go test -v -run=^$ -bench=Benchmark_App_ETag -benchmem -count=4 1376 func Benchmark_App_ETag(b *testing.B) { 1377 app := New() 1378 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1379 defer app.ReleaseCtx(c) 1380 err := c.Send([]byte("Hello, World!")) 1381 utils.AssertEqual(b, nil, err) 1382 for n := 0; n < b.N; n++ { 1383 setETag(c, false) 1384 } 1385 utils.AssertEqual(b, `"13-1831710635"`, string(c.Response().Header.Peek(HeaderETag))) 1386 } 1387 1388 // go test -v -run=^$ -bench=Benchmark_App_ETag_Weak -benchmem -count=4 1389 func Benchmark_App_ETag_Weak(b *testing.B) { 1390 app := New() 1391 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1392 defer app.ReleaseCtx(c) 1393 utils.AssertEqual(b, nil, c.Send([]byte("Hello, World!"))) 1394 for n := 0; n < b.N; n++ { 1395 setETag(c, true) 1396 } 1397 utils.AssertEqual(b, `W/"13-1831710635"`, string(c.Response().Header.Peek(HeaderETag))) 1398 } 1399 1400 // go test -run Test_NewError 1401 func Test_NewError(t *testing.T) { 1402 t.Parallel() 1403 err := NewError(StatusForbidden, "permission denied") 1404 utils.AssertEqual(t, StatusForbidden, err.Code) 1405 utils.AssertEqual(t, "permission denied", err.Message) 1406 } 1407 1408 // go test -run Test_Test_Timeout 1409 func Test_Test_Timeout(t *testing.T) { 1410 t.Parallel() 1411 app := New() 1412 app.config.DisableStartupMessage = true 1413 1414 app.Get("/", testEmptyHandler) 1415 1416 resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil), -1) 1417 utils.AssertEqual(t, nil, err, "app.Test(req)") 1418 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 1419 1420 app.Get("timeout", func(c *Ctx) error { 1421 time.Sleep(200 * time.Millisecond) 1422 return nil 1423 }) 1424 1425 _, err = app.Test(httptest.NewRequest(MethodGet, "/timeout", nil), 20) 1426 utils.AssertEqual(t, true, err != nil, "app.Test(req)") 1427 } 1428 1429 type errorReader int 1430 1431 func (errorReader) Read([]byte) (int, error) { 1432 return 0, errors.New("errorReader") 1433 } 1434 1435 // go test -run Test_Test_DumpError 1436 func Test_Test_DumpError(t *testing.T) { 1437 t.Parallel() 1438 app := New() 1439 app.config.DisableStartupMessage = true 1440 1441 app.Get("/", testEmptyHandler) 1442 1443 resp, err := app.Test(httptest.NewRequest(MethodGet, "/", errorReader(0))) 1444 utils.AssertEqual(t, true, resp == nil) 1445 utils.AssertEqual(t, "failed to dump request: errorReader", err.Error()) 1446 } 1447 1448 // go test -run Test_App_Handler 1449 func Test_App_Handler(t *testing.T) { 1450 t.Parallel() 1451 h := New().Handler() 1452 utils.AssertEqual(t, "fasthttp.RequestHandler", reflect.TypeOf(h).String()) 1453 } 1454 1455 type invalidView struct{} 1456 1457 func (invalidView) Load() error { return errors.New("invalid view") } 1458 1459 func (invalidView) Render(io.Writer, string, interface{}, ...string) error { panic("implement me") } 1460 1461 // go test -run Test_App_Init_Error_View 1462 func Test_App_Init_Error_View(t *testing.T) { 1463 t.Parallel() 1464 app := New(Config{Views: invalidView{}}) 1465 1466 defer func() { 1467 if err := recover(); err != nil { 1468 utils.AssertEqual(t, "implement me", fmt.Sprintf("%v", err)) 1469 } 1470 }() 1471 1472 err := app.config.Views.Render(nil, "", nil) 1473 utils.AssertEqual(t, nil, err) 1474 } 1475 1476 // go test -run Test_App_Stack 1477 func Test_App_Stack(t *testing.T) { 1478 t.Parallel() 1479 app := New() 1480 1481 app.Use("/path0", testEmptyHandler) 1482 app.Get("/path1", testEmptyHandler) 1483 app.Get("/path2", testEmptyHandler) 1484 app.Post("/path3", testEmptyHandler) 1485 1486 stack := app.Stack() 1487 methodList := app.config.RequestMethods 1488 utils.AssertEqual(t, len(methodList), len(stack)) 1489 utils.AssertEqual(t, 3, len(stack[app.methodInt(MethodGet)])) 1490 utils.AssertEqual(t, 3, len(stack[app.methodInt(MethodHead)])) 1491 utils.AssertEqual(t, 2, len(stack[app.methodInt(MethodPost)])) 1492 utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodPut)])) 1493 utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodPatch)])) 1494 utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodDelete)])) 1495 utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodConnect)])) 1496 utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodOptions)])) 1497 utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodTrace)])) 1498 } 1499 1500 // go test -run Test_App_HandlersCount 1501 func Test_App_HandlersCount(t *testing.T) { 1502 t.Parallel() 1503 app := New() 1504 1505 app.Use("/path0", testEmptyHandler) 1506 app.Get("/path2", testEmptyHandler) 1507 app.Post("/path3", testEmptyHandler) 1508 1509 count := app.HandlersCount() 1510 utils.AssertEqual(t, uint32(4), count) 1511 } 1512 1513 // go test -run Test_App_ReadTimeout 1514 func Test_App_ReadTimeout(t *testing.T) { 1515 t.Parallel() 1516 app := New(Config{ 1517 ReadTimeout: time.Nanosecond, 1518 IdleTimeout: time.Minute, 1519 DisableStartupMessage: true, 1520 DisableKeepalive: true, 1521 }) 1522 1523 app.Get("/read-timeout", func(c *Ctx) error { 1524 return c.SendString("I should not be sent") 1525 }) 1526 1527 go func() { 1528 time.Sleep(500 * time.Millisecond) 1529 1530 conn, err := net.Dial(NetworkTCP4, "127.0.0.1:4004") 1531 utils.AssertEqual(t, nil, err) 1532 defer func(conn net.Conn) { 1533 err := conn.Close() 1534 utils.AssertEqual(t, nil, err) 1535 }(conn) 1536 1537 _, err = conn.Write([]byte("HEAD /read-timeout HTTP/1.1\r\n")) 1538 utils.AssertEqual(t, nil, err) 1539 1540 buf := make([]byte, 1024) 1541 var n int 1542 n, err = conn.Read(buf) 1543 1544 utils.AssertEqual(t, nil, err) 1545 utils.AssertEqual(t, true, bytes.Contains(buf[:n], []byte("408 Request Timeout"))) 1546 1547 utils.AssertEqual(t, nil, app.Shutdown()) 1548 }() 1549 1550 utils.AssertEqual(t, nil, app.Listen(":4004")) 1551 } 1552 1553 // go test -run Test_App_BadRequest 1554 func Test_App_BadRequest(t *testing.T) { 1555 t.Parallel() 1556 app := New(Config{ 1557 DisableStartupMessage: true, 1558 }) 1559 1560 app.Get("/bad-request", func(c *Ctx) error { 1561 return c.SendString("I should not be sent") 1562 }) 1563 1564 go func() { 1565 time.Sleep(500 * time.Millisecond) 1566 conn, err := net.Dial(NetworkTCP4, "127.0.0.1:4005") 1567 utils.AssertEqual(t, nil, err) 1568 defer func(conn net.Conn) { 1569 err := conn.Close() 1570 utils.AssertEqual(t, nil, err) 1571 }(conn) 1572 1573 _, err = conn.Write([]byte("BadRequest\r\n")) 1574 utils.AssertEqual(t, nil, err) 1575 1576 buf := make([]byte, 1024) 1577 var n int 1578 n, err = conn.Read(buf) 1579 utils.AssertEqual(t, nil, err) 1580 1581 utils.AssertEqual(t, true, bytes.Contains(buf[:n], []byte("400 Bad Request"))) 1582 1583 utils.AssertEqual(t, nil, app.Shutdown()) 1584 }() 1585 1586 utils.AssertEqual(t, nil, app.Listen(":4005")) 1587 } 1588 1589 // go test -run Test_App_SmallReadBuffer 1590 func Test_App_SmallReadBuffer(t *testing.T) { 1591 t.Parallel() 1592 app := New(Config{ 1593 ReadBufferSize: 1, 1594 DisableStartupMessage: true, 1595 }) 1596 1597 app.Get("/small-read-buffer", func(c *Ctx) error { 1598 return c.SendString("I should not be sent") 1599 }) 1600 1601 go func() { 1602 time.Sleep(500 * time.Millisecond) 1603 req, err := http.NewRequestWithContext(context.Background(), MethodGet, "http://127.0.0.1:4006/small-read-buffer", http.NoBody) 1604 utils.AssertEqual(t, nil, err) 1605 var client http.Client 1606 resp, err := client.Do(req) 1607 utils.AssertEqual(t, nil, err) 1608 utils.AssertEqual(t, 431, resp.StatusCode) 1609 utils.AssertEqual(t, nil, app.Shutdown()) 1610 }() 1611 1612 utils.AssertEqual(t, nil, app.Listen(":4006")) 1613 } 1614 1615 func Test_App_Server(t *testing.T) { 1616 t.Parallel() 1617 app := New() 1618 1619 utils.AssertEqual(t, false, app.Server() == nil) 1620 } 1621 1622 func Test_App_Error_In_Fasthttp_Server(t *testing.T) { 1623 t.Parallel() 1624 app := New() 1625 app.config.ErrorHandler = func(ctx *Ctx, err error) error { 1626 return errors.New("fake error") 1627 } 1628 app.server.GetOnly = true 1629 1630 resp, err := app.Test(httptest.NewRequest(MethodPost, "/", nil)) 1631 utils.AssertEqual(t, nil, err) 1632 utils.AssertEqual(t, 500, resp.StatusCode) 1633 } 1634 1635 // go test -race -run Test_App_New_Test_Parallel 1636 func Test_App_New_Test_Parallel(t *testing.T) { 1637 t.Parallel() 1638 t.Run("Test_App_New_Test_Parallel_1", func(t *testing.T) { 1639 t.Parallel() 1640 app := New(Config{Immutable: true}) 1641 _, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) 1642 utils.AssertEqual(t, nil, err) 1643 }) 1644 t.Run("Test_App_New_Test_Parallel_2", func(t *testing.T) { 1645 t.Parallel() 1646 app := New(Config{Immutable: true}) 1647 _, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) 1648 utils.AssertEqual(t, nil, err) 1649 }) 1650 } 1651 1652 func Test_App_ReadBodyStream(t *testing.T) { 1653 t.Parallel() 1654 app := New(Config{StreamRequestBody: true}) 1655 app.Post("/", func(c *Ctx) error { 1656 // Calling c.Body() automatically reads the entire stream. 1657 return c.SendString(fmt.Sprintf("%v %s", c.Request().IsBodyStream(), c.Body())) 1658 }) 1659 testString := "this is a test" 1660 resp, err := app.Test(httptest.NewRequest(MethodPost, "/", bytes.NewBufferString(testString))) 1661 utils.AssertEqual(t, nil, err, "app.Test(req)") 1662 body, err := io.ReadAll(resp.Body) 1663 utils.AssertEqual(t, nil, err, "io.ReadAll(resp.Body)") 1664 utils.AssertEqual(t, fmt.Sprintf("true %s", testString), string(body)) 1665 } 1666 1667 func Test_App_DisablePreParseMultipartForm(t *testing.T) { 1668 t.Parallel() 1669 // Must be used with both otherwise there is no point. 1670 testString := "this is a test" 1671 1672 app := New(Config{DisablePreParseMultipartForm: true, StreamRequestBody: true}) 1673 app.Post("/", func(c *Ctx) error { 1674 req := c.Request() 1675 mpf, err := req.MultipartForm() 1676 if err != nil { 1677 return err 1678 } 1679 if !req.IsBodyStream() { 1680 return fmt.Errorf("not a body stream") 1681 } 1682 file, err := mpf.File["test"][0].Open() 1683 if err != nil { 1684 return fmt.Errorf("failed to open: %w", err) 1685 } 1686 buffer := make([]byte, len(testString)) 1687 n, err := file.Read(buffer) 1688 if err != nil { 1689 return fmt.Errorf("failed to read: %w", err) 1690 } 1691 if n != len(testString) { 1692 return fmt.Errorf("bad read length") 1693 } 1694 return c.Send(buffer) 1695 }) 1696 b := &bytes.Buffer{} 1697 w := multipart.NewWriter(b) 1698 writer, err := w.CreateFormFile("test", "test") 1699 utils.AssertEqual(t, nil, err, "w.CreateFormFile") 1700 n, err := writer.Write([]byte(testString)) 1701 utils.AssertEqual(t, nil, err, "writer.Write") 1702 utils.AssertEqual(t, len(testString), n, "writer n") 1703 utils.AssertEqual(t, nil, w.Close(), "w.Close()") 1704 1705 req := httptest.NewRequest(MethodPost, "/", b) 1706 req.Header.Set("Content-Type", w.FormDataContentType()) 1707 resp, err := app.Test(req) 1708 utils.AssertEqual(t, nil, err, "app.Test(req)") 1709 body, err := io.ReadAll(resp.Body) 1710 utils.AssertEqual(t, nil, err, "io.ReadAll(resp.Body)") 1711 1712 utils.AssertEqual(t, testString, string(body)) 1713 } 1714 1715 func Test_App_Test_no_timeout_infinitely(t *testing.T) { 1716 t.Parallel() 1717 var err error 1718 c := make(chan int) 1719 1720 go func() { 1721 defer func() { c <- 0 }() 1722 app := New() 1723 app.Get("/", func(c *Ctx) error { 1724 runtime.Goexit() 1725 return nil 1726 }) 1727 1728 req := httptest.NewRequest(MethodGet, "/", http.NoBody) 1729 _, err = app.Test(req, -1) 1730 }() 1731 1732 tk := time.NewTimer(5 * time.Second) 1733 defer tk.Stop() 1734 1735 select { 1736 case <-tk.C: 1737 t.Error("hanging test") 1738 t.FailNow() 1739 case <-c: 1740 } 1741 1742 if err == nil { 1743 t.Error("unexpected success request") 1744 t.FailNow() 1745 } 1746 } 1747 1748 func Test_App_SetTLSHandler(t *testing.T) { 1749 t.Parallel() 1750 tlsHandler := &TLSHandler{clientHelloInfo: &tls.ClientHelloInfo{ 1751 ServerName: "example.golang", 1752 }} 1753 1754 app := New() 1755 app.SetTLSHandler(tlsHandler) 1756 1757 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1758 defer app.ReleaseCtx(c) 1759 1760 utils.AssertEqual(t, "example.golang", c.ClientHelloInfo().ServerName) 1761 } 1762 1763 func Test_App_AddCustomRequestMethod(t *testing.T) { 1764 t.Parallel() 1765 methods := append(DefaultMethods, "TEST") //nolint:gocritic // We want a new slice here 1766 app := New(Config{ 1767 RequestMethods: methods, 1768 }) 1769 appMethods := app.config.RequestMethods 1770 1771 // method name is always uppercase - https://datatracker.ietf.org/doc/html/rfc7231#section-4.1 1772 utils.AssertEqual(t, len(app.stack), len(appMethods)) 1773 utils.AssertEqual(t, len(app.stack), len(appMethods)) 1774 utils.AssertEqual(t, "TEST", appMethods[len(appMethods)-1]) 1775 } 1776 1777 func TestApp_GetRoutes(t *testing.T) { 1778 t.Parallel() 1779 app := New() 1780 app.Use(func(c *Ctx) error { 1781 return c.Next() 1782 }) 1783 handler := func(c *Ctx) error { 1784 return c.SendStatus(StatusOK) 1785 } 1786 app.Delete("/delete", handler).Name("delete") 1787 app.Post("/post", handler).Name("post") 1788 routes := app.GetRoutes(false) 1789 utils.AssertEqual(t, 2+len(app.config.RequestMethods), len(routes)) 1790 methodMap := map[string]string{"/delete": "delete", "/post": "post"} 1791 for _, route := range routes { 1792 name, ok := methodMap[route.Path] 1793 if ok { 1794 utils.AssertEqual(t, name, route.Name) 1795 } 1796 } 1797 1798 routes = app.GetRoutes(true) 1799 utils.AssertEqual(t, 2, len(routes)) 1800 for _, route := range routes { 1801 name, ok := methodMap[route.Path] 1802 utils.AssertEqual(t, true, ok) 1803 utils.AssertEqual(t, name, route.Name) 1804 } 1805 } 1806 1807 func Test_Middleware_Route_Naming_With_Use(t *testing.T) { 1808 named := "named" 1809 app := New() 1810 1811 app.Get("/unnamed", func(c *Ctx) error { 1812 return c.Next() 1813 }) 1814 1815 app.Post("/named", func(c *Ctx) error { 1816 return c.Next() 1817 }).Name(named) 1818 1819 app.Use(func(c *Ctx) error { 1820 return c.Next() 1821 }) // no name - logging MW 1822 1823 app.Use(func(c *Ctx) error { 1824 return c.Next() 1825 }).Name("corsMW") 1826 1827 app.Use(func(c *Ctx) error { 1828 return c.Next() 1829 }).Name("compressMW") 1830 1831 app.Use(func(c *Ctx) error { 1832 return c.Next() 1833 }) // no name - cache MW 1834 1835 grp := app.Group("/pages").Name("pages.") 1836 grp.Use(func(c *Ctx) error { 1837 return c.Next() 1838 }).Name("csrfMW") 1839 1840 grp.Get("/home", func(c *Ctx) error { 1841 return c.Next() 1842 }).Name("home") 1843 1844 grp.Get("/unnamed", func(c *Ctx) error { 1845 return c.Next() 1846 }) 1847 1848 for _, route := range app.GetRoutes() { 1849 switch route.Path { 1850 case "/": 1851 utils.AssertEqual(t, "compressMW", route.Name) 1852 case "/unnamed": 1853 utils.AssertEqual(t, "", route.Name) 1854 case "named": 1855 utils.AssertEqual(t, named, route.Name) 1856 case "/pages": 1857 utils.AssertEqual(t, "pages.csrfMW", route.Name) 1858 case "/pages/home": 1859 utils.AssertEqual(t, "pages.home", route.Name) 1860 case "/pages/unnamed": 1861 utils.AssertEqual(t, "", route.Name) 1862 } 1863 } 1864 }