1- package idempotency_test
1+ package idempotency
22
33import (
44 "errors"
5+ "fmt"
56 "io"
7+ "net/http"
68 "net/http/httptest"
79 "strconv"
810 "sync"
@@ -11,7 +13,6 @@ import (
1113 "time"
1214
1315 "github.com/gofiber/fiber/v3"
14- "github.com/gofiber/fiber/v3/middleware/idempotency"
1516 "github.com/valyala/fasthttp"
1617
1718 "github.com/stretchr/testify/assert"
@@ -29,7 +30,7 @@ func Test_Idempotency(t *testing.T) {
2930 }
3031
3132 isMethodSafe := fiber .IsMethodSafe (c .Method ())
32- isIdempotent := idempotency . IsFromCache (c ) || idempotency . WasPutToCache (c )
33+ isIdempotent := IsFromCache (c ) || WasPutToCache (c )
3334 hasReqHeader := c .Get ("X-Idempotency-Key" ) != ""
3435
3536 if isMethodSafe {
@@ -53,7 +54,7 @@ func Test_Idempotency(t *testing.T) {
5354 // Needs to be at least a second as the memory storage doesn't support shorter durations.
5455 const lifetime = 2 * time .Second
5556
56- app .Use (idempotency . New (idempotency. Config {
57+ app .Use (New (Config {
5758 Lifetime : lifetime ,
5859 }))
5960
@@ -136,7 +137,7 @@ func Benchmark_Idempotency(b *testing.B) {
136137 // Needs to be at least a second as the memory storage doesn't support shorter durations.
137138 const lifetime = 1 * time .Second
138139
139- app .Use (idempotency . New (idempotency. Config {
140+ app .Use (New (Config {
140141 Lifetime : lifetime ,
141142 }))
142143
@@ -169,3 +170,248 @@ func Benchmark_Idempotency(b *testing.B) {
169170 }
170171 })
171172}
173+
174+ // ---------- additional tests (moved from config_extra_test.go and idempotency_additional_test.go) ----------
175+
176+ const validKey = "00000000-0000-0000-0000-000000000000"
177+
178+ func Test_configDefault_defaults (t * testing.T ) {
179+ t .Parallel ()
180+
181+ cfg := configDefault ()
182+ require .NotNil (t , cfg .Lock )
183+ require .NotNil (t , cfg .Storage )
184+ require .Equal (t , ConfigDefault .Lifetime , cfg .Lifetime )
185+ require .Equal (t , ConfigDefault .KeyHeader , cfg .KeyHeader )
186+ require .Nil (t , cfg .KeepResponseHeaders )
187+
188+ app := fiber .New ()
189+
190+ fctx := & fasthttp.RequestCtx {}
191+ fctx .Request .Header .SetMethod (fiber .MethodGet )
192+ ctx := app .AcquireCtx (fctx )
193+ assert .True (t , cfg .Next (ctx ))
194+ app .ReleaseCtx (ctx )
195+
196+ fctx = & fasthttp.RequestCtx {}
197+ fctx .Request .Header .SetMethod (fiber .MethodPost )
198+ ctx = app .AcquireCtx (fctx )
199+ assert .False (t , cfg .Next (ctx ))
200+ app .ReleaseCtx (ctx )
201+
202+ assert .NoError (t , cfg .KeyHeaderValidate (validKey ))
203+ assert .Error (t , cfg .KeyHeaderValidate ("short" ))
204+ }
205+
206+ func Test_configDefault_override (t * testing.T ) {
207+ t .Parallel ()
208+
209+ l := & stubLock {}
210+ s := & stubStorage {}
211+
212+ cfg := configDefault (Config {
213+ Lifetime : 42 * time .Second ,
214+ KeyHeader : "Foo" ,
215+ KeepResponseHeaders : []string {},
216+ Lock : l ,
217+ Storage : s ,
218+ })
219+
220+ require .Equal (t , 42 * time .Second , cfg .Lifetime )
221+ require .Equal (t , "Foo" , cfg .KeyHeader )
222+ require .Nil (t , cfg .KeepResponseHeaders )
223+ require .Equal (t , l , cfg .Lock )
224+ require .Equal (t , s , cfg .Storage )
225+ require .NotNil (t , cfg .Next )
226+ require .NotNil (t , cfg .KeyHeaderValidate )
227+ }
228+
229+ // helper to perform request
230+ func do (app * fiber.App , req * http.Request ) (* http.Response , string ) {
231+ resp , err := app .Test (req , fiber.TestConfig {Timeout : 5 * time .Second })
232+ if err != nil {
233+ panic (err )
234+ }
235+ body , _ := io .ReadAll (resp .Body )
236+ return resp , string (body )
237+ }
238+
239+ func Test_New_NextSkip (t * testing.T ) {
240+ t .Parallel ()
241+ app := fiber .New ()
242+ var count int
243+
244+ app .Use (New (Config {Next : func (c fiber.Ctx ) bool { return true }}))
245+
246+ app .Post ("/" , func (c fiber.Ctx ) error {
247+ count ++
248+ return c .SendString (fmt .Sprintf ("%d" , count ))
249+ })
250+
251+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
252+ req .Header .Set (ConfigDefault .KeyHeader , validKey )
253+ _ , body1 := do (app , req )
254+
255+ req2 := httptest .NewRequest (http .MethodPost , "/" , nil )
256+ req2 .Header .Set (ConfigDefault .KeyHeader , validKey )
257+ _ , body2 := do (app , req2 )
258+
259+ require .Equal (t , "1" , body1 )
260+ require .Equal (t , "2" , body2 )
261+ }
262+
263+ func Test_New_InvalidKey (t * testing.T ) {
264+ t .Parallel ()
265+ app := fiber .New ()
266+ app .Use (New ())
267+ app .Post ("/" , func (c fiber.Ctx ) error { return nil })
268+
269+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
270+ req .Header .Set (ConfigDefault .KeyHeader , "bad" )
271+ resp , body := do (app , req )
272+
273+ require .Equal (t , fiber .StatusInternalServerError , resp .StatusCode )
274+ assert .Contains (t , body , "invalid length" )
275+ }
276+
277+ func Test_New_StorageGetError (t * testing.T ) {
278+ t .Parallel ()
279+ app := fiber .New ()
280+ s := & stubStorage {getErr : errors .New ("boom" )}
281+ app .Use (New (Config {Storage : s , Lock : & stubLock {}}))
282+ app .Post ("/" , func (c fiber.Ctx ) error { return c .SendString ("ok" ) })
283+
284+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
285+ req .Header .Set (ConfigDefault .KeyHeader , validKey )
286+ resp , body := do (app , req )
287+
288+ require .Equal (t , fiber .StatusInternalServerError , resp .StatusCode )
289+ assert .Contains (t , body , "failed to write cached response at fastpath" )
290+ }
291+
292+ func Test_New_UnmarshalError (t * testing.T ) {
293+ t .Parallel ()
294+ app := fiber .New ()
295+ s := & stubStorage {data : map [string ][]byte {validKey : []byte ("bad" )}}
296+ app .Use (New (Config {Storage : s , Lock : & stubLock {}}))
297+ app .Post ("/" , func (c fiber.Ctx ) error { return c .SendString ("ok" ) })
298+
299+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
300+ req .Header .Set (ConfigDefault .KeyHeader , validKey )
301+ resp , body := do (app , req )
302+
303+ require .Equal (t , fiber .StatusInternalServerError , resp .StatusCode )
304+ assert .Contains (t , body , "failed to write cached response at fastpath" )
305+ }
306+
307+ func Test_New_StoreRetrieve_FilterHeaders (t * testing.T ) {
308+ t .Parallel ()
309+ app := fiber .New ()
310+ s := & stubStorage {}
311+ app .Use (New (Config {
312+ Storage : s ,
313+ Lock : & stubLock {},
314+ KeepResponseHeaders : []string {"Foo" },
315+ }))
316+
317+ var count int
318+ app .Post ("/" , func (c fiber.Ctx ) error {
319+ count ++
320+ c .Set ("Foo" , "foo" )
321+ c .Set ("Bar" , "bar" )
322+ return c .SendString (fmt .Sprintf ("resp%d" , count ))
323+ })
324+
325+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
326+ req .Header .Set (ConfigDefault .KeyHeader , validKey )
327+ resp , body := do (app , req )
328+ require .Equal (t , "resp1" , body )
329+ require .Equal (t , "foo" , resp .Header .Get ("Foo" ))
330+ require .Equal (t , "bar" , resp .Header .Get ("Bar" ))
331+
332+ req2 := httptest .NewRequest (http .MethodPost , "/" , nil )
333+ req2 .Header .Set (ConfigDefault .KeyHeader , validKey )
334+ resp2 , body2 := do (app , req2 )
335+ require .Equal (t , "resp1" , body2 )
336+ require .Equal (t , "foo" , resp2 .Header .Get ("Foo" ))
337+ require .Empty (t , resp2 .Header .Get ("Bar" ))
338+ require .Equal (t , 1 , count )
339+ require .Equal (t , 1 , s .setCount )
340+ }
341+
342+ func Test_New_HandlerError (t * testing.T ) {
343+ t .Parallel ()
344+ app := fiber .New ()
345+ s := & stubStorage {}
346+ app .Use (New (Config {Storage : s , Lock : & stubLock {}}))
347+ app .Post ("/" , func (c fiber.Ctx ) error { return errors .New ("boom" ) })
348+
349+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
350+ req .Header .Set (ConfigDefault .KeyHeader , validKey )
351+ resp , body := do (app , req )
352+ require .Equal (t , fiber .StatusInternalServerError , resp .StatusCode )
353+ require .Equal (t , "boom" , body )
354+ require .Equal (t , 0 , s .setCount )
355+
356+ resp2 , body2 := do (app , req )
357+ require .Equal (t , fiber .StatusInternalServerError , resp2 .StatusCode )
358+ require .Equal (t , "boom" , body2 )
359+ require .Equal (t , 0 , s .setCount )
360+ }
361+
362+ func Test_New_LockError (t * testing.T ) {
363+ t .Parallel ()
364+ app := fiber .New ()
365+ l := & stubLock {lockErr : errors .New ("fail" )}
366+ app .Use (New (Config {Lock : l , Storage : & stubStorage {}}))
367+ app .Post ("/" , func (c fiber.Ctx ) error { return c .SendString ("ok" ) })
368+
369+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
370+ req .Header .Set (ConfigDefault .KeyHeader , validKey )
371+ resp , body := do (app , req )
372+ require .Equal (t , fiber .StatusInternalServerError , resp .StatusCode )
373+ assert .Contains (t , body , "failed to lock" )
374+ }
375+
376+ func Test_New_StorageSetError (t * testing.T ) {
377+ t .Parallel ()
378+ app := fiber .New ()
379+ s := & stubStorage {setErr : errors .New ("nope" )}
380+ app .Use (New (Config {Storage : s , Lock : & stubLock {}}))
381+ app .Post ("/" , func (c fiber.Ctx ) error { return c .SendString ("ok" ) })
382+
383+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
384+ req .Header .Set (ConfigDefault .KeyHeader , validKey )
385+ resp , body := do (app , req )
386+ require .Equal (t , fiber .StatusInternalServerError , resp .StatusCode )
387+ assert .Contains (t , body , "failed to save response" )
388+ }
389+
390+ func Test_New_UnlockError (t * testing.T ) {
391+ t .Parallel ()
392+ app := fiber .New ()
393+ l := & stubLock {unlockErr : errors .New ("u" )}
394+ app .Use (New (Config {Lock : l , Storage : & stubStorage {}}))
395+ app .Post ("/" , func (c fiber.Ctx ) error { return c .SendString ("ok" ) })
396+
397+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
398+ req .Header .Set (ConfigDefault .KeyHeader , validKey )
399+ resp , body := do (app , req )
400+ require .Equal (t , fiber .StatusOK , resp .StatusCode )
401+ require .Equal (t , "ok" , body )
402+ }
403+
404+ func Test_New_SecondPassReadError (t * testing.T ) {
405+ t .Parallel ()
406+ app := fiber .New ()
407+ s := & stubStorage {}
408+ l := & stubLock {afterLock : func () { s .getErr = errors .New ("g" ) }}
409+ app .Use (New (Config {Lock : l , Storage : s }))
410+ app .Post ("/" , func (c fiber.Ctx ) error { return c .SendString ("ok" ) })
411+
412+ req := httptest .NewRequest (http .MethodPost , "/" , nil )
413+ req .Header .Set (ConfigDefault .KeyHeader , validKey )
414+ resp , body := do (app , req )
415+ require .Equal (t , fiber .StatusInternalServerError , resp .StatusCode )
416+ assert .Contains (t , body , "failed to write cached response while locked" )
417+ }
0 commit comments