github.com/thanos-io/thanos@v0.32.5/pkg/cache/memcached_test.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package cache 5 6 import ( 7 "context" 8 "testing" 9 "time" 10 11 "github.com/go-kit/log" 12 "github.com/pkg/errors" 13 prom_testutil "github.com/prometheus/client_golang/prometheus/testutil" 14 15 "github.com/efficientgo/core/testutil" 16 ) 17 18 func TestMemcachedCache(t *testing.T) { 19 t.Parallel() 20 21 // Init some data to conveniently define test cases later one. 22 key1 := "key1" 23 key2 := "key2" 24 key3 := "key3" 25 value1 := []byte{1} 26 value2 := []byte{2} 27 value3 := []byte{3} 28 29 tests := map[string]struct { 30 setup map[string][]byte 31 mockedErr error 32 fetchKeys []string 33 expectedHits map[string][]byte 34 }{ 35 "should return no hits on empty cache": { 36 setup: nil, 37 fetchKeys: []string{key1, key2}, 38 expectedHits: map[string][]byte{}, 39 }, 40 "should return no misses on 100% hit ratio": { 41 setup: map[string][]byte{ 42 key1: value1, 43 key2: value2, 44 key3: value3, 45 }, 46 fetchKeys: []string{key1}, 47 expectedHits: map[string][]byte{ 48 key1: value1, 49 }, 50 }, 51 "should return hits and misses on partial hits": { 52 setup: map[string][]byte{ 53 key1: value1, 54 key2: value2, 55 }, 56 fetchKeys: []string{key1, key3}, 57 expectedHits: map[string][]byte{key1: value1}, 58 }, 59 "should return no hits on memcached error": { 60 setup: map[string][]byte{ 61 key1: value1, 62 key2: value2, 63 key3: value3, 64 }, 65 mockedErr: errors.New("mocked error"), 66 fetchKeys: []string{key1}, 67 expectedHits: nil, 68 }, 69 } 70 71 for testName, testData := range tests { 72 t.Run(testName, func(t *testing.T) { 73 memcached := newMockedMemcachedClient(testData.mockedErr) 74 c := NewMemcachedCache("test", log.NewNopLogger(), memcached, nil) 75 76 // Store the postings expected before running the test. 77 ctx := context.Background() 78 c.Store(testData.setup, time.Hour) 79 80 // Fetch postings from cached and assert on it. 81 hits := c.Fetch(ctx, testData.fetchKeys) 82 testutil.Equals(t, testData.expectedHits, hits) 83 84 // Assert on metrics. 85 testutil.Equals(t, float64(len(testData.fetchKeys)), prom_testutil.ToFloat64(c.requests)) 86 testutil.Equals(t, float64(len(testData.expectedHits)), prom_testutil.ToFloat64(c.hits)) 87 }) 88 } 89 } 90 91 // mockedMemcachedClient is a mocked memcached client for testing. 92 type mockedMemcachedClient struct { 93 cache map[string][]byte 94 getMultiErr error 95 } 96 97 // newMockedMemcachedClient returns a mocked memcached client. 98 func newMockedMemcachedClient(getMultiErr error) *mockedMemcachedClient { 99 return &mockedMemcachedClient{ 100 cache: map[string][]byte{}, 101 getMultiErr: getMultiErr, 102 } 103 } 104 105 func (c *mockedMemcachedClient) GetMulti(_ context.Context, keys []string) map[string][]byte { 106 if c.getMultiErr != nil { 107 return nil 108 } 109 110 hits := map[string][]byte{} 111 112 for _, key := range keys { 113 if value, ok := c.cache[key]; ok { 114 hits[key] = value 115 } 116 } 117 118 return hits 119 } 120 121 func (c *mockedMemcachedClient) SetAsync(key string, value []byte, _ time.Duration) error { 122 c.cache[key] = value 123 return nil 124 } 125 126 func (c *mockedMemcachedClient) Stop() { 127 // Nothing to do. 128 }