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  }