github.com/gofiber/fiber/v2@v2.47.0/mount_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 "errors" 10 "io" 11 "net/http" 12 "net/http/httptest" 13 "testing" 14 15 "github.com/gofiber/fiber/v2/internal/template/html" 16 "github.com/gofiber/fiber/v2/utils" 17 ) 18 19 // go test -run Test_App_Mount 20 func Test_App_Mount(t *testing.T) { 21 t.Parallel() 22 micro := New() 23 micro.Get("/doe", func(c *Ctx) error { 24 return c.SendStatus(StatusOK) 25 }) 26 27 app := New() 28 app.Mount("/john", micro) 29 resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", http.NoBody)) 30 utils.AssertEqual(t, nil, err, "app.Test(req)") 31 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 32 utils.AssertEqual(t, uint32(2), app.handlersCount) 33 } 34 35 func Test_App_Mount_RootPath_Nested(t *testing.T) { 36 t.Parallel() 37 app := New() 38 dynamic := New() 39 apiserver := New() 40 41 apiroutes := apiserver.Group("/v1") 42 apiroutes.Get("/home", func(c *Ctx) error { 43 return c.SendString("home") 44 }) 45 46 dynamic.Mount("/api", apiserver) 47 app.Mount("/", dynamic) 48 49 resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/v1/home", http.NoBody)) 50 utils.AssertEqual(t, nil, err, "app.Test(req)") 51 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 52 utils.AssertEqual(t, uint32(2), app.handlersCount) 53 } 54 55 // go test -run Test_App_Mount_Nested 56 func Test_App_Mount_Nested(t *testing.T) { 57 t.Parallel() 58 app := New() 59 one := New() 60 two := New() 61 three := New() 62 63 two.Mount("/three", three) 64 app.Mount("/one", one) 65 one.Mount("/two", two) 66 67 one.Get("/doe", func(c *Ctx) error { 68 return c.SendStatus(StatusOK) 69 }) 70 71 two.Get("/nested", func(c *Ctx) error { 72 return c.SendStatus(StatusOK) 73 }) 74 75 three.Get("/test", func(c *Ctx) error { 76 return c.SendStatus(StatusOK) 77 }) 78 79 resp, err := app.Test(httptest.NewRequest(MethodGet, "/one/doe", http.NoBody)) 80 utils.AssertEqual(t, nil, err, "app.Test(req)") 81 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 82 83 resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/nested", http.NoBody)) 84 utils.AssertEqual(t, nil, err, "app.Test(req)") 85 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 86 87 resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/three/test", http.NoBody)) 88 utils.AssertEqual(t, nil, err, "app.Test(req)") 89 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 90 91 utils.AssertEqual(t, uint32(6), app.handlersCount) 92 utils.AssertEqual(t, uint32(6), app.routesCount) 93 } 94 95 // go test -run Test_App_Mount_Express_Behavior 96 func Test_App_Mount_Express_Behavior(t *testing.T) { 97 t.Parallel() 98 createTestHandler := func(body string) func(c *Ctx) error { 99 return func(c *Ctx) error { 100 return c.SendString(body) 101 } 102 } 103 testEndpoint := func(app *App, route, expectedBody string, expectedStatusCode int) { 104 resp, err := app.Test(httptest.NewRequest(MethodGet, route, http.NoBody)) 105 utils.AssertEqual(t, nil, err, "app.Test(req)") 106 body, err := io.ReadAll(resp.Body) 107 utils.AssertEqual(t, nil, err) 108 utils.AssertEqual(t, expectedStatusCode, resp.StatusCode, "Status code") 109 utils.AssertEqual(t, expectedBody, string(body), "Unexpected response body") 110 } 111 112 app := New() 113 subApp := New() 114 // app setup 115 { 116 subApp.Get("/hello", createTestHandler("subapp hello!")) 117 subApp.Get("/world", createTestHandler("subapp world!")) // <- wins 118 119 app.Get("/hello", createTestHandler("app hello!")) // <- wins 120 app.Mount("/", subApp) // <- subApp registration 121 app.Get("/world", createTestHandler("app world!")) 122 123 app.Get("/bar", createTestHandler("app bar!")) 124 subApp.Get("/bar", createTestHandler("subapp bar!")) // <- wins 125 126 subApp.Get("/foo", createTestHandler("subapp foo!")) // <- wins 127 app.Get("/foo", createTestHandler("app foo!")) 128 129 // 404 Handler 130 app.Use(func(c *Ctx) error { 131 return c.SendStatus(StatusNotFound) 132 }) 133 } 134 // expectation check 135 testEndpoint(app, "/world", "subapp world!", StatusOK) 136 testEndpoint(app, "/hello", "app hello!", StatusOK) 137 testEndpoint(app, "/bar", "subapp bar!", StatusOK) 138 testEndpoint(app, "/foo", "subapp foo!", StatusOK) 139 testEndpoint(app, "/unknown", ErrNotFound.Message, StatusNotFound) 140 141 utils.AssertEqual(t, uint32(17), app.handlersCount) 142 utils.AssertEqual(t, uint32(16+9), app.routesCount) 143 } 144 145 // go test -run Test_App_Mount_RoutePositions 146 func Test_App_Mount_RoutePositions(t *testing.T) { 147 t.Parallel() 148 testEndpoint := func(app *App, route, expectedBody string) { 149 resp, err := app.Test(httptest.NewRequest(MethodGet, route, http.NoBody)) 150 utils.AssertEqual(t, nil, err, "app.Test(req)") 151 body, err := io.ReadAll(resp.Body) 152 utils.AssertEqual(t, nil, err) 153 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 154 utils.AssertEqual(t, expectedBody, string(body), "Unexpected response body") 155 } 156 157 app := New() 158 subApp1 := New() 159 subApp2 := New() 160 // app setup 161 { 162 app.Use(func(c *Ctx) error { 163 // set initial value 164 c.Locals("world", "world") 165 return c.Next() 166 }) 167 app.Mount("/subApp1", subApp1) 168 app.Use(func(c *Ctx) error { 169 return c.Next() 170 }) 171 app.Get("/bar", func(c *Ctx) error { 172 return c.SendString("ok") 173 }) 174 app.Use(func(c *Ctx) error { 175 // is overwritten in case the positioning is not correct 176 c.Locals("world", "hello") 177 return c.Next() 178 }) 179 methods := subApp2.Group("/subApp2") 180 methods.Get("/world", func(c *Ctx) error { 181 v, ok := c.Locals("world").(string) 182 if !ok { 183 panic("unexpected data type") 184 } 185 return c.SendString(v) 186 }) 187 app.Mount("", subApp2) 188 } 189 190 testEndpoint(app, "/subApp2/world", "hello") 191 192 routeStackGET := app.Stack()[0] 193 utils.AssertEqual(t, true, routeStackGET[0].use) 194 utils.AssertEqual(t, "/", routeStackGET[0].path) 195 196 utils.AssertEqual(t, true, routeStackGET[1].use) 197 utils.AssertEqual(t, "/", routeStackGET[1].path) 198 utils.AssertEqual(t, true, routeStackGET[0].pos < routeStackGET[1].pos, "wrong position of route 0") 199 200 utils.AssertEqual(t, false, routeStackGET[2].use) 201 utils.AssertEqual(t, "/bar", routeStackGET[2].path) 202 utils.AssertEqual(t, true, routeStackGET[1].pos < routeStackGET[2].pos, "wrong position of route 1") 203 204 utils.AssertEqual(t, true, routeStackGET[3].use) 205 utils.AssertEqual(t, "/", routeStackGET[3].path) 206 utils.AssertEqual(t, true, routeStackGET[2].pos < routeStackGET[3].pos, "wrong position of route 2") 207 208 utils.AssertEqual(t, false, routeStackGET[4].use) 209 utils.AssertEqual(t, "/subapp2/world", routeStackGET[4].path) 210 utils.AssertEqual(t, true, routeStackGET[3].pos < routeStackGET[4].pos, "wrong position of route 3") 211 212 utils.AssertEqual(t, 5, len(routeStackGET)) 213 } 214 215 // go test -run Test_App_MountPath 216 func Test_App_MountPath(t *testing.T) { 217 t.Parallel() 218 app := New() 219 one := New() 220 two := New() 221 three := New() 222 223 two.Mount("/three", three) 224 one.Mount("/two", two) 225 app.Mount("/one", one) 226 227 utils.AssertEqual(t, "/one", one.MountPath()) 228 utils.AssertEqual(t, "/one/two", two.MountPath()) 229 utils.AssertEqual(t, "/one/two/three", three.MountPath()) 230 utils.AssertEqual(t, "", app.MountPath()) 231 } 232 233 func Test_App_ErrorHandler_GroupMount(t *testing.T) { 234 t.Parallel() 235 micro := New(Config{ 236 ErrorHandler: func(c *Ctx, err error) error { 237 utils.AssertEqual(t, "0: GET error", err.Error()) 238 return c.Status(500).SendString("1: custom error") 239 }, 240 }) 241 micro.Get("/doe", func(c *Ctx) error { 242 return errors.New("0: GET error") 243 }) 244 245 app := New() 246 v1 := app.Group("/v1") 247 v1.Mount("/john", micro) 248 249 resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) 250 testErrorResponse(t, err, resp, "1: custom error") 251 } 252 253 func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) { 254 t.Parallel() 255 micro := New(Config{ 256 ErrorHandler: func(c *Ctx, err error) error { 257 utils.AssertEqual(t, "0: GET error", err.Error()) 258 return c.Status(500).SendString("1: custom error") 259 }, 260 }) 261 micro.Get("/john/doe", func(c *Ctx) error { 262 return errors.New("0: GET error") 263 }) 264 265 app := New() 266 v1 := app.Group("/v1") 267 v1.Mount("/", micro) 268 269 resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) 270 testErrorResponse(t, err, resp, "1: custom error") 271 } 272 273 // go test -run Test_App_Group_Mount 274 func Test_App_Group_Mount(t *testing.T) { 275 t.Parallel() 276 micro := New() 277 micro.Get("/doe", func(c *Ctx) error { 278 return c.SendStatus(StatusOK) 279 }) 280 281 app := New() 282 v1 := app.Group("/v1") 283 v1.Mount("/john", micro) 284 285 resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) 286 utils.AssertEqual(t, nil, err, "app.Test(req)") 287 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 288 utils.AssertEqual(t, uint32(2), app.handlersCount) 289 } 290 291 func Test_App_UseParentErrorHandler(t *testing.T) { 292 t.Parallel() 293 app := New(Config{ 294 ErrorHandler: func(ctx *Ctx, err error) error { 295 return ctx.Status(500).SendString("hi, i'm a custom error") 296 }, 297 }) 298 299 fiber := New() 300 fiber.Get("/", func(c *Ctx) error { 301 return errors.New("something happened") 302 }) 303 304 app.Mount("/api", fiber) 305 306 resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", http.NoBody)) 307 testErrorResponse(t, err, resp, "hi, i'm a custom error") 308 } 309 310 func Test_App_UseMountedErrorHandler(t *testing.T) { 311 t.Parallel() 312 app := New() 313 314 fiber := New(Config{ 315 ErrorHandler: func(ctx *Ctx, err error) error { 316 return ctx.Status(500).SendString("hi, i'm a custom error") 317 }, 318 }) 319 fiber.Get("/", func(c *Ctx) error { 320 return errors.New("something happened") 321 }) 322 323 app.Mount("/api", fiber) 324 325 resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", http.NoBody)) 326 testErrorResponse(t, err, resp, "hi, i'm a custom error") 327 } 328 329 func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) { 330 t.Parallel() 331 app := New() 332 333 fiber := New(Config{ 334 ErrorHandler: func(ctx *Ctx, err error) error { 335 return ctx.Status(500).SendString("hi, i'm a custom error") 336 }, 337 }) 338 fiber.Get("/api", func(c *Ctx) error { 339 return errors.New("something happened") 340 }) 341 342 app.Mount("/", fiber) 343 344 resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", http.NoBody)) 345 testErrorResponse(t, err, resp, "hi, i'm a custom error") 346 } 347 348 func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { 349 t.Parallel() 350 app := New() 351 352 tsf := func(ctx *Ctx, err error) error { 353 return ctx.Status(200).SendString("hi, i'm a custom sub sub fiber error") 354 } 355 tripleSubFiber := New(Config{ 356 ErrorHandler: tsf, 357 }) 358 tripleSubFiber.Get("/", func(c *Ctx) error { 359 return errors.New("something happened") 360 }) 361 362 sf := func(ctx *Ctx, err error) error { 363 return ctx.Status(200).SendString("hi, i'm a custom sub fiber error") 364 } 365 subfiber := New(Config{ 366 ErrorHandler: sf, 367 }) 368 subfiber.Get("/", func(c *Ctx) error { 369 return errors.New("something happened") 370 }) 371 subfiber.Mount("/third", tripleSubFiber) 372 373 f := func(ctx *Ctx, err error) error { 374 return ctx.Status(200).SendString("hi, i'm a custom error") 375 } 376 fiber := New(Config{ 377 ErrorHandler: f, 378 }) 379 fiber.Get("/", func(c *Ctx) error { 380 return errors.New("something happened") 381 }) 382 fiber.Mount("/sub", subfiber) 383 384 app.Mount("/api", fiber) 385 386 resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", http.NoBody)) 387 utils.AssertEqual(t, nil, err, "/api/sub req") 388 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 389 390 b, err := io.ReadAll(resp.Body) 391 utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") 392 utils.AssertEqual(t, "hi, i'm a custom sub fiber error", string(b), "Response body") 393 394 resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", http.NoBody)) 395 utils.AssertEqual(t, nil, err, "/api/sub/third req") 396 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 397 398 b, err = io.ReadAll(resp2.Body) 399 utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") 400 utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") 401 } 402 403 // go test -run Test_Ctx_Render_Mount 404 func Test_Ctx_Render_Mount(t *testing.T) { 405 t.Parallel() 406 407 sub := New(Config{ 408 Views: html.New("./.github/testdata/template", ".gohtml"), 409 }) 410 411 sub.Get("/:name", func(ctx *Ctx) error { 412 return ctx.Render("hello_world", Map{ 413 "Name": ctx.Params("name"), 414 }) 415 }) 416 417 app := New() 418 app.Mount("/hello", sub) 419 420 resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", http.NoBody)) 421 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 422 utils.AssertEqual(t, nil, err, "app.Test(req)") 423 424 body, err := io.ReadAll(resp.Body) 425 utils.AssertEqual(t, nil, err) 426 utils.AssertEqual(t, "<h1>Hello a!</h1>", string(body)) 427 } 428 429 // go test -run Test_Ctx_Render_Mount_ParentOrSubHasViews 430 func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { 431 t.Parallel() 432 433 engine := &testTemplateEngine{} 434 err := engine.Load() 435 utils.AssertEqual(t, nil, err) 436 437 engine2 := &testTemplateEngine{path: "testdata2"} 438 err = engine2.Load() 439 utils.AssertEqual(t, nil, err) 440 441 sub := New(Config{ 442 Views: html.New("./.github/testdata/template", ".gohtml"), 443 }) 444 445 sub2 := New(Config{ 446 Views: engine2, 447 }) 448 449 app := New(Config{ 450 Views: engine, 451 }) 452 453 app.Get("/test", func(c *Ctx) error { 454 return c.Render("index.tmpl", Map{ 455 "Title": "Hello, World!", 456 }) 457 }) 458 459 sub.Get("/world/:name", func(c *Ctx) error { 460 return c.Render("hello_world", Map{ 461 "Name": c.Params("name"), 462 }) 463 }) 464 465 sub2.Get("/moment", func(c *Ctx) error { 466 return c.Render("bruh.tmpl", Map{}) 467 }) 468 469 sub.Mount("/bruh", sub2) 470 app.Mount("/hello", sub) 471 472 resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/world/a", http.NoBody)) 473 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 474 utils.AssertEqual(t, nil, err, "app.Test(req)") 475 476 body, err := io.ReadAll(resp.Body) 477 utils.AssertEqual(t, nil, err) 478 utils.AssertEqual(t, "<h1>Hello a!</h1>", string(body)) 479 480 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test", http.NoBody)) 481 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 482 utils.AssertEqual(t, nil, err, "app.Test(req)") 483 484 body, err = io.ReadAll(resp.Body) 485 utils.AssertEqual(t, nil, err) 486 utils.AssertEqual(t, "<h1>Hello, World!</h1>", string(body)) 487 488 resp, err = app.Test(httptest.NewRequest(MethodGet, "/hello/bruh/moment", http.NoBody)) 489 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 490 utils.AssertEqual(t, nil, err, "app.Test(req)") 491 492 body, err = io.ReadAll(resp.Body) 493 utils.AssertEqual(t, nil, err) 494 utils.AssertEqual(t, "<h1>I'm Bruh</h1>", string(body)) 495 } 496 497 func Test_Ctx_Render_MountGroup(t *testing.T) { 498 t.Parallel() 499 500 micro := New(Config{ 501 Views: html.New("./.github/testdata/template", ".gohtml"), 502 }) 503 504 micro.Get("/doe", func(c *Ctx) error { 505 return c.Render("hello_world", Map{ 506 "Name": "doe", 507 }) 508 }) 509 510 app := New() 511 v1 := app.Group("/v1") 512 v1.Mount("/john", micro) 513 514 resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) 515 utils.AssertEqual(t, nil, err, "app.Test(req)") 516 utils.AssertEqual(t, 200, resp.StatusCode, "Status code") 517 518 body, err := io.ReadAll(resp.Body) 519 utils.AssertEqual(t, nil, err) 520 utils.AssertEqual(t, "<h1>Hello doe!</h1>", string(body)) 521 }