github.com/gofiber/fiber/v2@v2.47.0/middleware/etag/etag_test.go (about) 1 package etag 2 3 import ( 4 "bytes" 5 "io" 6 "net/http/httptest" 7 "testing" 8 9 "github.com/gofiber/fiber/v2" 10 "github.com/gofiber/fiber/v2/utils" 11 12 "github.com/valyala/fasthttp" 13 ) 14 15 // go test -run Test_ETag_Next 16 func Test_ETag_Next(t *testing.T) { 17 t.Parallel() 18 app := fiber.New() 19 app.Use(New(Config{ 20 Next: func(_ *fiber.Ctx) bool { 21 return true 22 }, 23 })) 24 25 resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) 26 utils.AssertEqual(t, nil, err) 27 utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) 28 } 29 30 // go test -run Test_ETag_SkipError 31 func Test_ETag_SkipError(t *testing.T) { 32 t.Parallel() 33 app := fiber.New() 34 35 app.Use(New()) 36 37 app.Get("/", func(c *fiber.Ctx) error { 38 return fiber.ErrForbidden 39 }) 40 41 resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) 42 utils.AssertEqual(t, nil, err) 43 utils.AssertEqual(t, fiber.StatusForbidden, resp.StatusCode) 44 } 45 46 // go test -run Test_ETag_NotStatusOK 47 func Test_ETag_NotStatusOK(t *testing.T) { 48 t.Parallel() 49 app := fiber.New() 50 51 app.Use(New()) 52 53 app.Get("/", func(c *fiber.Ctx) error { 54 return c.SendStatus(fiber.StatusCreated) 55 }) 56 57 resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) 58 utils.AssertEqual(t, nil, err) 59 utils.AssertEqual(t, fiber.StatusCreated, resp.StatusCode) 60 } 61 62 // go test -run Test_ETag_NoBody 63 func Test_ETag_NoBody(t *testing.T) { 64 t.Parallel() 65 app := fiber.New() 66 67 app.Use(New()) 68 69 app.Get("/", func(c *fiber.Ctx) error { 70 return nil 71 }) 72 73 resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) 74 utils.AssertEqual(t, nil, err) 75 utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) 76 } 77 78 // go test -run Test_ETag_NewEtag 79 func Test_ETag_NewEtag(t *testing.T) { 80 t.Parallel() 81 t.Run("without HeaderIfNoneMatch", func(t *testing.T) { 82 t.Parallel() 83 testETagNewEtag(t, false, false) 84 }) 85 t.Run("with HeaderIfNoneMatch and not matched", func(t *testing.T) { 86 t.Parallel() 87 testETagNewEtag(t, true, false) 88 }) 89 t.Run("with HeaderIfNoneMatch and matched", func(t *testing.T) { 90 t.Parallel() 91 testETagNewEtag(t, true, true) 92 }) 93 } 94 95 func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { //nolint:revive // We're in a test, so using bools as a flow-control is fine 96 t.Helper() 97 98 app := fiber.New() 99 100 app.Use(New()) 101 102 app.Get("/", func(c *fiber.Ctx) error { 103 return c.SendString("Hello, World!") 104 }) 105 106 req := httptest.NewRequest(fiber.MethodGet, "/", nil) 107 if headerIfNoneMatch { 108 etag := `"non-match"` 109 if matched { 110 etag = `"13-1831710635"` 111 } 112 req.Header.Set(fiber.HeaderIfNoneMatch, etag) 113 } 114 115 resp, err := app.Test(req) 116 utils.AssertEqual(t, nil, err) 117 118 if !headerIfNoneMatch || !matched { 119 utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) 120 utils.AssertEqual(t, `"13-1831710635"`, resp.Header.Get(fiber.HeaderETag)) 121 return 122 } 123 124 if matched { 125 utils.AssertEqual(t, fiber.StatusNotModified, resp.StatusCode) 126 b, err := io.ReadAll(resp.Body) 127 utils.AssertEqual(t, nil, err) 128 utils.AssertEqual(t, 0, len(b)) 129 } 130 } 131 132 // go test -run Test_ETag_WeakEtag 133 func Test_ETag_WeakEtag(t *testing.T) { 134 t.Parallel() 135 t.Run("without HeaderIfNoneMatch", func(t *testing.T) { 136 t.Parallel() 137 testETagWeakEtag(t, false, false) 138 }) 139 t.Run("with HeaderIfNoneMatch and not matched", func(t *testing.T) { 140 t.Parallel() 141 testETagWeakEtag(t, true, false) 142 }) 143 t.Run("with HeaderIfNoneMatch and matched", func(t *testing.T) { 144 t.Parallel() 145 testETagWeakEtag(t, true, true) 146 }) 147 } 148 149 func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { //nolint:revive // We're in a test, so using bools as a flow-control is fine 150 t.Helper() 151 152 app := fiber.New() 153 154 app.Use(New(Config{Weak: true})) 155 156 app.Get("/", func(c *fiber.Ctx) error { 157 return c.SendString("Hello, World!") 158 }) 159 160 req := httptest.NewRequest(fiber.MethodGet, "/", nil) 161 if headerIfNoneMatch { 162 etag := `W/"non-match"` 163 if matched { 164 etag = `W/"13-1831710635"` 165 } 166 req.Header.Set(fiber.HeaderIfNoneMatch, etag) 167 } 168 169 resp, err := app.Test(req) 170 utils.AssertEqual(t, nil, err) 171 172 if !headerIfNoneMatch || !matched { 173 utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) 174 utils.AssertEqual(t, `W/"13-1831710635"`, resp.Header.Get(fiber.HeaderETag)) 175 return 176 } 177 178 if matched { 179 utils.AssertEqual(t, fiber.StatusNotModified, resp.StatusCode) 180 b, err := io.ReadAll(resp.Body) 181 utils.AssertEqual(t, nil, err) 182 utils.AssertEqual(t, 0, len(b)) 183 } 184 } 185 186 // go test -run Test_ETag_CustomEtag 187 func Test_ETag_CustomEtag(t *testing.T) { 188 t.Parallel() 189 t.Run("without HeaderIfNoneMatch", func(t *testing.T) { 190 t.Parallel() 191 testETagCustomEtag(t, false, false) 192 }) 193 t.Run("with HeaderIfNoneMatch and not matched", func(t *testing.T) { 194 t.Parallel() 195 testETagCustomEtag(t, true, false) 196 }) 197 t.Run("with HeaderIfNoneMatch and matched", func(t *testing.T) { 198 t.Parallel() 199 testETagCustomEtag(t, true, true) 200 }) 201 } 202 203 func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { //nolint:revive // We're in a test, so using bools as a flow-control is fine 204 t.Helper() 205 206 app := fiber.New() 207 208 app.Use(New()) 209 210 app.Get("/", func(c *fiber.Ctx) error { 211 c.Set(fiber.HeaderETag, `"custom"`) 212 if bytes.Equal(c.Request().Header.Peek(fiber.HeaderIfNoneMatch), []byte(`"custom"`)) { 213 return c.SendStatus(fiber.StatusNotModified) 214 } 215 return c.SendString("Hello, World!") 216 }) 217 218 req := httptest.NewRequest(fiber.MethodGet, "/", nil) 219 if headerIfNoneMatch { 220 etag := `"non-match"` 221 if matched { 222 etag = `"custom"` 223 } 224 req.Header.Set(fiber.HeaderIfNoneMatch, etag) 225 } 226 227 resp, err := app.Test(req) 228 utils.AssertEqual(t, nil, err) 229 230 if !headerIfNoneMatch || !matched { 231 utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) 232 utils.AssertEqual(t, `"custom"`, resp.Header.Get(fiber.HeaderETag)) 233 return 234 } 235 236 if matched { 237 utils.AssertEqual(t, fiber.StatusNotModified, resp.StatusCode) 238 b, err := io.ReadAll(resp.Body) 239 utils.AssertEqual(t, nil, err) 240 utils.AssertEqual(t, 0, len(b)) 241 } 242 } 243 244 // go test -run Test_ETag_CustomEtagPut 245 func Test_ETag_CustomEtagPut(t *testing.T) { 246 t.Parallel() 247 app := fiber.New() 248 249 app.Use(New()) 250 251 app.Put("/", func(c *fiber.Ctx) error { 252 c.Set(fiber.HeaderETag, `"custom"`) 253 if !bytes.Equal(c.Request().Header.Peek(fiber.HeaderIfMatch), []byte(`"custom"`)) { 254 return c.SendStatus(fiber.StatusPreconditionFailed) 255 } 256 return c.SendString("Hello, World!") 257 }) 258 259 req := httptest.NewRequest(fiber.MethodPut, "/", nil) 260 req.Header.Set(fiber.HeaderIfMatch, `"non-match"`) 261 resp, err := app.Test(req) 262 utils.AssertEqual(t, nil, err) 263 utils.AssertEqual(t, fiber.StatusPreconditionFailed, resp.StatusCode) 264 } 265 266 // go test -v -run=^$ -bench=Benchmark_Etag -benchmem -count=4 267 func Benchmark_Etag(b *testing.B) { 268 app := fiber.New() 269 270 app.Use(New()) 271 272 app.Get("/", func(c *fiber.Ctx) error { 273 return c.SendString("Hello, World!") 274 }) 275 276 h := app.Handler() 277 278 fctx := &fasthttp.RequestCtx{} 279 fctx.Request.Header.SetMethod(fiber.MethodGet) 280 fctx.Request.SetRequestURI("/") 281 282 b.ReportAllocs() 283 b.ResetTimer() 284 285 for n := 0; n < b.N; n++ { 286 h(fctx) 287 } 288 289 utils.AssertEqual(b, 200, fctx.Response.Header.StatusCode()) 290 utils.AssertEqual(b, `"13-1831710635"`, string(fctx.Response.Header.Peek(fiber.HeaderETag))) 291 }