@@ -4,13 +4,13 @@ import (
44 "context"
55 "encoding/json"
66 "errors"
7- "net/http"
87 "sync"
98 "testing"
109 "time"
1110
1211 "github.com/valkey-io/valkey-go"
1312 "github.com/zalando/skipper/metrics"
13+ "github.com/zalando/skipper/metrics/metricstest"
1414 skpnet "github.com/zalando/skipper/net"
1515 "github.com/zalando/skipper/net/valkeytest"
1616)
@@ -88,58 +88,6 @@ func (s *stubValkeyClient) Del(_ context.Context, key string) (int64, error) {
8888 return 1 , nil
8989}
9090
91- // testMetrics is a minimal metrics.Metrics stub for testing.
92- // Only IncCounter does real work; all other methods are no-ops.
93- type testMetrics struct {
94- mu sync.Mutex
95- counters map [string ]int
96- }
97-
98- var _ metrics.Metrics = (* testMetrics )(nil )
99-
100- func (m * testMetrics ) IncCounter (key string ) {
101- m .mu .Lock ()
102- defer m .mu .Unlock ()
103- if m .counters == nil {
104- m .counters = make (map [string ]int )
105- }
106- m .counters [key ]++
107- }
108-
109- func (m * testMetrics ) counter (key string ) int {
110- m .mu .Lock ()
111- defer m .mu .Unlock ()
112- return m .counters [key ]
113- }
114-
115- // metrics.Metrics no-op implementations
116- func (m * testMetrics ) MeasureSince (key string , start time.Time ) {}
117- func (m * testMetrics ) IncCounterBy (key string , value int64 ) {}
118- func (m * testMetrics ) IncFloatCounterBy (key string , value float64 ) {}
119- func (m * testMetrics ) MeasureRouteLookup (start time.Time ) {}
120- func (m * testMetrics ) MeasureFilterCreate (filterName string , start time.Time ) {}
121- func (m * testMetrics ) MeasureFilterRequest (filterName string , start time.Time ) {}
122- func (m * testMetrics ) MeasureAllFiltersRequest (routeId string , start time.Time ) {}
123- func (m * testMetrics ) MeasureBackendRequestHeader (host string , size int ) {}
124- func (m * testMetrics ) MeasureBackend (routeId string , start time.Time ) {}
125- func (m * testMetrics ) MeasureBackendHost (routeBackendHost string , start time.Time ) {}
126- func (m * testMetrics ) MeasureBackendZone (zone string , start time.Time ) {}
127- func (m * testMetrics ) MeasureFilterResponse (filterName string , start time.Time ) {}
128- func (m * testMetrics ) MeasureAllFiltersResponse (routeId string , start time.Time ) {}
129- func (m * testMetrics ) MeasureResponse (code int , method string , routeId string , start time.Time ) {}
130- func (m * testMetrics ) MeasureResponseSize (host string , size int64 ) {}
131- func (m * testMetrics ) MeasureProxy (requestDuration , responseDuration time.Duration ) {}
132- func (m * testMetrics ) MeasureServe (routeId , host , method string , code int , start time.Time ) {}
133- func (m * testMetrics ) IncRoutingFailures () {}
134- func (m * testMetrics ) IncErrorsBackend (routeId string ) {}
135- func (m * testMetrics ) MeasureBackend5xx (t time.Time ) {}
136- func (m * testMetrics ) IncErrorsStreaming (routeId string ) {}
137- func (m * testMetrics ) RegisterHandler (path string , handler * http.ServeMux ) {}
138- func (m * testMetrics ) UpdateGauge (key string , value float64 ) {}
139- func (m * testMetrics ) SetInvalidRoute (routeId , reason string ) {}
140- func (m * testMetrics ) Close () {}
141- func (m * testMetrics ) String () string { return "testMetrics" }
142-
14391func TestValkeyStorage_GetSetDelete (t * testing.T ) {
14492 addr , done := valkeytest .NewTestValkey (t )
14593 defer done ()
@@ -153,7 +101,7 @@ func TestValkeyStorage_GetSetDelete(t *testing.T) {
153101 defer ring .Close ()
154102
155103 lru := NewLRUStorage (64 << 20 , nil , metrics .Default )
156- s := NewL2Storage (ring , lru , & testMetrics {}, 0 , valkey .IsValkeyNil )
104+ s := NewL2Storage (ring , lru , & metricstest. MockMetrics {}, 0 , valkey .IsValkeyNil )
157105
158106 ctx := context .Background ()
159107 key := "test-key"
@@ -205,7 +153,7 @@ func TestValkeyStorage_FallsBackToL1OnValkeyUnavailable(t *testing.T) {
205153 defer ring .Close ()
206154
207155 lru := NewLRUStorage (64 << 20 , nil , metrics .Default )
208- m := & testMetrics {}
156+ m := & metricstest. MockMetrics {}
209157 s := NewL2Storage (ring , lru , m , 0 , valkey .IsValkeyNil )
210158
211159 // Stop valkey before exercising fallback paths.
@@ -231,11 +179,13 @@ func TestValkeyStorage_FallsBackToL1OnValkeyUnavailable(t *testing.T) {
231179 if got == nil {
232180 t .Fatal ("expected L1 fallback hit, got nil" )
233181 }
234- if m . counter ("cache.l1_hit" ) == 0 {
182+ if v , _ := m . Counter ("cache.l1_hit" ); v == 0 {
235183 t .Error ("expected l1_hit to be incremented: Set fallback warmed L1, Get should serve from it" )
236184 }
237- if m .counter ("cache.l2_get_error" ) != 0 {
238- t .Errorf ("expected l2_get_error=0 (L1 served before Valkey was contacted), got %d" , m .counter ("cache.l2_get_error" ))
185+
186+ l2Error , _ := m .Counter ("cache.l2_get_error" )
187+ if l2Error != 0 {
188+ t .Errorf ("expected l2_get_error=0 (L1 served before Valkey was contacted), got %d" , l2Error )
239189 }
240190
241191 // Confirm the entry was physically written to L1 — not just returned via some
@@ -252,7 +202,7 @@ func TestValkeyStorage_FallsBackToL1OnValkeyUnavailable(t *testing.T) {
252202func TestValkeyStorage_RecordsValkeyMiss (t * testing.T ) {
253203 // Uses a stub client — no Docker or live Valkey needed.
254204 stub := newStubValkeyClient ()
255- m := & testMetrics {}
205+ m := & metricstest. MockMetrics {}
256206 lru := NewLRUStorage (64 << 20 , nil , metrics .Default )
257207 s := NewL2Storage (stub , lru , m , 0 , valkey .IsValkeyNil )
258208
@@ -263,17 +213,19 @@ func TestValkeyStorage_RecordsValkeyMiss(t *testing.T) {
263213 if got != nil {
264214 t .Fatalf ("expected nil on miss, got %+v" , got )
265215 }
266- if m .counter ("cache.l2_miss" ) != 1 {
267- t .Errorf ("expected l2_miss=1, got %d" , m .counter ("cache.l2_miss" ))
216+ l2Miss , _ := m .Counter ("cache.l2_miss" )
217+ if l2Miss != 1 {
218+ t .Errorf ("expected l2_miss=1, got %d" , l2Miss )
268219 }
269- if m .counter ("cache.l2_get_error" ) != 0 {
270- t .Errorf ("expected l2_get_error=0 on clean miss, got %d" , m .counter ("cache.l2_get_error" ))
220+ l2Error , _ := m .Counter ("cache.l2_get_error" )
221+ if l2Error != 0 {
222+ t .Errorf ("expected l2_get_error=0 on clean miss, got %d" , l2Error )
271223 }
272224}
273225
274226func TestValkeyStorage_WriteThroughWarmsL1 (t * testing.T ) {
275227 stub := newStubValkeyClient ()
276- m := & testMetrics {}
228+ m := & metricstest. MockMetrics {}
277229 lru := NewLRUStorage (64 << 20 , nil , metrics .Default )
278230 s := NewL2Storage (stub , lru , m , 60 * time .Second , valkey .IsValkeyNil )
279231
@@ -303,15 +255,16 @@ func TestValkeyStorage_WriteThroughWarmsL1(t *testing.T) {
303255 if string (got .Payload ) != "warm" {
304256 t .Errorf ("payload: got %q, want %q" , string (got .Payload ), "warm" )
305257 }
306- if m .counter ("cache.l1_hit" ) != 1 {
307- t .Errorf ("expected l1_hit=1, got %d" , m .counter ("cache.l1_hit" ))
258+ l1hit , _ := m .Counter ("cache.l1_hit" )
259+ if l1hit != 1 {
260+ t .Errorf ("expected l1_hit=1, got %d" , l1hit )
308261 }
309262}
310263
311264func TestValkeyStorage_L1TTLBoundedToEntryTTL (t * testing.T ) {
312265 stub := newStubValkeyClient ()
313266 lru := NewLRUStorage (64 << 20 , nil , metrics .Default )
314- s := NewL2Storage (stub , lru , & testMetrics {}, 60 * time .Second , valkey .IsValkeyNil )
267+ s := NewL2Storage (stub , lru , & metricstest. MockMetrics {}, 60 * time .Second , valkey .IsValkeyNil )
315268
316269 ctx := context .Background ()
317270 key := "bounded-key"
@@ -341,7 +294,7 @@ func TestValkeyStorage_L1TTLBoundedToEntryTTL(t *testing.T) {
341294
342295func TestValkeyStorage_L1TTL_Zero_DisablesWarming (t * testing.T ) {
343296 stub := newStubValkeyClient ()
344- m := & testMetrics {}
297+ m := & metricstest. MockMetrics {}
345298 lru := NewLRUStorage (64 << 20 , nil , metrics .Default )
346299 s := NewL2Storage (stub , lru , m , 0 , valkey .IsValkeyNil )
347300
@@ -372,7 +325,7 @@ func TestValkeyStorage_L1TTL_Zero_DisablesWarming(t *testing.T) {
372325
373326func TestValkeyStorage_RecordsL2Hit (t * testing.T ) {
374327 stub := newStubValkeyClient ()
375- m := & testMetrics {}
328+ m := & metricstest. MockMetrics {}
376329 lru := NewLRUStorage (64 << 20 , nil , metrics .Default )
377330 s := NewL2Storage (stub , lru , m , 0 , valkey .IsValkeyNil )
378331
@@ -391,11 +344,13 @@ func TestValkeyStorage_RecordsL2Hit(t *testing.T) {
391344 if got == nil {
392345 t .Fatal ("expected entry from Valkey, got nil" )
393346 }
394- if m .counter ("cache.l2_hit" ) != 1 {
395- t .Errorf ("expected l2_hit=1, got %d" , m .counter ("cache.l2_hit" ))
347+ l2Hit , _ := m .Counter ("cache.l2_hit" )
348+ if l2Hit != 1 {
349+ t .Errorf ("expected l2_hit=1, got %d" , l2Hit )
396350 }
397- if m .counter ("cache.l1_hit" ) != 0 {
398- t .Errorf ("expected l1_hit=0 (write-around), got %d" , m .counter ("cache.l1_hit" ))
351+ l1Hit , _ := m .Counter ("cache.l1_hit" )
352+ if l1Hit != 0 {
353+ t .Errorf ("expected l1_hit=0 (write-around), got %d" , l1Hit )
399354 }
400355}
401356
@@ -405,32 +360,38 @@ func TestValkeyStorage_SplitFallbackCounters(t *testing.T) {
405360 // Get checks L1 first (L1-first reads) and finds the entry — incrementing l1_hit,
406361 // not l2_get_error.
407362 stub := newBrokenStubValkeyClient ()
408- m := & testMetrics {}
363+ m := & metricstest. MockMetrics {}
409364 lru := NewLRUStorage (64 << 20 , nil , metrics .Default )
410365 s := NewL2Storage (stub , lru , m , 0 , valkey .IsValkeyNil )
411366
412367 ctx := context .Background ()
413368 entry := & Entry {StatusCode : 200 , Payload : []byte ("x" ), TTL : time .Minute , CreatedAt : time .Now ()}
414369
415370 _ = s .Set (ctx , "k" , entry )
416- if m .counter ("cache.l2_set_fallback" ) != 1 {
417- t .Errorf ("expected l2_set_fallback=1, got %d" , m .counter ("cache.l2_set_fallback" ))
371+ l2SetFallback , _ := m .Counter ("cache.l2_set_fallback" )
372+ if l2SetFallback != 1 {
373+ t .Errorf ("expected l2_set_fallback=1, got %d" , l2SetFallback )
418374 }
419- if m .counter ("cache.l2_get_error" ) != 0 {
420- t .Errorf ("expected l2_get_error=0 after Set, got %d" , m .counter ("cache.l2_get_error" ))
375+ l2GetError , _ := m .Counter ("cache.l2_get_error" )
376+ if l2GetError != 0 {
377+ t .Errorf ("expected l2_get_error=0 after Set, got %d" , l2GetError )
421378 }
422379
423380 // L1-first: the entry was written to L1 by the Set fallback path, so Get returns
424381 // it from L1 without ever touching (broken) Valkey.
425382 _ , _ = s .Get (ctx , "k" )
426- if m .counter ("cache.l1_hit" ) != 1 {
427- t .Errorf ("expected l1_hit=1, got %d" , m .counter ("cache.l1_hit" ))
383+ l1Hit , _ := m .Counter ("cache.l1_hit" )
384+ if l1Hit != 1 {
385+ t .Errorf ("expected l1_hit=1, got %d" , l1Hit )
428386 }
429- if m .counter ("cache.l2_get_error" ) != 0 {
430- t .Errorf ("expected l2_get_error=0 (L1 served before Valkey check), got %d" , m .counter ("cache.l2_get_error" ))
387+
388+ l2GetError , _ = m .Counter ("cache.l2_get_error" )
389+ if l2GetError != 0 {
390+ t .Errorf ("expected l2_get_error=0 (L1 served before Valkey check), got %d" , l2GetError )
431391 }
432- if m .counter ("cache.l2_set_fallback" ) != 1 {
433- t .Errorf ("l2_set_fallback should still be 1, got %d" , m .counter ("cache.l2_set_fallback" ))
392+ l2SetFallback , _ = m .Counter ("cache.l2_set_fallback" )
393+ if l2SetFallback != 1 {
394+ t .Errorf ("l2_set_fallback should still be 1, got %d" , l2SetFallback )
434395 }
435396}
436397
@@ -439,7 +400,7 @@ func TestValkeyStorage_DeleteCleansL1EvenOnValkeyError(t *testing.T) {
439400 // regardless of the Expire error from Valkey.
440401 stub := newBrokenStubValkeyClient ()
441402 lru := NewLRUStorage (64 << 20 , nil , metrics .Default )
442- s := NewL2Storage (stub , lru , & testMetrics {}, 0 , valkey .IsValkeyNil )
403+ s := NewL2Storage (stub , lru , & metricstest. MockMetrics {}, 0 , valkey .IsValkeyNil )
443404
444405 ctx := context .Background ()
445406 entry := & Entry {StatusCode : 200 , Payload : []byte ("body" ), TTL : time .Minute , CreatedAt : time .Now ()}
@@ -461,17 +422,17 @@ func TestValkeyStorage_DeleteCleansL1EvenOnValkeyError(t *testing.T) {
461422
462423func TestL2Storage_NewL2Storage_NegativeL1TTL_ClampsToDefault (t * testing.T ) {
463424 stub := newStubValkeyClient ()
464- lru := NewLRUStorage (64 << 20 , nil , & testMetrics {})
465- s := NewL2Storage (stub , lru , & testMetrics {}, - time .Second , valkey .IsValkeyNil )
425+ lru := NewLRUStorage (64 << 20 , nil , & metricstest. MockMetrics {})
426+ s := NewL2Storage (stub , lru , & metricstest. MockMetrics {}, - time .Second , valkey .IsValkeyNil )
466427 if s .l1TTL != defaultMinTTL {
467428 t .Errorf ("expected l1TTL clamped to %v, got %v" , defaultMinTTL , s .l1TTL )
468429 }
469430}
470431
471432func TestL2Storage_Get_CorruptJSON_ReturnsError (t * testing.T ) {
472433 stub := newStubValkeyClient ()
473- lru := NewLRUStorage (64 << 20 , nil , & testMetrics {})
474- s := NewL2Storage (stub , lru , & testMetrics {}, 0 , valkey .IsValkeyNil )
434+ lru := NewLRUStorage (64 << 20 , nil , & metricstest. MockMetrics {})
435+ s := NewL2Storage (stub , lru , & metricstest. MockMetrics {}, 0 , valkey .IsValkeyNil )
475436
476437 // Write corrupt JSON directly into the stub, bypassing Set.
477438 stub .mu .Lock ()
@@ -486,7 +447,7 @@ func TestL2Storage_Get_CorruptJSON_ReturnsError(t *testing.T) {
486447
487448func TestL2Storage_Get_L2Hit_WarmsL1 (t * testing.T ) {
488449 stub := newStubValkeyClient ()
489- m := & testMetrics {}
450+ m := & metricstest. MockMetrics {}
490451 lru := NewLRUStorage (64 << 20 , nil , m )
491452 s := NewL2Storage (stub , lru , m , 60 * time .Second , valkey .IsValkeyNil )
492453
@@ -505,8 +466,9 @@ func TestL2Storage_Get_L2Hit_WarmsL1(t *testing.T) {
505466 if err != nil || got == nil {
506467 t .Fatalf ("expected L2 hit: err=%v, got=%v" , err , got )
507468 }
508- if m .counter ("cache.l2_hit" ) != 1 {
509- t .Errorf ("expected l2_hit=1, got %d" , m .counter ("cache.l2_hit" ))
469+ l2Hit , _ := m .Counter ("cache.l2_hit" )
470+ if l2Hit != 1 {
471+ t .Errorf ("expected l2_hit=1, got %d" , l2Hit )
510472 }
511473
512474 // Break L2 — second Get must come from L1 (write-through warmed it).
@@ -515,14 +477,15 @@ func TestL2Storage_Get_L2Hit_WarmsL1(t *testing.T) {
515477 if err != nil || got2 == nil {
516478 t .Fatalf ("expected L1 hit after warming: err=%v, got=%v" , err , got2 )
517479 }
518- if m .counter ("cache.l1_hit" ) != 1 {
519- t .Errorf ("expected l1_hit=1 after L1 warming, got %d" , m .counter ("cache.l1_hit" ))
480+ l1Hit , _ := m .Counter ("cache.l1_hit" )
481+ if l1Hit != 1 {
482+ t .Errorf ("expected l1_hit=1 after L1 warming, got %d" , l1Hit )
520483 }
521484}
522485
523486func TestL2Storage_Get_L2Hit_ExpiredEntry_SkipsL1Warming (t * testing.T ) {
524487 stub := newStubValkeyClient ()
525- m := & testMetrics {}
488+ m := & metricstest. MockMetrics {}
526489 lru := NewLRUStorage (64 << 20 , nil , m )
527490 s := NewL2Storage (stub , lru , m , 60 * time .Second , valkey .IsValkeyNil )
528491
@@ -552,8 +515,8 @@ func TestL2Storage_Get_L2Hit_ExpiredEntry_SkipsL1Warming(t *testing.T) {
552515
553516func TestL2Storage_Set_ZeroTTL_UsesDefaultMinTTL (t * testing.T ) {
554517 stub := newStubValkeyClient ()
555- lru := NewLRUStorage (64 << 20 , nil , & testMetrics {})
556- s := NewL2Storage (stub , lru , & testMetrics {}, 0 , valkey .IsValkeyNil )
518+ lru := NewLRUStorage (64 << 20 , nil , & metricstest. MockMetrics {})
519+ s := NewL2Storage (stub , lru , & metricstest. MockMetrics {}, 0 , valkey .IsValkeyNil )
557520
558521 ctx := context .Background ()
559522 // TTL=0, StaleIfError=0, StaleWhileRevalidate=0 → l2TTL=0 → must use defaultMinTTL.
0 commit comments