github.com/gofiber/fiber/v2@v2.47.0/middleware/limiter/limiter_test.go (about)

     1  package limiter
     2  
     3  import (
     4  	"io"
     5  	"net/http/httptest"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/gofiber/fiber/v2"
    11  	"github.com/gofiber/fiber/v2/internal/storage/memory"
    12  	"github.com/gofiber/fiber/v2/utils"
    13  
    14  	"github.com/valyala/fasthttp"
    15  )
    16  
    17  // go test -run Test_Limiter_Concurrency_Store -race -v
    18  func Test_Limiter_Concurrency_Store(t *testing.T) {
    19  	t.Parallel()
    20  	// Test concurrency using a custom store
    21  
    22  	app := fiber.New()
    23  
    24  	app.Use(New(Config{
    25  		Max:        50,
    26  		Expiration: 2 * time.Second,
    27  		Storage:    memory.New(),
    28  	}))
    29  
    30  	app.Get("/", func(c *fiber.Ctx) error {
    31  		return c.SendString("Hello tester!")
    32  	})
    33  
    34  	var wg sync.WaitGroup
    35  	singleRequest := func(wg *sync.WaitGroup) {
    36  		defer wg.Done()
    37  		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
    38  		utils.AssertEqual(t, nil, err)
    39  		utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
    40  
    41  		body, err := io.ReadAll(resp.Body)
    42  		utils.AssertEqual(t, nil, err)
    43  		utils.AssertEqual(t, "Hello tester!", string(body))
    44  	}
    45  
    46  	for i := 0; i <= 49; i++ {
    47  		wg.Add(1)
    48  		go singleRequest(&wg)
    49  	}
    50  
    51  	wg.Wait()
    52  
    53  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
    54  	utils.AssertEqual(t, nil, err)
    55  	utils.AssertEqual(t, 429, resp.StatusCode)
    56  
    57  	time.Sleep(3 * time.Second)
    58  
    59  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
    60  	utils.AssertEqual(t, nil, err)
    61  	utils.AssertEqual(t, 200, resp.StatusCode)
    62  }
    63  
    64  // go test -run Test_Limiter_Concurrency -race -v
    65  func Test_Limiter_Concurrency(t *testing.T) {
    66  	t.Parallel()
    67  	// Test concurrency using a default store
    68  
    69  	app := fiber.New()
    70  
    71  	app.Use(New(Config{
    72  		Max:        50,
    73  		Expiration: 2 * time.Second,
    74  	}))
    75  
    76  	app.Get("/", func(c *fiber.Ctx) error {
    77  		return c.SendString("Hello tester!")
    78  	})
    79  
    80  	var wg sync.WaitGroup
    81  	singleRequest := func(wg *sync.WaitGroup) {
    82  		defer wg.Done()
    83  		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
    84  		utils.AssertEqual(t, nil, err)
    85  		utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
    86  
    87  		body, err := io.ReadAll(resp.Body)
    88  		utils.AssertEqual(t, nil, err)
    89  		utils.AssertEqual(t, "Hello tester!", string(body))
    90  	}
    91  
    92  	for i := 0; i <= 49; i++ {
    93  		wg.Add(1)
    94  		go singleRequest(&wg)
    95  	}
    96  
    97  	wg.Wait()
    98  
    99  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
   100  	utils.AssertEqual(t, nil, err)
   101  	utils.AssertEqual(t, 429, resp.StatusCode)
   102  
   103  	time.Sleep(3 * time.Second)
   104  
   105  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
   106  	utils.AssertEqual(t, nil, err)
   107  	utils.AssertEqual(t, 200, resp.StatusCode)
   108  }
   109  
   110  // go test -run Test_Limiter_Fixed_Window_No_Skip_Choices -v
   111  func Test_Limiter_Fixed_Window_No_Skip_Choices(t *testing.T) {
   112  	t.Parallel()
   113  	app := fiber.New()
   114  
   115  	app.Use(New(Config{
   116  		Max:                    2,
   117  		Expiration:             2 * time.Second,
   118  		SkipFailedRequests:     false,
   119  		SkipSuccessfulRequests: false,
   120  		LimiterMiddleware:      FixedWindow{},
   121  	}))
   122  
   123  	app.Get("/:status", func(c *fiber.Ctx) error {
   124  		if c.Params("status") == "fail" { //nolint:goconst // False positive
   125  			return c.SendStatus(400)
   126  		}
   127  		return c.SendStatus(200)
   128  	})
   129  
   130  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   131  	utils.AssertEqual(t, nil, err)
   132  	utils.AssertEqual(t, 400, resp.StatusCode)
   133  
   134  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   135  	utils.AssertEqual(t, nil, err)
   136  	utils.AssertEqual(t, 200, resp.StatusCode)
   137  
   138  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   139  	utils.AssertEqual(t, nil, err)
   140  	utils.AssertEqual(t, 429, resp.StatusCode)
   141  
   142  	time.Sleep(3 * time.Second)
   143  
   144  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   145  	utils.AssertEqual(t, nil, err)
   146  	utils.AssertEqual(t, 200, resp.StatusCode)
   147  }
   148  
   149  // go test -run Test_Limiter_Fixed_Window_Custom_Storage_No_Skip_Choices -v
   150  func Test_Limiter_Fixed_Window_Custom_Storage_No_Skip_Choices(t *testing.T) {
   151  	t.Parallel()
   152  	app := fiber.New()
   153  
   154  	app.Use(New(Config{
   155  		Max:                    2,
   156  		Expiration:             2 * time.Second,
   157  		SkipFailedRequests:     false,
   158  		SkipSuccessfulRequests: false,
   159  		Storage:                memory.New(),
   160  		LimiterMiddleware:      FixedWindow{},
   161  	}))
   162  
   163  	app.Get("/:status", func(c *fiber.Ctx) error {
   164  		if c.Params("status") == "fail" {
   165  			return c.SendStatus(400)
   166  		}
   167  		return c.SendStatus(200)
   168  	})
   169  
   170  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   171  	utils.AssertEqual(t, nil, err)
   172  	utils.AssertEqual(t, 400, resp.StatusCode)
   173  
   174  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   175  	utils.AssertEqual(t, nil, err)
   176  	utils.AssertEqual(t, 200, resp.StatusCode)
   177  
   178  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   179  	utils.AssertEqual(t, nil, err)
   180  	utils.AssertEqual(t, 429, resp.StatusCode)
   181  
   182  	time.Sleep(3 * time.Second)
   183  
   184  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   185  	utils.AssertEqual(t, nil, err)
   186  	utils.AssertEqual(t, 200, resp.StatusCode)
   187  }
   188  
   189  // go test -run Test_Limiter_Sliding_Window_No_Skip_Choices -v
   190  func Test_Limiter_Sliding_Window_No_Skip_Choices(t *testing.T) {
   191  	t.Parallel()
   192  	app := fiber.New()
   193  
   194  	app.Use(New(Config{
   195  		Max:                    2,
   196  		Expiration:             2 * time.Second,
   197  		SkipFailedRequests:     false,
   198  		SkipSuccessfulRequests: false,
   199  		LimiterMiddleware:      SlidingWindow{},
   200  	}))
   201  
   202  	app.Get("/:status", func(c *fiber.Ctx) error {
   203  		if c.Params("status") == "fail" {
   204  			return c.SendStatus(400)
   205  		}
   206  		return c.SendStatus(200)
   207  	})
   208  
   209  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   210  	utils.AssertEqual(t, nil, err)
   211  	utils.AssertEqual(t, 400, resp.StatusCode)
   212  
   213  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   214  	utils.AssertEqual(t, nil, err)
   215  	utils.AssertEqual(t, 200, resp.StatusCode)
   216  
   217  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   218  	utils.AssertEqual(t, nil, err)
   219  	utils.AssertEqual(t, 429, resp.StatusCode)
   220  
   221  	time.Sleep(4 * time.Second)
   222  
   223  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   224  	utils.AssertEqual(t, nil, err)
   225  	utils.AssertEqual(t, 200, resp.StatusCode)
   226  }
   227  
   228  // go test -run Test_Limiter_Sliding_Window_Custom_Storage_No_Skip_Choices -v
   229  func Test_Limiter_Sliding_Window_Custom_Storage_No_Skip_Choices(t *testing.T) {
   230  	t.Parallel()
   231  	app := fiber.New()
   232  
   233  	app.Use(New(Config{
   234  		Max:                    2,
   235  		Expiration:             2 * time.Second,
   236  		SkipFailedRequests:     false,
   237  		SkipSuccessfulRequests: false,
   238  		Storage:                memory.New(),
   239  		LimiterMiddleware:      SlidingWindow{},
   240  	}))
   241  
   242  	app.Get("/:status", func(c *fiber.Ctx) error {
   243  		if c.Params("status") == "fail" {
   244  			return c.SendStatus(400)
   245  		}
   246  		return c.SendStatus(200)
   247  	})
   248  
   249  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   250  	utils.AssertEqual(t, nil, err)
   251  	utils.AssertEqual(t, 400, resp.StatusCode)
   252  
   253  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   254  	utils.AssertEqual(t, nil, err)
   255  	utils.AssertEqual(t, 200, resp.StatusCode)
   256  
   257  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   258  	utils.AssertEqual(t, nil, err)
   259  	utils.AssertEqual(t, 429, resp.StatusCode)
   260  
   261  	time.Sleep(4 * time.Second)
   262  
   263  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   264  	utils.AssertEqual(t, nil, err)
   265  	utils.AssertEqual(t, 200, resp.StatusCode)
   266  }
   267  
   268  // go test -run Test_Limiter_Fixed_Window_Skip_Failed_Requests -v
   269  func Test_Limiter_Fixed_Window_Skip_Failed_Requests(t *testing.T) {
   270  	t.Parallel()
   271  	app := fiber.New()
   272  
   273  	app.Use(New(Config{
   274  		Max:                1,
   275  		Expiration:         2 * time.Second,
   276  		SkipFailedRequests: true,
   277  		LimiterMiddleware:  FixedWindow{},
   278  	}))
   279  
   280  	app.Get("/:status", func(c *fiber.Ctx) error {
   281  		if c.Params("status") == "fail" {
   282  			return c.SendStatus(400)
   283  		}
   284  		return c.SendStatus(200)
   285  	})
   286  
   287  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   288  	utils.AssertEqual(t, nil, err)
   289  	utils.AssertEqual(t, 400, resp.StatusCode)
   290  
   291  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   292  	utils.AssertEqual(t, nil, err)
   293  	utils.AssertEqual(t, 200, resp.StatusCode)
   294  
   295  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   296  	utils.AssertEqual(t, nil, err)
   297  	utils.AssertEqual(t, 429, resp.StatusCode)
   298  
   299  	time.Sleep(3 * time.Second)
   300  
   301  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   302  	utils.AssertEqual(t, nil, err)
   303  	utils.AssertEqual(t, 200, resp.StatusCode)
   304  }
   305  
   306  // go test -run Test_Limiter_Fixed_Window_Custom_Storage_Skip_Failed_Requests -v
   307  func Test_Limiter_Fixed_Window_Custom_Storage_Skip_Failed_Requests(t *testing.T) {
   308  	t.Parallel()
   309  	app := fiber.New()
   310  
   311  	app.Use(New(Config{
   312  		Max:                1,
   313  		Expiration:         2 * time.Second,
   314  		Storage:            memory.New(),
   315  		SkipFailedRequests: true,
   316  		LimiterMiddleware:  FixedWindow{},
   317  	}))
   318  
   319  	app.Get("/:status", func(c *fiber.Ctx) error {
   320  		if c.Params("status") == "fail" {
   321  			return c.SendStatus(400)
   322  		}
   323  		return c.SendStatus(200)
   324  	})
   325  
   326  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   327  	utils.AssertEqual(t, nil, err)
   328  	utils.AssertEqual(t, 400, resp.StatusCode)
   329  
   330  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   331  	utils.AssertEqual(t, nil, err)
   332  	utils.AssertEqual(t, 200, resp.StatusCode)
   333  
   334  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   335  	utils.AssertEqual(t, nil, err)
   336  	utils.AssertEqual(t, 429, resp.StatusCode)
   337  
   338  	time.Sleep(3 * time.Second)
   339  
   340  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   341  	utils.AssertEqual(t, nil, err)
   342  	utils.AssertEqual(t, 200, resp.StatusCode)
   343  }
   344  
   345  // go test -run Test_Limiter_Sliding_Window_Skip_Failed_Requests -v
   346  func Test_Limiter_Sliding_Window_Skip_Failed_Requests(t *testing.T) {
   347  	t.Parallel()
   348  	app := fiber.New()
   349  
   350  	app.Use(New(Config{
   351  		Max:                1,
   352  		Expiration:         2 * time.Second,
   353  		SkipFailedRequests: true,
   354  		LimiterMiddleware:  SlidingWindow{},
   355  	}))
   356  
   357  	app.Get("/:status", func(c *fiber.Ctx) error {
   358  		if c.Params("status") == "fail" {
   359  			return c.SendStatus(400)
   360  		}
   361  		return c.SendStatus(200)
   362  	})
   363  
   364  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   365  	utils.AssertEqual(t, nil, err)
   366  	utils.AssertEqual(t, 400, resp.StatusCode)
   367  
   368  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   369  	utils.AssertEqual(t, nil, err)
   370  	utils.AssertEqual(t, 200, resp.StatusCode)
   371  
   372  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   373  	utils.AssertEqual(t, nil, err)
   374  	utils.AssertEqual(t, 429, resp.StatusCode)
   375  
   376  	time.Sleep(4 * time.Second)
   377  
   378  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   379  	utils.AssertEqual(t, nil, err)
   380  	utils.AssertEqual(t, 200, resp.StatusCode)
   381  }
   382  
   383  // go test -run Test_Limiter_Sliding_Window_Custom_Storage_Skip_Failed_Requests -v
   384  func Test_Limiter_Sliding_Window_Custom_Storage_Skip_Failed_Requests(t *testing.T) {
   385  	t.Parallel()
   386  	app := fiber.New()
   387  
   388  	app.Use(New(Config{
   389  		Max:                1,
   390  		Expiration:         2 * time.Second,
   391  		Storage:            memory.New(),
   392  		SkipFailedRequests: true,
   393  		LimiterMiddleware:  SlidingWindow{},
   394  	}))
   395  
   396  	app.Get("/:status", func(c *fiber.Ctx) error {
   397  		if c.Params("status") == "fail" {
   398  			return c.SendStatus(400)
   399  		}
   400  		return c.SendStatus(200)
   401  	})
   402  
   403  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   404  	utils.AssertEqual(t, nil, err)
   405  	utils.AssertEqual(t, 400, resp.StatusCode)
   406  
   407  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   408  	utils.AssertEqual(t, nil, err)
   409  	utils.AssertEqual(t, 200, resp.StatusCode)
   410  
   411  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   412  	utils.AssertEqual(t, nil, err)
   413  	utils.AssertEqual(t, 429, resp.StatusCode)
   414  
   415  	time.Sleep(4 * time.Second)
   416  
   417  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   418  	utils.AssertEqual(t, nil, err)
   419  	utils.AssertEqual(t, 200, resp.StatusCode)
   420  }
   421  
   422  // go test -run Test_Limiter_Fixed_Window_Skip_Successful_Requests -v
   423  func Test_Limiter_Fixed_Window_Skip_Successful_Requests(t *testing.T) {
   424  	t.Parallel()
   425  	// Test concurrency using a default store
   426  
   427  	app := fiber.New()
   428  
   429  	app.Use(New(Config{
   430  		Max:                    1,
   431  		Expiration:             2 * time.Second,
   432  		SkipSuccessfulRequests: true,
   433  		LimiterMiddleware:      FixedWindow{},
   434  	}))
   435  
   436  	app.Get("/:status", func(c *fiber.Ctx) error {
   437  		if c.Params("status") == "fail" {
   438  			return c.SendStatus(400)
   439  		}
   440  		return c.SendStatus(200)
   441  	})
   442  
   443  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   444  	utils.AssertEqual(t, nil, err)
   445  	utils.AssertEqual(t, 200, resp.StatusCode)
   446  
   447  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   448  	utils.AssertEqual(t, nil, err)
   449  	utils.AssertEqual(t, 400, resp.StatusCode)
   450  
   451  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   452  	utils.AssertEqual(t, nil, err)
   453  	utils.AssertEqual(t, 429, resp.StatusCode)
   454  
   455  	time.Sleep(3 * time.Second)
   456  
   457  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   458  	utils.AssertEqual(t, nil, err)
   459  	utils.AssertEqual(t, 400, resp.StatusCode)
   460  }
   461  
   462  // go test -run Test_Limiter_Fixed_Window_Custom_Storage_Skip_Successful_Requests -v
   463  func Test_Limiter_Fixed_Window_Custom_Storage_Skip_Successful_Requests(t *testing.T) {
   464  	t.Parallel()
   465  	// Test concurrency using a default store
   466  
   467  	app := fiber.New()
   468  
   469  	app.Use(New(Config{
   470  		Max:                    1,
   471  		Expiration:             2 * time.Second,
   472  		Storage:                memory.New(),
   473  		SkipSuccessfulRequests: true,
   474  		LimiterMiddleware:      FixedWindow{},
   475  	}))
   476  
   477  	app.Get("/:status", func(c *fiber.Ctx) error {
   478  		if c.Params("status") == "fail" {
   479  			return c.SendStatus(400)
   480  		}
   481  		return c.SendStatus(200)
   482  	})
   483  
   484  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   485  	utils.AssertEqual(t, nil, err)
   486  	utils.AssertEqual(t, 200, resp.StatusCode)
   487  
   488  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   489  	utils.AssertEqual(t, nil, err)
   490  	utils.AssertEqual(t, 400, resp.StatusCode)
   491  
   492  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   493  	utils.AssertEqual(t, nil, err)
   494  	utils.AssertEqual(t, 429, resp.StatusCode)
   495  
   496  	time.Sleep(3 * time.Second)
   497  
   498  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   499  	utils.AssertEqual(t, nil, err)
   500  	utils.AssertEqual(t, 400, resp.StatusCode)
   501  }
   502  
   503  // go test -run Test_Limiter_Sliding_Window_Skip_Successful_Requests -v
   504  func Test_Limiter_Sliding_Window_Skip_Successful_Requests(t *testing.T) {
   505  	t.Parallel()
   506  	// Test concurrency using a default store
   507  
   508  	app := fiber.New()
   509  
   510  	app.Use(New(Config{
   511  		Max:                    1,
   512  		Expiration:             2 * time.Second,
   513  		SkipSuccessfulRequests: true,
   514  		LimiterMiddleware:      SlidingWindow{},
   515  	}))
   516  
   517  	app.Get("/:status", func(c *fiber.Ctx) error {
   518  		if c.Params("status") == "fail" {
   519  			return c.SendStatus(400)
   520  		}
   521  		return c.SendStatus(200)
   522  	})
   523  
   524  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   525  	utils.AssertEqual(t, nil, err)
   526  	utils.AssertEqual(t, 200, resp.StatusCode)
   527  
   528  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   529  	utils.AssertEqual(t, nil, err)
   530  	utils.AssertEqual(t, 400, resp.StatusCode)
   531  
   532  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   533  	utils.AssertEqual(t, nil, err)
   534  	utils.AssertEqual(t, 429, resp.StatusCode)
   535  
   536  	time.Sleep(4 * time.Second)
   537  
   538  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   539  	utils.AssertEqual(t, nil, err)
   540  	utils.AssertEqual(t, 400, resp.StatusCode)
   541  }
   542  
   543  // go test -run Test_Limiter_Sliding_Window_Custom_Storage_Skip_Successful_Requests -v
   544  func Test_Limiter_Sliding_Window_Custom_Storage_Skip_Successful_Requests(t *testing.T) {
   545  	t.Parallel()
   546  	// Test concurrency using a default store
   547  
   548  	app := fiber.New()
   549  
   550  	app.Use(New(Config{
   551  		Max:                    1,
   552  		Expiration:             2 * time.Second,
   553  		Storage:                memory.New(),
   554  		SkipSuccessfulRequests: true,
   555  		LimiterMiddleware:      SlidingWindow{},
   556  	}))
   557  
   558  	app.Get("/:status", func(c *fiber.Ctx) error {
   559  		if c.Params("status") == "fail" {
   560  			return c.SendStatus(400)
   561  		}
   562  		return c.SendStatus(200)
   563  	})
   564  
   565  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
   566  	utils.AssertEqual(t, nil, err)
   567  	utils.AssertEqual(t, 200, resp.StatusCode)
   568  
   569  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   570  	utils.AssertEqual(t, nil, err)
   571  	utils.AssertEqual(t, 400, resp.StatusCode)
   572  
   573  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   574  	utils.AssertEqual(t, nil, err)
   575  	utils.AssertEqual(t, 429, resp.StatusCode)
   576  
   577  	time.Sleep(4 * time.Second)
   578  
   579  	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
   580  	utils.AssertEqual(t, nil, err)
   581  	utils.AssertEqual(t, 400, resp.StatusCode)
   582  }
   583  
   584  // go test -v -run=^$ -bench=Benchmark_Limiter_Custom_Store -benchmem -count=4
   585  func Benchmark_Limiter_Custom_Store(b *testing.B) {
   586  	app := fiber.New()
   587  
   588  	app.Use(New(Config{
   589  		Max:        100,
   590  		Expiration: 60 * time.Second,
   591  		Storage:    memory.New(),
   592  	}))
   593  
   594  	app.Get("/", func(c *fiber.Ctx) error {
   595  		return c.SendString("Hello, World!")
   596  	})
   597  
   598  	h := app.Handler()
   599  
   600  	fctx := &fasthttp.RequestCtx{}
   601  	fctx.Request.Header.SetMethod(fiber.MethodGet)
   602  	fctx.Request.SetRequestURI("/")
   603  
   604  	b.ResetTimer()
   605  
   606  	for n := 0; n < b.N; n++ {
   607  		h(fctx)
   608  	}
   609  }
   610  
   611  // go test -run Test_Limiter_Next
   612  func Test_Limiter_Next(t *testing.T) {
   613  	t.Parallel()
   614  	app := fiber.New()
   615  	app.Use(New(Config{
   616  		Next: func(_ *fiber.Ctx) bool {
   617  			return true
   618  		},
   619  	}))
   620  
   621  	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
   622  	utils.AssertEqual(t, nil, err)
   623  	utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode)
   624  }
   625  
   626  func Test_Limiter_Headers(t *testing.T) {
   627  	t.Parallel()
   628  	app := fiber.New()
   629  
   630  	app.Use(New(Config{
   631  		Max:        50,
   632  		Expiration: 2 * time.Second,
   633  	}))
   634  
   635  	app.Get("/", func(c *fiber.Ctx) error {
   636  		return c.SendString("Hello tester!")
   637  	})
   638  
   639  	fctx := &fasthttp.RequestCtx{}
   640  	fctx.Request.Header.SetMethod(fiber.MethodGet)
   641  	fctx.Request.SetRequestURI("/")
   642  
   643  	app.Handler()(fctx)
   644  
   645  	utils.AssertEqual(t, "50", string(fctx.Response.Header.Peek("X-RateLimit-Limit")))
   646  	if v := string(fctx.Response.Header.Peek("X-RateLimit-Remaining")); v == "" {
   647  		t.Errorf("The X-RateLimit-Remaining header is not set correctly - value is an empty string.")
   648  	}
   649  	if v := string(fctx.Response.Header.Peek("X-RateLimit-Reset")); !(v == "1" || v == "2") {
   650  		t.Errorf("The X-RateLimit-Reset header is not set correctly - value is out of bounds.")
   651  	}
   652  }
   653  
   654  // go test -v -run=^$ -bench=Benchmark_Limiter -benchmem -count=4
   655  func Benchmark_Limiter(b *testing.B) {
   656  	app := fiber.New()
   657  
   658  	app.Use(New(Config{
   659  		Max:        100,
   660  		Expiration: 60 * time.Second,
   661  	}))
   662  
   663  	app.Get("/", func(c *fiber.Ctx) error {
   664  		return c.SendString("Hello, World!")
   665  	})
   666  
   667  	h := app.Handler()
   668  
   669  	fctx := &fasthttp.RequestCtx{}
   670  	fctx.Request.Header.SetMethod(fiber.MethodGet)
   671  	fctx.Request.SetRequestURI("/")
   672  
   673  	b.ResetTimer()
   674  
   675  	for n := 0; n < b.N; n++ {
   676  		h(fctx)
   677  	}
   678  }
   679  
   680  // go test -run Test_Sliding_Window -race -v
   681  func Test_Sliding_Window(t *testing.T) {
   682  	t.Parallel()
   683  	app := fiber.New()
   684  	app.Use(New(Config{
   685  		Max:               10,
   686  		Expiration:        2 * time.Second,
   687  		Storage:           memory.New(),
   688  		LimiterMiddleware: SlidingWindow{},
   689  	}))
   690  
   691  	app.Get("/", func(c *fiber.Ctx) error {
   692  		return c.SendString("Hello tester!")
   693  	})
   694  
   695  	singleRequest := func(shouldFail bool) {
   696  		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
   697  		if shouldFail {
   698  			utils.AssertEqual(t, nil, err)
   699  			utils.AssertEqual(t, 429, resp.StatusCode)
   700  		} else {
   701  			utils.AssertEqual(t, nil, err)
   702  			utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
   703  		}
   704  	}
   705  
   706  	for i := 0; i < 5; i++ {
   707  		singleRequest(false)
   708  	}
   709  
   710  	time.Sleep(2 * time.Second)
   711  
   712  	for i := 0; i < 5; i++ {
   713  		singleRequest(false)
   714  	}
   715  
   716  	time.Sleep(3 * time.Second)
   717  
   718  	for i := 0; i < 5; i++ {
   719  		singleRequest(false)
   720  	}
   721  
   722  	time.Sleep(4 * time.Second)
   723  
   724  	for i := 0; i < 9; i++ {
   725  		singleRequest(false)
   726  	}
   727  }